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