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 - 2021 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 if(!listeners.isEmpty() && isOpen()) { 201 // don't add event if executor has been shutdown or terminated 202 if(!executor.isTerminated() && !executor.isShutdown()) { 203 try { 204 executor.execute(new SerialDataEventDispatchTaskImpl(sde, listeners)); 205 } 206 catch(java.util.concurrent.RejectedExecutionException e){ 207 // do nothing, we are most likely in a shutdown 208 } 209 } 210 } 211 } 212 catch (IOException e) { 213 e.printStackTrace(); 214 } 215 } 216 }); 217 218 // ensure file descriptor is valid 219 if (fileDescriptor == -1) { 220 throw new IOException("Cannot open serial port"); 221 } 222 } 223 224 /** 225 * <p> 226 * This opens and initializes the serial port/device and sets the communication parameters. 227 * It sets the port into raw mode (character at a time and no translations). 228 * 229 * This method will use the following default serial configuration parameters: 230 * - DATA BITS = 8 231 * - PARITY = NONE 232 * - STOP BITS = 1 233 * - FLOW CONTROL = NONE 234 * 235 * </p> 236 * 237 * <p> 238 * (ATTENTION: the 'device' argument can only be a maximum of 128 characters.) 239 * </p> 240 * 241 * @see #DEFAULT_COM_PORT 242 * 243 * @param device 244 * The device address of the serial port to access. You can use constant 245 * 'DEFAULT_COM_PORT' if you wish to access the default serial port provided via the 246 * GPIO header. 247 * @param baud 248 * The baud rate to use with the serial port. 249 * 250 * @throws IOException thrown on any error. 251 */ 252 @Override 253 public void open(String device, int baud) throws IOException{ 254 // open the serial port with config settings of "8N1" and no flow control 255 open(device, 256 baud, 257 com.pi4j.jni.Serial.DATA_BITS_8, 258 com.pi4j.jni.Serial.PARITY_NONE, 259 com.pi4j.jni.Serial.STOP_BITS_1, 260 com.pi4j.jni.Serial.FLOW_CONTROL_NONE); 261 } 262 263 /** 264 * <p> 265 * This opens and initializes the serial port/device and sets the communication parameters. 266 * It sets the port into raw mode (character at a time and no translations). 267 * </p> 268 * 269 * <p> 270 * (ATTENTION: the 'device' argument can only be a maximum of 128 characters.) 271 * </p> 272 * 273 * @see #DEFAULT_COM_PORT 274 * 275 * @param device 276 * The device address of the serial port to access. You can use constant 277 * 'DEFAULT_COM_PORT' if you wish to access the default serial port provided via the 278 * GPIO header. 279 * @param baud 280 * The baud rate to use with the serial port. 281 * @param dataBits 282 * The data bits to use for serial communication. (5,6,7,8) 283 * @param parity 284 * The parity setting to use for serial communication. (None, Event, Odd, Mark, Space) 285 * @param stopBits 286 * The stop bits to use for serial communication. (1,2) 287 * @param flowControl 288 * The flow control option to use for serial communication. (none, hardware, software) 289 * 290 * @throws IOException thrown on any error. 291 */ 292 @Override 293 public void open(String device, Baud baud, DataBits dataBits, Parity parity, StopBits stopBits, 294 FlowControl flowControl) throws IOException{ 295 // open the serial port with NO ECHO and NO (forced) BUFFER FLUSH 296 open(device, baud.getValue(), dataBits.getValue(), parity.getIndex(), 297 stopBits.getValue(), flowControl.getIndex()); 298 } 299 300 /** 301 * <p> 302 * This opens and initializes the serial port/device and sets the communication parameters. 303 * It sets the port into raw mode (character at a time and no translations). 304 * </p> 305 * 306 * <p> 307 * (ATTENTION: the 'device' argument can only be a maximum of 128 characters.) 308 * </p> 309 * 310 * @see #DEFAULT_COM_PORT 311 * 312 * @param serialConfig 313 * A serial configuration object that contains the device, baud rate, data bits, parity, 314 * stop bits, and flow control settings. 315 * 316 * @throws IOException thrown on any error. 317 */ 318 @Override 319 public void open(SerialConfig serialConfig) throws IOException{ 320 // open the serial port with config settings 321 open(serialConfig.device(), 322 serialConfig.baud().getValue(), 323 serialConfig.dataBits().getValue(), 324 serialConfig.parity().getIndex(), 325 serialConfig.stopBits().getValue(), 326 serialConfig.flowControl().getIndex()); 327 } 328 329 /** 330 * This method is called to determine if the serial port is already open. 331 * 332 * @see #open(String, int) 333 * @return a value of 'true' is returned if the serial port is already open. 334 */ 335 @Override 336 public boolean isOpen() { 337 return (fileDescriptor >= 0); 338 } 339 340 /** 341 * This method is called to determine if the serial port is already closed. 342 * 343 * @see #open(String, int) 344 * @return a value of 'true' is returned if the serial port is already in the closed state. 345 */ 346 @Override 347 public boolean isClosed(){ 348 return !(isOpen()); 349 } 350 351 352 /** 353 * This method is called to close a currently open open serial port. 354 * 355 * @throws IllegalStateException thrown if the serial port is not already open. 356 * @throws IOException thrown on any error. 357 */ 358 @Override 359 public void close() throws IllegalStateException, IOException { 360 361 // validate state 362 if (isClosed()) 363 throw new IllegalStateException("Serial connection is not open; cannot 'close()'."); 364 365 // remove serial port listener 366 SerialInterrupt.removeListener(fileDescriptor); 367 368 // close serial port now 369 com.pi4j.jni.Serial.close(fileDescriptor); 370 371 // reset file descriptor 372 fileDescriptor = -1; 373 } 374 375 376 /** 377 * <p> 378 * Forces the transmission of any remaining data in the serial port transmit buffer. 379 * </p> 380 * 381 * @throws IllegalStateException thrown if the serial port is not already open. 382 * @throws IOException thrown on any error. 383 */ 384 @Override 385 public void flush() throws IllegalStateException, IOException{ 386 // validate state 387 if (isClosed()) 388 throw new IllegalStateException("Serial connection is not open; cannot 'flush()'."); 389 390 // flush data to serial port immediately 391 com.pi4j.jni.Serial.flush(fileDescriptor); 392 } 393 394 /** 395 * <p> 396 * Discards any data in the serial receive (input) buffer. 397 * </p> 398 * 399 * @throws IllegalStateException thrown if the serial port is not already open. 400 * @throws IOException thrown on any error. 401 */ 402 @Override 403 public void discardInput() throws IllegalStateException, IOException{ 404 // validate state 405 if (isClosed()) 406 throw new IllegalStateException("Serial connection is not open; cannot 'discardInput()'."); 407 408 // flush data to serial port immediately 409 com.pi4j.jni.Serial.discardInput(fileDescriptor); 410 } 411 412 /** 413 * <p> 414 * Discards any data in the serial transmit (output) buffer. 415 * </p> 416 * 417 * @throws IllegalStateException thrown if the serial port is not already open. 418 * @throws IOException thrown on any error. 419 */ 420 @Override 421 public void discardOutput() throws IllegalStateException, IOException{ 422 // validate state 423 if (isClosed()) 424 throw new IllegalStateException("Serial connection is not open; cannot 'discardOutput()'."); 425 426 // flush data to serial port immediately 427 com.pi4j.jni.Serial.discardOutput(fileDescriptor); 428 } 429 430 /** 431 * <p> 432 * Discards any data in both the serial receive and transmit buffers. 433 * </p> 434 * 435 * @throws IllegalStateException thrown if the serial port is not already open. 436 * @throws IOException thrown on any error. 437 */ 438 @Override 439 public void discardAll() throws IllegalStateException, IOException{ 440 // validate state 441 if (isClosed()) 442 throw new IllegalStateException("Serial connection is not open; cannot 'discardAll()'."); 443 444 // flush data to serial port immediately 445 com.pi4j.jni.Serial.discardAll(fileDescriptor); 446 } 447 448 /** 449 * <p> 450 * Send a BREAK signal to connected device. 451 * </p> 452 * 453 * @param duration 454 * The length of time (milliseconds) to send the BREAK signal 455 * @throws IllegalStateException thrown if the serial port is not already open. 456 * @throws IOException thrown on any error. 457 */ 458 @Override 459 public void sendBreak(int duration) throws IllegalStateException, IOException{ 460 // validate state 461 if (isClosed()) 462 throw new IllegalStateException("Serial connection is not open; cannot 'sendBreak()'."); 463 464 // send BREAK signal to serial port immediately 465 com.pi4j.jni.Serial.sendBreak(fileDescriptor, duration); 466 } 467 468 /** 469 * <p> 470 * Send a BREAK signal to connected device for at least 0.25 seconds, and not more than 0.5 seconds 471 * </p> 472 * 473 * @throws IllegalStateException thrown if the serial port is not already open. 474 * @throws IOException thrown on any error. 475 */ 476 @Override 477 public void sendBreak() throws IllegalStateException, IOException{ 478 sendBreak(0); 479 } 480 481 /** 482 * <p> 483 * Send a constant BREAK signal to connected device. (Turn break on/off) 484 * When enabled this will send a steady stream of zero bits. 485 * When enabled, no (other) data transmitting is possible. 486 * </p> 487 * 488 * @param enabled 489 * The enable or disable state to control the BREAK signal 490 * @throws IllegalStateException thrown if the serial port is not already open. 491 * @throws IOException thrown on any error. 492 */ 493 @Override 494 public void setBreak(boolean enabled) throws IllegalStateException, IOException{ 495 // validate state 496 if (isClosed()) 497 throw new IllegalStateException("Serial connection is not open; cannot 'setBreak()'."); 498 499 // control the constant state of the BREAK signal 500 com.pi4j.jni.Serial.setBreak(fileDescriptor, enabled); 501 } 502 503 /** 504 * <p> 505 * Control the RTS (request-to-send) pin state. 506 * When enabled this will set the RTS pin to the HIGH state. 507 * </p> 508 * 509 * @param enabled 510 * The enable or disable state to control the RTS pin state. 511 * @throws IllegalStateException thrown if the serial port is not already open. 512 * @throws IOException thrown on any error. 513 */ 514 @Override 515 public void setRTS(boolean enabled) throws IllegalStateException, IOException{ 516 // validate state 517 if (isClosed()) 518 throw new IllegalStateException("Serial connection is not open; cannot 'setRTS()'."); 519 520 // control the constant state of the RTS signal 521 com.pi4j.jni.Serial.setRTS(fileDescriptor, enabled); 522 } 523 524 /** 525 * <p> 526 * Control the DTR (data-terminal-ready) pin state. 527 * When enabled this will set the DTR pin to the HIGH state. 528 * </p> 529 * 530 * @param enabled 531 * The enable or disable state to control the RTS pin state. 532 * @throws IllegalStateException thrown if the serial port is not already open. 533 * @throws IOException thrown on any error. 534 */ 535 @Override 536 public void setDTR(boolean enabled) throws IllegalStateException, IOException{ 537 // validate state 538 if (isClosed()) 539 throw new IllegalStateException("Serial connection is not open; cannot 'setDTR()'."); 540 541 // control the constant state of the DTR signal 542 com.pi4j.jni.Serial.setDTR(fileDescriptor, enabled); 543 } 544 545 /** 546 * <p> 547 * Get the RTS (request-to-send) pin state. 548 * </p> 549 * 550 * @throws IllegalStateException thrown if the serial port is not already open. 551 * @throws IOException thrown on any error. 552 */ 553 public boolean getRTS() throws IllegalStateException, IOException{ 554 // validate state 555 if (isClosed()) 556 throw new IllegalStateException("Serial connection is not open; cannot 'getRTS()'."); 557 558 // get pin state 559 return com.pi4j.jni.Serial.getRTS(fileDescriptor); 560 } 561 562 /** 563 * <p> 564 * Get the DTR (data-terminal-ready) pin state. 565 * </p> 566 * 567 * @throws IllegalStateException thrown if the serial port is not already open. 568 * @throws IOException thrown on any error. 569 */ 570 public boolean getDTR() throws IllegalStateException, IOException{ 571 // validate state 572 if (isClosed()) 573 throw new IllegalStateException("Serial connection is not open; cannot 'getDTR()'."); 574 575 // get pin state 576 return com.pi4j.jni.Serial.getDTR(fileDescriptor); 577 } 578 579 /** 580 * <p> 581 * Get the CTS (clean-to-send) pin state. 582 * </p> 583 * 584 * @throws IllegalStateException thrown if the serial port is not already open. 585 * @throws IOException thrown on any error. 586 */ 587 public boolean getCTS() throws IllegalStateException, IOException{ 588 // validate state 589 if (isClosed()) 590 throw new IllegalStateException("Serial connection is not open; cannot 'getCTS()'."); 591 592 // get pin state 593 return com.pi4j.jni.Serial.getCTS(fileDescriptor); 594 } 595 596 /** 597 * <p> 598 * Get the DSR (data-set-ready) pin state. 599 * </p> 600 * 601 * @throws IllegalStateException thrown if the serial port is not already open. 602 * @throws IOException thrown on any error. 603 */ 604 public boolean getDSR() throws IllegalStateException, IOException{ 605 // validate state 606 if (isClosed()) 607 throw new IllegalStateException("Serial connection is not open; cannot 'getDSR()'."); 608 609 // get pin state 610 return com.pi4j.jni.Serial.getDSR(fileDescriptor); 611 } 612 613 /** 614 * <p> 615 * Get the RI (ring-indicator) pin state. 616 * </p> 617 * 618 * @throws IllegalStateException thrown if the serial port is not already open. 619 * @throws IOException thrown on any error. 620 */ 621 public boolean getRI() throws IllegalStateException, IOException{ 622 // validate state 623 if (isClosed()) 624 throw new IllegalStateException("Serial connection is not open; cannot 'getRI()'."); 625 626 // get pin state 627 return com.pi4j.jni.Serial.getRI(fileDescriptor); 628 } 629 630 /** 631 * <p> 632 * Get the CD (carrier-detect) pin state. 633 * </p> 634 * 635 * @throws IllegalStateException thrown if the serial port is not already open. 636 * @throws IOException thrown on any error. 637 */ 638 public boolean getCD() throws IllegalStateException, IOException{ 639 // validate state 640 if (isClosed()) 641 throw new IllegalStateException("Serial connection is not open; cannot 'getCD()'."); 642 643 // get pin state 644 return com.pi4j.jni.Serial.getCD(fileDescriptor); 645 } 646 647 // ---------------------------------------- 648 // READ OPERATIONS 649 // ---------------------------------------- 650 651 /** 652 * Gets the number of bytes available for reading, or -1 for any error condition. 653 * 654 * @return Returns the number of bytes available for reading, or -1 for any error 655 * @throws IllegalStateException thrown if the serial port is not already open. 656 * @throws IOException thrown on any error. 657 */ 658 @Override 659 public int available() throws IllegalStateException, IOException { 660 // validate state 661 if (isClosed()) 662 throw new IllegalStateException("Serial connection is not open; cannot 'available()'."); 663 664 // get the number of available bytes in the serial port's receive buffer 665 //return com.pi4j.jni.Serial.available(fileDescriptor); 666 return receiveBuffer.getInputStream().available(); 667 } 668 669 /** 670 * <p>Reads all available bytes from the serial port/device.</p> 671 * 672 * @return Returns a byte array with the data read from the serial port. 673 * @throws IllegalStateException thrown if the serial port is not already open. 674 * @throws IOException thrown on any error. 675 */ 676 @Override 677 public byte[] read() throws IllegalStateException, IOException{ 678 // validate state 679 if (isClosed()) 680 throw new IllegalStateException("Serial connection is not open; cannot 'read()'."); 681 682 // read serial data from receive buffer 683 byte[] buffer = new byte[available()]; 684 receiveBuffer.getInputStream().read(buffer); 685 return buffer; 686 } 687 688 /** 689 * <p>Reads a length of bytes from the port/serial device.</p> 690 * 691 * @param length 692 * The number of bytes to get from the serial port/device. 693 * This number must not be higher than the number of available bytes. 694 * 695 * @return Returns a byte array with the data read from the serial port. 696 * @throws IllegalStateException thrown if the serial port is not already open. 697 * @throws IOException thrown on any error. 698 */ 699 @Override 700 public byte[] read(int length) throws IllegalStateException, IOException{ 701 // validate state 702 if (isClosed()) 703 throw new IllegalStateException("Serial connection is not open; cannot 'read()'."); 704 705 // read serial data from receive buffer 706 byte[] buffer = new byte[length]; 707 receiveBuffer.getInputStream().read(buffer, 0 , length); 708 return buffer; 709 } 710 711 712 // ---------------------------------------- 713 // WRITE OPERATIONS 714 // ---------------------------------------- 715 716 /** 717 * <p>Sends an array of bytes to the serial port/device identified by the given file descriptor.</p> 718 * 719 * @param data 720 * A ByteBuffer of data to be transmitted. 721 * @param offset 722 * The starting index (inclusive) in the array to send from. 723 * @param length 724 * The number of bytes from the byte array to transmit to the serial port. 725 * @throws IllegalStateException thrown if the serial port is not already open. 726 * @throws IOException thrown on any error. 727 */ 728 @Override 729 public void write(byte[] data, int offset, int length) throws IllegalStateException, IOException{ 730 // validate state 731 if (isClosed()) { 732 throw new IllegalStateException("Serial connection is not open; cannot 'write()'."); 733 } 734 735 // write serial data to transmit buffer 736 com.pi4j.jni.Serial.write(fileDescriptor, data, offset, length); 737 } 738 739 740 // ---------------------------------------- 741 // EVENT OPERATIONS 742 // ---------------------------------------- 743 744 /** 745 * <p>Add Serial Event Listener</p> 746 * 747 * <p> Java consumer code can call this method to register itself as a listener for serial data 748 * events. </p> 749 * 750 * @see com.pi4j.io.serial.SerialDataEventListener 751 * @see com.pi4j.io.serial.SerialDataEvent 752 * 753 * @param listener A class instance that implements the SerialListener interface. 754 */ 755 @Override 756 public synchronized void addListener(SerialDataEventListener... listener) { 757 // add the new listener to the list of listeners 758 Collections.addAll(listeners, listener); 759 } 760 761 /** 762 * <p>Remove Serial Event Listener</p> 763 * 764 * <p> Java consumer code can call this method to unregister itself as a listener for serial data 765 * events. </p> 766 * 767 * @see com.pi4j.io.serial.SerialDataEventListener 768 * @see com.pi4j.io.serial.SerialDataEvent 769 * 770 * @param listener A class instance that implements the SerialListener interface. 771 */ 772 @Override 773 public synchronized void removeListener(SerialDataEventListener... listener) { 774 // remove the listener from the list of listeners 775 for (SerialDataEventListener lsnr : listener) { 776 listeners.remove(lsnr); 777 } 778 } 779 780 /** 781 * This method returns the serial device file descriptor 782 * @return fileDescriptor file descriptor 783 */ 784 @Override 785 public int getFileDescriptor() { 786 return fileDescriptor; 787 } 788 789 /** 790 * This method returns the input data stream for the serial port's receive buffer 791 * @return InputStream input stream 792 */ 793 @Override 794 public InputStream getInputStream() { 795 return receiveBuffer.getInputStream(); 796 } 797 798 /** 799 * This method returns the output data stream for the serial port's transmit buffer 800 * @return OutputStream output stream 801 */ 802 @Override 803 public OutputStream getOutputStream() { 804 return new SerialOutputStream(); 805 } 806 807 808 /** 809 * This method returns the buffering state for data received from the serial device/port. 810 * @return 'true' if buffering is enabled; else 'false' 811 */ 812 @Override 813 public boolean isBufferingDataReceived(){ 814 return bufferingDataReceived; 815 } 816 817 /** 818 * <p> 819 * This method controls the buffering state for data received from the serial device/port. 820 * </p> 821 * <p> 822 * If the buffering state is enabled, then all data bytes received from the serial port will 823 * get copied into a data receive buffer. You can use the 'getInputStream()' or and of the 'read()' 824 * methods to access this data. The data will also be available via the 'SerialDataEvent' event. 825 * It is important to know that if you are using data buffering, the data will continue to grow 826 * in memory until your program consume it from the data reader/stream. 827 * </p> 828 * <p> 829 * If the buffering state is disabled, then all data bytes received from the serial port will NOT 830 * get copied into the data receive buffer, but will be included in the 'SerialDataEvent' event's 831 * data payload. If you program does not care about or use data received from the serial port, 832 * then you should disable the data buffering state to prevent memory waste/leak. 833 * </p> 834 * 835 * @param enabled sets the buffering behavior state 836 */ 837 @Override 838 public void setBufferingDataReceived(boolean enabled){ 839 bufferingDataReceived = enabled; 840 } 841 842 843 private class SerialOutputStream extends OutputStream { 844 845 @Override 846 public void write(byte b[]) throws IOException { 847 SerialImpl.this.write(b); 848 } 849 850 @Override 851 public void write(int b) throws IOException { 852 SerialImpl.this.write((byte)b); 853 } 854 855 public void write(byte b[], int offset, int length) throws IOException { 856 SerialImpl.this.write(b, offset, length); 857 } 858 859 @Override 860 public void flush() throws IOException { 861 SerialImpl.this.flush(); 862 } 863 } 864 865 @SuppressWarnings("unused") 866 private class SerialInputStream extends InputStream { 867 868 @Override 869 public int read() throws IOException { 870 return 0; 871 } 872 } 873}