All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ch.jodersky.flow.SerialConnection.scala Maven / Gradle / Ivy

The newest version!
package ch.jodersky.flow

import java.nio.ByteBuffer
import java.util.concurrent.atomic.AtomicBoolean

/**
 * Represents a serial connection in a more secure and object-oriented style than `UnsafeSerial`. In
 * contrast to the latter, this class encapsulates and secures any pointers used to communicate with
 * the native backend and is thread-safe.
 *
 * The underlying serial port is assumed open when this class is initialized.
 */
private[flow] class SerialConnection private (
  unsafe: UnsafeSerial,
  val port: String
) {

  private var reading: Boolean = false
  private val readLock = new Object

  private var writing: Boolean = false
  private val writeLock = new Object

  private val closed = new AtomicBoolean(false)

  /**
   * Checks if this serial port is closed.
   */
  def isClosed = closed.get()

  /**
   * Closes the underlying serial connection. Any callers blocked on read or write will return.
   * A call of this method has no effect if the serial port is already closed.
   * @throws IOException on IO error
   */
  def close(): Unit = this.synchronized {
    if (!closed.get) {
      closed.set(true)
      unsafe.cancelRead()
      readLock.synchronized {
        while (reading) this.wait()
      }
      writeLock.synchronized {
        while (writing) this.wait()
      }
      unsafe.close()
    }
  }

  /**
   * Reads data from underlying serial connection into a ByteBuffer.
   * Note that data is read into the buffer's memory, its attributes
   * such as position and limit are not modified.
   *
   * A call to this method is blocking, however it is interrupted
   * if the connection is closed.
   *
   * This method works for direct and indirect buffers but is optimized
   * for the former.
   *
   * @param buffer a ByteBuffer into which data is read
   * @return the actual number of bytes read
   * @throws PortInterruptedException if port is closed while reading
   * @throws IOException on IO error
   */
  def read(buffer: ByteBuffer): Int = readLock.synchronized {
    if (!closed.get) {
      try {
        reading = true
        unsafe.read(buffer)
      } finally {
        reading = false
        if (closed.get) readLock.notify()
      }
    } else {
      throw new PortClosedException(s"${port} is closed")
    }
  }

  /**
   * Writes data from a ByteBuffer to underlying serial connection.
   * Note that data is read from the buffer's memory, its attributes
   * such as position and limit are not modified.
   *
   * The write is non-blocking, this function returns as soon as the data is copied into the kernel's
   * transmission buffer.
   *
   * This method works for direct and indirect buffers but is optimized
   * for the former.
   *
   * @param buffer a ByteBuffer from which data is taken
   * @return the actual number of bytes written
   * @throws IOException on IO error
   */
  def write(buffer: ByteBuffer): Int = writeLock.synchronized {
    if (!closed.get) {
      try {
        writing = true
        unsafe.write(buffer, buffer.position)
      } finally {
        writing = false
        if (closed.get) writeLock.notify()
      }
    } else {
      throw new PortClosedException(s"${port} is closed")
    }
  }

}

private[flow] object SerialConnection {

  /**
   * Opens a new connection to a serial port.
   * This method acts as a factory to creating serial connections.
   *
   * @param port name of serial port to open
   * @param settings settings with which to initialize the connection
   * @return an instance of the open serial connection
   * @throws NoSuchPortException if the given port does not exist
   * @throws AccessDeniedException if permissions of the current user are not sufficient to open port
   * @throws PortInUseException if port is already in use
   * @throws InvalidSettingsException if any of the specified settings are invalid
   * @throws IOException on IO error
   */
  def open(
    port: String,
    settings: SerialSettings
  ): SerialConnection = synchronized {
    val pointer = UnsafeSerial.open(
      port,
      settings.baud,
      settings.characterSize,
      settings.twoStopBits,
      settings.parity.id
    )
    new SerialConnection(new UnsafeSerial(pointer), port)
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy