diff options
Diffstat (limited to 'vfd-uav/src/main/scala/vfd')
-rw-r--r-- | vfd-uav/src/main/scala/vfd/uav/Connection.scala | 26 | ||||
-rw-r--r-- | vfd-uav/src/main/scala/vfd/uav/MockConnection.scala | 40 | ||||
-rw-r--r-- | vfd-uav/src/main/scala/vfd/uav/SerialConnection.scala | 101 |
3 files changed, 104 insertions, 63 deletions
diff --git a/vfd-uav/src/main/scala/vfd/uav/Connection.scala b/vfd-uav/src/main/scala/vfd/uav/Connection.scala index 5d9ed54..afc1c1a 100644 --- a/vfd-uav/src/main/scala/vfd/uav/Connection.scala +++ b/vfd-uav/src/main/scala/vfd/uav/Connection.scala @@ -4,21 +4,43 @@ import scala.collection.mutable.ArrayBuffer import akka.actor.Actor import akka.actor.ActorRef +import akka.actor.Terminated +import akka.actor.actorRef2Scala +import akka.util.ByteString object Connection { trait Event trait Command + + //received data from the uav (or any other systems on the link) + case class Received(bstr: ByteString) extends Event + + //the connection closed or could be opened + case class Closed(message: String) extends Event + + //register the sender to be notified on events case object Register extends Command - case class Received(bytes: Array[Byte]) extends Event + //send given bytes out to the uav (or any other systems on the link) + case class Send(bstr: ByteString) extends Command } trait Connection { that: Actor => private val _clients = new ArrayBuffer[ActorRef] + def clients = _clients.toSeq + def register(client: ActorRef) = { _clients += client; that.context.watch(client) } + def unregister(client: ActorRef) = _clients -= client -} + + def sendAll(msg: Any) = clients foreach (_ ! msg) + + def registration: Receive = { + case Connection.Register => register(sender) + case Terminated(client) if clients contains client => unregister(client) + } +}
\ No newline at end of file diff --git a/vfd-uav/src/main/scala/vfd/uav/MockConnection.scala b/vfd-uav/src/main/scala/vfd/uav/MockConnection.scala index 94a14db..126fa9a 100644 --- a/vfd-uav/src/main/scala/vfd/uav/MockConnection.scala +++ b/vfd-uav/src/main/scala/vfd/uav/MockConnection.scala @@ -1,33 +1,32 @@ package vfd.uav import java.util.concurrent.TimeUnit.MILLISECONDS + import scala.concurrent.duration.FiniteDuration +import scala.util.Random + +import Connection.Received import akka.actor.Actor -import akka.actor.Props -import akka.actor.Terminated -import akka.actor.actorRef2Scala import akka.actor.ActorLogging -import scala.util.Random +import akka.actor.Props +import akka.util.ByteString class MockConnection extends Actor with ActorLogging with Connection { import Connection._ import context._ val messageInterval = FiniteDuration(500, MILLISECONDS) - + override def preStart() = { context.system.scheduler.schedule(messageInterval, messageInterval) { val data = MockPackets.random() - + this.log.debug("sending mock flight data: " + data.mkString("(", ",", ")")) - clients foreach (_ ! Received(data)) + sendAll(Received(ByteString(data))) } } - def receive = { - case Connection.Register => register(sender) - case Terminated(client) => unregister(client) - } + def receive = registration } @@ -35,17 +34,14 @@ object MockConnection { def apply = Props(classOf[MockConnection]) } -object MockPackets { - - def random() = { - Random.nextInt(2) match { - case 0 => invalidCrc - case 1 => invalidOverflow - } - +object MockPackets { + + def random() = Random.nextInt(2) match { + case 0 => invalidCrc + case 1 => invalidOverflow } - - val invalidCrc = Array(254,1,123,13,13).map(_.toByte) + + val invalidCrc = Array(254, 1, 123, 13, 13).map(_.toByte) val invalidOverflow = { val data = Array.fill[Byte](1006)(42) data(0) = -2 @@ -53,5 +49,5 @@ object MockPackets { data(1) = -1 data } - + }
\ No newline at end of file diff --git a/vfd-uav/src/main/scala/vfd/uav/SerialConnection.scala b/vfd-uav/src/main/scala/vfd/uav/SerialConnection.scala index f1d3186..3b08215 100644 --- a/vfd-uav/src/main/scala/vfd/uav/SerialConnection.scala +++ b/vfd-uav/src/main/scala/vfd/uav/SerialConnection.scala @@ -1,73 +1,95 @@ package vfd.uav +import java.util.concurrent.TimeUnit.MILLISECONDS + +import scala.concurrent.duration.FiniteDuration + import com.github.jodersky.flow.Parity +import com.github.jodersky.flow.Serial import com.github.jodersky.flow.SerialSettings import akka.actor.Actor +import akka.actor.ActorRef import akka.actor.Props +import akka.actor.Terminated +import akka.actor.actorRef2Scala +import akka.io.IO +import akka.util.ByteString -class SerialConnection(id: Byte, heartbeat: Int, port: String, settings: SerialSettings) extends Actor with Connection { +class SerialConnection(id: Byte, heartbeat: Option[FiniteDuration], port: String, settings: SerialSettings) extends Actor with Connection { import context._ - - //TODO implement actor logic - def receive = { - case _ => () - } -/* + val Heartbeat = ByteString( + Array(-2, 9, -121, 20, -56, 0, 0, 0, 0, 0, 2, 0, 0, 3, 3, -112, 76).map(_.toByte)) + override def preStart() = { - context.system.scheduler.schedule(messageInterval, messageInterval){ - self ! Connection.Write(Array(-2, 9, -121, 20, -56, 0, 0, 0, 0, 0, 2, 0, 0, 3, 3, -112, 76).map(_.toByte)) + heartbeat foreach { interval => + context.system.scheduler.schedule(interval, interval, self, Connection.Send(Heartbeat)) } } + def _closed: Receive = { - def receive = closed - - def closed: Receive = { - case Connection.Register(client) => - register(client) - IO(Serial) ! Open(port, settings) + case Connection.Register => + register(sender) + IO(Serial) ! Serial.Open(port, settings) context become opening - case Terminated(client) if (clients contains client) => unregister(client) - - case Connection.Write(data) => - IO(Serial) ! Open(port, settings) + case Connection.Send(_) => + IO(Serial) ! Serial.Open(port, settings) context become opening } - def opening: Receive = { - case Connection.Register(client) => register(client) - case Terminated(client) if (clients contains client) => unregister(client) - - case Connection.Write(data) => + def _opening: Receive = { case Serial.CommandFailed(cmd: Serial.Open, reason) => - Log(reason) - //for (c <- clients) client ! Error //TODO send proper error code + sendAll(Connection.Closed(reason.toString)) context become closed - case Serial.Opened(settings) => - val operator = sender - context watch operator - context become open(operator) + case Serial.Opened(_) => + context watch (sender) + context become opened(sender) + + case Connection.Send(_) => () // ignore + /* + * During opening, any outgoing messages are discarded. + * By using some kind of message stashing, maybe messages could be treated + * once the port has been opened. However, in such a case failure also needs + * to be considered complicating the protocol. Since opening is typically + * quite fast and since mavlink uses heartbeats and acknowledgements (in certain + * circumstances) anyway, keeping messages is not really required. + */ } - def open(operator: ActorRef): Receive = { - case Terminated(`operator`) => - //for (client <- clients) ! Error //TODO send error code + def _opened(operator: ActorRef): Receive = { + + case Terminated(`operator`) => + sendAll(Connection.Closed("Serial connection crashed.")) + context become closed + + case Serial.Closed => + sendAll(Connection.Closed("Serial connection was closed.")) context become closed - case Connection.Write(data) => operator ! ByteString(data) + case Serial.Received(bstr) => + sendAll(Connection.Received(bstr)) + + case Connection.Send(bstr) => + operator ! Serial.Write(bstr) + } -*/ + + def receive = closed + def closed = _closed orElse registration + def opening = _opening orElse registration + def opened(op: ActorRef) = _opened(op) orElse registration + } object SerialConnection { - def apply(id: Byte, hearbeat: Int, port: String, baud: Int, tsb: Boolean, parity: Int) = { + def apply(id: Byte, heartbeat: Int, port: String, baud: Int, tsb: Boolean, parity: Int) = { val settings = SerialSettings( baud, 8, @@ -76,8 +98,9 @@ object SerialConnection { case 0 => Parity.None case 1 => Parity.Odd case 2 => Parity.Even - } - ) - Props(classOf[SerialConnection], id, hearbeat, port, settings) + }) + val hb = if (heartbeat == 0) None else Some(FiniteDuration(heartbeat, MILLISECONDS)) + + Props(classOf[SerialConnection], id, hb, port, settings) } }
\ No newline at end of file |