Skip to content

Instantly share code, notes, and snippets.

@joesan
Last active January 5, 2017 15:16
Show Gist options
  • Save joesan/9c883161b610652cfb74385e8fba09e3 to your computer and use it in GitHub Desktop.
Save joesan/9c883161b610652cfb74385e8fba09e3 to your computer and use it in GitHub Desktop.
Akka Flow Serial Actor Example
import akka.actor.{Actor, ActorRef, Props, Terminated}
import akka.util.ByteString
import ch.jodersky.flow.Serial
import com.typesafe.scalalogging.LazyLogging
import my.samples.deviceManager.DeviceWatcherActor.InitializeSerialPort
class DeviceExplorerActor(serialActorRef: ActorRef)
extends Actor with LazyLogging {
def receive: Receive = {
case InitializeSerialPort(serialPort, serialSettings) =>
serialActorRef ! Serial.Open(serialPort, serialSettings)
case Serial.CommandFailed(cmd, reason) =>
logger.error(s"Connection failed, stopping terminal. Reason: $reason")
//context stop self
case Serial.Opened(port) =>
logger.info(s"Port $port is now open.")
context become opened(sender)
//context watch sender // get notified in the event the operator crashes
}
def opened(operator: ActorRef): Receive = {
case Serial.Received(data) =>
logger.info(s"Received data: " + data)
case Serial.Closed =>
logger.info("Operator closed normally, exiting terminal.")
context stop self
case Terminated(`operator`) =>
logger.error("Operator crashed unexpectedly, exiting terminal.")
context stop self
case ":q" =>
operator ! Serial.Close
case str: String =>
operator ! Serial.Write(ByteString(str))
}
}
object DeviceExplorerActor {
def props(serialActorRef: ActorRef) =
Props(new DeviceExplorerActor(serialActorRef))
}
import akka.actor.{Actor, ActorRef, Kill, Props}
import ch.jodersky.flow.{Serial, SerialSettings}
import com.typesafe.scalalogging.LazyLogging
import my.samples.deviceManager.DeviceWatcherActor.{InitializeSerialPort, Terminate}
class DeviceWatcherActor(portSettings: Map[String, SerialSettings], serialActorRef: ActorRef)
extends Actor with LazyLogging {
val ports = portSettings.keys.toSeq
val portActorRef =
ports.map(port => port -> context.actorOf(DeviceExplorerActor.props(serialActorRef))).toMap
override def preStart() = {
val cmd = Serial.Watch()
serialActorRef ! Serial.Watch()
logger.info(s"Watching ${cmd.directory} for new devices.")
}
override def receive: Receive = {
case Terminate =>
portActorRef.values.toSeq.foreach(actorRef => actorRef ! ":q")
context stop self
case Serial.CommandFailed(w: Serial.Watch, err) =>
logger.error(s"Could not get a watch on ${w.directory}.", err)
self ! Kill // TODO: catch the ActorKilledException in the parent
case Serial.Connected(path) =>
logger.info(s"New device: $path")
val found: Option[(String, SerialSettings)] = portSettings.find(_._1.matches(path))
found match {
case Some((port, serialSettings)) =>
logger.info(s"Device is a serial device.")
portActorRef(port) ! InitializeSerialPort(port, serialSettings)
case None =>
logger.warn(s"Device is NOT serial device.")
}
}
}
object DeviceWatcherActor {
def props(portSettings: Map[String, SerialSettings], serialActorRef: ActorRef) =
Props(new DeviceWatcherActor(portSettings, serialActorRef))
case object Terminate
case class InitializeSerialPort(port: String, serialSettings: SerialSettings)
}
@jodersky
Copy link

jodersky commented Jan 5, 2017

I don't think that the serialActorRef parameter is required here (from a quick, superficial overview). You can simply use IO(Serial) instead. This uses the Akka extension mechanism to provide you with the same single actor instance, regardless of the number of invocations.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment