001package jmri.jmrix.zimo; 002 003import static jmri.jmrix.zimo.Mx1Message.PROGCMD; 004 005import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 006import java.io.DataInputStream; 007import java.io.OutputStream; 008import java.util.ArrayList; 009import java.util.LinkedList; 010import java.util.NoSuchElementException; 011import java.util.concurrent.ConcurrentHashMap; 012import org.slf4j.Logger; 013import org.slf4j.LoggerFactory; 014 015/** 016 * Access to Zimo Mx1 messages via stream-based I/O. The "Mx1Interface" * side 017 * sends/receives Mx1Message objects. The connection to a Mx1PortController is 018 * via a pair of *Streams, which then carry sequences of characters for 019 * transmission. 020 * <p> 021 * Messages come to this via the main GUI thread, and are forwarded back to 022 * listeners in that same thread. Reception and transmission are handled in 023 * dedicated threads by RcvHandler and XmtHandler objects. Those are internal 024 * classes defined here. The thread priorities are: 025 * <ul> 026 * <li> RcvHandler - at highest available priority 027 * <li> XmtHandler - down one, which is assumed to be above the GUI 028 * <li> (everything else) 029 * </ul> 030 * 031 * @author Bob Jacobsen Copyright (C) 2001 032 * 033 * Adapted by Sip Bosch for use with zimo Mx-1 034 * 035 */ 036public class Mx1Packetizer extends Mx1TrafficController { 037 038 public Mx1Packetizer(Mx1CommandStation pCommandStation, boolean prot) { 039 super(pCommandStation, prot); 040 protocol = prot; 041 } 042 043 // The methods to implement the Mx1Interface 044 @Override 045 public boolean status() { 046 return (ostream != null && istream != null); 047 } 048 049 /** 050 * Synchronized list used as a transmit queue 051 */ 052 LinkedList<byte[]> xmtList = new LinkedList<>(); 053 054 ConcurrentHashMap<Integer, MessageQueued> xmtPackets = new ConcurrentHashMap<>(16, 0.9f, 1); 055 056 /** 057 * XmtHandler (a local class) object to implement the transmit thread 058 */ 059 XmtHandler xmtHandler = new XmtHandler(); 060 061 /** 062 * XmtHandler (a local class) object to implement the transmit thread 063 */ 064 RetryHandler retryHandler = new RetryHandler(this); 065 066 /** 067 * RcvHandler (a local class) object to implement the receive thread 068 */ 069 RcvHandler rcvHandler = new RcvHandler(this); 070 071 /** 072 * Forward a preformatted Mx1Message to the actual interface. 073 * 074 * End of Message is added here, then the message is converted to a byte 075 * array and queued for transmission 076 * 077 * @param m Message to send; will be updated with CRC 078 */ 079 @Override 080 public void sendMx1Message(Mx1Message m, Mx1Listener reply) { 081 byte msg[]; 082 if (protocol) { 083 processPacketForSending(m); 084 msg = m.getRawPacket(); 085 if (m.replyL1Expected()) { 086 xmtPackets.put(m.getSequenceNo(), new MessageQueued(m, reply)); 087 } 088 //notify(new Mx1Message(msg), reply); //Sends a fully formated packet to the command monitor 089 } else { 090 // set the CR code byte 091 int len = m.getNumDataElements(); 092 m.setElement(len - 1, 0x0D); // CR is last element of message 093 // notify all _other_ listeners 094 // stream to port in single write, as that's needed by serial 095 msg = new byte[len]; 096 for (int i = 0; i < len; i++) { 097 msg[i] = (byte) m.getElement(i); 098 } 099 if (log.isDebugEnabled()) { 100 log.debug("queue outgoing packet: {}", m.toString()); 101 } 102 // in an atomic operation, queue the request and wake the xmit thread 103 } 104 notifyLater(m, reply); 105 //notify(m, reply); 106 synchronized (xmtHandler) { 107 xmtList.addLast(msg); 108 xmtHandler.notify(); 109 } 110 } 111 112 byte getNextSequenceNo() { 113 lastSequence++; 114 if ((lastSequence & 0xff) == 0xff) { 115 lastSequence = 0x00; 116 } 117 //lastSequenceSent = (byte)(lastSequence&0xff); 118 return lastSequence; 119 } 120 121 void processPacketForSending(Mx1Message m) { 122 ArrayList<Byte> msgFormat = new ArrayList<>(); 123 //Add <SOH> 124 msgFormat.add((byte) SOH); 125 msgFormat.add((byte) SOH); 126 127 //m.setSequenceNo((byte)(lastSequenceSent&0xff)); 128 m.setSequenceNo(getNextSequenceNo()); 129 130 for (int i = 0; i < m.getNumDataElements(); i++) { 131 formatByteToPacket((byte) m.getElement(i), msgFormat); 132 } 133 //Add CRC 134 if (m.getLongMessage()) { 135 int crc = get16BitCRC(m); 136 formatByteToPacket((byte) ((crc >>> 8) & 0xff), msgFormat); 137 formatByteToPacket((byte) (crc & 0xff), msgFormat); 138 } else { 139 //add 8bit crc 140 int checksum = get8BitCRC(m); 141 formatByteToPacket((byte) checksum, msgFormat); 142 } 143 //Add EOT 144 msgFormat.add((byte) EOT); 145 byte msg[] = new byte[msgFormat.size()]; 146 for (int i = 0; i < msgFormat.size(); i++) { 147 msg[i] = msgFormat.get(i); 148 } 149 m.setRawPacket(msg); 150 m.setTimeStamp(System.currentTimeMillis()); 151 } 152 153 //Format any bytes that need to be masked with DLE 154 void formatByteToPacket(byte b, ArrayList<Byte> message) { 155 b = (byte) (b & 0xff); 156 157 if (b == SOH || b == EOT || b == DLE) { 158 message.add((byte) DLE); 159 message.add((byte) (b ^ 0x20)); 160 } else { 161 message.add((byte) (b & 0xff)); 162 } 163 } 164 165 // methods to connect/disconnect to a source of data in a Mx1PortController 166 private Mx1PortController controller = null; 167 168 /** 169 * Make connection to existing Mx1PortController object. 170 * 171 * @param p Port controller for connected. Save this for a later disconnect 172 * call 173 */ 174 public void connectPort(Mx1PortController p) { 175 istream = p.getInputStream(); 176 ostream = p.getOutputStream(); 177 if (controller != null) { 178 log.warn("connectPort: connect called while connected"); 179 } 180 controller = p; 181 } 182 183 /** 184 * Break connection to existing LnPortController object. Once broken, 185 * attempts to send via "message" member will fail. 186 * 187 * @param p previously connected port 188 */ 189 public void disconnectPort(Mx1PortController p) { 190 istream = null; 191 ostream = null; 192 if (controller != p) { 193 log.warn("disconnectPort: disconnect called from non-connected Mx1PortController"); 194 } 195 controller = null; 196 } 197 198// data members to hold the streams 199 DataInputStream istream = null; 200 OutputStream ostream = null; 201 202 /** 203 * Read a single byte, protecting against various timeouts, etc. 204 * <p> 205 * When a port is set to have a receive timeout (via the 206 * enableReceiveTimeout() method), some will return zero bytes or an 207 * EOFException at the end of the timeout. In that case, the read should be 208 * repeated to get the next real character. 209 * 210 * @param istream the input stream 211 * @return the first byte in the stream 212 * @throws java.io.IOException if unable to read istream 213 */ 214 protected byte readByteProtected(DataInputStream istream) throws java.io.IOException { 215 while (true) { // loop will repeat until character found 216 int nchars; 217 nchars = istream.read(rcvBuffer, 0, 1); 218 if (nchars > 0) { 219 return rcvBuffer[0]; 220 } 221 } 222 } 223 224 private byte[] rcvBuffer = new byte[1]; 225 226 byte lastSequence = 0x00; 227 //byte lastSequenceSent = 0x00; 228 229 final static int SOH = 0x01; 230 final static int EOT = 0x17; 231 final static int DLE = 0x10; 232 233 /** 234 * Handle incoming characters. This is a permanent loop, looking for input 235 * messages in character form on the stream connected to the 236 * Mx1PortController via <code>connectPort</code>. Terminates with the input 237 * stream breaking out of the try block. 238 */ 239 class RcvHandler implements Runnable { 240 241 /** 242 * Remember the Packetizer object 243 */ 244 Mx1Packetizer trafficController; 245 246 public RcvHandler(Mx1Packetizer lt) { 247 trafficController = lt; 248 } 249 250 @Override 251 public void run() { 252 int opCode; 253 if (protocol) { 254 ArrayList<Integer> message; 255 while (true) { 256 try { 257 int firstByte = readByteProtected(istream) & 0xFF; 258 int secondByte = readByteProtected(istream) & 0xFF; 259 // start by looking for command 0x01, 0x01 260 while (firstByte != SOH && secondByte != SOH) { 261 log.debug("Skipping: {} {}", Integer.toHexString(firstByte), Integer.toHexString(secondByte)); 262 firstByte = secondByte; 263 secondByte = readByteProtected(istream) & 0xFF; 264 } 265 message = new ArrayList<>(); 266 while (true) { 267 int b = readByteProtected(istream) & 0xFF; 268 if (b == EOT) //End of Frame 269 { 270 break; 271 } 272 if (b == DLE) { //0x10 is a ident to inform that the next byte will need to be xor'd with 0x20 to get correct value. 273 b = readByteProtected(istream) & 0xFF; 274 b = b ^ 0x20; 275 } 276 message.add(b); 277 log.debug("char is: {}", Integer.toHexString(b)); 278 } 279 //lastSequence = ((byte)(message.get(0)&0xff)); 280 //Remove the message from the list 281 synchronized (rcvHandler) { 282 xmtPackets.remove(message.get(3)); 283 } 284 Mx1Message msg; 285 //boolean error = false; 286 if ((message.get(1) & 0x80) == 0x80) { //Long Packet 287 //Remove crc element 288 msg = new Mx1Message(message.size() - 2, Mx1Packetizer.BINARY); 289 for (int i = 0; i < message.size() - 2; i++) { 290 msg.setElement(i, message.get(i)); 291 } 292 } else { //Short packet 293 msg = new Mx1Message(message.size() - 1, Mx1Packetizer.BINARY); 294 for (int i = 0; i < message.size() - 1; i++) { 295 msg.setElement(i, message.get(i)); 296 } 297 if (message.get(message.size() - 1) != (get8BitCRC(msg) & 0xff)) { 298 log.error("Message with invalid CRC received Expecting:{} found:{}", get8BitCRC(msg) & 0xff, message.get(message.size() - 1)); 299 msg.setCRCError(); 300 } else { 301 xmtPackets.remove(message.get(3)); 302 } 303 } 304 isAckReplyRequired(msg); 305 final Mx1Message thisMsg = msg; 306 final Mx1Packetizer thisTc = trafficController; 307 // return a notification via the queue to ensure end 308 Runnable r = new Runnable() { 309 Mx1Message msgForLater = thisMsg; 310 Mx1Packetizer myTc = thisTc; 311 312 @Override 313 public void run() { 314 myTc.notify(msgForLater, null); 315 } 316 }; 317 log.debug("schedule notify of incoming packet"); 318 javax.swing.SwingUtilities.invokeLater(r); 319 320 } catch (java.io.IOException e) { 321 // fired when write-end of HexFile reaches end 322 log.debug("IOException, should only happen with HexFIle", e); 323 disconnectPort(controller); 324 return; 325 } catch (RuntimeException e) { 326 log.warn("run: unexpected exception:", e); 327 } 328 } 329 } else { 330 //Original version 331 while (true) { // loop permanently, program close will exit 332 try { 333 // start by looking for command 334 opCode = istream.readByte() & 0xFF; 335 // Create output message 336 log.debug("RcvHandler: Start message with opcode: {}", Integer.toHexString(opCode)); 337 int len = 1; 338 Mx1Message msgn = new Mx1Message(15); 339 msgn.setElement(0, opCode); 340 // message exists, now fill it 341 for (int i = 1; i < 15; i++) { 342 int b = istream.readByte() & 0xFF; 343 len = len + 1; 344 //if end of message 345 if (b == 0x0D || b == 0x0A) { 346 msgn.setElement(i, b); 347 break; 348 } 349 msgn.setElement(i, b); 350 } 351 //transfer to array with now known size 352 Mx1Message msg = new Mx1Message(len); 353 for (int i = 0; i < len; i++) { 354 msg.setElement(i, msgn.getElement(i) & 0xFF); 355 } 356 // message is complete, dispatch it !! 357 { 358 final Mx1Message thisMsg = msg; 359 final Mx1Packetizer thisTc = trafficController; 360 // return a notification via the queue to ensure end 361 Runnable r = new Runnable() { 362 Mx1Message msgForLater = thisMsg; 363 Mx1Packetizer myTc = thisTc; 364 365 @Override 366 public void run() { 367 myTc.notify(msgForLater, null); 368 } 369 }; 370 log.debug("schedule notify of incoming packet"); 371 javax.swing.SwingUtilities.invokeLater(r); 372 } 373 } catch (java.io.EOFException e) { 374 // posted from idle port when enableReceiveTimeout used 375 log.debug("EOFException, is serial I/O using timeouts?"); 376 } catch (java.io.IOException e) { 377 // fired when write-end of HexFile reaches end 378 log.debug("IOException, should only happen with HexFile", e); 379 disconnectPort(controller); 380 return; 381 } catch (RuntimeException e) { 382 log.warn("run: unexpected exception", e); 383 } 384 } // end of permanent loop 385 } 386 } 387 } 388 389 void notifyLater(Mx1Message m, Mx1Listener reply) { 390 final Mx1Message thisMsg = m; 391 final Mx1Packetizer thisTc = this; 392 final Mx1Listener thisLst = reply; 393 Runnable r = new Runnable() { 394 Mx1Message msgForLater = thisMsg; 395 Mx1Packetizer myTc = thisTc; 396 Mx1Listener myListener = thisLst; 397 398 @Override 399 public void run() { 400 myTc.notify(msgForLater, myListener); 401 } 402 }; 403 log.debug("schedule notify of incoming packet"); 404 javax.swing.SwingUtilities.invokeLater(r); 405 } 406 407 void isAckReplyRequired(Mx1Message m) { 408 if (m.isCRCError()) { 409 //Send a NAK message 410 Mx1Message nack = new Mx1Message(3, BINARY); 411 //ack.setElement(0, getNextSequenceNo()&0xff); 412 nack.setElement(1, 0x10); 413 nack.setElement(2, 0x00); 414 //ack.setElement(2, 3); 415 processPacketForSending(nack); 416 byte msg[] = nack.getRawPacket(); 417 notify(nack, null); 418 //notifyLater(ack, null); 419 synchronized (xmtHandler) { 420 xmtList.addFirst(msg); 421 xmtHandler.notify(); 422 } 423 return; 424 } 425 if ((m.getElement(1) & 0x80) == 0x80) { //Long Packet 426 427 } else { //Short Packet 428 if (((m.getElement(1) & 0x40) == 0x40) || ((m.getElement(1) & 0x60) == 0x60)) { 429 //Message is a reply/Ack so no need to acknowledge 430 return; 431 } 432 if ((m.getElement(2) & PROGCMD) == PROGCMD) { 433 //log.info("Send L1 reply"); 434 l1AckPacket(m); 435 } else if ((m.getElement(1) & 0x20) == 0x20) {//Level 2 Reply, will need to send back ack L2 436 //log.info("Send L2 reply"); 437 l2AckPacket(m); 438 } 439 440 } 441 } 442 443 void l1AckPacket(Mx1Message m) { 444 Mx1Message ack = new Mx1Message(5, BINARY); 445 //ack.setElement(0, getNextSequenceNo()&0xff); 446 ack.setElement(1, 0x50); 447 ack.setElement(2, m.getElement(2) & 0xff); 448 //ack.setElement(2, 3); 449 ack.setElement(3, m.getElement(0) & 0xff); 450 processPacketForSending(ack); 451 byte msg[] = ack.getRawPacket(); 452 notify(ack, null); 453 //notifyLater(ack, null); 454 synchronized (xmtHandler) { 455 xmtList.addFirst(msg); 456 xmtHandler.notify(); 457 } 458 } 459 460 void l2AckPacket(Mx1Message m) { 461 Mx1Message ack = new Mx1Message(5, BINARY); 462 //ack.setElement(0, getNextSequenceNo()&0xff); 463 ack.setElement(1, 0x70);//was b0 464 ack.setElement(2, m.getElement(2) & 0xff); 465 //ack.setElement(2, 3); 466 ack.setElement(3, m.getElement(0) & 0xff); 467 processPacketForSending(ack); 468 byte msg[] = ack.getRawPacket(); 469 notify(ack, null); 470 //notifyLater(ack, null); 471 synchronized (xmtHandler) { 472 xmtList.addFirst(msg); 473 xmtHandler.notify(); 474 } 475 } 476 477 /** 478 * Captive class to handle transmission 479 */ 480 @SuppressFBWarnings(value = "UW_UNCOND_WAIT", 481 justification = "while loop controls access") 482 class XmtHandler implements Runnable { 483 484 @Override 485 public void run() { 486 while (true) { // loop permanently 487 // any input? 488 try { 489 // get content; failure is a NoSuchElementException 490 log.debug("check for input"); 491 byte msg[] = null; 492 synchronized (this) { 493 msg = xmtList.removeFirst(); 494 } 495 // input - now send 496 try { 497 if (ostream != null) { 498 //if (!controller.okToSend()) log.warn("Mx1 port not ready to receive"); 499 log.debug("start write to stream"); 500 ostream.write(msg); 501 ostream.flush(); 502 if (protocol) { 503 //Tell the retry handler that a packet has been transmitted and to handle any retry. 504 synchronized (retryHandler) { 505 retryHandler.notify(); 506 } 507 } 508 509 log.debug("end write to stream"); 510 } else { 511 // no stream connected 512 log.warn("send message: no connection established"); 513 } 514 } catch (java.io.IOException e) { 515 log.warn("send message: IOException: {}", e.toString()); 516 } 517 } catch (NoSuchElementException e) { 518 //Check retry queue? 519 // message queue was empty, wait for input 520 log.debug("start wait"); 521 try { 522 synchronized (this) { 523 wait(); 524 } 525 } catch (java.lang.InterruptedException ei) { 526 Thread.currentThread().interrupt(); // retain if needed later 527 } 528 log.debug("end wait"); 529 } 530 } 531 } 532 } 533 534 /** 535 * Class to handle the re-transmission of messages that have not had a Level 536 * 1 response from the command station. 537 */ 538 class RetryHandler implements Runnable { 539 540 Mx1Packetizer trafficController; 541 542 public RetryHandler(Mx1Packetizer lt) { 543 trafficController = lt; 544 } 545 546 @SuppressFBWarnings(value = "UW_UNCOND_WAIT", justification="false postive, guarded by if statement") 547 @Override 548 public void run() { 549 while (true) { // loop permanently 550 if (xmtPackets.isEmpty()) { 551 try { 552 synchronized (this) { 553 wait(); 554 } 555 } catch (java.lang.InterruptedException ei) { 556 Thread.currentThread().interrupt(); // retain if needed later 557 } 558 } else { 559 for (int key : xmtPackets.keySet()) { 560 MessageQueued mq = xmtPackets.get(key); 561 Mx1Message m = mq.getMessage(); 562 if (m.getRetry() <= 0) { 563 xmtPackets.remove(key); 564 } else if (m.getTimeStamp() + 200 < System.currentTimeMillis()) { 565 m.setRetries(m.getRetry() - 1); 566 m.setTimeStamp(System.currentTimeMillis()); 567 trafficController.notify(m, mq.getListener()); 568 synchronized (xmtHandler) { 569 log.warn("Packet not replied to so will retry"); 570 xmtList.addFirst(m.getRawPacket()); 571 xmtHandler.notify(); 572 } 573 } else { 574 //Using a linked list, so if the first packet we come too isn't 575 break; 576 } 577 } 578 //As the retry packet list is not empty, we will wait for 200ms before rechecking it. 579 if (!xmtPackets.isEmpty()) { 580 try { 581 synchronized (this) { 582 wait(200); 583 } 584 } catch (java.lang.InterruptedException ei) { 585 Thread.currentThread().interrupt(); // retain if needed later 586 } 587 } 588 589 } 590 } 591 } 592 } 593 594 static class MessageQueued { 595 596 Mx1Message msg; 597 Mx1Listener reply; 598 599 MessageQueued(Mx1Message m, Mx1Listener r) { 600 msg = m; 601 reply = r; 602 } 603 604 Mx1Message getMessage() { 605 return msg; 606 } 607 608 Mx1Listener getListener() { 609 return reply; 610 } 611 612 } 613 614 /** 615 * Invoked at startup to start the threads needed here. 616 */ 617 public void startThreads() { 618 int priority = Thread.currentThread().getPriority(); 619 log.debug("startThreads current priority = {} max available = " + Thread.MAX_PRIORITY + " default = " + Thread.NORM_PRIORITY + " min available = " + Thread.MIN_PRIORITY, priority); 620 621 // start the RetryHandler in a thread of its own simply use standard priority 622 Thread retryThread = new Thread(retryHandler, "MX1 retry handler"); 623 //rcvThread.setPriority(Thread.MAX_PRIORITY-); 624 retryThread.start(); 625 626 // make sure that the xmt priority is no lower than the current priority 627 int xmtpriority = (Thread.MAX_PRIORITY - 1 > priority ? Thread.MAX_PRIORITY - 1 : Thread.MAX_PRIORITY); 628 // start the XmtHandler in a thread of its own 629 Thread xmtThread = new Thread(xmtHandler, "MX1 transmit handler"); 630 log.debug("Xmt thread starts at priority {}", xmtpriority); 631 xmtThread.setPriority(Thread.MAX_PRIORITY - 1); 632 xmtThread.start(); 633 634 // start the RcvHandler in a thread of its own 635 Thread rcvThread = new Thread(rcvHandler, "MX1 receive handler"); 636 rcvThread.setPriority(Thread.MAX_PRIORITY); 637 rcvThread.start(); 638 } 639 640 public int get16BitCRC(Mx1Message m) { 641 int POLYNOMIAL = 0x8408; 642 int PRESET_VALUE = 0; 643 byte[] array = new byte[m.getNumDataElements()]; 644 for (int i = 0; i < m.getNumDataElements(); i++) { 645 array[i] = (byte) m.getElement(i); 646 } 647 int current_crc_value = PRESET_VALUE; 648 for (int i = 0; i < array.length; i++) { 649 current_crc_value ^= array[i] & 0xFF; 650 for (int j = 0; j < 8; j++) { 651 if ((current_crc_value & 1) != 0) { 652 current_crc_value = (current_crc_value >>> 1) ^ POLYNOMIAL; 653 } else { 654 current_crc_value = current_crc_value >>> 1; 655 } 656 } 657 } 658 return current_crc_value & 0xFFFF; 659 } 660 661 byte get8BitCRC(Mx1Message m) { 662 int checksum = 0xff; 663 664 for (int i = 0; i < m.getNumDataElements(); i++) { 665 checksum = crc8bit_table[(checksum ^ ((m.getElement(i)) & 0xff))]; 666 } 667 return (byte) (checksum & 0xff); 668 } 669 670 final int crc8bit_table[] = new int[]{ 671 0x00, 0x5e, 0xbc, 0xe2, 0x61, 0x3f, 0xdd, 0x83, 0xc2, 0x9c, 0x7e, 0x20, 0xa3, 0xfd, 0x1f, 672 0x41, 0x9d, 0xc3, 0x21, 0x7f, 0xfc, 0xa2, 0x40, 0x1e, 0x5f, 0x01, 0xe3, 0xbd, 0x3e, 0x60, 673 0x82, 0xdc, 0x23, 0x7d, 0x9f, 0xc1, 0x42, 0x1c, 0xfe, 0xa0, 0xe1, 0xbf, 0x5d, 0x03, 0x80, 674 0xde, 0x3c, 0x62, 0xbe, 0xe0, 0x02, 0x5c, 0xdf, 0x81, 0x63, 0x3d, 0x7c, 0x22, 0xc0, 0x9e, 675 0x1d, 0x43, 0xa1, 0xff, 0x46, 0x18, 0xfa, 0xa4, 0x27, 0x79, 0x9b, 0xc5, 0x84, 0xda, 0x38, 676 0x66, 0xe5, 0xbb, 0x59, 0x07, 0xdb, 0x85, 0x67, 0x39, 0xba, 0xe4, 0x06, 0x58, 0x19, 0x47, 677 0xa5, 0xfb, 0x78, 0x26, 0xc4, 0x9a, 0x65, 0x3b, 0xd9, 0x87, 0x04, 0x5a, 0xb8, 0xe6, 0xa7, 678 0xf9, 0x1b, 0x45, 0xc6, 0x98, 0x7a, 0x24, 0xf8, 0xa6, 0x44, 0x1a, 0x99, 0xc7, 0x25, 0x7b, 679 0x3a, 0x64, 0x86, 0xd8, 0x5b, 0x05, 0xe7, 0xb9, 0x8c, 0xd2, 0x30, 0x6e, 0xed, 0xb3, 0x51, 680 0x0f, 0x4e, 0x10, 0xf2, 0xac, 0x2f, 0x71, 0x93, 0xcd, 0x11, 0x4f, 0xad, 0xf3, 0x70, 0x2e, 681 0xcc, 0x92, 0xd3, 0x8d, 0x6f, 0x31, 0xb2, 0xec, 0x0e, 0x50, 0xaf, 0xf1, 0x13, 0x4d, 0xce, 682 0x90, 0x72, 0x2c, 0x6d, 0x33, 0xd1, 0x8f, 0x0c, 0x52, 0xb0, 0xee, 0x32, 0x6c, 0x8e, 0xd0, 683 0x53, 0x0d, 0xef, 0xb1, 0xf0, 0xae, 0x4c, 0x12, 0x91, 0xcf, 0x2d, 0x73, 0xca, 0x94, 0x76, 684 0x28, 0xab, 0xf5, 0x17, 0x49, 0x08, 0x56, 0xb4, 0xea, 0x69, 0x37, 0xd5, 0x8b, 0x57, 0x09, 685 0xeb, 0xb5, 0x36, 0x68, 0x8a, 0xd4, 0x95, 0xcb, 0x29, 0x77, 0xf4, 0xaa, 0x48, 0x16, 0xe9, 686 0xb7, 0x55, 0x0b, 0x88, 0xd6, 0x34, 0x6a, 0x2b, 0x75, 0x97, 0xc9, 0x4a, 0x14, 0xf6, 0xa8, 687 0x74, 0x2a, 0xc8, 0x96, 0x15, 0x4b, 0xa9, 0xf7, 0xb6, 0xe8, 0x0a, 0x54, 0xd7, 0x89, 0x6b, 0x35 688 }; 689 690 private final static Logger log = LoggerFactory.getLogger(Mx1Packetizer.class); 691}