aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--project/Build.scala2
-rw-r--r--vfd-dashboard/src/main/scala/vfd/dashboard/ui/Layout.scala172
-rw-r--r--vfd-dashboard/src/main/scala/vfd/dashboard/ui/components/instruments.scala126
-rw-r--r--vfd-dashboard/src/main/scala/vfd/dashboard/ui/instruments/Altimeter.scala19
-rw-r--r--vfd-dashboard/src/main/scala/vfd/dashboard/ui/instruments/Balance.scala27
-rw-r--r--vfd-dashboard/src/main/scala/vfd/dashboard/ui/instruments/Bar.scala19
-rw-r--r--vfd-dashboard/src/main/scala/vfd/dashboard/ui/instruments/Compass.scala18
-rw-r--r--vfd-dashboard/src/main/scala/vfd/dashboard/ui/instruments/Generic.scala40
-rw-r--r--vfd-dashboard/src/main/scala/vfd/dashboard/ui/instruments/Horizon.scala20
-rw-r--r--vfd-dashboard/src/main/scala/vfd/dashboard/ui/instruments/Instrument.scala28
-rw-r--r--vfd-dashboard/src/main/scala/vfd/dashboard/ui/instruments/Led.scala18
-rw-r--r--vfd-dashboard/src/main/scala/vfd/dashboard/ui/instruments/SvgInstrument.scala (renamed from vfd-dashboard/src/main/scala/vfd/dashboard/ui/components/SvgInstrument.scala)35
-rw-r--r--vfd-dashboard/src/main/scala/vfd/dashboard/ui/panels/Communication.scala95
-rw-r--r--vfd-dashboard/src/main/scala/vfd/dashboard/ui/panels/Primary.scala49
-rw-r--r--vfd-main/app/views/dashboard.scala.html27
-rw-r--r--vfd-main/public/stylesheets/main.css191
16 files changed, 499 insertions, 387 deletions
diff --git a/project/Build.scala b/project/Build.scala
index e469ce7..c99b088 100644
--- a/project/Build.scala
+++ b/project/Build.scala
@@ -81,7 +81,7 @@ object ApplicationBuild extends Build {
val scalajs = Seq(
libraryDependencies ++= Seq(
"org.scala-js" %%% "scalajs-dom" % "0.8.0",
- "com.lihaoyi" %%% "scalatags" % "0.4.6",
+ "com.lihaoyi" %%% "scalatags" % "0.5.0",
"com.lihaoyi" %%% "scalarx" % "0.2.8"
)
)
diff --git a/vfd-dashboard/src/main/scala/vfd/dashboard/ui/Layout.scala b/vfd-dashboard/src/main/scala/vfd/dashboard/ui/Layout.scala
index 4ec695e..f17e91a 100644
--- a/vfd-dashboard/src/main/scala/vfd/dashboard/ui/Layout.scala
+++ b/vfd-dashboard/src/main/scala/vfd/dashboard/ui/Layout.scala
@@ -1,27 +1,41 @@
package vfd.dashboard.ui
-import org.scalajs.dom.html
-
-import scalatags.JsDom.all.ExtendedString
-import scalatags.JsDom.all.Int2CssNumber
-import scalatags.JsDom.all.bindNode
-import scalatags.JsDom.all.`class`
-import scalatags.JsDom.all.div
-import scalatags.JsDom.all.height
-import scalatags.JsDom.all.iframe
-import scalatags.JsDom.all.p
-import scalatags.JsDom.all.src
-import scalatags.JsDom.all.stringAttr
-import scalatags.JsDom.all.stringFrag
-import scalatags.JsDom.all.stringPixelStyle
-import scalatags.JsDom.all.style
-import scalatags.JsDom.all.width
+import org.mavlink.messages.Attitude
+import org.mavlink.messages.Motor
+import org.mavlink.messages.Power
+
+import rx.Obs
+import scalatags.JsDom.all._
import vfd.dashboard.Environment
import vfd.dashboard.MavlinkSocket
-import vfd.dashboard.ui.panels.Communication
-import vfd.dashboard.ui.panels.Primary
+import vfd.dashboard.ui.instruments.Altimeter
+import vfd.dashboard.ui.instruments.Balance
+import vfd.dashboard.ui.instruments.Bar
+import vfd.dashboard.ui.instruments.Compass
+import vfd.dashboard.ui.instruments.Generic
+import vfd.dashboard.ui.instruments.Horizon
+
+class Layout(socket: MavlinkSocket)(implicit env: Environment) {
+
+ private def panel(contents: Frag*) = div(`class` := "d-panel")(contents: _*)
+
+ private def mode(name: String, kind: String, on: Boolean = false) = div(`class` := s"mode $kind ${if (!on) "off"}")(name)
-class Layout(socket: MavlinkSocket) {
+ val modes = div(
+ mode("MANUAL", "warning", true),
+ mode("STABILIZED", "info", true),
+ mode("GUIDED", "success", true),
+ mode("AUTO", "success", true))
+
+ val infos = div(
+ mode("BAY", "info"),
+ mode("RECOVERY", "danger"),
+ mode("NOGPS", "warning", true),
+ mode("OVERLOAD", "danger", true),
+ mode("BATTERY", "danger", false),
+ mode("LINK", "danger", true),
+ mode("SOCKET", "danger", true),
+ div(style := "float: right")(mode("CRITICAL", "danger", true)))
val map = iframe(
width := 100.pct,
@@ -35,24 +49,106 @@ class Layout(socket: MavlinkSocket) {
val feed = div(style := "width: 100%; height: 460px; color: #ffffff; background-color: #c2c2c2; text-align: center;")(
p(style := "padding-top: 220px")("video feed"))
- def element(implicit env: Environment): html.Element = div(`class` := "container-fluid")(
- div(`class` := "row")(
- div(`class` := "col-xs-12")(
- div(`class` := "panel panel-default")(
- div(`class` := "panel-body")()))),
- div(`class` := "row")(
- div(`class` := "col-xs-4")(
- div(`class` := "panel panel-default")(
- div(`class` := "panel-body")(
- map))),
- div(`class` := "col-xs-5")(
- div(`class` := "panel panel-default")(
- div(`class` := "panel-body")(
- feed)),
- div(`class` := "panel panel-default")(
- div(`class` := "panel-body")(Primary(socket)))),
- div(`class` := "col-xs-3")(
- div(`class` := "panel panel-default")(
- div(`class` := "panel-body")(Communication(socket)))))).render
+ val altimeter = new Altimeter
+ val horizon = new Horizon
+ val compass = new Compass
+ val motor0 = new Generic(0, 50, 100, "%")
+ val motor1 = new Generic(0, 50, 100, "%")
+ val motor2 = new Generic(0, 50, 100, "%")
+ val motor3 = new Generic(0, 50, 100, "%")
+ val powerDistribution = new Balance()
+ val batteryLevel = new Bar()
+
+ val top = header(
+ div("Flight Control Panel"),
+ div("00:00:00"),
+ div("System #"))
+
+ val left = panel(
+ map,
+ table(`class` := "table-instrument")(
+ thead("Motors"),
+ tbody(
+ tr(
+ td(motor0.element),
+ td(),
+ td(motor1.element),
+ td()
+ ),
+ tr(
+ td(),
+ td(powerDistribution.element),
+ td(),
+ td()
+ ),
+ tr(
+ td(motor2.element),
+ td(),
+ td(motor3.element),
+ td()
+ )
+ )
+ )
+ )
+
+ val center = panel(feed)
+
+ val below = panel(
+ table(`class` := "table-instrument")(
+ tbody(
+ tr(
+ td(compass.element),
+ td(horizon.element),
+ td(altimeter.element)
+ )
+ )
+ )
+ )
+
+ val right = panel()
+
+ val element = div(`class` := "d-container d-column")(
+ div(`class` := "d-above")(
+ top),
+ div(`class` := "d-above d-container d-row")(
+ panel(modes),
+ panel(infos)),
+ div(`class` := "d-container d-row")(
+ div(`class` := "d-container d-details")(
+ panel("foo")),
+ div(`class` := "d-container d-left")(
+ left),
+ div(`class` := "d-container d-column d-middle")(
+ div(`class` := "d-container d-center")(
+ center),
+ div(`class` := "d-container d-below")(
+ below)
+ ),
+ div(`class` := "d-container d-right")(
+ right
+ )
+ )
+ ).render
+
+ //message router
+ Obs(socket.message, skipInitial = true) {
+ socket.message() match {
+
+ case Motor(m0, m1, m2, m3) =>
+ motor0.value() = m0
+ motor1.value() = m1
+ motor2.value() = m2
+ motor3.value() = m3
+ powerDistribution.value() = (m0, m1, m2, m3)
+
+ case Power(mV) =>
+ batteryLevel.value() = (100 * (mV - 9600) / 12600)
+
+ case Attitude(roll, pitch, yaw) =>
+ horizon.value() = (pitch, roll)
+ compass.value() = (yaw)
+
+ }
+ }
} \ No newline at end of file
diff --git a/vfd-dashboard/src/main/scala/vfd/dashboard/ui/components/instruments.scala b/vfd-dashboard/src/main/scala/vfd/dashboard/ui/components/instruments.scala
deleted file mode 100644
index a508eb0..0000000
--- a/vfd-dashboard/src/main/scala/vfd/dashboard/ui/components/instruments.scala
+++ /dev/null
@@ -1,126 +0,0 @@
-package vfd.dashboard.ui.components
-
-import org.scalajs.dom
-import org.scalajs.dom.html
-
-import scalatags.JsDom.all.ExtendedString
-import scalatags.JsDom.all.Int2CssNumber
-import scalatags.JsDom.all.`object`
-import scalatags.JsDom.all.stringAttr
-import scalatags.JsDom.all.stringFrag
-import scalatags.JsDom.all.stringPixelStyle
-import scalatags.JsDom.all.`type`
-import scalatags.JsDom.all.width
-import vfd.dashboard.Environment
-
-class Led(implicit env: Environment) extends SvgInstrument[String] {
- lazy val element = `object`(`type` := "image/svg+xml", "data".attr := env.asset("images/leds/led.svg"), width := 100.pct)(
- "Error loading led.").render
-
- def update(color: String) = {
- content.getElementById("light").setAttribute("fill", color)
- }
-
- protected def moveable = Seq()
-
-}
-
-class Horizon(implicit env: Environment) extends SvgInstrument[(Double, Double)] {
- lazy val element = SvgInstrument.svg("horizon")
-
- def pitch = content.getElementById("pitch").asInstanceOf[html.Element]
- def roll = content.getElementById("roll").asInstanceOf[html.Element]
- protected def moveable = Seq(pitch, roll)
-
- def update(pitchRoll: (Double, Double)) = {
- SvgInstrument.translate(pitch, 0, pitchRoll._1.toInt)
- SvgInstrument.rotate(roll, pitchRoll._2.toInt)
- }
-}
-
-class Altimeter(implicit env: Environment) extends SvgInstrument[Double] {
- lazy val element = SvgInstrument.svg("altimeter")
-
- def hand = content.getElementById("hand").asInstanceOf[html.Element]
- protected def moveable = Seq(hand)
-
- // 36deg === 1m
- def update(altitude: Double) = {
- SvgInstrument.rotate(hand, (altitude * 36).toInt)
- }
-}
-
-class Compass(implicit env: Environment) extends SvgInstrument[Double] {
- lazy val element = SvgInstrument.svg("compass")
-
- def plate = content.getElementById("heading").asInstanceOf[html.Element]
- protected def moveable = Seq(plate)
-
- def update(heading: Double) = {
- SvgInstrument.rotate(plate, heading.toInt)
- }
-}
-
-class Generic(
- min: Double,
- med: Double,
- max: Double,
- unit: String)(implicit env: Environment) extends SvgInstrument[Double] {
-
- lazy val element = SvgInstrument.svg("generic")
-
- def handElement = content.getElementById("hand").asInstanceOf[html.Element]
- def unitElement = content.getElementById("unit")
- def valueElement = content.getElementById("value")
- def minElement = content.getElementById("min")
- def medElement = content.getElementById("med")
- def maxElement = content.getElementById("max")
- protected def moveable = Seq(handElement)
-
- override protected def load(e: dom.Event) = {
- unitElement.textContent = unit
- minElement.textContent = min.toString
- medElement.textContent = med.toString
- maxElement.textContent = max.toString
- update(min)
- super.load(e)
- }
-
- def update(value: Double) = {
- SvgInstrument.rotate(handElement, (value * 270 / (max - min)).toInt)
- valueElement.textContent = value.toString
- }
-}
-
-class Bar(implicit env: Environment) extends SvgInstrument[Double] {
-
- lazy val element = SvgInstrument.svg("bar")
-
- def level = content.getElementById("level").asInstanceOf[html.Element]
- protected def moveable = Seq(level)
-
- def update(value: Double) = {
- SvgInstrument.translate(level, 0, (97 * (1 - value / 100)).toInt)
- }
-
-}
-
-class Balance(implicit env: Environment) extends SvgInstrument[(Double, Double, Double, Double)] {
- lazy val element = SvgInstrument.svg("balance")
-
- def position = content.getElementById("position").asInstanceOf[html.Element]
- protected def moveable = Seq(position)
-
- def update(value: (Double, Double, Double, Double)) = {
- val m0 = value._1
- val m1 = value._2
- val m2 = value._3
- val m3 = value._4
- val s = m0 + m1 + m2 + m3
- val i = (m0 - m2) / s
- val j = (m1 - m3) / s
- val x = 0.5 * (i - j)
- val y = 0.5 * (-i - j)
- SvgInstrument.translate(position, (x * 50).toInt, (y * 50).toInt)
- }
-} \ No newline at end of file
diff --git a/vfd-dashboard/src/main/scala/vfd/dashboard/ui/instruments/Altimeter.scala b/vfd-dashboard/src/main/scala/vfd/dashboard/ui/instruments/Altimeter.scala
new file mode 100644
index 0000000..9ff3bb5
--- /dev/null
+++ b/vfd-dashboard/src/main/scala/vfd/dashboard/ui/instruments/Altimeter.scala
@@ -0,0 +1,19 @@
+package vfd.dashboard.ui.instruments
+
+import org.scalajs.dom.html
+import vfd.dashboard.Environment
+
+class Altimeter(implicit env: Environment) extends SvgInstrument[Double] {
+ import SvgInstrument._
+
+ val initial = 0.0
+
+ lazy val element = svgObject("altimeter")
+ lazy val hand = part("hand")
+ lazy val moveable = Seq(hand)
+
+ // 36deg === 1m
+ protected def update(altitude: Double) = {
+ rotate(hand, (altitude * 36).toInt)
+ }
+} \ No newline at end of file
diff --git a/vfd-dashboard/src/main/scala/vfd/dashboard/ui/instruments/Balance.scala b/vfd-dashboard/src/main/scala/vfd/dashboard/ui/instruments/Balance.scala
new file mode 100644
index 0000000..ef5a3cc
--- /dev/null
+++ b/vfd-dashboard/src/main/scala/vfd/dashboard/ui/instruments/Balance.scala
@@ -0,0 +1,27 @@
+package vfd.dashboard.ui.instruments
+
+import org.scalajs.dom.html
+import vfd.dashboard.Environment
+
+class Balance(implicit env: Environment) extends SvgInstrument[(Double, Double, Double, Double)] {
+ import SvgInstrument._
+
+ val initial = (0.0, 0.0, 0.0, 0.0)
+
+ lazy val element = svgObject("balance")
+ lazy val position = part("position")
+ lazy val moveable = Seq(position)
+
+ protected def update(value: (Double, Double, Double, Double)) = {
+ val m0 = value._1
+ val m1 = value._2
+ val m2 = value._3
+ val m3 = value._4
+ val s = m0 + m1 + m2 + m3
+ val i = (m0 - m2) / s
+ val j = (m1 - m3) / s
+ val x = 0.5 * (i - j)
+ val y = 0.5 * (-i - j)
+ translate(position, (x * 50).toInt, (y * 50).toInt)
+ }
+} \ No newline at end of file
diff --git a/vfd-dashboard/src/main/scala/vfd/dashboard/ui/instruments/Bar.scala b/vfd-dashboard/src/main/scala/vfd/dashboard/ui/instruments/Bar.scala
new file mode 100644
index 0000000..7cda301
--- /dev/null
+++ b/vfd-dashboard/src/main/scala/vfd/dashboard/ui/instruments/Bar.scala
@@ -0,0 +1,19 @@
+package vfd.dashboard.ui.instruments
+
+import org.scalajs.dom.html
+import vfd.dashboard.Environment
+
+class Bar(implicit env: Environment) extends SvgInstrument[Double] {
+ import SvgInstrument._
+
+ val initial = 0.0
+
+ lazy val element = svgObject("bar")
+ lazy val level = part("level")
+ lazy val moveable = Seq(level)
+
+ protected def update(value: Double) = {
+ translate(level, 0, (97 * (1 - value / 100)).toInt)
+ }
+
+} \ No newline at end of file
diff --git a/vfd-dashboard/src/main/scala/vfd/dashboard/ui/instruments/Compass.scala b/vfd-dashboard/src/main/scala/vfd/dashboard/ui/instruments/Compass.scala
new file mode 100644
index 0000000..e2d79e4
--- /dev/null
+++ b/vfd-dashboard/src/main/scala/vfd/dashboard/ui/instruments/Compass.scala
@@ -0,0 +1,18 @@
+package vfd.dashboard.ui.instruments
+
+import org.scalajs.dom.html
+import vfd.dashboard.Environment
+
+class Compass(implicit env: Environment) extends SvgInstrument[Double] {
+ import SvgInstrument._
+
+ val initial = 0.0
+
+ lazy val element = svgObject("compass")
+ lazy val plate = part("heading")
+ lazy val moveable = Seq(plate)
+
+ protected def update(heading: Double) = {
+ rotate(plate, heading.toInt)
+ }
+} \ No newline at end of file
diff --git a/vfd-dashboard/src/main/scala/vfd/dashboard/ui/instruments/Generic.scala b/vfd-dashboard/src/main/scala/vfd/dashboard/ui/instruments/Generic.scala
new file mode 100644
index 0000000..c46bdcc
--- /dev/null
+++ b/vfd-dashboard/src/main/scala/vfd/dashboard/ui/instruments/Generic.scala
@@ -0,0 +1,40 @@
+package vfd.dashboard.ui.instruments
+
+import org.scalajs.dom
+import org.scalajs.dom.html
+import vfd.dashboard.Environment
+
+class Generic(
+ min: Double,
+ med: Double,
+ max: Double,
+ unit: String)(implicit env: Environment)
+ extends SvgInstrument[Double] {
+
+ import SvgInstrument._
+
+ val initial = 0.0
+
+ lazy val element = svgObject("generic")
+ lazy val handElement = part("hand")
+ lazy val unitElement = element.contentDocument.getElementById("unit")
+ lazy val valueElement = element.contentDocument.getElementById("value")
+ lazy val minElement = element.contentDocument.getElementById("min")
+ lazy val medElement = element.contentDocument.getElementById("med")
+ lazy val maxElement = element.contentDocument.getElementById("max")
+ lazy val moveable = Seq(handElement)
+
+ override protected def load(e: dom.Event) = {
+ unitElement.textContent = unit
+ minElement.textContent = min.toString
+ medElement.textContent = med.toString
+ maxElement.textContent = max.toString
+ update(min)
+ super.load(e)
+ }
+
+ protected def update(value: Double) = {
+ rotate(handElement, (value * 270 / (max - min)).toInt)
+ valueElement.textContent = value.toString
+ }
+} \ No newline at end of file
diff --git a/vfd-dashboard/src/main/scala/vfd/dashboard/ui/instruments/Horizon.scala b/vfd-dashboard/src/main/scala/vfd/dashboard/ui/instruments/Horizon.scala
new file mode 100644
index 0000000..2484561
--- /dev/null
+++ b/vfd-dashboard/src/main/scala/vfd/dashboard/ui/instruments/Horizon.scala
@@ -0,0 +1,20 @@
+package vfd.dashboard.ui.instruments
+
+import org.scalajs.dom.html
+import vfd.dashboard.Environment
+
+class Horizon(implicit env: Environment) extends SvgInstrument[(Double, Double)] {
+ import SvgInstrument._
+
+ val initial = (0.0, 0.0)
+
+ lazy val element = svgObject("horizon")
+ lazy val pitch = part("pitch")
+ lazy val roll = part("roll")
+ lazy val moveable = Seq(pitch, roll)
+
+ protected def update(pitchRoll: (Double, Double)) = {
+ translate(pitch, 0, pitchRoll._1.toInt)
+ rotate(roll, pitchRoll._2.toInt)
+ }
+}
diff --git a/vfd-dashboard/src/main/scala/vfd/dashboard/ui/instruments/Instrument.scala b/vfd-dashboard/src/main/scala/vfd/dashboard/ui/instruments/Instrument.scala
new file mode 100644
index 0000000..5b0a13d
--- /dev/null
+++ b/vfd-dashboard/src/main/scala/vfd/dashboard/ui/instruments/Instrument.scala
@@ -0,0 +1,28 @@
+package vfd.dashboard.ui.instruments
+
+import rx._
+import org.scalajs.dom.html
+
+/** Common trait to all flight instruments. */
+trait Instrument[A] {
+
+ /** Initial value. */
+ val initial: A
+
+ /** Current value that is displayed in the instrument. */
+ val value: Var[A] = Var(initial)
+
+ /** HTML element that contains the rendered instrument */
+ val element: html.Element
+
+ /** Performs the actual UI update of this instrument. */
+ protected def update(newValue: A): Unit
+
+ /** Call when instrument has finished setting up its UI. */
+ protected def ready() = {
+ Obs(value, skipInitial = true) {
+ update(value())
+ }
+ }
+
+} \ No newline at end of file
diff --git a/vfd-dashboard/src/main/scala/vfd/dashboard/ui/instruments/Led.scala b/vfd-dashboard/src/main/scala/vfd/dashboard/ui/instruments/Led.scala
new file mode 100644
index 0000000..4173f37
--- /dev/null
+++ b/vfd-dashboard/src/main/scala/vfd/dashboard/ui/instruments/Led.scala
@@ -0,0 +1,18 @@
+package vfd.dashboard.ui.instruments
+
+import scalatags.JsDom.all._
+import vfd.dashboard.Environment
+
+class Led(implicit env: Environment) extends SvgInstrument[String] {
+
+ val initial = "none"
+
+ lazy val element = `object`(`type` := "image/svg+xml", "data".attr := env.asset("images/leds/led.svg"), width := 100.pct)(
+ "Error loading led.").render
+ protected def moveable = Seq()
+
+ protected def update(color: String) = {
+ part("light").setAttribute("fill", color)
+ }
+
+} \ No newline at end of file
diff --git a/vfd-dashboard/src/main/scala/vfd/dashboard/ui/components/SvgInstrument.scala b/vfd-dashboard/src/main/scala/vfd/dashboard/ui/instruments/SvgInstrument.scala
index dc8ee5e..c639cec 100644
--- a/vfd-dashboard/src/main/scala/vfd/dashboard/ui/components/SvgInstrument.scala
+++ b/vfd-dashboard/src/main/scala/vfd/dashboard/ui/instruments/SvgInstrument.scala
@@ -1,55 +1,52 @@
-package vfd.dashboard.ui.components
+package vfd.dashboard.ui.instruments
-import scala.scalajs.js.Any.fromFunction1
import org.scalajs.dom
import org.scalajs.dom.html
-import scalatags.JsDom.all.ExtendedString
-import scalatags.JsDom.all.Int2CssNumber
-import scalatags.JsDom.all.`object`
-import scalatags.JsDom.all.stringAttr
-import scalatags.JsDom.all.stringFrag
-import scalatags.JsDom.all.stringPixelStyle
-import scalatags.JsDom.all.`type`
-import scalatags.JsDom.all.width
+import scalatags.JsDom.all._
+
import vfd.dashboard.Environment
-trait SvgInstrument[A] {
+/** An instrument backed by an SVG image. */
+trait SvgInstrument[A] extends Instrument[A] {
/** SVG object element that contains the rendered instrument */
- def element: html.Object
+ val element: html.Object
- /** Actual svg document */
- protected def content = element.contentDocument
+ /** Retrieves an element of the underlying SVG document by ID. */
+ protected def part(id: String) = element.contentDocument.getElementById(id).asInstanceOf[html.Element]
- /** Moveable parts of the instrument */
+ /** Movable parts of the instrument */
protected def moveable: Seq[html.Element]
- /** Updates the instrument to show a new value */
- def update(value: A): Unit
-
+ /** Called when element has been loaded. */
protected def load(event: dom.Event): Unit = {
for (part <- moveable) {
part.style.transition = "transform 250ms ease-out"
}
+ ready()
}
element.addEventListener("load", (e: dom.Event) => load(e))
}
+/** Contains helpers for SVG instruments. */
object SvgInstrument {
- def svg(name: String)(implicit app: Environment): html.Object = {
+ /** Retrieves an SVG object element by its instrument's name. */
+ def svgObject(name: String)(implicit app: Environment): html.Object = {
val path = app.asset("images/instruments/" + name + ".svg")
`object`(`type` := "image/svg+xml", "data".attr := path, width := 100.pct)(
"Error loading instrument " + name).render
}
+ /** Applies translation styling to an element. */
def translate(elem: html.Element, x: Int, y: Int): Unit = {
elem.style.transform = "translate(" + x + "px, " + y + "px)";
}
+ /** Applies rotation styling to an element. */
def rotate(elem: html.Element, deg: Int): Unit = {
elem.style.transform = "rotateZ(" + deg + "deg)";
}
diff --git a/vfd-dashboard/src/main/scala/vfd/dashboard/ui/panels/Communication.scala b/vfd-dashboard/src/main/scala/vfd/dashboard/ui/panels/Communication.scala
deleted file mode 100644
index 2b79478..0000000
--- a/vfd-dashboard/src/main/scala/vfd/dashboard/ui/panels/Communication.scala
+++ /dev/null
@@ -1,95 +0,0 @@
-package vfd.dashboard.ui.panels
-
-import org.scalajs.dom.html
-
-import rx.core.Obs
-import scalatags.JsDom.all.bindNode
-import scalatags.JsDom.all.`class`
-import scalatags.JsDom.all.div
-import scalatags.JsDom.all.i
-import scalatags.JsDom.all.stringAttr
-import scalatags.JsDom.all.stringFrag
-import scalatags.JsDom.all.stringPixelStyle
-import scalatags.JsDom.all.style
-import scalatags.JsDom.all.table
-import scalatags.JsDom.all.tbody
-import scalatags.JsDom.all.td
-import scalatags.JsDom.all.thead
-import scalatags.JsDom.all.tr
-import scalatags.JsDom.all.width
-import vfd.dashboard.Environment
-import vfd.dashboard.MavlinkSocket
-import vfd.dashboard.ui.components.Balance
-import vfd.dashboard.ui.components.Bar
-import vfd.dashboard.ui.components.Generic
-import vfd.dashboard.ui.components.Led
-
-object Communication {
-
- def apply(socket: MavlinkSocket)(implicit app: Environment): html.Element = {
-
- val hb = i(`class` := "fa fa-heart heartbeat").render
-
- def foo() = {
- hb.textContent = ""
- }
-
- val motor0 = new Generic(0, 50, 100, "%")
- val motor1 = new Generic(0, 50, 100, "%")
- val motor2 = new Generic(0, 50, 100, "%")
- val motor3 = new Generic(0, 50, 100, "%")
- val powerDistribution = new Balance()
- val batteryLevel = new Bar()
-
- Obs(socket.message, skipInitial = true) {
- socket.message() match {
- //TODO match message and update UI
- case _ =>
- }
- }
-
- div(
- table(`class` := "table")(
- thead("Communication"),
- tbody(
- tr(
- td("Conn"),
- div(width := "20px")(td((new Led()).element)),
- td("Server"),
- td("5 ms")),
- tr(
- td("Uplink"),
- td("-20 dBm"),
- td("Heartbeat"),
- td(hb)))),
- table(`class` := "table-instrument", style := "height: 100px")(
- tbody(
- tr(
- td(),
- td(),
- td(),
- td(),
- td(),
- td(),
- td(),
- td(),
- td(),
- td(batteryLevel.element)))),
- table (`class` := "table-instrument")(
- thead("Motors"),
- tbody(
- tr(
- td(motor0.element),
- td(),
- td(motor1.element)),
- tr(
- td(),
- td(powerDistribution.element),
- td()),
- tr(
- td(motor2.element),
- td(),
- td(motor3.element))))).render
- }
-
-} \ No newline at end of file
diff --git a/vfd-dashboard/src/main/scala/vfd/dashboard/ui/panels/Primary.scala b/vfd-dashboard/src/main/scala/vfd/dashboard/ui/panels/Primary.scala
deleted file mode 100644
index b85e657..0000000
--- a/vfd-dashboard/src/main/scala/vfd/dashboard/ui/panels/Primary.scala
+++ /dev/null
@@ -1,49 +0,0 @@
-package vfd.dashboard.ui.panels
-
-import org.mavlink.messages.{VfrHud, Attitude, GlobalPositionInt}
-import org.scalajs.dom.html
-
-import rx.core.Obs
-import scalatags.JsDom.all.bindNode
-import scalatags.JsDom.all.`class`
-import scalatags.JsDom.all.stringAttr
-import scalatags.JsDom.all.table
-import scalatags.JsDom.all.tbody
-import scalatags.JsDom.all.td
-import scalatags.JsDom.all.tr
-import vfd.dashboard.Environment
-import vfd.dashboard.MavlinkSocket
-import vfd.dashboard.ui.components.Altimeter
-import vfd.dashboard.ui.components.Compass
-import vfd.dashboard.ui.components.Horizon
-
-object Primary {
-
- def apply(socket: MavlinkSocket)(implicit env: Environment): html.Element = {
-
- val compass = new Compass
- val horizon = new Horizon
- val altimeter = new Altimeter
-
- Obs(socket.message, skipInitial = true) {
- socket.message() match {
- case a: Attitude =>
-// compass.update(a.yaw / Math.PI * 360)
-// horizon.update(a.pitch, a.roll)
- case vh: VfrHud =>
-// altimeter.update(vh.alt)
-// compass.update(vh.heading / Math.PI * 360)
- case _ => ()
- }
- }
-
- table(`class` := "table-instrument")(
- tbody(
- tr(
- td(compass.element),
- td(horizon.element),
- td(altimeter.element)))).render
-
- }
-
-} \ No newline at end of file
diff --git a/vfd-main/app/views/dashboard.scala.html b/vfd-main/app/views/dashboard.scala.html
index 02a79f7..b2cfdfd 100644
--- a/vfd-main/app/views/dashboard.scala.html
+++ b/vfd-main/app/views/dashboard.scala.html
@@ -2,33 +2,6 @@
@main("Main"){
- <header>
- <nav class="navbar navbar-inverse navbar-static-top" role="navigation">
- <div class="container-fluid">
- <!-- Brand and toggle get grouped for better mobile display -->
- <div class="navbar-header">
- <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#navbar-main-collapse">
- <span class="sr-only">Toggle navigation</span>
- <span class="icon-bar"></span>
- <span class="icon-bar"></span>
- <span class="icon-bar"></span>
- </button>
- <a class="navbar-brand" href="@routes.Application.index">
- <!-- <img style="max-height: 100%;" src="@routes.Assets.at("images/logo-invert.svg")" alt="logo"> -->
- Flight Control Panel
- </a>
- </div>
-
- <!-- Collect the nav links, forms, and other content for toggling -->
- <div class="collapse navbar-collapse" id="navbar-main-collapse">
- <div class="nav navbar-nav navbar-right">
- <p class="navbar-text">Remote System @remoteSystemId</p>
- </div>
- </div><!-- /.navbar-collapse -->
- </div><!-- /.container-fluid -->
- </nav>
- </header>
-
@app("vfd-dashboard")(
"socketUrl" -> socket,
"remoteSystemId" -> remoteSystemId.toString,
diff --git a/vfd-main/public/stylesheets/main.css b/vfd-main/public/stylesheets/main.css
index e92cd24..072f167 100644
--- a/vfd-main/public/stylesheets/main.css
+++ b/vfd-main/public/stylesheets/main.css
@@ -1,18 +1,9 @@
-@font-face {
- font-family: ds-digi;
- src: url('../fonts/ds-digi.ttf')
-}
-
-html {
- height: 100%;
-}
-
-body {
- height: 100%;
+html, body {
+ height: 100%;
}
body {
- background-color: #e6e6e6;
+ background-color: #e6e6e6;
}
.loader {
@@ -21,6 +12,144 @@ body {
text-align: center;
}
+#vfd-dashboard {
+ width: 100%;
+ height: 100%;
+}
+
+#vfd-dashboard header {
+ color: #eeeeee;
+ background-color: #222222;
+ padding-left: 15px;
+ padding-right: 15px;
+ padding-top: 5px;
+ padding-bottom: 5px;
+ margin-bottom: 5px;
+ display: flex;
+}
+
+#vfd-dashboard header > * {
+ margin: 5px;
+ flex: 1;
+}
+
+#vfd-dashboard header > :nth-child(1) {
+ text-align: left;
+}
+
+#vfd-dashboard header > :nth-child(2) {
+ text-align: center;
+}
+
+#vfd-dashboard header > :nth-child(3) {
+ text-align: right;
+}
+
+/* dashboard layout */
+.d-container {
+ display: flex;
+ align-content: flex-start;
+ align-items: stretch;
+}
+
+.d-container > * {
+ flex: 1;
+}
+
+.d-column {
+ flex-direction: column;
+ height: 100%;
+}
+
+.d-row {
+ flex-direction: row;
+ width: 100%;
+}
+
+.d-above {
+ flex: none;
+}
+
+.d-left {
+ flex: 1 1 30%;
+}
+
+.d-middle {
+ flex: 1 1 45%;
+}
+
+.d-right {
+ flex: 1 1 25%;
+}
+
+.d-center {
+ flex: none;
+}
+
+.d-below {
+ flex: 1;
+}
+
+.d-details {
+ display: none;
+}
+
+.d-detailed .d-left, .d-detailed .d-right {
+ display: none;
+}
+
+.d-detailed .d-middle {
+ flex: 1 1 25%;
+}
+
+.d-detailed .d-details {
+ display: flex;
+ flex: 1 1 75%;
+ overerflow-y: scroll;
+}
+
+.d-panel {
+ margin: 5px;
+ padding: 15px;
+ background-color: white;
+ border-radius: 3px;
+}
+
+
+/* Mode styles */
+.mode {
+ display: inline-block;
+ box-sizing: border-box;
+ text-decoration: normal;
+ margin-right: 5px;
+}
+
+.mode.danger {
+ color: #d9534f;
+ text-shadow: 0 0 5px #d9534f;
+}
+
+.mode.warning {
+ color: #f0ad4e;
+ text-shadow: 0 0 5px #f0ad4e;
+}
+
+.mode.info {
+ color: #5bc0de;
+ text-shadow: 0 0 5px #5bc0de;
+}
+
+.mode.success {
+ color: #5cb85c;
+ text-shadow: 0 0 5px #5cb85c;
+}
+
+.mode.off {
+ color: #eeeeee;
+ text-shadow: none;
+}
+
+/* TODO: Rules below are maybe obsolete and need to be reviewed */
.table-instrument {
table-layout: fixed;
width: 100%;
@@ -31,25 +160,23 @@ body {
}
.heartbeat {
- color: rgba(165, 25, 25, 1);
- animation: heartbeat 2s linear infinite;
+ color: rgba(165, 25, 25, 1);
+ animation: heartbeat 2s linear infinite;
}
-
@keyframes heartbeat {
- 0% {
- transform: scale(1);
- }
- 7% {
- transform: scale(1.3);
- }
- 14% {
- transform: scale(1);
- }
- 21% {
- transform: scale(1.3);
- }
- 28% {
- transform: scale(1);
- }
-}
-
+ 0% {
+ transform: scale(1);
+ }
+ 7% {
+ transform: scale(1.3);
+ }
+ 14% {
+ transform: scale(1);
+ }
+ 21% {
+ transform: scale(1.3);
+ }
+ 28% {
+ transform: scale(1);
+ }
+} \ No newline at end of file