aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakob Odersky <jodersky@gmail.com>2015-04-15 20:12:53 +0200
committerJakob Odersky <jodersky@gmail.com>2015-04-15 20:12:53 +0200
commita3ba670da8f7de94b5e5c6e307fda65b8dd5d791 (patch)
tree8014c845f0f16085a5cf13bd141e4ac0bbcaf9ba
parentd74d619e0abc8b9a024b07b9d89d2da1e539a8ed (diff)
downloadmavigator-a3ba670da8f7de94b5e5c6e307fda65b8dd5d791.tar.gz
mavigator-a3ba670da8f7de94b5e5c6e307fda65b8dd5d791.tar.bz2
mavigator-a3ba670da8f7de94b5e5c6e307fda65b8dd5d791.zip
layout: slimer panels and more instruments
-rw-r--r--vfd-dashboard/src/main/scala/vfd/dashboard/RxUtil.scala43
-rw-r--r--vfd-dashboard/src/main/scala/vfd/dashboard/ui/Layout.scala194
-rw-r--r--vfd-main/app/views/main.scala.html1
-rw-r--r--vfd-main/public/stylesheets/main.css58
4 files changed, 210 insertions, 86 deletions
diff --git a/vfd-dashboard/src/main/scala/vfd/dashboard/RxUtil.scala b/vfd-dashboard/src/main/scala/vfd/dashboard/RxUtil.scala
index 51e2fcd..a73c3ba 100644
--- a/vfd-dashboard/src/main/scala/vfd/dashboard/RxUtil.scala
+++ b/vfd-dashboard/src/main/scala/vfd/dashboard/RxUtil.scala
@@ -1,6 +1,23 @@
package vfd.dashboard
-import rx._
+import scala.language.implicitConversions
+import scala.util.Failure
+import scala.util.Success
+
+import org.scalajs.dom.html
+
+import rx.Obs
+import rx.Rx
+import rx.Rx
+import rx.Var
+import rx.Var
+import scalatags.JsDom.all.Frag
+import scalatags.JsDom.all.HtmlTag
+import scalatags.JsDom.all.backgroundColor
+import scalatags.JsDom.all.bindNode
+import scalatags.JsDom.all.span
+import scalatags.JsDom.all.stringFrag
+import scalatags.JsDom.all.stringStyle
package object rxutil {
@@ -14,7 +31,7 @@ package object rxutil {
* @param pf the partial function which filters and maps this Rx
* @return a new Rx resulting from applying the given partial
* function pf to each value on which it is defined and collecting
- * the result
+ * the result
*/
def collect[B](initial: B)(pf: PartialFunction[Any, B]): Rx[B] = {
val result: Var[B] = Var(initial)
@@ -28,4 +45,26 @@ package object rxutil {
}
+ /**
+ * Copied from https://github.com/lihaoyi/workbench-example-app/blob/todomvc/src/main/scala/example/Framework.scala
+ *
+ * Sticks some Rx into a Scalatags fragment, which means hooking up an Obs
+ * to propagate changes into the DOM via the element's ID. Monkey-patches
+ * the Obs onto the element itself so we have a reference to kill it when
+ * the element leaves the DOM (e.g. it gets deleted).
+ */
+ implicit def rxMod[T <: html.Element](r: Rx[HtmlTag]): Frag = {
+ def rSafe = r.toTry match {
+ case Success(v) => v.render
+ case Failure(e) => span(e.toString, backgroundColor := "red").render
+ }
+ var last = rSafe
+ Obs(r, skipInitial = true) {
+ val newLast = rSafe
+ last.parentElement.replaceChild(newLast, last)
+ last = newLast
+ }
+ bindNode(last)
+ }
+
} \ No newline at end of file
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 1d6090d..b3c9387 100644
--- a/vfd-dashboard/src/main/scala/vfd/dashboard/ui/Layout.scala
+++ b/vfd-dashboard/src/main/scala/vfd/dashboard/ui/Layout.scala
@@ -31,15 +31,13 @@ class Layout(socket: MavlinkSocket)(implicit env: Environment) {
div(style := "float: right")(mode("CRITICAL", "danger", true)))
val map = iframe(
- width := 100.pct,
- height := 350.px,
"frameborder".attr := "0",
"scrolling".attr := "no",
"marginheight".attr := "0",
"marginwidth".attr := "0",
src := "http://www.openstreetmap.org/export/embed.html?bbox=6.5611016750335684%2C46.51718501017836%2C6.570038795471191%2C46.520577350893525&amp;layer=mapnik")
- val feed = div(style := "width: 100%; height: 460px; color: #ffffff; background-color: #c2c2c2; text-align: center;")(
+ val feed = div(style := "width: 100%; color: #ffffff; background-color: #c2c2c2; text-align: center;")(
p(style := "padding-top: 220px")("video feed"))
val altimeter = new Altimeter(
@@ -78,69 +76,163 @@ class Layout(socket: MavlinkSocket)(implicit env: Environment) {
div("UAV " + socket.remoteSystemId)
)
- val left = panel(
- map,
- table(`class` := "table-instrument")(
- thead("Motors"),
- tbody(
- tr(
- td(motor1.element),
- td(),
- td(motor0.element),
- td()
- ),
- tr(
- td(),
- td(powerDistribution.element),
- td(),
- td()
- ),
- tr(
- td(motor2.element),
- td(),
- td(motor3.element),
- td()
+ val left = div(
+ panel(
+ table(`class` := "table-instrument")(
+ thead("Communication"),
+ tbody(
+ tr(
+ td("Uplink RSSI"),
+ td("89"),
+ td("Socket"),
+ td("5ms")
+ ),
+ tr(
+ td("Something else"),
+ td("unknown"),
+ td("Heartbeat"),
+ td(i(`class` := "fa fa-heart heartbeat"))
+ )
+ )
+ ),
+ table(`class` := "table-instrument")(
+ thead("Packets"),
+ tbody(
+ tr(
+ td("OK"),
+ Rx{td(socket.stats.packets())},
+ td("CRC"),
+ Rx{td(socket.stats.crcErrors())},
+ td("OFLW"),
+ Rx{td(socket.stats.overflows())},
+ td("BID"),
+ Rx{td(socket.stats.wrongIds())}
+ ),
+ tr(
+ td("Ratio"),
+ Rx{
+ import socket.stats._
+ val sum = packets() + crcErrors() + overflows() + wrongIds()
+ td(1.0 * packets() / sum formatted "%.2f")
+ },
+ td(),
+ td(),
+ td(),
+ td(),
+ td(),
+ td()
+ )
)
)
- )
- )
-
- val center = panel(feed)
-
- val below = panel(
- table(`class` := "table-instrument")(
- tbody(
- tr(
- td(compass.element),
- td(horizon.element),
- td(altimeter.element)
+ ),
+ panel(
+ table(`class` := "table-instrument")(
+ tbody(
+ tr(
+ td(compass.element),
+ td(horizon.element),
+ td(altimeter.element),
+ td(altimeter.element)
+ )
+ )
+ )
+ ),
+ panel(
+ div(style := "width: 50%; display: inline-block;")(
+ table(`class` := "table-instrument")(
+ tbody(
+ tr(
+ td(motor1.element),
+ td(),
+ td(motor0.element)
+ ),
+ tr(
+ td(),
+ td(powerDistribution.element),
+ td()
+ ),
+ tr(
+ td(motor2.element),
+ td(),
+ td(motor3.element)
+ )
+ )
+ )
+ ),
+ div(style := "width: 50%; display: inline-block;")(
+ table(`class` := "table-instrument")(
+ thead("Power"),
+ tbody(
+ tr(
+ td("VHIGH"),
+ td("12.6V"),
+ td("VLOW"),
+ td("9V")
+ ),
+ tr(
+ td("Voltage"),
+ td("11.2V"),
+ td("Remaining"),
+ td("80%")
+ ),
+ tr(
+ td("Flight"),
+ td("05:00"),
+ td("Endurance"),
+ td("12:00")
+ )
+ )
+ ),
+ table(`class` := "table-instrument")(
+ thead("Navigation"),
+ tbody(
+ tr(
+ td("Satellites"),
+ td("5"),
+ td("Precision"),
+ td("10cm")
+ ),
+ tr(
+ td("LON"),
+ td(""),
+ td("LAT"),
+ td("")
+ ),
+ tr(
+ td("GSpeed"),
+ td("3 m/s"),
+ td(),
+ td()
+ ),
+ tr(
+ td("Travelled"),
+ td("5000m"),
+ td("Home"),
+ td("1200m")
+ )
+ )
)
)
)
)
- val right = panel()
-
val element = div(`class` := "d-container d-column")(
div(`class` := "d-above")(
- top),
+ top
+ ),
div(`class` := "d-above d-container d-row")(
panel(modes),
- panel(infos)),
+ panel(infos)
+ ),
div(`class` := "d-container d-row")(
- div(`class` := "d-container d-details")(
- panel("foo")),
div(`class` := "d-container d-left")(
- left),
+ left
+ ),
div(`class` := "d-container d-column d-middle")(
- div(`class` := "d-container d-center")(
- center),
- div(`class` := "d-container d-below")(
- below)
+ panel(feed),
+ panel(map)
),
- div(`class` := "d-container d-right")(
- right
- )
+ div(`class` := "d-container d-right")()
)
).render
diff --git a/vfd-main/app/views/main.scala.html b/vfd-main/app/views/main.scala.html
index 6300e1f..79a9f2b 100644
--- a/vfd-main/app/views/main.scala.html
+++ b/vfd-main/app/views/main.scala.html
@@ -7,6 +7,7 @@
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
+ <meta name="mobile-web-app-capable" content="yes">
<title>VFD - @title</title>
<link rel="shortcut icon" href="@routes.Assets.at("images/logo.svg")">
diff --git a/vfd-main/public/stylesheets/main.css b/vfd-main/public/stylesheets/main.css
index 00d3a31..5430334 100644
--- a/vfd-main/public/stylesheets/main.css
+++ b/vfd-main/public/stylesheets/main.css
@@ -20,11 +20,11 @@ body {
#vfd-dashboard header {
color: #eeeeee;
background-color: #222222;
- padding-left: 15px;
- padding-right: 15px;
- padding-top: 5px;
- padding-bottom: 5px;
- margin-bottom: 5px;
+ padding-left: 8px;
+ padding-right: 8px;
+ padding-top: 3px;
+ padding-bottom: 3px;
+ margin-bottom: 3px;
display: flex;
}
@@ -82,35 +82,9 @@ body {
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;
+ margin: 3px;
+ padding: 10px;
background-color: white;
border-radius: 3px;
}
@@ -166,6 +140,7 @@ body {
.heartbeat {
color: rgba(165, 25, 25, 1);
animation: heartbeat 2s linear infinite;
+ -webkit-animation: heartbeat 2s linear infinite;
}
@keyframes heartbeat {
@@ -185,6 +160,23 @@ body {
transform: scale(1);
}
}
+@-webkit-keyframes heartbeat {
+ 0% {
+ transform: scale(1);
+ }
+ 7% {
+ transform: scale(1.3);
+ }
+ 14% {
+ transform: scale(1);
+ }
+ 21% {
+ transform: scale(1.3);
+ }
+ 28% {
+ transform: scale(1);
+ }
+}
@keyframes danger-blink {
0% {