aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--backend/app/controllers/Application.scala52
-rw-r--r--backend/app/uav/UavPlugin.scala34
-rw-r--r--backend/app/uav/connections.scala123
-rw-r--r--backend/conf/play.plugins1
-rw-r--r--project/Build.scala61
-rw-r--r--project/Dependencies.scala11
-rw-r--r--project/util.scala32
-rw-r--r--vfd-backend/.gitignore (renamed from backend/.gitignore)0
-rw-r--r--vfd-backend/app/controllers/Application.scala31
-rw-r--r--vfd-backend/app/plugins/UavPlugin.scala44
-rw-r--r--vfd-backend/app/views/index.scala.html (renamed from backend/app/views/index.scala.html)0
-rw-r--r--vfd-backend/app/views/main.scala.html (renamed from backend/app/views/main.scala.html)3
-rw-r--r--vfd-backend/app/views/panels/eicas.scala.html (renamed from backend/app/views/panels/eicas.scala.html)0
-rw-r--r--vfd-backend/app/views/panels/pfd.scala.html (renamed from backend/app/views/panels/pfd.scala.html)0
-rw-r--r--vfd-backend/conf/application.conf (renamed from backend/conf/application.conf)6
-rw-r--r--vfd-backend/conf/play.plugins1
-rw-r--r--vfd-backend/conf/routes (renamed from backend/conf/routes)0
-rw-r--r--vfd-backend/public/fonts/DIGITAL.TXT (renamed from backend/public/fonts/DIGITAL.TXT)0
-rw-r--r--vfd-backend/public/fonts/DS-DIGIB.TTF (renamed from backend/public/fonts/DS-DIGIB.TTF)bin24896 -> 24896 bytes
-rw-r--r--vfd-backend/public/fonts/DS-DIGII.TTF (renamed from backend/public/fonts/DS-DIGII.TTF)bin24676 -> 24676 bytes
-rw-r--r--vfd-backend/public/fonts/DS-DIGIT.TTF (renamed from backend/public/fonts/DS-DIGIT.TTF)bin25480 -> 25480 bytes
-rw-r--r--vfd-backend/public/fonts/ds-digi.ttf (renamed from backend/public/fonts/ds-digi.ttf)bin24448 -> 24448 bytes
-rw-r--r--vfd-backend/public/images/instruments/altitude.svg (renamed from backend/public/images/instruments/altitude.svg)0
-rw-r--r--vfd-backend/public/images/instruments/attitude.svg (renamed from backend/public/images/instruments/attitude.svg)0
-rw-r--r--vfd-backend/public/images/instruments/distance.svg (renamed from backend/public/images/instruments/distance.svg)0
-rw-r--r--vfd-backend/public/images/instruments/generic.svg (renamed from backend/public/images/instruments/generic.svg)0
-rw-r--r--vfd-backend/public/images/instruments/heading.svg (renamed from backend/public/images/instruments/heading.svg)0
-rw-r--r--vfd-backend/public/images/leds/green-off.svg (renamed from backend/public/images/leds/green-off.svg)0
-rw-r--r--vfd-backend/public/images/leds/green-on.svg (renamed from backend/public/images/leds/green-on.svg)0
-rw-r--r--vfd-backend/public/images/leds/none.svg (renamed from backend/public/images/leds/none.svg)0
-rw-r--r--vfd-backend/public/images/leds/red-off.svg (renamed from backend/public/images/leds/red-off.svg)0
-rw-r--r--vfd-backend/public/images/leds/red-on.svg (renamed from backend/public/images/leds/red-on.svg)0
-rw-r--r--vfd-backend/public/images/leds/yellow-on.svg (renamed from backend/public/images/leds/yellow-on.svg)0
-rw-r--r--vfd-backend/public/images/logo-invert.svg (renamed from backend/public/images/logo.svg)0
-rw-r--r--vfd-backend/public/images/logo.svg87
-rw-r--r--vfd-backend/public/stylesheets/main.css (renamed from backend/public/stylesheets/main.css)0
-rw-r--r--vfd-frontend/.gitignore (renamed from frontend/.gitignore)0
-rw-r--r--vfd-frontend/src/main/scala/Frontend.scala (renamed from frontend/src/main/scala/Frontend.scala)3
-rw-r--r--vfd-shared/src/main/scala/vfd/uav/DataFrame.scala18
-rw-r--r--vfd-uav/src/main/scala/vfd/uav/Connection.scala27
-rw-r--r--vfd-uav/src/main/scala/vfd/uav/DummyConnection.scala43
-rw-r--r--vfd-uav/src/main/scala/vfd/uav/FcuConnection.scala53
-rw-r--r--vfd-uav/src/main/scala/vfd/uav/Framer.scala70
43 files changed, 451 insertions, 249 deletions
diff --git a/backend/app/controllers/Application.scala b/backend/app/controllers/Application.scala
deleted file mode 100644
index 1abaee0..0000000
--- a/backend/app/controllers/Application.scala
+++ /dev/null
@@ -1,52 +0,0 @@
-package controllers
-
-import uav._
-import play.api._
-import play.api.mvc._
-import play.api.Play.current
-import play.api.mvc.WebSocket.FrameFormatter
-
-import play.api.libs.functional.syntax._
-import play.api.libs.json._
-
-
-object Application extends Controller {
- def use[A <: Plugin](implicit app: Application, m: Manifest[A]) = {
- app.plugin[A].getOrElse(throw new RuntimeException(m.runtimeClass.toString + " plugin should be available at this point"))
- }
-
-
-
- implicit val dataWrites: Writes[UavConnection.Data] = (
- (__ \ "roll").write[Double] and
- (__ \ "pitch").write[Double] and
- (__ \ "heading").write[Double] and
- (__ \ "altitude").write[Double] and
- (__ \ "temperature").write[Double])(unlift(UavConnection.Data.unapply))
-
- implicit val dataReads: Reads[UavConnection.Data] = (
- (__ \ "roll").read[Double] and
- (__ \ "pitch").read[Double] and
- (__ \ "heading").read[Double] and
- (__ \ "altitude").read[Double] and
- (__ \ "temperature").read[Double])(UavConnection.Data.apply _)
-
- implicit val dataFormat = Format(dataReads, dataWrites)
-
-
- //implicit val inEventFormat = Json.format[uav.UavConnection.Data]
- //implicit val outEventFormat = Json.format[uav.Data]
-
-
-
- def index() = Action {
- Ok(views.html.index())
- }
-
- implicit val dataFrameFormatter = FrameFormatter.jsonFrame[UavConnection.Data]
-
-
- def socket = WebSocket.acceptWithActor[String, UavConnection.Data] { request =>
- out => use[UavPlugin].register(out)
- }
-} \ No newline at end of file
diff --git a/backend/app/uav/UavPlugin.scala b/backend/app/uav/UavPlugin.scala
deleted file mode 100644
index 060e3dc..0000000
--- a/backend/app/uav/UavPlugin.scala
+++ /dev/null
@@ -1,34 +0,0 @@
-package uav
-
-import akka.actor._
-import play.api._
-import play.api.libs.concurrent.Akka
-
-class UavPlugin(app: Application) extends Plugin {
-
- lazy val uav: ActorRef = Akka.system(app).actorOf(UavConnection.fcu, name = "uav")
-
- def register(out: ActorRef): Props = Repeater(out, uav)
-
-
-}
-
-class Repeater(out: ActorRef, uav: ActorRef) extends Actor {
-
- override def preStart = {
- uav ! UavConnection.Register
- }
-
- def receive = {
- case msg => sender match {
- case `out` => uav ! msg
- case `uav` => out ! msg
- case _ => throw new RuntimeException("Unknown sender")
- }
- }
-
- }
-
-object Repeater {
- def apply(out: ActorRef, uav: ActorRef) = Props(classOf[Repeater], out, uav)
-} \ No newline at end of file
diff --git a/backend/app/uav/connections.scala b/backend/app/uav/connections.scala
deleted file mode 100644
index a922519..0000000
--- a/backend/app/uav/connections.scala
+++ /dev/null
@@ -1,123 +0,0 @@
-package uav
-
-import akka.actor.Actor
-import akka.actor.ActorRef
-import akka.actor.Props
-import akka.actor.Terminated
-import scala.concurrent.duration.FiniteDuration
-import scala.collection.mutable.ArrayBuffer
-import java.util.concurrent.TimeUnit._
-import akka.io.IO
-import com.github.jodersky.flow._
-import com.github.jodersky.flow.Serial
-
-object UavConnection {
- def dummy = Props(classOf[DummyConnection])
- def fcu = Props(classOf[FcuConnection])
-
- trait Event
- trait Command
- case object Register extends Command
-
- case class Data(
- roll: Double,
- pitch: Double,
- heading: Double,
- altitude: Double,
- temperature: Double
- ) extends Event
-}
-
-trait UavConnection {selfs: Actor =>
- private val _clients = new ArrayBuffer[ActorRef]
- def clients = _clients.toSeq
- def register(client: ActorRef) = {
- _clients += client;
- selfs.context.watch(client)
- }
- def unregister(client: ActorRef) = _clients -= client
-}
-
-class DummyConnection extends Actor with UavConnection {
- import UavConnection._
- import context._
-
- var time = 0.0
- val messageInterval = FiniteDuration(20, MILLISECONDS)
-
- def flightData(time: Double) = {
- val speed = 5.0 / 1000
- val roll = 5.0/180*math.Pi
- val pitch = 10.0/180*math.Pi
- Data(
- roll,
- pitch,
- (roll * time * speed) % math.Pi,
- (pitch * time * speed),
- 22
- )
- }
-
-
- override def preStart() = {
- context.system.scheduler.schedule(messageInterval, messageInterval){
- time += messageInterval.toMillis
- clients foreach (_ ! flightData(time))
- }
- }
-
- def receive = {
- case Register => register(sender)
- case Terminated(client) => unregister(client)
- case _ => ()
- }
-
-}
-
-
-class FcuConnection extends Actor with UavConnection {
- import UavConnection._
- import context._
-
- val port = "/dev/ttyACM0"
- val settings = SerialSettings(
- baud = 9600,
- characterSize = 8,
- twoStopBits = false,
- parity = Parity.None
- )
-
- override def preStart() = {
- IO(Serial) ! Serial.Open(port, settings)
- }
-
- def receive = {
- case Register => register(sender)
- case Terminated(client) => unregister(client)
- case Serial.CommandFailed(cmd: Serial.Open, reason: AccessDeniedException) => println("you're not allowed to open that port!")
- case Serial.CommandFailed(cmd: Serial.Open, reason) => println("could not open port for some other reason: " + reason)
- case Serial.Opened(settings) => {
- val operator = sender
-
- }
- case Serial.Received(bstr) =>
- val str = (new String(bstr.toArray, "UTF-8")).trim
-
-
- val LinePattern = ".*[(](.+)[)].*".r
- val Number = "([-]?\\d+[.]\\d+?)".r
- val Components = "(.+),(.+),(.+),(.+),(.+)".r
-
- str match {
- case LinePattern(Components(Number(r),Number(p),Number(h),Number(a),Number(t))) =>
- val data = Data(r.toDouble, p.toDouble, h.toDouble, a.toDouble, t.toDouble)
- println(data)
- for (client <- clients) {
- client ! data
- }
- case _ => println("unknown message: " + str)
- }
- }
-
-}
-
diff --git a/backend/conf/play.plugins b/backend/conf/play.plugins
deleted file mode 100644
index 2211f4a..0000000
--- a/backend/conf/play.plugins
+++ /dev/null
@@ -1 +0,0 @@
- 10000:uav.UavPlugin \ No newline at end of file
diff --git a/project/Build.scala b/project/Build.scala
index af78ff5..9c6789b 100644
--- a/project/Build.scala
+++ b/project/Build.scala
@@ -1,66 +1,65 @@
import sbt._
import sbt.Keys._
+import util._
import play._
import play.PlayImport.PlayKeys._
import scala.scalajs.sbtplugin.ScalaJSPlugin
import scala.scalajs.sbtplugin.ScalaJSPlugin.ScalaJSKeys._
+import Dependencies._
object ApplicationBuild extends Build {
val common = Seq(
scalaVersion := "2.11.2",
- scalacOptions ++= Seq("-feature")
+ scalacOptions ++= Seq("-feature"),
+ unmanagedSourceDirectories in Compile += (baseDirectory in ThisBuild).value / "vfd-shared" / "src" / "main" / "scala",
+ unmanagedResourceDirectories in Compile += (baseDirectory in ThisBuild).value / "vfd-shared" / "src" / "main" / "resources"
)
lazy val root = Project("root", file(".")).aggregate(
+ uav,
backend,
frontend
)
+ lazy val uav = (
+ Project("vfd-uav", file("vfd-uav"))
+ settings(common: _*)
+ settings(
+ libraryDependencies ++= Seq(
+ akkaActor,
+ flow,
+ flowNative
+ )
+ )
+ )
+
lazy val backend = (
- Project("vfd-backend", file("backend"))
+ Project("vfd-backend", file("vfd-backend"))
enablePlugins(PlayScala)
settings(common: _*)
settings(
- libraryDependencies ++= Dependencies.backend
+ libraryDependencies ++= Seq(
+ bootstrap,
+ fontawesome,
+ jquery
+ )
)
+ dependsOn(uav)
dependsOnJs(frontend)
)
lazy val frontend = (
- Project("vfd-frontend", file("frontend"))
+ Project("vfd-frontend", file("vfd-frontend"))
settings(ScalaJSPlugin.scalaJSSettings: _*)
settings(common: _*)
settings(
- libraryDependencies ++= Dependencies.frontend
+ libraryDependencies ++= Seq(
+ rx,
+ dom
+ )
)
)
-
-
- implicit class ScalaJSPlayProject(val project: Project) {
- def dependsOnJs(reference: Project): Project = project.settings(
- resourceGenerators in Compile += Def.task{
- val outDir: File = (resourceManaged in Compile).value / "public" / "lib"
-
- val optimized: Seq[File] = (fastOptJS in (reference, Compile)).value.allCode.map(_.path).map(file(_))
-
- val outFiles = optimized.map(file => outDir / file.name)
-
- for ((opt, out) <- optimized zip outFiles) {
- if (!out.exists || out.lastModified < opt.lastModified) {
- IO.copyFile(opt, out, true)
- val map = opt.getParentFile / (out.name + ".map")
- IO.copyFile(map, outDir / map.name)
- }
- }
- outFiles
- }.taskValue,
- playMonitoredFiles ++= (watchSources in reference).value.map(_.getCanonicalPath)
- )
- }
-
-
-
} \ No newline at end of file
diff --git a/project/Dependencies.scala b/project/Dependencies.scala
index 6e30920..73b07ac 100644
--- a/project/Dependencies.scala
+++ b/project/Dependencies.scala
@@ -3,17 +3,16 @@ import scala.scalajs.sbtplugin.ScalaJSPlugin._
object Dependencies {
- val flow = "com.github.jodersky" %% "flow" % "2.0.4"
- val flowNative = "com.github.jodersky" % "flow-native" % "2.0.4"
+ val akkaActor = "com.typesafe.akka" %% "akka-actor" % "2.3.6"
- val dom = "org.scala-lang.modules.scalajs" %%%! "scalajs-dom" % "0.6"
- val rx = "com.scalarx" %%%! "scalarx" % "0.2.5"
+ val flow = "com.github.jodersky" %% "flow" % "2.0.6"
+ val flowNative = "com.github.jodersky" % "flow-native" % "2.0.6"
val bootstrap = "org.webjars" % "bootstrap" % "3.2.0"
val fontawesome = "org.webjars" % "font-awesome" % "4.2.0"
val jquery = "org.webjars" % "jquery" % "2.1.1"
+ val dom = "org.scala-lang.modules.scalajs" %%%! "scalajs-dom" % "0.6"
+ val rx = "com.scalarx" %%%! "scalarx" % "0.2.5"
- def backend = Seq(bootstrap, fontawesome, jquery, flow, flowNative)
- def frontend = Seq(dom, rx)
} \ No newline at end of file
diff --git a/project/util.scala b/project/util.scala
new file mode 100644
index 0000000..595d7fe
--- /dev/null
+++ b/project/util.scala
@@ -0,0 +1,32 @@
+import sbt._
+import sbt.Keys._
+import play._
+import play.PlayImport.PlayKeys._
+import scala.scalajs.sbtplugin.ScalaJSPlugin
+import scala.scalajs.sbtplugin.ScalaJSPlugin.ScalaJSKeys._
+
+package object util {
+
+ implicit class ScalaJSPlayProject(val project: Project) {
+ def dependsOnJs(reference: Project): Project = project.settings(
+ resourceGenerators in Compile += Def.task{
+ val outDir: File = (resourceManaged in Compile).value / "public" / "lib"
+
+ val optimized: Seq[File] = (fastOptJS in (reference, Compile)).value.allCode.map(_.path).map(file(_))
+
+ val outFiles = optimized.map(file => outDir / file.name)
+
+ for ((opt, out) <- optimized zip outFiles) {
+ if (!out.exists || out.lastModified < opt.lastModified) {
+ IO.copyFile(opt, out, true)
+ val map = opt.getParentFile / (out.name + ".map")
+ IO.copyFile(map, outDir / map.name)
+ }
+ }
+ outFiles
+ }.taskValue,
+ playMonitoredFiles ++= (watchSources in reference).value.map(_.getCanonicalPath)
+ )
+ }
+
+} \ No newline at end of file
diff --git a/backend/.gitignore b/vfd-backend/.gitignore
index ed1de16..ed1de16 100644
--- a/backend/.gitignore
+++ b/vfd-backend/.gitignore
diff --git a/vfd-backend/app/controllers/Application.scala b/vfd-backend/app/controllers/Application.scala
new file mode 100644
index 0000000..5a473b4
--- /dev/null
+++ b/vfd-backend/app/controllers/Application.scala
@@ -0,0 +1,31 @@
+package controllers
+
+import play.api._
+import play.api.mvc._
+import play.api.Play.current
+import play.api.mvc.WebSocket.FrameFormatter
+
+import play.api.libs.functional.syntax._
+import play.api.libs.json._
+
+import vfd.uav.DataFrame
+import plugins.UavPlugin
+
+
+object Application extends Controller {
+
+ def use[A <: Plugin](implicit app: Application, m: Manifest[A]) = {
+ app.plugin[A].getOrElse(throw new RuntimeException(m.runtimeClass.toString + " plugin should be available at this point"))
+ }
+
+ def index() = Action {
+ Ok(views.html.index())
+ }
+
+ implicit val dataFrameFormat = Json.format[DataFrame]
+ implicit val dataFrameFormatter = FrameFormatter.jsonFrame[DataFrame]
+
+ def socket = WebSocket.acceptWithActor[String, DataFrame] { request =>
+ out => use[UavPlugin].register(out)
+ }
+} \ No newline at end of file
diff --git a/vfd-backend/app/plugins/UavPlugin.scala b/vfd-backend/app/plugins/UavPlugin.scala
new file mode 100644
index 0000000..a94ed9d
--- /dev/null
+++ b/vfd-backend/app/plugins/UavPlugin.scala
@@ -0,0 +1,44 @@
+package plugins
+
+import akka.actor._
+import play.api._
+import play.api.libs.concurrent.Akka
+import vfd.uav._
+
+class UavPlugin(app: Application) extends Plugin {
+
+ object conf {
+ private val config = app.configuration.getConfig("uav")
+ val connection = config.flatMap(_.getString("connection")).getOrElse("mock")
+ val port = config.flatMap(_.getString("port")).getOrElse("/dev/ttyACM0")
+ val baud = config.flatMap(_.getInt("baud")).getOrElse(9600)
+ }
+
+ lazy val connection: ActorRef = {
+ val props = conf.connection match {
+ case "mock" => Connection.dummy
+ case "fcu" => Connection.fcu(conf.port, conf.baud)
+ case _ => throw new RuntimeException("Unknown connection type.")
+ }
+ Akka.system(app).actorOf(props, name = "uav")
+ }
+
+ def register(out: ActorRef): Props = Repeater(out, connection)
+
+}
+
+class Repeater(out: ActorRef, connection: ActorRef) extends Actor {
+
+ override def preStart = {
+ connection ! Connection.Register
+ }
+
+ def receive = {
+ case Connection.NewDataFrame(df) => out ! df
+ }
+
+ }
+
+object Repeater {
+ def apply(out: ActorRef, connection: ActorRef) = Props(classOf[Repeater], out, connection)
+} \ No newline at end of file
diff --git a/backend/app/views/index.scala.html b/vfd-backend/app/views/index.scala.html
index 654c2a1..654c2a1 100644
--- a/backend/app/views/index.scala.html
+++ b/vfd-backend/app/views/index.scala.html
diff --git a/backend/app/views/main.scala.html b/vfd-backend/app/views/main.scala.html
index 86f1384..db894d2 100644
--- a/backend/app/views/main.scala.html
+++ b/vfd-backend/app/views/main.scala.html
@@ -9,6 +9,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>VFD - @title</title>
+ <link rel="shortcut icon" href="@routes.Assets.at("images/logo.svg")">
<link rel="stylesheet" media="screen" href="@routes.Assets.at("lib/bootstrap/css/bootstrap.css")">
<link rel="stylesheet" media="screen" href="@routes.Assets.at("stylesheets/main.css")">
</head>
@@ -25,7 +26,7 @@
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="@routes.Application.index">
- <img style="max-height: 32px;" src="@routes.Assets.at("images/logo.svg")" alt="logo">
+ <!-- <img style="max-height: 100%;" src="@routes.Assets.at("images/logo-invert.svg")" alt="logo"> -->
Virtual Flight Deck
</a>
</div>
diff --git a/backend/app/views/panels/eicas.scala.html b/vfd-backend/app/views/panels/eicas.scala.html
index 211adba..211adba 100644
--- a/backend/app/views/panels/eicas.scala.html
+++ b/vfd-backend/app/views/panels/eicas.scala.html
diff --git a/backend/app/views/panels/pfd.scala.html b/vfd-backend/app/views/panels/pfd.scala.html
index 9c89d6a..9c89d6a 100644
--- a/backend/app/views/panels/pfd.scala.html
+++ b/vfd-backend/app/views/panels/pfd.scala.html
diff --git a/backend/conf/application.conf b/vfd-backend/conf/application.conf
index ba58ed1..96478c0 100644
--- a/backend/conf/application.conf
+++ b/vfd-backend/conf/application.conf
@@ -60,3 +60,9 @@ logger.play=INFO
# Logger provided to your application:
logger.application=DEBUG
+# UAV
+uav.connection=mock
+uav.port="/dev/ttyACM0"
+uav.baud=9600
+
+
diff --git a/vfd-backend/conf/play.plugins b/vfd-backend/conf/play.plugins
new file mode 100644
index 0000000..40c58dd
--- /dev/null
+++ b/vfd-backend/conf/play.plugins
@@ -0,0 +1 @@
+ 10000:plugins.UavPlugin \ No newline at end of file
diff --git a/backend/conf/routes b/vfd-backend/conf/routes
index b211b07..b211b07 100644
--- a/backend/conf/routes
+++ b/vfd-backend/conf/routes
diff --git a/backend/public/fonts/DIGITAL.TXT b/vfd-backend/public/fonts/DIGITAL.TXT
index 484dd47..484dd47 100644
--- a/backend/public/fonts/DIGITAL.TXT
+++ b/vfd-backend/public/fonts/DIGITAL.TXT
diff --git a/backend/public/fonts/DS-DIGIB.TTF b/vfd-backend/public/fonts/DS-DIGIB.TTF
index 064ad47..064ad47 100644
--- a/backend/public/fonts/DS-DIGIB.TTF
+++ b/vfd-backend/public/fonts/DS-DIGIB.TTF
Binary files differ
diff --git a/backend/public/fonts/DS-DIGII.TTF b/vfd-backend/public/fonts/DS-DIGII.TTF
index 2aae3d8..2aae3d8 100644
--- a/backend/public/fonts/DS-DIGII.TTF
+++ b/vfd-backend/public/fonts/DS-DIGII.TTF
Binary files differ
diff --git a/backend/public/fonts/DS-DIGIT.TTF b/vfd-backend/public/fonts/DS-DIGIT.TTF
index 65642f9..65642f9 100644
--- a/backend/public/fonts/DS-DIGIT.TTF
+++ b/vfd-backend/public/fonts/DS-DIGIT.TTF
Binary files differ
diff --git a/backend/public/fonts/ds-digi.ttf b/vfd-backend/public/fonts/ds-digi.ttf
index 0925877..0925877 100644
--- a/backend/public/fonts/ds-digi.ttf
+++ b/vfd-backend/public/fonts/ds-digi.ttf
Binary files differ
diff --git a/backend/public/images/instruments/altitude.svg b/vfd-backend/public/images/instruments/altitude.svg
index 24146a1..24146a1 100644
--- a/backend/public/images/instruments/altitude.svg
+++ b/vfd-backend/public/images/instruments/altitude.svg
diff --git a/backend/public/images/instruments/attitude.svg b/vfd-backend/public/images/instruments/attitude.svg
index 7f0452c..7f0452c 100644
--- a/backend/public/images/instruments/attitude.svg
+++ b/vfd-backend/public/images/instruments/attitude.svg
diff --git a/backend/public/images/instruments/distance.svg b/vfd-backend/public/images/instruments/distance.svg
index 1f4d6ff..1f4d6ff 100644
--- a/backend/public/images/instruments/distance.svg
+++ b/vfd-backend/public/images/instruments/distance.svg
diff --git a/backend/public/images/instruments/generic.svg b/vfd-backend/public/images/instruments/generic.svg
index 02e4caa..02e4caa 100644
--- a/backend/public/images/instruments/generic.svg
+++ b/vfd-backend/public/images/instruments/generic.svg
diff --git a/backend/public/images/instruments/heading.svg b/vfd-backend/public/images/instruments/heading.svg
index 83cf17b..83cf17b 100644
--- a/backend/public/images/instruments/heading.svg
+++ b/vfd-backend/public/images/instruments/heading.svg
diff --git a/backend/public/images/leds/green-off.svg b/vfd-backend/public/images/leds/green-off.svg
index 1c227fd..1c227fd 100644
--- a/backend/public/images/leds/green-off.svg
+++ b/vfd-backend/public/images/leds/green-off.svg
diff --git a/backend/public/images/leds/green-on.svg b/vfd-backend/public/images/leds/green-on.svg
index c358e3a..c358e3a 100644
--- a/backend/public/images/leds/green-on.svg
+++ b/vfd-backend/public/images/leds/green-on.svg
diff --git a/backend/public/images/leds/none.svg b/vfd-backend/public/images/leds/none.svg
index 85bc475..85bc475 100644
--- a/backend/public/images/leds/none.svg
+++ b/vfd-backend/public/images/leds/none.svg
diff --git a/backend/public/images/leds/red-off.svg b/vfd-backend/public/images/leds/red-off.svg
index ecd3ca5..ecd3ca5 100644
--- a/backend/public/images/leds/red-off.svg
+++ b/vfd-backend/public/images/leds/red-off.svg
diff --git a/backend/public/images/leds/red-on.svg b/vfd-backend/public/images/leds/red-on.svg
index e7fffea..e7fffea 100644
--- a/backend/public/images/leds/red-on.svg
+++ b/vfd-backend/public/images/leds/red-on.svg
diff --git a/backend/public/images/leds/yellow-on.svg b/vfd-backend/public/images/leds/yellow-on.svg
index 3b271b0..3b271b0 100644
--- a/backend/public/images/leds/yellow-on.svg
+++ b/vfd-backend/public/images/leds/yellow-on.svg
diff --git a/backend/public/images/logo.svg b/vfd-backend/public/images/logo-invert.svg
index eb38e8f..eb38e8f 100644
--- a/backend/public/images/logo.svg
+++ b/vfd-backend/public/images/logo-invert.svg
diff --git a/vfd-backend/public/images/logo.svg b/vfd-backend/public/images/logo.svg
new file mode 100644
index 0000000..b905aad
--- /dev/null
+++ b/vfd-backend/public/images/logo.svg
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="64"
+ height="64"
+ id="svg2985"
+ version="1.1"
+ inkscape:version="0.48.5 r10040"
+ sodipodi:docname="logo.svg"
+ viewBox="0 0 64 64">
+ <defs
+ id="defs2987" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="11"
+ inkscape:cx="39.433014"
+ inkscape:cy="29.099049"
+ inkscape:current-layer="layer1"
+ showgrid="true"
+ inkscape:document-units="px"
+ inkscape:grid-bbox="true"
+ inkscape:window-width="1920"
+ inkscape:window-height="1029"
+ inkscape:window-x="0"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1" />
+ <metadata
+ id="metadata2990">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer">
+ <path
+ style="fill:#222222;fill-opacity:1;stroke:#222222;stroke-opacity:1"
+ d="m 15.863636,5.6278409 c -5.595824,0 -10.144886,4.5490621 -10.144886,10.1448861 0,5.595824 4.549062,10.144886 10.144886,10.144886 5.595824,0 10.144886,-4.549062 10.144886,-10.144886 0,-5.595824 -4.549062,-10.1448861 -10.144886,-10.1448861 z m 0,1.7765239 c 4.619372,0 8.368362,3.7489902 8.368362,8.3683622 0,4.619372 -3.74899,8.368362 -8.368362,8.368362 -4.619372,0 -8.3683621,-3.74899 -8.3683621,-8.368362 0,-4.619372 3.7489901,-8.3683622 8.3683621,-8.3683622 z"
+ id="path2993"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path3008"
+ d="m 47.863636,5.6278409 c -5.595824,0 -10.144886,4.5490621 -10.144886,10.1448861 0,5.595824 4.549062,10.144886 10.144886,10.144886 5.595824,0 10.144886,-4.549062 10.144886,-10.144886 0,-5.595824 -4.549062,-10.1448861 -10.144886,-10.1448861 z m 0,1.7765239 c 4.619372,0 8.368362,3.7489902 8.368362,8.3683622 0,4.619372 -3.74899,8.368362 -8.368362,8.368362 -4.619372,0 -8.368362,-3.74899 -8.368362,-8.368362 0,-4.619372 3.74899,-8.3683622 8.368362,-8.3683622 z"
+ style="fill:#222222;fill-opacity:1;stroke:#222222;stroke-opacity:1" />
+ <path
+ style="fill:#222222;fill-opacity:1;stroke:#222222;stroke-opacity:1"
+ d="m 47.863636,37.627841 c -5.595824,0 -10.144886,4.549062 -10.144886,10.144886 0,5.595824 4.549062,10.144886 10.144886,10.144886 5.595824,0 10.144886,-4.549062 10.144886,-10.144886 0,-5.595824 -4.549062,-10.144886 -10.144886,-10.144886 z m 0,1.776524 c 4.619372,0 8.368362,3.74899 8.368362,8.368362 0,4.619372 -3.74899,8.368362 -8.368362,8.368362 -4.619372,0 -8.368362,-3.74899 -8.368362,-8.368362 0,-4.619372 3.74899,-8.368362 8.368362,-8.368362 z"
+ id="path3010"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path3012"
+ d="m 15.863636,37.627841 c -5.595824,0 -10.1448856,4.549062 -10.1448856,10.144886 0,5.595824 4.5490616,10.144886 10.1448856,10.144886 5.595824,0 10.144886,-4.549062 10.144886,-10.144886 0,-5.595824 -4.549062,-10.144886 -10.144886,-10.144886 z m 0,1.776524 c 4.619372,0 8.368362,3.74899 8.368362,8.368362 0,4.619372 -3.74899,8.368362 -8.368362,8.368362 -4.619372,0 -8.3683616,-3.74899 -8.3683616,-8.368362 0,-4.619372 3.7489896,-8.368362 8.3683616,-8.368362 z"
+ style="fill:#222222;fill-opacity:1;stroke:#222222;stroke-opacity:1" />
+ <path
+ style="fill:#222222;stroke:#222222;stroke-width:1.79999994999999990;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;fill-opacity:1"
+ d="M 22.454545,22.090909 41.363636,41"
+ id="path3014"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:#222222;stroke:#222222;stroke-width:1.79999994999999990;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;fill-opacity:1"
+ d="m 22.090909,41.090909 19,-19"
+ id="path3016"
+ inkscape:connector-curvature="0" />
+ </g>
+</svg>
diff --git a/backend/public/stylesheets/main.css b/vfd-backend/public/stylesheets/main.css
index 815d2b9..815d2b9 100644
--- a/backend/public/stylesheets/main.css
+++ b/vfd-backend/public/stylesheets/main.css
diff --git a/frontend/.gitignore b/vfd-frontend/.gitignore
index ed1de16..ed1de16 100644
--- a/frontend/.gitignore
+++ b/vfd-frontend/.gitignore
diff --git a/frontend/src/main/scala/Frontend.scala b/vfd-frontend/src/main/scala/Frontend.scala
index 0e3d554..9a628f6 100644
--- a/frontend/src/main/scala/Frontend.scala
+++ b/vfd-frontend/src/main/scala/Frontend.scala
@@ -31,8 +31,9 @@ class Frontend(attitudeSelector: String, azimuthSelector: String, altitudeSelect
val connection = new dom.WebSocket("ws://localhost:9000/socket");
connection.onmessage = (e: dom.MessageEvent) => {
+ Console.println(e.data);
val data = js.JSON.parse(e.data.asInstanceOf[String]).asInstanceOf[Data]
- Console.println(data.roll);
+
roll.setAttribute("transform", "rotate(" + data.roll.toDouble + ")");
pitch.setAttribute("transform", "translate(0, " + data.pitch.toDouble + ")");
heading.setAttribute("transform", "rotate(" + data.heading.toDouble + ")");
diff --git a/vfd-shared/src/main/scala/vfd/uav/DataFrame.scala b/vfd-shared/src/main/scala/vfd/uav/DataFrame.scala
new file mode 100644
index 0000000..5e1bcb7
--- /dev/null
+++ b/vfd-shared/src/main/scala/vfd/uav/DataFrame.scala
@@ -0,0 +1,18 @@
+package vfd.uav
+
+/**
+ * Data from UAV.
+ * SI units unless indicated otherwise
+ * @param roll roll angle [rad]
+ * @param pitch pitch angle (to horizon) [rad]
+ * @param heading heading angle to magnetic north [rad]
+ * @param altitude altitude to mean sea level [m]
+ * @param temperature ambient temperature [deg C]
+ */
+case class DataFrame(
+ roll: Double,
+ pitch: Double,
+ heading: Double,
+ altitude: Double,
+ temperature: Double
+) \ No newline at end of file
diff --git a/vfd-uav/src/main/scala/vfd/uav/Connection.scala b/vfd-uav/src/main/scala/vfd/uav/Connection.scala
new file mode 100644
index 0000000..b4e6493
--- /dev/null
+++ b/vfd-uav/src/main/scala/vfd/uav/Connection.scala
@@ -0,0 +1,27 @@
+package vfd.uav
+
+import akka.actor.Actor
+import akka.actor.ActorRef
+import akka.actor.Props
+import scala.collection.mutable.ArrayBuffer
+
+object Connection {
+ def dummy = Props(classOf[DummyConnection])
+ def fcu(port: String, baud: Int) = Props(classOf[FcuConnection], port, baud)
+
+ trait Event
+ trait Command
+ case object Register extends Command
+ case class NewDataFrame(df: DataFrame) extends Event
+
+}
+
+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
+}
diff --git a/vfd-uav/src/main/scala/vfd/uav/DummyConnection.scala b/vfd-uav/src/main/scala/vfd/uav/DummyConnection.scala
new file mode 100644
index 0000000..bf8714f
--- /dev/null
+++ b/vfd-uav/src/main/scala/vfd/uav/DummyConnection.scala
@@ -0,0 +1,43 @@
+package vfd.uav
+
+import akka.actor.Actor
+import akka.actor.Terminated
+import scala.concurrent.duration.FiniteDuration
+import java.util.concurrent.TimeUnit._
+
+class DummyConnection extends Actor with Connection {
+ import context._
+
+ var time = 0.0
+ val messageInterval = FiniteDuration(20, MILLISECONDS)
+
+ def flightData(time: Double) = {
+ val speed = 5.0 / 1000
+ val roll = 5.0/180*math.Pi
+ val pitch = 10.0/180*math.Pi
+ Connection.NewDataFrame(DataFrame(
+ roll,
+ pitch,
+ (roll * time * speed) % math.Pi,
+ (pitch * time * speed),
+ 22
+ ))
+ }
+
+
+ override def preStart() = {
+ context.system.scheduler.schedule(messageInterval, messageInterval){
+ time += messageInterval.toMillis
+ clients foreach (_ ! flightData(time))
+ }
+ }
+
+ def receive = {
+ case Connection.Register => register(sender)
+ case Terminated(client) => unregister(client)
+ }
+
+}
+
+
+
diff --git a/vfd-uav/src/main/scala/vfd/uav/FcuConnection.scala b/vfd-uav/src/main/scala/vfd/uav/FcuConnection.scala
new file mode 100644
index 0000000..f9f267d
--- /dev/null
+++ b/vfd-uav/src/main/scala/vfd/uav/FcuConnection.scala
@@ -0,0 +1,53 @@
+package vfd.uav
+
+import akka.actor.Actor
+import akka.actor.Props
+import akka.actor.Terminated
+import akka.io.IO
+import com.github.jodersky.flow._
+import com.github.jodersky.flow.Serial
+
+
+class FcuConnection(port: String, baud: Int) extends Actor with Connection {
+ import context._
+
+ val settings = SerialSettings(
+ baud = this.baud,
+ characterSize = 8,
+ twoStopBits = false,
+ parity = Parity.None
+ )
+
+ override def preStart() = {
+ IO(Serial) ! Serial.Open(port, settings)
+ }
+
+ def receive = {
+ case Connection.Register => register(sender)
+ case Terminated(client) => unregister(client)
+ case Serial.CommandFailed(cmd: Serial.Open, reason: AccessDeniedException) => println("you're not allowed to open that port!")
+ case Serial.CommandFailed(cmd: Serial.Open, reason) => println("could not open port for some other reason: " + reason)
+ case Serial.Opened(settings) => {
+ val operator = sender
+
+ }
+ case Serial.Received(bstr) =>
+ val str = (new String(bstr.toArray, "UTF-8")).trim
+
+
+ val LinePattern = ".*[(](.+)[)].*".r
+ val Number = "([-]?\\d+[.]\\d+?)".r
+ val Components = "(.+),(.+),(.+),(.+),(.+)".r
+
+ str match {
+ case LinePattern(Components(Number(r),Number(p),Number(h),Number(a),Number(t))) =>
+ val data = Connection.NewDataFrame(DataFrame(r.toDouble, p.toDouble, h.toDouble, a.toDouble, t.toDouble))
+ println(data)
+ for (client <- clients) {
+ client ! data
+ }
+ case _ => println("unknown message: " + str)
+ }
+ }
+
+}
diff --git a/vfd-uav/src/main/scala/vfd/uav/Framer.scala b/vfd-uav/src/main/scala/vfd/uav/Framer.scala
new file mode 100644
index 0000000..f587c9e
--- /dev/null
+++ b/vfd-uav/src/main/scala/vfd/uav/Framer.scala
@@ -0,0 +1,70 @@
+package vfd.uav
+
+import scala.collection.mutable.ArrayBuffer
+
+class Framer {
+ final val MTU: Int = 1024
+
+ final val START: Byte = 0xfd.toByte
+ final val STOP: Byte = 0xfe.toByte
+ final val ESCAPE: Byte = 0xff.toByte
+
+ final val WAITING = 0
+ final val RECEIVING = 1
+ final val ESCAPING = 2
+
+ private val data = new Array[Byte](MTU)
+ private var index = 0
+ private var state = WAITING
+
+ private def add(byte: Byte): Unit = {
+ data(index) = byte
+ index += 1
+ if (index >= MTU) index = 0
+ }
+
+ private def clear(): Unit = index = 0
+
+
+ def push(byte: Byte): Option[Array[Byte]] = state match {
+ case WAITING =>
+ if (byte == START) {
+ clear()
+ state = RECEIVING
+ }
+ None
+
+ case RECEIVING => byte match {
+ case START =>
+ clear()
+ state = RECEIVING
+ None
+ case ESCAPE =>
+ state = ESCAPING
+ None
+ case STOP =>
+ state = WAITING
+ Some(java.util.Arrays.copyOfRange(data, 0, index))
+
+ case _ =>
+ add(byte)
+ None
+ }
+ case ESCAPING =>
+ add(byte)
+ state = RECEIVING
+ None
+ }
+
+ def push(bytes: Array[Byte]): Seq[Array[Byte]] = {
+ val messages = new ArrayBuffer[Array[Byte]]
+
+ for (byte <- bytes) push(byte) match {
+ case None => ()
+ case Some(message) => messages += message
+ }
+
+ messages
+ }
+
+} \ No newline at end of file