From 23959966760174477a6b0fcbf9dd1e8ef37c643b Mon Sep 17 00:00:00 2001 From: Jakob Odersky Date: Sun, 8 Jan 2017 21:16:25 +0100 Subject: Rename project to akka-serial --- .../scala/akka/serial/sync/SerialConnection.scala | 142 +++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 sync/src/main/scala/akka/serial/sync/SerialConnection.scala (limited to 'sync/src/main/scala/akka/serial/sync/SerialConnection.scala') diff --git a/sync/src/main/scala/akka/serial/sync/SerialConnection.scala b/sync/src/main/scala/akka/serial/sync/SerialConnection.scala new file mode 100644 index 0000000..7d80a4c --- /dev/null +++ b/sync/src/main/scala/akka/serial/sync/SerialConnection.scala @@ -0,0 +1,142 @@ +package akka.serial +package sync + +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. + */ +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, starting at the + * first position. The buffer's limit is set to the number of bytes + * read. + * + * A call to this method is blocking, however it is interrupted + * if the connection is closed. + * + * This method works only for direct buffers. + * + * @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 + val n = unsafe.read(buffer) + buffer.limit(n) + n + } 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 only for direct buffers. + * + * @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") + } + } + +} + +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) + } + +} -- cgit v1.2.3