001package com.pi4j.io.serial.impl; 002 003/* 004 * #%L 005 * ********************************************************************** 006 * ORGANIZATION : Pi4J 007 * PROJECT : Pi4J :: Java Library (Core) 008 * FILENAME : SerialImpl.java 009 * 010 * This file is part of the Pi4J project. More information about 011 * this project can be found here: https://www.pi4j.com/ 012 * ********************************************************************** 013 * %% 014 * Copyright (C) 2012 - 2019 Pi4J 015 * %% 016 * This program is free software: you can redistribute it and/or modify 017 * it under the terms of the GNU Lesser General Public License as 018 * published by the Free Software Foundation, either version 3 of the 019 * License, or (at your option) any later version. 020 * 021 * This program is distributed in the hope that it will be useful, 022 * but WITHOUT ANY WARRANTY; without even the implied warranty of 023 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 024 * GNU General Lesser Public License for more details. 025 * 026 * You should have received a copy of the GNU General Lesser Public 027 * License along with this program. If not, see 028 * <http://www.gnu.org/licenses/lgpl-3.0.html>. 029 * #L% 030 */ 031 032 033import com.pi4j.io.serial.*; 034import com.pi4j.io.serial.tasks.SerialDataEventDispatchTaskImpl; 035import com.pi4j.jni.SerialInterrupt; 036import com.pi4j.jni.SerialInterruptEvent; 037import com.pi4j.jni.SerialInterruptListener; 038 039import java.io.IOException; 040import java.io.InputStream; 041import java.io.OutputStream; 042import java.util.Collections; 043import java.util.concurrent.CopyOnWriteArrayList; 044import java.util.concurrent.ExecutorService; 045 046/** 047 * <p> This implementation class implements the 'Serial' interface using the WiringPi Serial library.</p> 048 * 049 * <p> 050 * Before using the Pi4J library, you need to ensure that the Java VM in configured with access to 051 * the following system libraries: 052 * <ul> 053 * <li>pi4j</li> 054 * <li>wiringPi</li> 055 * </ul> 056 * <blockquote> This library depends on the wiringPi native system library.</br> (developed by 057 * Gordon Henderson @ <a href="http://wiringpi.com/">http://wiringpi.com/</a>) 058 * </blockquote> 059 * </p> 060 * 061 * @see com.pi4j.io.serial.Serial 062 * @see com.pi4j.io.serial.SerialDataEvent 063 * @see com.pi4j.io.serial.SerialDataEventListener 064 * @see com.pi4j.io.serial.SerialFactory 065 * 066 * @see <a href="https://www.pi4j.com/">https://www.pi4j.com/</a> 067 * @author Robert Savage (<a 068 * href="http://www.savagehomeautomation.com">http://www.savagehomeautomation.com</a>) 069 */ 070public class SerialImpl extends AbstractSerialDataReaderWriter implements Serial { 071 072 protected int fileDescriptor = -1; 073 protected final CopyOnWriteArrayList<SerialDataEventListener> listeners; 074 protected final ExecutorService executor; 075 protected final SerialByteBuffer receiveBuffer; 076 protected boolean bufferingDataReceived = true; 077 078 /** 079 * default constructor 080 */ 081 public SerialImpl(){ 082 listeners = new CopyOnWriteArrayList<>(); 083 executor = SerialFactory.getExecutorServiceFactory().newSingleThreadExecutorService(); 084 receiveBuffer = new SerialByteBuffer(); 085 086 // register shutdown callback hook class 087 Runtime.getRuntime().addShutdownHook(new ShutdownHook()); 088 } 089 090 /** 091 * This class is used to perform any configured shutdown actions 092 * for the serial impl 093 * 094 * @author Robert Savage 095 * 096 */ 097 private class ShutdownHook extends Thread { 098 public void run() { 099 100 // close serial port 101 if(isOpen()){ 102 try { 103 close(); 104 } catch (IOException e) { 105 e.printStackTrace(); 106 } 107 } 108 109 // remove serial port listener 110 SerialInterrupt.removeListener(fileDescriptor); 111 112 // perform shutdown of any monitoring threads 113 SerialFactory.shutdown(); 114 } 115 } 116 117 /** 118 * <p> 119 * This opens and initializes the serial port/device and sets the communication parameters. 120 * It sets the port into raw mode (character at a time and no translations). 121 * </p> 122 * 123 * <p> 124 * (ATTENTION: the 'device' argument can only be a maximum of 128 characters.) 125 * </p> 126 * 127 * @see #DEFAULT_COM_PORT 128 * 129 * @param device 130 * The device address of the serial port to access. You can use constant 131 * 'DEFAULT_COM_PORT' if you wish to access the default serial port provided via the 132 * GPIO header. 133 * @param baud 134 * The baud rate to use with the serial port. (Custom baud rate are not supported) 135 * @param dataBits 136 * The data bits to use for serial communication. (5,6,7,8) 137 * @param parity 138 * The parity setting to use for serial communication. (None, Event, Odd, Mark, Space) 139 * @param stopBits 140 * The stop bits to use for serial communication. (1,2) 141 * @param flowControl 142 * The flow control option to use for serial communication. (none, hardware, software) 143 * 144 * @throws IOException thrown on any error. 145 */ 146 @Override 147 public void open(String device, int baud, int dataBits, int parity, int stopBits, int flowControl) 148 throws IOException{ 149 150 // open serial port 151 fileDescriptor = com.pi4j.jni.Serial.open(device, baud, dataBits, parity, stopBits, flowControl); 152 153 // read in initial buffered data (if any) into the receive buffer 154 int available = com.pi4j.jni.Serial.available(fileDescriptor); 155 156 if(available > 0) { 157 byte[] initial_data = com.pi4j.jni.Serial.read(fileDescriptor, available); 158 if (initial_data.length > 0) { 159 try { 160 // write data to the receive buffer 161 receiveBuffer.write(initial_data); 162 } 163 catch (IOException e) { 164 e.printStackTrace(); 165 } 166 } 167 } 168 169 // create a serial data listener event for data receive events from the serial device 170 SerialInterrupt.addListener(fileDescriptor, new SerialInterruptListener() { 171 @Override 172 public void onDataReceive(SerialInterruptEvent event) { 173 174 // ignore any event triggers that are missing data 175 if(event.getLength() <= 0) return; 176 177 try { 178 SerialDataEvent sde = null; 179 180 if(isBufferingDataReceived()) { 181 // stuff event data payload into the receive buffer 182 receiveBuffer.write(event.getData()); 183 184 //System.out.println("BUFFER SIZE : " + receiveBuffer.capacity()); 185 //System.out.println("BUFFER LEFT : " + receiveBuffer.remaining()); 186 //System.out.println("BUFFER AVAIL: " + receiveBuffer.available()); 187 188 // create the serial data event; since we are buffering data 189 // it will be located in the receive buffer 190 sde = new SerialDataEvent(SerialImpl.this); 191 } 192 else{ 193 // create the serial data event; since we are NOT buffering data 194 // we will pass the specific data payload directly into the event 195 sde = new SerialDataEvent(SerialImpl.this, event.getData()); 196 } 197 198 // add a new serial data event notification to the thread pool for *immediate* execution 199 // we notify the event listeners on a separate thread to prevent blocking the native monitoring thread 200 executor.execute(new SerialDataEventDispatchTaskImpl(sde, listeners)); 201 } 202 catch (IOException e) { 203 e.printStackTrace(); 204 } 205 } 206 }); 207 208 // ensure file descriptor is valid 209 if (fileDescriptor == -1) { 210 throw new IOException("Cannot open serial port"); 211 } 212 } 213 214 /** 215 * <p> 216 * This opens and initializes the serial port/device and sets the communication parameters. 217 * It sets the port into raw mode (character at a time and no translations). 218 * 219 * This method will use the following default serial configuration parameters: 220 * - DATA BITS = 8 221 * - PARITY = NONE 222 * - STOP BITS = 1 223 * - FLOW CONTROL = NONE 224 * 225 * </p> 226 * 227 * <p> 228 * (ATTENTION: the 'device' argument can only be a maximum of 128 characters.) 229 * </p> 230 * 231 * @see #DEFAULT_COM_PORT 232 * 233 * @param device 234 * The device address of the serial port to access. You can use constant 235 * 'DEFAULT_COM_PORT' if you wish to access the default serial port provided via the 236 * GPIO header. 237 * @param baud 238 * The baud rate to use with the serial port. 239 * 240 * @throws IOException thrown on any error. 241 */ 242 @Override 243 public void open(String device, int baud) throws IOException{ 244 // open the serial port with config settings of "8N1" and no flow control 245 open(device, 246 baud, 247 com.pi4j.jni.Serial.DATA_BITS_8, 248 com.pi4j.jni.Serial.PARITY_NONE, 249 com.pi4j.jni.Serial.STOP_BITS_1, 250 com.pi4j.jni.Serial.FLOW_CONTROL_NONE); 251 } 252 253 /** 254 * <p> 255 * This opens and initializes the serial port/device and sets the communication parameters. 256 * It sets the port into raw mode (character at a time and no translations). 257 * </p> 258 * 259 * <p> 260 * (ATTENTION: the 'device' argument can only be a maximum of 128 characters.) 261 * </p> 262 * 263 * @see #DEFAULT_COM_PORT 264 * 265 * @param device 266 * The device address of the serial port to access. You can use constant 267 * 'DEFAULT_COM_PORT' if you wish to access the default serial port provided via the 268 * GPIO header. 269 * @param baud 270 * The baud rate to use with the serial port. 271 * @param dataBits 272 * The data bits to use for serial communication. (5,6,7,8) 273 * @param parity 274 * The parity setting to use for serial communication. (None, Event, Odd, Mark, Space) 275 * @param stopBits 276 * The stop bits to use for serial communication. (1,2) 277 * @param flowControl 278 * The flow control option to use for serial communication. (none, hardware, software) 279 * 280 * @throws IOException thrown on any error. 281 */ 282 @Override 283 public void open(String device, Baud baud, DataBits dataBits, Parity parity, StopBits stopBits, 284 FlowControl flowControl) throws IOException{ 285 // open the serial port with NO ECHO and NO (forced) BUFFER FLUSH 286 open(device, baud.getValue(), dataBits.getValue(), parity.getIndex(), 287 stopBits.getValue(), flowControl.getIndex()); 288 } 289 290 /** 291 * <p> 292 * This opens and initializes the serial port/device and sets the communication parameters. 293 * It sets the port into raw mode (character at a time and no translations). 294 * </p> 295 * 296 * <p> 297 * (ATTENTION: the 'device' argument can only be a maximum of 128 characters.) 298 * </p> 299 * 300 * @see #DEFAULT_COM_PORT 301 * 302 * @param serialConfig 303 * A serial configuration object that contains the device, baud rate, data bits, parity, 304 * stop bits, and flow control settings. 305 * 306 * @throws IOException thrown on any error. 307 */ 308 @Override 309 public void open(SerialConfig serialConfig) throws IOException{ 310 // open the serial port with config settings 311 open(serialConfig.device(), 312 serialConfig.baud().getValue(), 313 serialConfig.dataBits().getValue(), 314 serialConfig.parity().getIndex(), 315 serialConfig.stopBits().getValue(), 316 serialConfig.flowControl().getIndex()); 317 } 318 319 /** 320 * This method is called to determine if the serial port is already open. 321 * 322 * @see #open(String, int) 323 * @return a value of 'true' is returned if the serial port is already open. 324 */ 325 @Override 326 public boolean isOpen() { 327 return (fileDescriptor >= 0); 328 } 329 330 /** 331 * This method is called to determine if the serial port is already closed. 332 * 333 * @see #open(String, int) 334 * @return a value of 'true' is returned if the serial port is already in the closed state. 335 */ 336 @Override 337 public boolean isClosed(){ 338 return !(isOpen()); 339 } 340 341 342 /** 343 * This method is called to close a currently open open serial port. 344 * 345 * @throws IllegalStateException thrown if the serial port is not already open. 346 * @throws IOException thrown on any error. 347 */ 348 @Override 349 public void close() throws IllegalStateException, IOException { 350 351 // validate state 352 if (isClosed()) 353 throw new IllegalStateException("Serial connection is not open; cannot 'close()'."); 354 355 // remove serial port listener 356 SerialInterrupt.removeListener(fileDescriptor); 357 358 // close serial port now 359 com.pi4j.jni.Serial.close(fileDescriptor); 360 361 // reset file descriptor 362 fileDescriptor = -1; 363 } 364 365 366 /** 367 * <p> 368 * Forces the transmission of any remaining data in the serial port transmit buffer. 369 * Please note that this does not force the transmission of data, it discards it! 370 * </p> 371 * 372 * @throws IllegalStateException thrown if the serial port is not already open. 373 * @throws IOException thrown on any error. 374 */ 375 @Override 376 public void flush() throws IllegalStateException, IOException{ 377 // validate state 378 if (isClosed()) 379 throw new IllegalStateException("Serial connection is not open; cannot 'flush()'."); 380 381 // flush data to serial port immediately 382 com.pi4j.jni.Serial.flush(fileDescriptor); 383 } 384 385 /** 386 * <p> 387 * Discards any data in the serial receive (input) buffer. 388 * Please note that this does not force the transmission of data, it discards it! 389 * </p> 390 * 391 * @throws IllegalStateException thrown if the serial port is not already open. 392 * @throws IOException thrown on any error. 393 */ 394 @Override 395 public void discardInput() throws IllegalStateException, IOException{ 396 // validate state 397 if (isClosed()) 398 throw new IllegalStateException("Serial connection is not open; cannot 'discardInput()'."); 399 400 // flush data to serial port immediately 401 com.pi4j.jni.Serial.discardInput(fileDescriptor); 402 } 403 404 /** 405 * <p> 406 * Discards any data in the serial transmit (output) buffer. 407 * Please note that this does not force the transmission of data, it discards it! 408 * </p> 409 * 410 * @throws IllegalStateException thrown if the serial port is not already open. 411 * @throws IOException thrown on any error. 412 */ 413 @Override 414 public void discardOutput() throws IllegalStateException, IOException{ 415 // validate state 416 if (isClosed()) 417 throw new IllegalStateException("Serial connection is not open; cannot 'discardOutput()'."); 418 419 // flush data to serial port immediately 420 com.pi4j.jni.Serial.discardOutput(fileDescriptor); 421 } 422 423 /** 424 * <p> 425 * Discards any data in both the serial receive and transmit buffers. 426 * Please note that this does not force the transmission of data, it discards it! 427 * </p> 428 * 429 * @throws IllegalStateException thrown if the serial port is not already open. 430 * @throws IOException thrown on any error. 431 */ 432 @Override 433 public void discardAll() throws IllegalStateException, IOException{ 434 // validate state 435 if (isClosed()) 436 throw new IllegalStateException("Serial connection is not open; cannot 'discardAll()'."); 437 438 // flush data to serial port immediately 439 com.pi4j.jni.Serial.discardAll(fileDescriptor); 440 } 441 442 /** 443 * <p> 444 * Send a BREAK signal to connected device. 445 * </p> 446 * 447 * @param duration 448 * The length of time (milliseconds) to send the BREAK signal 449 * @throws IllegalStateException thrown if the serial port is not already open. 450 * @throws IOException thrown on any error. 451 */ 452 @Override 453 public void sendBreak(int duration) throws IllegalStateException, IOException{ 454 // validate state 455 if (isClosed()) 456 throw new IllegalStateException("Serial connection is not open; cannot 'sendBreak()'."); 457 458 // send BREAK signal to serial port immediately 459 com.pi4j.jni.Serial.sendBreak(fileDescriptor, duration); 460 } 461 462 /** 463 * <p> 464 * Send a BREAK signal to connected device for at least 0.25 seconds, and not more than 0.5 seconds 465 * </p> 466 * 467 * @throws IllegalStateException thrown if the serial port is not already open. 468 * @throws IOException thrown on any error. 469 */ 470 @Override 471 public void sendBreak() throws IllegalStateException, IOException{ 472 sendBreak(0); 473 } 474 475 /** 476 * <p> 477 * Send a constant BREAK signal to connected device. (Turn break on/off) 478 * When enabled this will send a steady stream of zero bits. 479 * When enabled, no (other) data transmitting is possible. 480 * </p> 481 * 482 * @param enabled 483 * The enable or disable state to control the BREAK signal 484 * @throws IllegalStateException thrown if the serial port is not already open. 485 * @throws IOException thrown on any error. 486 */ 487 @Override 488 public void setBreak(boolean enabled) throws IllegalStateException, IOException{ 489 // validate state 490 if (isClosed()) 491 throw new IllegalStateException("Serial connection is not open; cannot 'setBreak()'."); 492 493 // control the constant state of the BREAK signal 494 com.pi4j.jni.Serial.setBreak(fileDescriptor, enabled); 495 } 496 497 /** 498 * <p> 499 * Control the RTS (request-to-send) pin state. 500 * When enabled this will set the RTS pin to the HIGH state. 501 * </p> 502 * 503 * @param enabled 504 * The enable or disable state to control the RTS pin state. 505 * @throws IllegalStateException thrown if the serial port is not already open. 506 * @throws IOException thrown on any error. 507 */ 508 @Override 509 public void setRTS(boolean enabled) throws IllegalStateException, IOException{ 510 // validate state 511 if (isClosed()) 512 throw new IllegalStateException("Serial connection is not open; cannot 'setRTS()'."); 513 514 // control the constant state of the RTS signal 515 com.pi4j.jni.Serial.setRTS(fileDescriptor, enabled); 516 } 517 518 /** 519 * <p> 520 * Control the DTR (data-terminal-ready) pin state. 521 * When enabled this will set the DTR pin to the HIGH state. 522 * </p> 523 * 524 * @param enabled 525 * The enable or disable state to control the RTS pin state. 526 * @throws IllegalStateException thrown if the serial port is not already open. 527 * @throws IOException thrown on any error. 528 */ 529 @Override 530 public void setDTR(boolean enabled) throws IllegalStateException, IOException{ 531 // validate state 532 if (isClosed()) 533 throw new IllegalStateException("Serial connection is not open; cannot 'setDTR()'."); 534 535 // control the constant state of the DTR signal 536 com.pi4j.jni.Serial.setDTR(fileDescriptor, enabled); 537 } 538 539 /** 540 * <p> 541 * Get the RTS (request-to-send) pin state. 542 * </p> 543 * 544 * @throws IllegalStateException thrown if the serial port is not already open. 545 * @throws IOException thrown on any error. 546 */ 547 public boolean getRTS() throws IllegalStateException, IOException{ 548 // validate state 549 if (isClosed()) 550 throw new IllegalStateException("Serial connection is not open; cannot 'getRTS()'."); 551 552 // get pin state 553 return com.pi4j.jni.Serial.getRTS(fileDescriptor); 554 } 555 556 /** 557 * <p> 558 * Get the DTR (data-terminal-ready) pin state. 559 * </p> 560 * 561 * @throws IllegalStateException thrown if the serial port is not already open. 562 * @throws IOException thrown on any error. 563 */ 564 public boolean getDTR() throws IllegalStateException, IOException{ 565 // validate state 566 if (isClosed()) 567 throw new IllegalStateException("Serial connection is not open; cannot 'getDTR()'."); 568 569 // get pin state 570 return com.pi4j.jni.Serial.getDTR(fileDescriptor); 571 } 572 573 /** 574 * <p> 575 * Get the CTS (clean-to-send) pin state. 576 * </p> 577 * 578 * @throws IllegalStateException thrown if the serial port is not already open. 579 * @throws IOException thrown on any error. 580 */ 581 public boolean getCTS() throws IllegalStateException, IOException{ 582 // validate state 583 if (isClosed()) 584 throw new IllegalStateException("Serial connection is not open; cannot 'getCTS()'."); 585 586 // get pin state 587 return com.pi4j.jni.Serial.getCTS(fileDescriptor); 588 } 589 590 /** 591 * <p> 592 * Get the DSR (data-set-ready) pin state. 593 * </p> 594 * 595 * @throws IllegalStateException thrown if the serial port is not already open. 596 * @throws IOException thrown on any error. 597 */ 598 public boolean getDSR() throws IllegalStateException, IOException{ 599 // validate state 600 if (isClosed()) 601 throw new IllegalStateException("Serial connection is not open; cannot 'getDSR()'."); 602 603 // get pin state 604 return com.pi4j.jni.Serial.getDSR(fileDescriptor); 605 } 606 607 /** 608 * <p> 609 * Get the RI (ring-indicator) pin state. 610 * </p> 611 * 612 * @throws IllegalStateException thrown if the serial port is not already open. 613 * @throws IOException thrown on any error. 614 */ 615 public boolean getRI() throws IllegalStateException, IOException{ 616 // validate state 617 if (isClosed()) 618 throw new IllegalStateException("Serial connection is not open; cannot 'getRI()'."); 619 620 // get pin state 621 return com.pi4j.jni.Serial.getRI(fileDescriptor); 622 } 623 624 /** 625 * <p> 626 * Get the CD (carrier-detect) pin state. 627 * </p> 628 * 629 * @throws IllegalStateException thrown if the serial port is not already open. 630 * @throws IOException thrown on any error. 631 */ 632 public boolean getCD() throws IllegalStateException, IOException{ 633 // validate state 634 if (isClosed()) 635 throw new IllegalStateException("Serial connection is not open; cannot 'getCD()'."); 636 637 // get pin state 638 return com.pi4j.jni.Serial.getCD(fileDescriptor); 639 } 640 641 // ---------------------------------------- 642 // READ OPERATIONS 643 // ---------------------------------------- 644 645 /** 646 * Gets the number of bytes available for reading, or -1 for any error condition. 647 * 648 * @return Returns the number of bytes available for reading, or -1 for any error 649 * @throws IllegalStateException thrown if the serial port is not already open. 650 * @throws IOException thrown on any error. 651 */ 652 @Override 653 public int available() throws IllegalStateException, IOException { 654 // validate state 655 if (isClosed()) 656 throw new IllegalStateException("Serial connection is not open; cannot 'available()'."); 657 658 // get the number of available bytes in the serial port's receive buffer 659 //return com.pi4j.jni.Serial.available(fileDescriptor); 660 return receiveBuffer.getInputStream().available(); 661 } 662 663 /** 664 * <p>Reads all available bytes from the serial port/device.</p> 665 * 666 * @return Returns a byte array with the data read from the serial port. 667 * @throws IllegalStateException thrown if the serial port is not already open. 668 * @throws IOException thrown on any error. 669 */ 670 @Override 671 public byte[] read() throws IllegalStateException, IOException{ 672 // validate state 673 if (isClosed()) 674 throw new IllegalStateException("Serial connection is not open; cannot 'read()'."); 675 676 // read serial data from receive buffer 677 byte[] buffer = new byte[available()]; 678 receiveBuffer.getInputStream().read(buffer); 679 return buffer; 680 } 681 682 /** 683 * <p>Reads a length of bytes from the port/serial device.</p> 684 * 685 * @param length 686 * The number of bytes to get from the serial port/device. 687 * This number must not be higher than the number of available bytes. 688 * 689 * @return Returns a byte array with the data read from the serial port. 690 * @throws IllegalStateException thrown if the serial port is not already open. 691 * @throws IOException thrown on any error. 692 */ 693 @Override 694 public byte[] read(int length) throws IllegalStateException, IOException{ 695 // validate state 696 if (isClosed()) 697 throw new IllegalStateException("Serial connection is not open; cannot 'read()'."); 698 699 // read serial data from receive buffer 700 byte[] buffer = new byte[length]; 701 receiveBuffer.getInputStream().read(buffer, 0 , length); 702 return buffer; 703 } 704 705 706 // ---------------------------------------- 707 // WRITE OPERATIONS 708 // ---------------------------------------- 709 710 /** 711 * <p>Sends an array of bytes to the serial port/device identified by the given file descriptor.</p> 712 * 713 * @param data 714 * A ByteBuffer of data to be transmitted. 715 * @param offset 716 * The starting index (inclusive) in the array to send from. 717 * @param length 718 * The number of bytes from the byte array to transmit to the serial port. 719 * @throws IllegalStateException thrown if the serial port is not already open. 720 * @throws IOException thrown on any error. 721 */ 722 @Override 723 public void write(byte[] data, int offset, int length) throws IllegalStateException, IOException{ 724 // validate state 725 if (isClosed()) { 726 throw new IllegalStateException("Serial connection is not open; cannot 'write()'."); 727 } 728 729 // write serial data to transmit buffer 730 com.pi4j.jni.Serial.write(fileDescriptor, data, offset, length); 731 } 732 733 734 // ---------------------------------------- 735 // EVENT OPERATIONS 736 // ---------------------------------------- 737 738 /** 739 * <p>Add Serial Event Listener</p> 740 * 741 * <p> Java consumer code can call this method to register itself as a listener for serial data 742 * events. </p> 743 * 744 * @see com.pi4j.io.serial.SerialDataEventListener 745 * @see com.pi4j.io.serial.SerialDataEvent 746 * 747 * @param listener A class instance that implements the SerialListener interface. 748 */ 749 @Override 750 public synchronized void addListener(SerialDataEventListener... listener) { 751 // add the new listener to the list of listeners 752 Collections.addAll(listeners, listener); 753 } 754 755 /** 756 * <p>Remove Serial Event Listener</p> 757 * 758 * <p> Java consumer code can call this method to unregister itself as a listener for serial data 759 * events. </p> 760 * 761 * @see com.pi4j.io.serial.SerialDataEventListener 762 * @see com.pi4j.io.serial.SerialDataEvent 763 * 764 * @param listener A class instance that implements the SerialListener interface. 765 */ 766 @Override 767 public synchronized void removeListener(SerialDataEventListener... listener) { 768 // remove the listener from the list of listeners 769 for (SerialDataEventListener lsnr : listener) { 770 listeners.remove(lsnr); 771 } 772 } 773 774 /** 775 * This method returns the serial device file descriptor 776 * @return fileDescriptor file descriptor 777 */ 778 @Override 779 public int getFileDescriptor() { 780 return fileDescriptor; 781 } 782 783 /** 784 * This method returns the input data stream for the serial port's receive buffer 785 * @return InputStream input stream 786 */ 787 @Override 788 public InputStream getInputStream() { 789 return receiveBuffer.getInputStream(); 790 } 791 792 /** 793 * This method returns the output data stream for the serial port's transmit buffer 794 * @return OutputStream output stream 795 */ 796 @Override 797 public OutputStream getOutputStream() { 798 return new SerialOutputStream(); 799 } 800 801 802 /** 803 * This method returns the buffering state for data received from the serial device/port. 804 * @return 'true' if buffering is enabled; else 'false' 805 */ 806 @Override 807 public boolean isBufferingDataReceived(){ 808 return bufferingDataReceived; 809 } 810 811 /** 812 * <p> 813 * This method controls the buffering state for data received from the serial device/port. 814 * </p> 815 * <p> 816 * If the buffering state is enabled, then all data bytes received from the serial port will 817 * get copied into a data receive buffer. You can use the 'getInputStream()' or and of the 'read()' 818 * methods to access this data. The data will also be available via the 'SerialDataEvent' event. 819 * It is important to know that if you are using data buffering, the data will continue to grow 820 * in memory until your program consume it from the data reader/stream. 821 * </p> 822 * <p> 823 * If the buffering state is disabled, then all data bytes received from the serial port will NOT 824 * get copied into the data receive buffer, but will be included in the 'SerialDataEvent' event's 825 * data payload. If you program does not care about or use data received from the serial port, 826 * then you should disable the data buffering state to prevent memory waste/leak. 827 * </p> 828 * 829 * @param enabled sets the buffering behavior state 830 */ 831 @Override 832 public void setBufferingDataReceived(boolean enabled){ 833 bufferingDataReceived = enabled; 834 } 835 836 837 private class SerialOutputStream extends OutputStream { 838 839 @Override 840 public void write(byte b[]) throws IOException { 841 SerialImpl.this.write(b); 842 } 843 844 @Override 845 public void write(int b) throws IOException { 846 SerialImpl.this.write((byte)b); 847 } 848 849 public void write(byte b[], int offset, int length) throws IOException { 850 SerialImpl.this.write(b, offset, length); 851 } 852 853 @Override 854 public void flush() throws IOException { 855 SerialImpl.this.flush(); 856 } 857 } 858 859 @SuppressWarnings("unused") 860 private class SerialInputStream extends InputStream { 861 862 @Override 863 public int read() throws IOException { 864 return 0; 865 } 866 } 867}