diff options
Diffstat (limited to 'flow-main')
5 files changed, 124 insertions, 68 deletions
diff --git a/flow-main/src/main/java/com/github/jodersky/flow/internal/NativeSerial.java b/flow-main/src/main/java/com/github/jodersky/flow/internal/NativeSerial.java index e7322ed..524803e 100644 --- a/flow-main/src/main/java/com/github/jodersky/flow/internal/NativeSerial.java +++ b/flow-main/src/main/java/com/github/jodersky/flow/internal/NativeSerial.java @@ -69,7 +69,7 @@ class NativeSerial { * @return E_IO on error */ native static int close(long serial); - /**Sets debugging option. If debugging is enabled, detailed error message are printed from method calls. */ + /**Sets debugging option. If debugging is enabled, detailed error message are printed (to stderr) from method calls. */ native static void debug(boolean value); } diff --git a/flow-main/src/main/scala/com/github/jodersky/flow/Serial.scala b/flow-main/src/main/scala/com/github/jodersky/flow/Serial.scala index 54fea6b..abc8f39 100644 --- a/flow-main/src/main/scala/com/github/jodersky/flow/Serial.scala +++ b/flow-main/src/main/scala/com/github/jodersky/flow/Serial.scala @@ -7,62 +7,100 @@ import akka.util.ByteString /** Defines messages used by flow's serial IO layer. */ object Serial extends ExtensionKey[SerialExt] { - /** A message extending this trait is to be viewed as a command, that is an out-bound message. */ + /** A message extending this trait is to be viewed as a command, an out-bound message issued by the client to flow's API. */ trait Command - /** A message extending this trait is to be viewed as an event, that is an in-bound message. */ + /** A message extending this trait is to be viewed as an event, an in-bound message issued by flow to the client. */ trait Event - + + /** A command has failed. */ case class CommandFailed(command: Command, reason: Throwable) extends Event /** - * Open a new serial port. Send this command to the serial manager to request the opening of a serial port. The manager will + * Open a new serial port. + * + * Send this command to the serial manager to request the opening of a serial port. The manager will * attempt to open a serial port with the specified parameters and, if successful, create a `SerialOperator` actor associated to the port. - * The operator actor acts as an intermediate to the underlying native serial port, dealing with threading issues and the such. It will send any events - * to the sender of this message. - * The manager will respond with an `OpenFailed` in case the port could not be opened, or the operator will respond with `Opened`. - * @param port name of serial port - * @param baud baud rate to use with serial port - * @param characterSize size of a character of the data sent through the serial port - * @param twoStopBits set to use two stop bits instead of one - * @param parity type of parity to use with serial port + * The operator actor acts as an intermediate to the underlying native serial port, dealing with threading issues and dispatching messages. + * + * In case the port is successfully opened, the operator will respond with an `Opened` message. + * In case the port cannot be opened, the manager will respond with a `CommandFailed` message. + * + * @param settings settings of serial port to open + */ + case class Open(settings: SerialSettings) extends Command + + /** + * A port has been successfully opened. + * + * Event sent by a port operator, indicating that a serial port was successfully opened. The sender + * of this message is the operator associated to the given serial port. Furthermore, an additional reference + * to the operator is provided in this class' `operator` field. + * + * @param settings settings of port that was opened + * @param operator operator associated with the serial port */ - case class Open(port: String, baud: Int, characterSize: Int = 8, twoStopBits: Boolean = false, parity: Parity.Parity = Parity.None) extends Command + case class Opened(settings: SerialSettings, operator: ActorRef) extends Event /** - * Event sent from a port operator, indicating that a serial port was successfully opened. The sender of this message is the operator associated to the given serial port. - * @param port name of serial port - * @param baud baud rate to use with serial port - * @param characterSize size of a character of the data sent through the serial port - * @param twoStopBits set to use two stop bits instead of one - * @param parity type of parity to use with serial port + * Register an actor to receive events. + * + * Send this command to a serial operator to register an actor for notification on the reception of data on the operator's associated port. + * Upon reception, data will be sent by the operator to registered actors in form of `Received` events. + * + * @param receiver actor to register */ - case class Opened(port: String, baud: Int, characterSize: Int, twoStopBits: Boolean, parity: Parity.Parity, operator: ActorRef) extends Event - - case class Register(receiver: ActorRef) extends Command + + /** + * Unregister an actor from receiving events. + * + * Send this command to a serial operator to unregister an actor for notification on the reception of data on the operator's associated port. + * + * @param receiver actor to unregister + */ case class Unregister(receiver: ActorRef) extends Command /** - * Event sent by operator, indicating that data was received from the operator's serial port. - * @param data data received by port + * Data has been received. + * + * Event sent by an operator, indicating that data was received on the operator's serial port. + * Clients must register (see `Register`) with a serial operator to receive these events. + * + * @param data data received on the port */ case class Received(data: ByteString) extends Event /** - * Write data to serial port. Send this command to an operator to write the given data to its associated serial port. + * Write data to a serial port. + * + * Send this command to an operator to write the given data to its associated serial port. + * An acknowledgment may be set, in which case it is sent back to the sender on a successful write. + * Note that a successful write does not guarantee the actual transmission of data through the serial port, + * it merely guarantees that the data has been stored in the operating system's kernel buffer, ready to + * be written. + * * @param data data to be written to port * @param ack acknowledgment sent back to sender once data has been enqueued in kernel for sending */ case class Write(data: ByteString, ack: Event = NoAck) extends Command - /** Special type of acknowledgment that is not sent back. */ + /** + * Special type of acknowledgment that is not sent back. + */ case object NoAck extends Event - /** Request closing of port. Send this command to an operator to close its associated port. */ + /** + * Request closing of port. + * + * Send this command to an operator to close its associated port. The operator will respond + * with a `Closed` message upon closing the serial port. + */ case object Close extends Command /** + * A port has been closed. + * * Event sent from operator, indicating that its port has been closed. */ case object Closed extends Event diff --git a/flow-main/src/main/scala/com/github/jodersky/flow/SerialManager.scala b/flow-main/src/main/scala/com/github/jodersky/flow/SerialManager.scala index 0d69213..b3128ac 100644 --- a/flow-main/src/main/scala/com/github/jodersky/flow/SerialManager.scala +++ b/flow-main/src/main/scala/com/github/jodersky/flow/SerialManager.scala @@ -20,6 +20,7 @@ import akka.actor.actorRef2Scala /** * Actor that manages serial port creation. Once opened, a serial port is handed over to * a dedicated operator actor that acts as an intermediate between client code and the native system serial port. + * @see SerialOperator */ class SerialManager extends Actor with ActorLogging { import SerialManager._ @@ -32,12 +33,12 @@ class SerialManager extends Actor with ActorLogging { } def receive = { - case c @ Open(port, baud, cs, tsb, parity) => Try { InternalSerial.open(port, baud, cs, tsb, parity.id) } match { + case c @ Open(s) => Try { InternalSerial.open(s.port, s.baud, s.characterSize, s.twoStopBits, s.parity.id) } match { case Failure(t) => sender ! CommandFailed(c, t) case Success(serial) => { - val operator = context.actorOf(Props(classOf[SerialOperator], serial), name = escapePortString(port)) - val opened = Opened(serial.port, serial.baud, serial.characterSize, serial.twoStopBits, Parity(serial.parity), operator) - sender.tell(opened, operator) + val operator = context.actorOf(SerialOperator(serial), name = escapePortString(s.port)) + val settings = SerialSettings(serial.port, serial.baud, serial.characterSize, serial.twoStopBits, Parity(serial.parity)) + sender.tell(Opened(settings, operator), operator) } } } diff --git a/flow-main/src/main/scala/com/github/jodersky/flow/SerialOperator.scala b/flow-main/src/main/scala/com/github/jodersky/flow/SerialOperator.scala index 1c24729..3ac50c0 100644 --- a/flow-main/src/main/scala/com/github/jodersky/flow/SerialOperator.scala +++ b/flow-main/src/main/scala/com/github/jodersky/flow/SerialOperator.scala @@ -10,48 +10,19 @@ import akka.actor.Terminated import akka.actor.actorRef2Scala import akka.util.ByteString import scala.collection.mutable.HashSet +import akka.actor.Props -/** Operator associated to an open serial port. All communication with a port is done via an operator. Operators are created though the serial manager. */ +/** + * Operator associated to an open serial port. All communication with a port is done via an operator. Operators are created though the serial manager. + * @see SerialManager + */ class SerialOperator(serial: InternalSerial) extends Actor with ActorLogging { import SerialOperator._ import context._ - override def preStart() = { - Reader.start() - } - - override def postStop = { - serial.close() - } - - def receive: Receive = { - - case Register(actor) => receiversLock.synchronized{ - receivers += actor - } - - case Unregister(actor) => receiversLock.synchronized{ - receivers -= actor - } - - case Write(data, ack) => { - val sent = serial.write(data.toArray) - if (ack != NoAck) sender ! ack - } - - case Close => { - sendAllReceivers(Closed) - context stop self - } - - //go down with reader thread - case ReadException(ex) => throw ex - - } - private val receivers = new HashSet[ActorRef] private val receiversLock = new Object - private def sendAllReceivers(msg: Any) = receiversLock.synchronized { + private def tellAllReceivers(msg: Any) = receiversLock.synchronized { receivers.foreach { receiver => receiver ! msg } @@ -63,7 +34,7 @@ class SerialOperator(serial: InternalSerial) extends Actor with ActorLogging { while (continueReading) { try { val data = ByteString(serial.read()) - sendAllReceivers(Received(data)) + tellAllReceivers(Received(data)) } catch { //port is closing, stop thread gracefully @@ -88,8 +59,43 @@ class SerialOperator(serial: InternalSerial) extends Actor with ActorLogging { } } + override def preStart() = { + Reader.start() + } + + override def postStop = { + serial.close() + } + + def receive: Receive = { + + case Register(actor) => receiversLock.synchronized { + receivers += actor + } + + case Unregister(actor) => receiversLock.synchronized { + receivers -= actor + } + + case Write(data, ack) => { + val sent = serial.write(data.toArray) + if (ack != NoAck) sender ! ack + } + + case Close => { + tellAllReceivers(Closed) + context stop self + } + + //go down with reader thread + case ReadException(ex) => throw ex + + } + } object SerialOperator { private case class ReadException(ex: Exception) + + def apply(serial: InternalSerial) = Props(classOf[SerialOperator], serial) }
\ No newline at end of file diff --git a/flow-main/src/main/scala/com/github/jodersky/flow/SerialSettings.scala b/flow-main/src/main/scala/com/github/jodersky/flow/SerialSettings.scala new file mode 100644 index 0000000..a3bc5e4 --- /dev/null +++ b/flow-main/src/main/scala/com/github/jodersky/flow/SerialSettings.scala @@ -0,0 +1,11 @@ +package com.github.jodersky.flow + +/** + * Groups settings used in communication over a serial port. + * @param port name of serial port + * @param baud baud rate to use with serial port + * @param characterSize size of a character of the data sent through the serial port + * @param twoStopBits set to use two stop bits instead of one + * @param parity type of parity to use with serial port + */ +case class SerialSettings(port: String, baud: Int, characterSize: Int = 8, twoStopBits: Boolean = false, parity: Parity.Parity = Parity.None)
\ No newline at end of file |