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 /** 485 * CTor 486 * 487 * Init heartbeat 488 */ 489 public XmtHandler() { 490 // Initialize heartbeat messgae 491 heartBeat = new Mx1Message(4, BINARY); 492 heartBeat.setElement(1, 0x10); // Short Primary message by PC 493 heartBeat.setElement(2, 11); // CommandStation IO state query 494 heartBeat.setElement(3, 0); // Must be zero 495 } 496 497 @Override 498 public void run() { 499 while (true) { // loop permanently 500 // any input? 501 try { 502 // get content; failure is a NoSuchElementException 503 log.debug("check for input"); 504 byte msg[] = null; 505 synchronized (this) { 506 msg = xmtList.removeFirst(); 507 } 508 // input - now send 509 try { 510 if (ostream != null) { 511 //if (!controller.okToSend()) log.warn("Mx1 port not ready to receive"); 512 log.debug("start write to stream"); 513 ostream.write(msg); 514 ostream.flush(); 515 if (protocol) { 516 //Tell the retry handler that a packet has been transmitted and to handle any retry. 517 synchronized (retryHandler) { 518 retryHandler.notify(); 519 } 520 } 521 522 log.debug("end write to stream"); 523 } else { 524 // no stream connected 525 log.warn("send message: no connection established"); 526 } 527 } catch (java.io.IOException e) { 528 log.warn("send message: IOException: {}", e.toString()); 529 } 530 } catch (NoSuchElementException e) { 531 //Check retry queue? 532 // message queue was empty, wait for input 533 log.debug("start wait"); 534 try { 535 synchronized (this) { 536 // Send heartbeat if idle for 2 seconds 537 wait(2000); 538 if (xmtPackets.size() == 0){ 539 log.debug("send heartbeat"); 540 sendMx1Message(heartBeat, null); 541 } 542 } 543 } catch (java.lang.InterruptedException ei) { 544 Thread.currentThread().interrupt(); // retain if needed later 545 } 546 log.debug("end wait"); 547 } 548 } 549 } 550 551 private Mx1Message heartBeat; // Store heartBeat 552 } 553 554 /** 555 * Class to handle the re-transmission of messages that have not had a Level 556 * 1 response from the command station. 557 */ 558 class RetryHandler implements Runnable { 559 560 Mx1Packetizer trafficController; 561 562 public RetryHandler(Mx1Packetizer lt) { 563 trafficController = lt; 564 } 565 566 @SuppressFBWarnings(value = "UW_UNCOND_WAIT", justification="false postive, guarded by if statement") 567 @Override 568 public void run() { 569 while (true) { // loop permanently 570 if (xmtPackets.isEmpty()) { 571 try { 572 synchronized (this) { 573 wait(); 574 } 575 } catch (java.lang.InterruptedException ei) { 576 Thread.currentThread().interrupt(); // retain if needed later 577 } 578 } else { 579 for (int key : xmtPackets.keySet()) { 580 MessageQueued mq = xmtPackets.get(key); 581 Mx1Message m = mq.getMessage(); 582 if (m.getRetry() <= 0) { 583 xmtPackets.remove(key); 584 } else if (m.getTimeStamp() + 200 < System.currentTimeMillis()) { 585 m.setRetries(m.getRetry() - 1); 586 m.setTimeStamp(System.currentTimeMillis()); 587 trafficController.notify(m, mq.getListener()); 588 synchronized (xmtHandler) { 589 log.warn("Packet not replied to so will retry"); 590 xmtList.addFirst(m.getRawPacket()); 591 xmtHandler.notify(); 592 } 593 } else { 594 //Using a linked list, so if the first packet we come too isn't 595 break; 596 } 597 } 598 //As the retry packet list is not empty, we will wait for 200ms before rechecking it. 599 if (!xmtPackets.isEmpty()) { 600 try { 601 synchronized (this) { 602 wait(200); 603 } 604 } catch (java.lang.InterruptedException ei) { 605 Thread.currentThread().interrupt(); // retain if needed later 606 } 607 } 608 609 } 610 } 611 } 612 } 613 614 static class MessageQueued { 615 616 Mx1Message msg; 617 Mx1Listener reply; 618 619 MessageQueued(Mx1Message m, Mx1Listener r) { 620 msg = m; 621 reply = r; 622 } 623 624 Mx1Message getMessage() { 625 return msg; 626 } 627 628 Mx1Listener getListener() { 629 return reply; 630 } 631 632 } 633 634 /** 635 * Invoked at startup to start the threads needed here. 636 */ 637 public void startThreads() { 638 int priority = Thread.currentThread().getPriority(); 639 log.debug("startThreads current priority = {} max available = " + Thread.MAX_PRIORITY + " default = " + Thread.NORM_PRIORITY + " min available = " + Thread.MIN_PRIORITY, priority); 640 641 // start the RetryHandler in a thread of its own simply use standard priority 642 Thread retryThread = new Thread(retryHandler, "MX1 retry handler"); 643 //rcvThread.setPriority(Thread.MAX_PRIORITY-); 644 retryThread.start(); 645 646 // make sure that the xmt priority is no lower than the current priority 647 int xmtpriority = (Thread.MAX_PRIORITY - 1 > priority ? Thread.MAX_PRIORITY - 1 : Thread.MAX_PRIORITY); 648 // start the XmtHandler in a thread of its own 649 Thread xmtThread = new Thread(xmtHandler, "MX1 transmit handler"); 650 log.debug("Xmt thread starts at priority {}", xmtpriority); 651 xmtThread.setPriority(Thread.MAX_PRIORITY - 1); 652 xmtThread.start(); 653 654 // start the RcvHandler in a thread of its own 655 Thread rcvThread = new Thread(rcvHandler, "MX1 receive handler"); 656 rcvThread.setPriority(Thread.MAX_PRIORITY); 657 rcvThread.start(); 658 } 659 660 public int get16BitCRC(Mx1Message m) { 661 int POLYNOMIAL = 0x8408; 662 int PRESET_VALUE = 0; 663 byte[] array = new byte[m.getNumDataElements()]; 664 for (int i = 0; i < m.getNumDataElements(); i++) { 665 array[i] = (byte) m.getElement(i); 666 } 667 int current_crc_value = PRESET_VALUE; 668 for (int i = 0; i < array.length; i++) { 669 current_crc_value ^= array[i] & 0xFF; 670 for (int j = 0; j < 8; j++) { 671 if ((current_crc_value & 1) != 0) { 672 current_crc_value = (current_crc_value >>> 1) ^ POLYNOMIAL; 673 } else { 674 current_crc_value = current_crc_value >>> 1; 675 } 676 } 677 } 678 return current_crc_value & 0xFFFF; 679 } 680 681 byte get8BitCRC(Mx1Message m) { 682 int checksum = 0xff; 683 684 for (int i = 0; i < m.getNumDataElements(); i++) { 685 checksum = crc8bit_table[(checksum ^ ((m.getElement(i)) & 0xff))]; 686 } 687 return (byte) (checksum & 0xff); 688 } 689 690 final int crc8bit_table[] = new int[]{ 691 0x00, 0x5e, 0xbc, 0xe2, 0x61, 0x3f, 0xdd, 0x83, 0xc2, 0x9c, 0x7e, 0x20, 0xa3, 0xfd, 0x1f, 692 0x41, 0x9d, 0xc3, 0x21, 0x7f, 0xfc, 0xa2, 0x40, 0x1e, 0x5f, 0x01, 0xe3, 0xbd, 0x3e, 0x60, 693 0x82, 0xdc, 0x23, 0x7d, 0x9f, 0xc1, 0x42, 0x1c, 0xfe, 0xa0, 0xe1, 0xbf, 0x5d, 0x03, 0x80, 694 0xde, 0x3c, 0x62, 0xbe, 0xe0, 0x02, 0x5c, 0xdf, 0x81, 0x63, 0x3d, 0x7c, 0x22, 0xc0, 0x9e, 695 0x1d, 0x43, 0xa1, 0xff, 0x46, 0x18, 0xfa, 0xa4, 0x27, 0x79, 0x9b, 0xc5, 0x84, 0xda, 0x38, 696 0x66, 0xe5, 0xbb, 0x59, 0x07, 0xdb, 0x85, 0x67, 0x39, 0xba, 0xe4, 0x06, 0x58, 0x19, 0x47, 697 0xa5, 0xfb, 0x78, 0x26, 0xc4, 0x9a, 0x65, 0x3b, 0xd9, 0x87, 0x04, 0x5a, 0xb8, 0xe6, 0xa7, 698 0xf9, 0x1b, 0x45, 0xc6, 0x98, 0x7a, 0x24, 0xf8, 0xa6, 0x44, 0x1a, 0x99, 0xc7, 0x25, 0x7b, 699 0x3a, 0x64, 0x86, 0xd8, 0x5b, 0x05, 0xe7, 0xb9, 0x8c, 0xd2, 0x30, 0x6e, 0xed, 0xb3, 0x51, 700 0x0f, 0x4e, 0x10, 0xf2, 0xac, 0x2f, 0x71, 0x93, 0xcd, 0x11, 0x4f, 0xad, 0xf3, 0x70, 0x2e, 701 0xcc, 0x92, 0xd3, 0x8d, 0x6f, 0x31, 0xb2, 0xec, 0x0e, 0x50, 0xaf, 0xf1, 0x13, 0x4d, 0xce, 702 0x90, 0x72, 0x2c, 0x6d, 0x33, 0xd1, 0x8f, 0x0c, 0x52, 0xb0, 0xee, 0x32, 0x6c, 0x8e, 0xd0, 703 0x53, 0x0d, 0xef, 0xb1, 0xf0, 0xae, 0x4c, 0x12, 0x91, 0xcf, 0x2d, 0x73, 0xca, 0x94, 0x76, 704 0x28, 0xab, 0xf5, 0x17, 0x49, 0x08, 0x56, 0xb4, 0xea, 0x69, 0x37, 0xd5, 0x8b, 0x57, 0x09, 705 0xeb, 0xb5, 0x36, 0x68, 0x8a, 0xd4, 0x95, 0xcb, 0x29, 0x77, 0xf4, 0xaa, 0x48, 0x16, 0xe9, 706 0xb7, 0x55, 0x0b, 0x88, 0xd6, 0x34, 0x6a, 0x2b, 0x75, 0x97, 0xc9, 0x4a, 0x14, 0xf6, 0xa8, 707 0x74, 0x2a, 0xc8, 0x96, 0x15, 0x4b, 0xa9, 0xf7, 0xb6, 0xe8, 0x0a, 0x54, 0xd7, 0x89, 0x6b, 0x35 708 }; 709 710 private final static Logger log = LoggerFactory.getLogger(Mx1Packetizer.class); 711}