aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakob Odersky <jakob@odersky.com>2016-02-27 17:11:10 -0800
committerJakob Odersky <jakob@odersky.com>2016-04-14 04:03:17 -0700
commitdae336ee5514970fee93b0db25ce272ab83bd3c7 (patch)
treea628a7c02c6956d99510d98f2a0321be00b241bf
parent773bc081728bf770281a22c1700d05b1def75542 (diff)
downloadmavigator-dae336ee5514970fee93b0db25ce272ab83bd3c7.tar.gz
mavigator-dae336ee5514970fee93b0db25ce272ab83bd3c7.tar.bz2
mavigator-dae336ee5514970fee93b0db25ce272ab83bd3c7.zip
Refactor UI to a HUD-style interface
-rw-r--r--CONTRIBUTING.md7
-rw-r--r--mavigator-cockpit/src/main/scala/mavigator/cockpit/Cockpit.scala26
-rw-r--r--mavigator-cockpit/src/main/scala/mavigator/cockpit/Instruments.scala173
-rw-r--r--mavigator-cockpit/src/main/scala/mavigator/cockpit/Layout.scala54
-rw-r--r--mavigator-cockpit/src/main/scala/mavigator/cockpit/MavlinkWebSockets.scala60
-rw-r--r--mavigator-cockpit/src/main/scala/mavigator/dashboard/Main.scala23
-rw-r--r--mavigator-cockpit/src/main/scala/mavigator/dashboard/MavlinkSocket.scala70
-rw-r--r--mavigator-cockpit/src/main/scala/mavigator/dashboard/RxUtil.scala70
-rw-r--r--mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/Hud.scala47
-rw-r--r--mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/Layout.scala266
-rw-r--r--mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/instruments/Altimeter.scala19
-rw-r--r--mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/instruments/Bar.scala18
-rw-r--r--mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/instruments/Clock.scala23
-rw-r--r--mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/instruments/Compass.scala17
-rw-r--r--mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/instruments/Distribution.scala25
-rw-r--r--mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/instruments/Generic.scala40
-rw-r--r--mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/instruments/Horizon.scala19
-rw-r--r--mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/instruments/Instrument.scala25
-rw-r--r--mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/instruments/Led.scala17
-rw-r--r--mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/instruments/SvgInstrument.scala54
-rw-r--r--mavigator-cockpit/src/main/scala/mavigator/index/ActiveVehicle.scala3
-rw-r--r--mavigator-cockpit/src/main/scala/mavigator/index/Main.scala71
-rw-r--r--mavigator-cockpit/src/main/scala/mavigator/index/Util.scala6
-rw-r--r--mavigator-cockpit/src/main/scala/mavigator/util/Application.scala12
-rw-r--r--mavigator-cockpit/src/main/scala/mavigator/util/Environment.scala15
-rw-r--r--mavigator-cockpit/src/main/scala/mavigator/util/Page.scala38
-rw-r--r--mavigator-cockpit/src/main/scala/mavigator/util/environment.scala24
-rw-r--r--mavigator-server/src/main/resources/assets/images/hud/attitude.svg815
-rw-r--r--mavigator-server/src/main/resources/assets/images/hud/hud.svg182
-rw-r--r--mavigator-server/src/main/resources/assets/images/hud/overlay.html29
-rw-r--r--mavigator-server/src/main/resources/assets/images/hud/roll.svg143
-rw-r--r--mavigator-server/src/main/resources/assets/stylesheets/main.css273
-rw-r--r--mavigator-server/src/main/resources/assets/stylesheets/reset.css48
-rw-r--r--mavigator-server/src/main/scala/mavigator/Router.scala4
-rw-r--r--mavigator-server/src/main/twirl/mavigator/views/app.scala.html12
-rw-r--r--mavigator-server/src/main/twirl/mavigator/views/index.scala.html5
-rw-r--r--mavigator-server/src/main/twirl/mavigator/views/main.scala.html5
-rw-r--r--mavigator-uav/src/main/scala/mavigator/uav/MavlinkUtil.scala.disabled (renamed from mavigator-uav/src/main/scala/mavigator/uav/MavlinkUtil.scala)0
-rw-r--r--mavigator-uav/src/main/scala/mavigator/uav/Multiplexer.scala.disabled (renamed from mavigator-uav/src/main/scala/mavigator/uav/Multiplexer.scala)0
-rw-r--r--mavigator-uav/src/main/scala/mavigator/uav/Uav.scala2
-rw-r--r--mavigator-uav/src/main/scala/mavigator/uav/mock/RandomFlightPlan.scala3
-rw-r--r--project/Dependencies.scala4
-rw-r--r--project/Js.scala2
-rw-r--r--project/plugins.sbt7
44 files changed, 1414 insertions, 1342 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
deleted file mode 100644
index a95c3bb..0000000
--- a/CONTRIBUTING.md
+++ /dev/null
@@ -1,7 +0,0 @@
-Thanks for taking the time to help out!
-
-# Pull Requests
-Please follow these basic guidelines when submitting pull requests:
- - Follow the Scala style guide
- - Keep commit history clean by squashing non-relevant commits and rebasing onto master prior to a pull request
- - Use the present tense in commit messages ("Add feature" not "Added feature") \ No newline at end of file
diff --git a/mavigator-cockpit/src/main/scala/mavigator/cockpit/Cockpit.scala b/mavigator-cockpit/src/main/scala/mavigator/cockpit/Cockpit.scala
new file mode 100644
index 0000000..f8aba76
--- /dev/null
+++ b/mavigator-cockpit/src/main/scala/mavigator/cockpit/Cockpit.scala
@@ -0,0 +1,26 @@
+package mavigator
+package cockpit
+
+import scala.scalajs.js.annotation.JSExport
+
+import mavigator.util.{Environment, Application}
+import scalatags.JsDom.all._
+import util._
+
+class Cockpit
+ extends Page
+ with Layout
+ with MavlinkWebSockets
+ with Instruments
+
+@JSExport("mavigator_cockpit_Main")
+object Cockpit extends Application {
+
+ override def main(args: Map[String, String])(implicit env: Environment): Unit = {
+ val app = new Cockpit
+ app.attach(env)
+ app.connect(args("socketUrl"), args("remoteSystemId").toInt)
+ }
+
+}
+
diff --git a/mavigator-cockpit/src/main/scala/mavigator/cockpit/Instruments.scala b/mavigator-cockpit/src/main/scala/mavigator/cockpit/Instruments.scala
new file mode 100644
index 0000000..7c4bf92
--- /dev/null
+++ b/mavigator-cockpit/src/main/scala/mavigator/cockpit/Instruments.scala
@@ -0,0 +1,173 @@
+package mavigator
+package cockpit
+
+import org.scalajs.dom
+import org.scalajs.dom.html
+
+import scalatags.JsDom.all._
+
+import util.Page
+
+trait Instruments { page: Page =>
+
+ trait Instrument[A] {
+ def element: html.Element
+ def update(newValue: A): Unit
+ }
+
+ /** Common behaviour for svg-based instruments. */
+ abstract class SvgInstrument[A](
+ path: String
+ ) extends Instrument[A] {
+
+ override def element: html.Element = objectElement
+
+ /** SVG object element that contains the rendered instrument */
+ lazy val objectElement: html.Object = SvgInstrument.svgObject(path)
+
+ /** Retrieves an element of the underlying SVG document by ID. */
+ protected def part(id: String): html.Element =
+ objectElement.contentDocument.getElementById(id).asInstanceOf[html.Element]
+
+ /** Movable parts of the instrument */
+ protected def moveable: Seq[html.Element]
+
+ /** Called when element has been loaded. */
+ private def load(event: dom.Event): Unit = {
+ for (part <- moveable) {
+ part.style.transition = "transform 20ms ease-out"
+ }
+ }
+
+ element.addEventListener("load", (e: dom.Event) => load(e))
+ }
+
+ /** Contains helpers for SVG instruments. */
+ object SvgInstrument {
+
+ /** Retrieves an SVG object element by its instrument's name. */
+ def svgObject(path: String): html.Object = {
+ val fullPath = page.asset("images/" + path)
+ `object`(`type` := "image/svg+xml", "data".attr := fullPath, width := 100.pct)(
+ "Error loading instrument " + fullPath).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, rad: Double): Unit = {
+ elem.style.transform = "rotateZ(" + rad + "rad)";
+ }
+
+ }
+
+ lazy val attitudeOverlay = new SvgInstrument[(Float, Float)]("hud/attitude.svg") {
+ override def element = div(`class`:="hud-overlay")(objectElement).render
+ private lazy val pitchPart = part("pitch")
+ private lazy val rollPart = part("roll")
+ override lazy val moveable = Seq(pitchPart, rollPart)
+
+ override def update(pitchRoll: (Float, Float)) = {
+ import SvgInstrument._
+ val (pitch, roll) = pitchRoll
+ translate(pitchPart, 0, (pitch * 180 / math.Pi * 10).toInt) // 1deg === 10px
+ rotate(rollPart, roll)
+ }
+ }
+
+ lazy val horizonOverlay = new SvgInstrument[(Float, Float)]("hud/horizon.svg") {
+ import SvgInstrument._
+
+ override def element = div(`class`:="hud-overlay")(objectElement).render
+ lazy val horizon = part("horizon")
+ lazy override val moveable = Seq(horizon)
+
+ override def update(pitchRoll: (Float, Float)) = {
+ val (pitch, roll) = pitchRoll
+
+ val t = (pitch * 180 / math.Pi * 10).toInt // 1deg === 10px
+
+ horizon.style.transform = s"rotateZ(${roll}rad)translate(0px, ${t}px) "
+ }
+ }
+
+
+ val overlayStyle = """
+ |.hud-overlay {
+ | position: absolute;
+ | left: 0;
+ | right: 0;
+ | top: 0;
+ | bottom: 0;
+ |
+ | display: flex;
+ | flex-direction: row;
+ | justify-content: center;
+ | align-items: center;
+ |}
+ |.hud-overlay > * {
+ | flex: 1 1 0;
+ | width: 100%;
+ | height: 100%;
+ | max-width: 100%;
+ | max-height: 100%;
+ |}""".stripMargin
+
+
+ def mode(name: String, kind: String, on: Boolean = false) = {
+ div(`class` := s"mode $kind ${if (!on) "off"}")(name)
+ }
+
+ //TODO make these into real instruments and lazy vals
+ def modes = div(style := "float: right;")(
+ mode("LINK", "danger", true),
+ mode("BAT", "warning", true),
+ mode("GPS", "warning", true),
+ mode("STABILIZED", "info", true)
+ )
+
+ val modeStyle = """
+.mode {
+ display: inline-block;
+ box-sizing: border-box;
+ text-decoration: normal;
+ margin-right: 5px;
+ padding: 5px;
+}
+
+.mode.danger {
+ color: #d9534f;
+ text-shadow: 0 0 5px #d9534f;
+ animation: danger-blink 0.5s linear infinite;
+ -webkit-animation: danger-blink 0.5s linear infinite;
+}
+
+.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: #e6e6e6;
+ text-shadow: none;
+ animation: none;
+ -webkit-animation: none;
+}
+"""
+
+ def instrumentStyles: Seq[String] = Seq(overlayStyle, modeStyle)
+
+}
diff --git a/mavigator-cockpit/src/main/scala/mavigator/cockpit/Layout.scala b/mavigator-cockpit/src/main/scala/mavigator/cockpit/Layout.scala
new file mode 100644
index 0000000..887c4d6
--- /dev/null
+++ b/mavigator-cockpit/src/main/scala/mavigator/cockpit/Layout.scala
@@ -0,0 +1,54 @@
+package mavigator
+package cockpit
+
+import org.scalajs.dom.html
+import scalatags.JsDom.all._
+
+import util._
+
+/** Provides main cockpit layout. */
+trait Layout { self: Page with Instruments =>
+
+ /** Elements to display in the mode control pannel (top panel). */
+ def mcp = div(id := "mcp")(
+ img(src := asset("images/logo-invert.svg"), style:="height: 20px; margin: 5px;"),
+ span(`class`:="mode warning")("Demo System"),
+ modes
+ )
+
+ /** Element to deisplay on heads-up display (main area). */
+ def hud = div(id :="hud")(
+ attitudeOverlay.element
+ )
+
+ val layoutStyle = """
+ |#cockpit {
+ | width: 100%;
+ | height: 100%;
+ | display: flex;
+ | flex-direction: column;
+ | justify-content: flex-start;
+ | align-items: stretch;
+ |
+ | background-color: pink;
+ |}
+ |
+ |#mcp {
+ | flex: 0 1 0;
+ | background-color: #222222;
+ |}
+ |
+ |#hud {
+ | flex: 1 1 auto;
+ | position: relative;
+ | background-color: lightblue;
+ |}""".stripMargin
+
+ override def styles = Seq(layoutStyle) ++ instrumentStyles
+
+ override def elements: Seq[html.Element] = Seq(div(id := "cockpit")(
+ mcp,
+ hud
+ ).render)
+
+}
diff --git a/mavigator-cockpit/src/main/scala/mavigator/cockpit/MavlinkWebSockets.scala b/mavigator-cockpit/src/main/scala/mavigator/cockpit/MavlinkWebSockets.scala
new file mode 100644
index 0000000..3797005
--- /dev/null
+++ b/mavigator-cockpit/src/main/scala/mavigator/cockpit/MavlinkWebSockets.scala
@@ -0,0 +1,60 @@
+package mavigator
+package cockpit
+
+import org.mavlink._
+import org.mavlink.Parser.Errors._
+import org.mavlink.messages._
+
+import org.scalajs.dom
+import scalajs.js
+
+trait MavlinkWebSockets { self: Instruments =>
+
+ private class MavlinkWebSocket(url: String, remoteSystemId: Int) {
+
+ private var _open = false
+ def open: Boolean = _open
+
+ private val parser = new Parser(
+ {
+ case pckt@Packet(seq, `remoteSystemId`, compId, msgId, payload) =>
+ val msg = Message.unpack(pckt.messageId, pckt.payload)
+ onMessage(msg)
+ //_packets() = _packets.now + 1
+ case _ =>
+ //_wrongIds() = _wrongIds.now + 1
+ },
+ {
+ case CrcError => //_crcErrors() = _crcErrors.now + 1
+ case OverflowError => //_overflows() = _overflows.now + 1
+ }
+ )
+
+ private val connection = new dom.WebSocket(url)
+
+ connection.binaryType = "arraybuffer"
+
+ connection.onopen = (e: dom.Event) => {
+ _open = true
+ }
+ connection.onmessage = (e: dom.MessageEvent) => {
+ val buffer = e.data.asInstanceOf[js.typedarray.ArrayBuffer]
+ val view = new js.typedarray.DataView(buffer)
+
+ for (i <- 0 until view.byteLength) {
+ parser.push(view.getInt8(i))
+ }
+ }
+ connection.onclose = (e: dom.CloseEvent) => {
+ _open = false
+ }
+
+ }
+
+ private def onMessage(msg: Message): Unit = msg match {
+ case a: Attitude => attitudeOverlay.update((a.pitch.toFloat, a.roll.toFloat))
+ case _ => ()
+ }
+
+ def connect(url: String, remoteSystemId: Int): Unit = new MavlinkWebSocket(url, remoteSystemId)
+}
diff --git a/mavigator-cockpit/src/main/scala/mavigator/dashboard/Main.scala b/mavigator-cockpit/src/main/scala/mavigator/dashboard/Main.scala
deleted file mode 100644
index f3a111a..0000000
--- a/mavigator-cockpit/src/main/scala/mavigator/dashboard/Main.scala
+++ /dev/null
@@ -1,23 +0,0 @@
-package mavigator.dashboard
-
-import scala.scalajs.js
-import scala.scalajs.js.annotation.JSExport
-
-import org.scalajs.dom.html
-
-import mavigator.dashboard.ui.Layout
-import mavigator.util.Environment
-import mavigator.util.Application
-
-import scalatags.JsDom.all._
-
-@JSExport("mavigator_dashboard_Main")
-object Main extends Application {
-
- override def main(args: Map[String, String])(implicit env: Environment): Unit = {
- val socket = new MavlinkSocket(args("socketUrl"), args("remoteSystemId").toInt)
- val layout = new Layout(socket)
-
- env.root.appendChild(layout.element)
- }
-}
diff --git a/mavigator-cockpit/src/main/scala/mavigator/dashboard/MavlinkSocket.scala b/mavigator-cockpit/src/main/scala/mavigator/dashboard/MavlinkSocket.scala
deleted file mode 100644
index 7f4ffdc..0000000
--- a/mavigator-cockpit/src/main/scala/mavigator/dashboard/MavlinkSocket.scala
+++ /dev/null
@@ -1,70 +0,0 @@
-package mavigator.dashboard
-
-import scala.scalajs.js
-import scala.scalajs.js.Any.fromFunction1
-import org.mavlink.Packet
-import org.mavlink.Parser
-import org.mavlink.Parser.Errors._
-import org.mavlink.messages.Message
-import org.scalajs.dom
-import scala.concurrent.duration._
-import rx._
-import rx.ops._
-import scala.concurrent.ExecutionContext.Implicits.global
-
-class MavlinkSocket(url: String, val remoteSystemId: Int) {
- implicit val scheduler = new DomScheduler
-
- lazy val packet: Var[Packet] = Var(Packet.empty)
- lazy val message: Rx[Message] = packet.map{p =>
- Message.unpack(p.messageId, p.payload)
- }
-
- object stats {
- private val DebounceTime = 1.seconds
-
- private[MavlinkSocket] val _crcErrors = Var(0)
- private[MavlinkSocket] val _overflows = Var(0)
- private[MavlinkSocket] val _wrongIds = Var(0)
- private[MavlinkSocket] val _packets = Var(0)
-
- val crcErrors = _crcErrors.debounce(DebounceTime)
- val overflows = _overflows.debounce(DebounceTime)
- val wrongIds = _wrongIds.debounce(DebounceTime)
- val packets = _packets.debounce(DebounceTime)
- val open = Var(false)
- }
-
- private val parser = new Parser(
- {
- case pckt@Packet(seq, `remoteSystemId`, compId, msgId, payload) =>
- packet() = pckt
- stats._packets() += 1
- case _ =>
- stats._wrongIds() += 1
- },
- {
- case CrcError => stats._crcErrors() += 1
- case OverflowError => stats._overflows() += 1
- })
-
- private val connection = new dom.WebSocket(url)
-
- connection.binaryType = "arraybuffer"
-
- connection.onopen = (e: dom.Event) => {
- stats.open() = true
- }
- connection.onmessage = (e: dom.MessageEvent) => {
- val buffer = e.data.asInstanceOf[js.typedarray.ArrayBuffer]
- val view = new js.typedarray.DataView(buffer)
-
- for (i <- 0 until view.byteLength) {
- parser.push(view.getInt8(i))
- }
- }
- connection.onclose = (e: dom.CloseEvent) => {
- stats.open() = false
- }
-
-}
diff --git a/mavigator-cockpit/src/main/scala/mavigator/dashboard/RxUtil.scala b/mavigator-cockpit/src/main/scala/mavigator/dashboard/RxUtil.scala
deleted file mode 100644
index 2637ddf..0000000
--- a/mavigator-cockpit/src/main/scala/mavigator/dashboard/RxUtil.scala
+++ /dev/null
@@ -1,70 +0,0 @@
-package mavigator.dashboard
-
-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 {
-
- /** Rx, implicitly enhanced with additional methods. */
- implicit class RichRx(val rx: Rx[_]) extends AnyVal {
-
- /**
- * Builds a new Rx by applying a partial function to all values of
- * this Rx on which the function is defined.
- * @param initial initial value of the returned Rx
- * @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
- */
- def collect[B](initial: B)(pf: PartialFunction[Any, B]): Rx[B] = {
- val result: Var[B] = Var(initial)
- Obs(rx, skipInitial = true) {
- if (pf.isDefinedAt(rx())) {
- result() = pf(rx())
- }
- }
- result
- }
-
- }
-
- /**
- * 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)
- }
-
-}
diff --git a/mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/Hud.scala b/mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/Hud.scala
deleted file mode 100644
index 18c5c27..0000000
--- a/mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/Hud.scala
+++ /dev/null
@@ -1,47 +0,0 @@
-package mavigator.dashboard.ui
-
-import mavigator.util.Environment
-import mavigator.dashboard.ui.instruments._
-
-import scalatags.JsDom.all._
-import rx._
-
-class Hud(attitude: Rx[(Double, Double)])(implicit env: Environment) {
-
- private def overlay(name: String, z: Int, thinnerThanWide: Boolean = false) = {
- val direction = if (thinnerThanWide) "row" else "column"
- div(
- style:=
- "position: absolute; left: 0; right: 0; top: 0; bottom: 0;" +
- s"display: flex; align-content: center; align-items: stretch; flex-direction: $direction;"+
- s"z-index: $z;"
- )(
- `object`(`type`:="image/svg+xml", "data".attr:=env.asset("images/hud/" + name + ".svg"), style := "flex: 1 1 100%;")
- )
- }
-
- object Horizon extends SvgInstrument[(Double, Double)] {
- import SvgInstrument._
-
- val value = attitude
-
- lazy val element = `object`(`type`:="image/svg+xml", "data".attr:=env.asset("images/hud/horizon.svg"), style:="flex: 1 1 100%;").render
- lazy val horizon = part("horizon")
- lazy val moveable = Seq(horizon)
-
- protected def update(pitchRoll: (Double, Double)) = {
- rotate(horizon, pitchRoll._2)
- //translate(horizon, 0, (pitchRoll._1 * 180 / math.Pi).toInt) // 1deg === 1px
- }
- }
-
- val element = div(
- style:=
- "position: absolute; left: 0; right: 0; top: 0; bottom: 0;" +
- "display: flex; align-content: stretch; align-items: stretch; flex-direction: row;"+
- "z-index: 0;"
- )(
- Horizon.element
- )
-}
-
diff --git a/mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/Layout.scala b/mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/Layout.scala
deleted file mode 100644
index 902cb9c..0000000
--- a/mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/Layout.scala
+++ /dev/null
@@ -1,266 +0,0 @@
-package mavigator.dashboard.ui
-
-import rx._
-import scalatags.JsDom.all._
-import mavigator.util.Environment
-import mavigator.dashboard.MavlinkSocket
-import mavigator.dashboard.ui.instruments._
-import org.mavlink.messages._
-import mavigator.dashboard.rxutil._
-
-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)
-
- 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(
- "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: 100%; color: #ffffff; background-color: #c2c2c2; text-align: center;")
-
- val altimeter = new Altimeter(
- Var(0.0)
- )
- val horizon = new Horizon(socket.message.collect((0.0, 0.0)) {
- case att: Attitude => (att.pitch, att.roll)
- })
- val compass = new Compass(socket.message.collect(0.0) {
- case att: Attitude => att.yaw
- })
- val motor0 = new Generic(0, 50, 100, "%", socket.message.collect(0.0) {
- case s: ServoOutputRaw => 100 * (s.servo1Raw - 1000) / 1000
- })
- val motor1 = new Generic(0, 50, 100, "%", socket.message.collect(0.0) {
- case s: ServoOutputRaw => 100 * (s.servo2Raw - 1000) / 1000
- })
- val motor2 = new Generic(0, 50, 100, "%", socket.message.collect(0.0) {
- case s: ServoOutputRaw => 100 * (s.servo3Raw - 1000) / 1000
- })
- val motor3 = new Generic(0, 50, 100, "%", socket.message.collect(0.0) {
- case s: ServoOutputRaw => 100 * (s.servo4Raw - 1000) / 1000
- })
- val powerDistribution = new Distribution(
- socket.message.collect((0.0, 0.0, 0.0, 0.0)) {
- case s: ServoOutputRaw =>
- (
- 1.0 * (s.servo1Raw - 1000) / 1000,
- 1.0 * (s.servo2Raw - 1000) / 1000,
- 1.0 * (s.servo3Raw - 1000) / 1000,
- 1.0 * (s.servo4Raw - 1000) / 1000
- )
- }
- )
- val batteryLevel = new Bar(
- Var(0.0)
- )
-
- val top = header(
- div("Flight Control Panel"),
- div((new Clock).element),
- div("UAV " + socket.remoteSystemId)
- )
-
- 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()
- )
- )
- )
- ),
- 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 hud = {
- def overlay(name: String, z: Int, thinnerThanWide: Boolean = false) = {
- val direction = if (thinnerThanWide) "row" else "column"
- div(
- style:=
- "position: absolute; left: 0; right: 0; top: 0; bottom: 0;" +
- s"display: flex; align-content: center; align-items: stretch; flex-direction: $direction;"+
- s"z-index: $z;"
- )(
- `object`(`type`:="image/svg+xml", "data".attr:=env.asset("images/hud/" + name + ".svg"), style := "flex: 1 1 100%;")
- )
-
- }
-
- Seq(
- overlay("horizon", 0),
- overlay("roll", 1)
- )
- }
-
-
-
- val element = div(`class` := "d-container d-column", style:="width: 100%; height: 100%;")(
- 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-left")(
- left
- ),
- div(`class` := "d-main", style:="position: relative;")(
- (new Hud(horizon.value)).element
- )
- )
- ).render
-
-}
diff --git a/mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/instruments/Altimeter.scala b/mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/instruments/Altimeter.scala
deleted file mode 100644
index 9919d33..0000000
--- a/mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/instruments/Altimeter.scala
+++ /dev/null
@@ -1,19 +0,0 @@
-package mavigator.dashboard.ui.instruments
-
-import mavigator.util.Environment
-
-import org.scalajs.dom.html
-import rx._
-
-class Altimeter(val value: Rx[Double])(implicit env: Environment) extends SvgInstrument[Double] {
- import SvgInstrument._
-
- lazy val element = svgObject("altimeter")
- lazy val hand = part("hand")
- lazy val moveable = Seq(hand)
-
- // 1m === 36deg = 2Pi / 10 ~= 0.62832
- protected def update(altitude: Double) = {
- rotate(hand, (altitude * 0.62832).toInt)
- }
-}
diff --git a/mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/instruments/Bar.scala b/mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/instruments/Bar.scala
deleted file mode 100644
index 0950eab..0000000
--- a/mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/instruments/Bar.scala
+++ /dev/null
@@ -1,18 +0,0 @@
-package mavigator.dashboard.ui.instruments
-
-import mavigator.util.Environment
-import org.scalajs.dom.html
-import rx._
-
-class Bar(val value: Rx[Double])(implicit env: Environment) extends SvgInstrument[Double] {
- import SvgInstrument._
-
- 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)
- }
-
-}
diff --git a/mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/instruments/Clock.scala b/mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/instruments/Clock.scala
deleted file mode 100644
index 160fd2d..0000000
--- a/mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/instruments/Clock.scala
+++ /dev/null
@@ -1,23 +0,0 @@
-package mavigator.dashboard.ui.instruments
-
-import org.scalajs.dom
-import rx._
-import scala.scalajs.js.Date
-import scalatags.JsDom.all._
-
-class Clock extends Instrument[Date] {
-
- def format(date: Date) = date.toLocaleTimeString()
-
- val value = Var(new Date)
-
- val element = span(format(value())).render
-
- protected def update(value: Date) = {
- element.innerHTML = format(value)
- }
-
- dom.setInterval(() => {value() = new Date}, 1000)
- ready()
-
-}
diff --git a/mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/instruments/Compass.scala b/mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/instruments/Compass.scala
deleted file mode 100644
index 117c7a3..0000000
--- a/mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/instruments/Compass.scala
+++ /dev/null
@@ -1,17 +0,0 @@
-package mavigator.dashboard.ui.instruments
-
-import org.scalajs.dom.html
-import rx._
-import mavigator.util.Environment
-
-class Compass(val value: Rx[Double])(implicit env: Environment) extends SvgInstrument[Double] {
- import SvgInstrument._
-
- lazy val element = svgObject("compass")
- lazy val plate = part("heading")
- lazy val moveable = Seq(plate)
-
- protected def update(heading: Double) = {
- rotate(plate, heading)
- }
-}
diff --git a/mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/instruments/Distribution.scala b/mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/instruments/Distribution.scala
deleted file mode 100644
index aadafe0..0000000
--- a/mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/instruments/Distribution.scala
+++ /dev/null
@@ -1,25 +0,0 @@
-package mavigator.dashboard.ui.instruments
-
-import org.scalajs.dom.html
-import rx._
-import mavigator.util.Environment
-
-class Distribution(val value: Rx[(Double, Double, Double, Double)])(implicit env: Environment) extends SvgInstrument[(Double, Double, Double, Double)] {
- import SvgInstrument._
-
- lazy val element = svgObject("distribution")
- lazy val position = part("position")
- lazy val moveable = Seq(position)
-
- private final val Radius = 50 //px
-
- protected def update(value: (Double, Double, Double, Double)) = {
- val sum = value._1 + value._2 + value._3 + value._4
- val i = (value._1 - value._3) / sum
- val j = (value._2 - value._4) / sum
- val x = math.sqrt(2) / 2 * (i - j)
- val y = math.sqrt(2) / 2 * (-i - j)
- translate(position, (x * Radius).toInt, (y * Radius).toInt)
- }
-
-}
diff --git a/mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/instruments/Generic.scala b/mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/instruments/Generic.scala
deleted file mode 100644
index 14f7de3..0000000
--- a/mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/instruments/Generic.scala
+++ /dev/null
@@ -1,40 +0,0 @@
-package mavigator.dashboard.ui.instruments
-
-import org.scalajs.dom
-import org.scalajs.dom.html
-import rx._
-import mavigator.util.Environment
-
-class Generic(
- min: Double,
- med: Double,
- max: Double,
- unit: String,
- val value: Rx[Double])
- (implicit env: Environment)
- extends SvgInstrument[Double] {
-
- import SvgInstrument._
-
- 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
- super.load(e)
- }
-
- protected def update(value: Double) = {
- rotate(handElement, value / (max - min) * math.Pi * 3 / 2)
- valueElement.textContent = value.toString
- }
-}
diff --git a/mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/instruments/Horizon.scala b/mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/instruments/Horizon.scala
deleted file mode 100644
index 249f892..0000000
--- a/mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/instruments/Horizon.scala
+++ /dev/null
@@ -1,19 +0,0 @@
-package mavigator.dashboard.ui.instruments
-
-import org.scalajs.dom.html
-import rx._
-import mavigator.util.Environment
-
-class Horizon(val value: Rx[(Double, Double)])(implicit env: Environment) extends SvgInstrument[(Double, Double)] {
- import SvgInstrument._
-
- 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 * 180 / math.Pi).toInt) // 1deg === 1px
- rotate(roll, pitchRoll._2)
- }
-}
diff --git a/mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/instruments/Instrument.scala b/mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/instruments/Instrument.scala
deleted file mode 100644
index 61f240c..0000000
--- a/mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/instruments/Instrument.scala
+++ /dev/null
@@ -1,25 +0,0 @@
-package mavigator.dashboard.ui.instruments
-
-import rx._
-import org.scalajs.dom.html
-
-/** Common trait to all flight instruments. */
-trait Instrument[A] {
-
- /** Current value that is displayed in the instrument. */
- val value: Rx[A]
-
- /** 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())
- }
- }
-
-}
diff --git a/mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/instruments/Led.scala b/mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/instruments/Led.scala
deleted file mode 100644
index c6f7d45..0000000
--- a/mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/instruments/Led.scala
+++ /dev/null
@@ -1,17 +0,0 @@
-package mavigator.dashboard.ui.instruments
-
-import rx._
-import scalatags.JsDom.all._
-import mavigator.util.Environment
-
-class Led(val value: Rx[String])(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
- protected def moveable = Seq()
-
- protected def update(color: String) = {
- part("light").setAttribute("fill", color)
- }
-
-}
diff --git a/mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/instruments/SvgInstrument.scala b/mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/instruments/SvgInstrument.scala
deleted file mode 100644
index 28c8d65..0000000
--- a/mavigator-cockpit/src/main/scala/mavigator/dashboard/ui/instruments/SvgInstrument.scala
+++ /dev/null
@@ -1,54 +0,0 @@
-package mavigator.dashboard.ui.instruments
-
-
-import org.scalajs.dom
-import org.scalajs.dom.html
-
-import scalatags.JsDom.all._
-
-import mavigator.util.Environment
-
-/** An instrument backed by an SVG image. */
-trait SvgInstrument[A] extends Instrument[A] {
-
- /** SVG object element that contains the rendered instrument */
- val element: html.Object
-
- /** Retrieves an element of the underlying SVG document by ID. */
- protected def part(id: String) = element.contentDocument.getElementById(id).asInstanceOf[html.Element]
-
- /** Movable parts of the instrument */
- protected def moveable: Seq[html.Element]
-
- /** Called when element has been loaded. */
- protected def load(event: dom.Event): Unit = {
- for (part <- moveable) {
- part.style.transition = "transform 50ms ease-out"
- }
- ready()
- }
-
- element.addEventListener("load", (e: dom.Event) => load(e))
-}
-
-/** Contains helpers for SVG instruments. */
-object SvgInstrument {
-
- /** 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, rad: Double): Unit = {
- elem.style.transform = "rotateZ(" + rad + "rad)";
- }
-
-}
diff --git a/mavigator-cockpit/src/main/scala/mavigator/index/ActiveVehicle.scala b/mavigator-cockpit/src/main/scala/mavigator/index/ActiveVehicle.scala
index 870d3be..7e59ed8 100644
--- a/mavigator-cockpit/src/main/scala/mavigator/index/ActiveVehicle.scala
+++ b/mavigator-cockpit/src/main/scala/mavigator/index/ActiveVehicle.scala
@@ -13,7 +13,8 @@ object ActiveVehicle {
id,
vehicleType(hb.`type`),
autopilot(hb.autopilot),
- state(hb.systemStatus))
+ state(hb.systemStatus)
+ )
def vehicleType(tpe: Int) = tpe match {
case MavType.MavTypeGeneric => "Generic"
diff --git a/mavigator-cockpit/src/main/scala/mavigator/index/Main.scala b/mavigator-cockpit/src/main/scala/mavigator/index/Main.scala
index 7724f89..452d517 100644
--- a/mavigator-cockpit/src/main/scala/mavigator/index/Main.scala
+++ b/mavigator-cockpit/src/main/scala/mavigator/index/Main.scala
@@ -5,7 +5,6 @@ import scala.scalajs.js.annotation.JSExport
import org.scalajs.dom.html
-import mavigator.dashboard.ui.Layout
import mavigator.util.Environment
import mavigator.util.Application
@@ -29,16 +28,17 @@ object Main extends Application {
val parser = new Parser(
packet => {
val m: Message = Message.unpack(packet.messageId, packet.payload)
- println(m)
m match {
case hb: Heartbeat =>
- active() += ActiveVehicle.fromHeartbeat(packet.systemId, hb)
+ active() = active.now + ActiveVehicle.fromHeartbeat(packet.systemId, hb)
case _ => ()
}
}
)
override def main(args: Map[String, String])(implicit env: Environment): Unit = {
+ import rx.Ctx.Owner.Unsafe._
+
val root = env.root
val connection = new dom.WebSocket(args("socketUrl"))
@@ -64,34 +64,45 @@ object Main extends Application {
}
- root.appendChild(div(
- table(`class` := "table table-hover")(
- thead(
- tr(
- th("System ID"),
- th("Type"),
- th("Autopilot"),
- th("State"),
- th("")
- )
- ),
- Rx {
- tbody(
- for (vehicle <- active().toSeq) yield {
- tr(
- td(vehicle.systemId),
- td(vehicle.vehicleType),
- td(vehicle.autopilot),
- td(vehicle.state),
- td(a(href := "/dashboard/" + vehicle.systemId, `class` := "btn btn-default")("Pilot"))
- )
- }
+ val elem =
+ div(`class` := "container")(
+ div(`class` := "col-md-6 col-md-offset-3 col-sm-6 col-sm-offset-3")(
+ div(`class` := "panel panel-default")(
+ div(`class` := "panel-heading")(
+ h3(`class` := "panel-title")("Available vehicles")
+ ),
+ div(`class` := "panel-body")(
+ table(`class` := "table table-hover")(
+ thead(
+ tr(
+ th("System ID"),
+ th("Type"),
+ th("Autopilot"),
+ th("State"),
+ th("")
+ )
+ ),
+ Rx {
+ tbody(
+ for (vehicle <- active().toSeq) yield {
+ tr(
+ td(vehicle.systemId),
+ td(vehicle.vehicleType),
+ td(vehicle.autopilot),
+ td(vehicle.state),
+ td(a(href := "/cockpit/" + vehicle.systemId, `class` := "btn btn-default")("Pilot"))
+ )
+ }
+ )
+ }
+ ),
+ p(i(`class`:="fa fa-spinner fa-spin")(), " Listening for heartbeats...")
+ )
)
- }
- ),
- i(`class`:="fa fa-spinner fa-spin")(),
- " Listening for heartbeats..."
- ).render)
+ )
+ ).render
+
+ root.appendChild(elem)
}
}
diff --git a/mavigator-cockpit/src/main/scala/mavigator/index/Util.scala b/mavigator-cockpit/src/main/scala/mavigator/index/Util.scala
index 16f3d5d..2671f2f 100644
--- a/mavigator-cockpit/src/main/scala/mavigator/index/Util.scala
+++ b/mavigator-cockpit/src/main/scala/mavigator/index/Util.scala
@@ -13,7 +13,7 @@ import scala.util.Failure
import scalatags.JsDom.all._
object Util {
-
+
/**
* Copied from https://github.com/lihaoyi/workbench-example-app/blob/todomvc/src/main/scala/example/Framework.scala
*
@@ -22,13 +22,13 @@ object Util {
* 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 = {
+ implicit def rxMod(r: Rx[HtmlTag])(implicit owner: Ctx.Owner): 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) {
+ r.triggerLater{
val newLast = rSafe
last.parentElement.replaceChild(newLast, last)
last = newLast
diff --git a/mavigator-cockpit/src/main/scala/mavigator/util/Application.scala b/mavigator-cockpit/src/main/scala/mavigator/util/Application.scala
index 7cb6f27..68a39b1 100644
--- a/mavigator-cockpit/src/main/scala/mavigator/util/Application.scala
+++ b/mavigator-cockpit/src/main/scala/mavigator/util/Application.scala
@@ -4,6 +4,7 @@ package util
import scala.scalajs.js.annotation.JSExport
import scala.scalajs.js
+import org.scalajs.dom.console
import org.scalajs.dom.html
trait Application {
@@ -13,13 +14,18 @@ trait Application {
@JSExport
final def start(settings: js.Dynamic): Unit = {
- val env = new StaticEnvironment(
+ console.info("Initializing environment,,,")
+ val env = new Environment(
root = settings.root.asInstanceOf[html.Element],
- assetsBase = settings.assetsBase.asInstanceOf[String]
+ styleRoot = settings.styleRoot.asInstanceOf[html.Element],
+ baseUrl = settings.baseUrl.asInstanceOf[String]
)
- val args = settings.args.asInstanceOf[js.Dictionary[Any]].mapValues(_.toString).toMap
+ console.info("Reading arguments...")
+ val args: Map[String, String] =
+ settings.args.asInstanceOf[js.Dictionary[Any]].mapValues(_.toString).toMap
+ console.info("Entering main...")
main(args)(env)
}
diff --git a/mavigator-cockpit/src/main/scala/mavigator/util/Environment.scala b/mavigator-cockpit/src/main/scala/mavigator/util/Environment.scala
new file mode 100644
index 0000000..b1076fc
--- /dev/null
+++ b/mavigator-cockpit/src/main/scala/mavigator/util/Environment.scala
@@ -0,0 +1,15 @@
+package mavigator
+package util
+
+import org.scalajs.dom
+import org.scalajs.dom.html
+
+/** Represents an application's environment
+ * @param root The application's root element.
+ * @param styleRoot An html 'style' tag to which app-specific styles are appended.
+ * @param baseUrl Base URL. */
+case class Environment(
+ root: html.Element,
+ styleRoot: html.Element,
+ baseUrl: String
+)
diff --git a/mavigator-cockpit/src/main/scala/mavigator/util/Page.scala b/mavigator-cockpit/src/main/scala/mavigator/util/Page.scala
new file mode 100644
index 0000000..f8f0dee
--- /dev/null
+++ b/mavigator-cockpit/src/main/scala/mavigator/util/Page.scala
@@ -0,0 +1,38 @@
+package mavigator
+package util
+
+import org.scalajs.dom
+import org.scalajs.dom.html
+
+/** Contents of a single-page app, consisting of styles and a main element. */
+trait Page {
+
+ private var isAttached = false
+ private def attached[A](action: => A) = if (!isAttached) {
+ sys.error("Page has not been attached to an environment yet.")
+ } else {
+ action
+ }
+
+ private var baseUrl: String = null
+
+ def asset(path: String): String = attached { baseUrl + "/" + path }
+
+ def styles: Seq[String]
+
+ def elements: Seq[html.Element]
+
+ /** Attach this page to a website. */
+ def attach(env: Environment): Unit = {
+ baseUrl = env.baseUrl
+ isAttached = true
+
+ val styleText = dom.document.createTextNode(styles.reduce(_ + _))
+ env.styleRoot.appendChild(styleText)
+
+ for (elem <- elements) {
+ env.root.appendChild(elem)
+ }
+ }
+
+}
diff --git a/mavigator-cockpit/src/main/scala/mavigator/util/environment.scala b/mavigator-cockpit/src/main/scala/mavigator/util/environment.scala
deleted file mode 100644
index 3d58c5a..0000000
--- a/mavigator-cockpit/src/main/scala/mavigator/util/environment.scala
+++ /dev/null
@@ -1,24 +0,0 @@
-package mavigator
-package util
-
-import org.scalajs.dom.html
-
-/** Represents an application's environment */
-trait Environment {
-
- /** The application's root element. */
- def root: html.Element
-
- /** Retrieve an asset's URL based on its file location. */
- def asset(file: String): String
-
-}
-
-class StaticEnvironment(
- override val root: html.Element,
- assetsBase: String
-) extends Environment {
-
- override def asset(file: String): String = assetsBase + "/" + file
-
-}
diff --git a/mavigator-server/src/main/resources/assets/images/hud/attitude.svg b/mavigator-server/src/main/resources/assets/images/hud/attitude.svg
new file mode 100644
index 0000000..eb7dd70
--- /dev/null
+++ b/mavigator-server/src/main/resources/assets/images/hud/attitude.svg
@@ -0,0 +1,815 @@
+<?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="1000"
+ height="1000"
+ viewBox="-500 -500 1000 1000"
+ id="svg2"
+ version="1.1"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="attitude.svg">
+ <defs
+ id="defs4">
+ <clipPath
+ clipPathUnits="userSpaceOnUse"
+ id="clipPath4472">
+ <circle
+ id="circle4474"
+ cx="0"
+ cy="0"
+ r="380" />
+ </clipPath>
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1"
+ inkscape:cx="415.60712"
+ inkscape:cy="547.77206"
+ inkscape:document-units="px"
+ inkscape:current-layer="svg2"
+ showgrid="true"
+ units="px"
+ inkscape:snap-grids="true"
+ inkscape:snap-bbox="true"
+ inkscape:bbox-paths="true"
+ inkscape:snap-bbox-midpoints="true"
+ inkscape:bbox-nodes="true"
+ inkscape:snap-bbox-edge-midpoints="true"
+ inkscape:snap-center="true"
+ inkscape:snap-intersection-paths="true"
+ inkscape:object-nodes="true"
+ inkscape:snap-smooth-nodes="true"
+ inkscape:object-paths="true"
+ inkscape:snap-midpoints="true"
+ inkscape:snap-object-midpoints="true"
+ inkscape:snap-text-baseline="true"
+ inkscape:window-width="1920"
+ inkscape:window-height="1034"
+ inkscape:window-x="0"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1"
+ showguides="false">
+ <inkscape:grid
+ type="xygrid"
+ id="grid4686" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <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="roll"
+ style="stroke:#1a1a1a"
+ clip-path="none">
+ <g
+ id="g4476"
+ clip-path="url(#clipPath4472)">
+ <g
+ id="pitch">
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -50,-100 100,0"
+ id="path5000"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -30,-50 60,0"
+ id="path5002"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path5028"
+ d="m -30,-150 60,0"
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -30,-250 60,0"
+ id="path5030"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path5032"
+ d="m -30,-350 60,0"
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path5034"
+ d="m -50,-200 100,0"
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -50,-300 100,0"
+ id="path5036"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path5038"
+ d="m -30,50 60,0"
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -30,150 60,0"
+ id="path5040"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path5042"
+ d="m -30,250 60,0"
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -30,350 60,0"
+ id="path5044"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path5046"
+ d="m -50,100 100,0"
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -50,200 100,0"
+ id="path5048"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path5050"
+ d="m -50,300 100,0"
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="50"
+ y="-95.449829"
+ id="text5054"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan5056"
+ x="50"
+ y="-95.449829">10</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="50"
+ y="-195.44983"
+ id="text5058"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan5060"
+ x="50"
+ y="-195.44983">20</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="50"
+ y="-295.44983"
+ id="text5062"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan5064"
+ x="50"
+ y="-295.44983">30</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="50"
+ y="104.55017"
+ id="text5070"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan5072"
+ x="50"
+ y="104.55017">10</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="50"
+ y="204.55017"
+ id="text5074"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan5076"
+ x="50"
+ y="204.55017">20</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="50"
+ y="304.55017"
+ id="text5078"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan5080"
+ x="50"
+ y="304.55017">30</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:end;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="-50"
+ y="-295.44983"
+ id="text5082"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan5084"
+ x="-50"
+ y="-295.44983">30</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:end;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="-50"
+ y="-195.44983"
+ id="text5086"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan5088"
+ x="-50"
+ y="-195.44983">20</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:end;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="-50"
+ y="-95.449829"
+ id="text5090"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan5092"
+ x="-50"
+ y="-95.449829">10</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:end;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="-50"
+ y="104.55017"
+ id="text5094"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan5096"
+ x="-50"
+ y="104.55017">10</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:end;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="-50"
+ y="204.55017"
+ id="text5098"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan5100"
+ x="-50"
+ y="204.55017">20</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:end;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="-50"
+ y="304.55017"
+ id="text5102"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ x="-50"
+ y="304.55017"
+ id="tspan5106">30</tspan></text>
+ <path
+ inkscape:connector-curvature="0"
+ id="path5110"
+ d="M -50,0 50,0"
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:end;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="-50"
+ y="4.5501709"
+ id="text5116"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan5118"
+ x="-50"
+ y="4.5501709">0</tspan></text>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,-70 20,0"
+ id="path5307"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path5309"
+ d="m -10,-80 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path5305"
+ d="m -10,-60 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="50"
+ y="4.5501709"
+ id="text5120"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan5122"
+ x="50"
+ y="4.5501709">0</tspan></text>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,-40 20,0"
+ id="path4231"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4233"
+ d="m -10,-30 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,-20 20,0"
+ id="path4235"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4237"
+ d="m -10,-10 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,-90 20,0"
+ id="path4239"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4241"
+ d="m -10,-270 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,-280 20,0"
+ id="path4243"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,-260 20,0"
+ id="path4245"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4247"
+ d="m -10,-240 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,-230 20,0"
+ id="path4249"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4251"
+ d="m -10,-220 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,-210 20,0"
+ id="path4253"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4255"
+ d="m -10,-290 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,-340 20,0"
+ id="path4263"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4265"
+ d="m -10,-330 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,-320 20,0"
+ id="path4267"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4269"
+ d="m -10,-310 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4273"
+ d="m -10,-170 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,-180 20,0"
+ id="path4275"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,-160 20,0"
+ id="path4277"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4279"
+ d="m -10,-140 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,-130 20,0"
+ id="path4281"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4283"
+ d="m -10,-120 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,-110 20,0"
+ id="path4285"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4287"
+ d="m -10,-190 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,30 20,0"
+ id="path4289"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4291"
+ d="m -10,20 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4293"
+ d="m -10,40 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,60 20,0"
+ id="path4295"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4297"
+ d="m -10,70 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,80 20,0"
+ id="path4299"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4301"
+ d="m -10,90 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,10 20,0"
+ id="path4303"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4305"
+ d="m -10,130 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,120 20,0"
+ id="path4307"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,140 20,0"
+ id="path4309"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4311"
+ d="m -10,160 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,170 20,0"
+ id="path4313"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4315"
+ d="m -10,180 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,190 20,0"
+ id="path4317"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4319"
+ d="m -10,110 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,230 20,0"
+ id="path4321"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4323"
+ d="m -10,220 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4325"
+ d="m -10,240 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,260 20,0"
+ id="path4327"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4329"
+ d="m -10,270 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,280 20,0"
+ id="path4331"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4333"
+ d="m -10,290 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,210 20,0"
+ id="path4335"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4337"
+ d="m -10,330 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,320 20,0"
+ id="path4339"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,340 20,0"
+ id="path4341"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4351"
+ d="m -10,310 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ </g>
+ </g>
+ <text
+ inkscape:transform-center-x="-387.965"
+ transform="matrix(0.5,0.8660254,-0.8660254,0.5,0,0)"
+ sodipodi:linespacing="125%"
+ id="text5215"
+ y="-443.43323"
+ x="7.9833984"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:end;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"
+ inkscape:transform-center-y="-223.9917"><tspan
+ y="-443.43323"
+ x="7.9833984"
+ id="tspan5217"
+ sodipodi:role="line">60</tspan></text>
+ <text
+ inkscape:transform-center-y="-387.965"
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:end;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="7.9833989"
+ y="-443.43323"
+ id="text5211"
+ sodipodi:linespacing="125%"
+ transform="matrix(0.8660254,0.5,-0.5,0.8660254,0,0)"
+ inkscape:transform-center-x="-223.9917"><tspan
+ sodipodi:role="line"
+ id="tspan5213"
+ x="7.9833989"
+ y="-443.43323">30</tspan></text>
+ <text
+ sodipodi:linespacing="125%"
+ id="text5207"
+ y="-443.43323"
+ x="0.85449171"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:center;letter-spacing:0px;word-spacing:0px;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"
+ inkscape:transform-center-y="-447.9834"><tspan
+ y="-443.43323"
+ x="0.85449171"
+ id="tspan5209"
+ sodipodi:role="line">0</tspan></text>
+ <text
+ inkscape:transform-center-y="-387.965"
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:end;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="7.9833989"
+ y="-443.43323"
+ id="text5203"
+ sodipodi:linespacing="125%"
+ transform="matrix(0.8660254,-0.5,0.5,0.8660254,0,0)"
+ inkscape:transform-center-x="223.9917"><tspan
+ sodipodi:role="line"
+ id="tspan5205"
+ x="7.9833989"
+ y="-443.43323">30</tspan></text>
+ <text
+ inkscape:transform-center-x="387.965"
+ transform="matrix(0.5,-0.8660254,0.8660254,0.5,0,0)"
+ sodipodi:linespacing="125%"
+ id="text5199"
+ y="-443.43323"
+ x="7.9833994"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:end;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"
+ inkscape:transform-center-y="-223.9917"><tspan
+ y="-443.43323"
+ x="7.9833994"
+ id="tspan5201"
+ sodipodi:role="line">60</tspan></text>
+ <path
+ sodipodi:open="true"
+ d="M -346.65485,-199.57559 A 400,400 0 0 1 -0.24561214,-399.99992 400,400 0 0 1 346.4095,-200.00115"
+ sodipodi:end="5.7595832"
+ sodipodi:start="3.6639667"
+ sodipodi:ry="400"
+ sodipodi:rx="400"
+ sodipodi:cy="0"
+ sodipodi:cx="0"
+ sodipodi:type="arc"
+ id="path4684"
+ style="fill:none;stroke:#1a1a1a;stroke-width:1;stroke-linecap:butt;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
+ <path
+ inkscape:transform-center-x="420"
+ inkscape:connector-curvature="0"
+ id="path4698"
+ d="m -400,0 -40,0"
+ style="fill:none;fill-rule:evenodd;stroke:#1a1a1a;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ inkscape:transform-center-y="-210"
+ style="fill:none;fill-rule:evenodd;stroke:#1a1a1a;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -346.41016,-200 -34.64102,-20"
+ id="path4700"
+ inkscape:connector-curvature="0"
+ inkscape:transform-center-x="363.73067" />
+ <path
+ inkscape:transform-center-x="210"
+ inkscape:connector-curvature="0"
+ id="path4702"
+ d="m -200,-346.41016 -20,-34.64102"
+ style="fill:none;fill-rule:evenodd;stroke:#1a1a1a;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ inkscape:transform-center-y="-363.73067" />
+ <path
+ inkscape:transform-center-x="-210"
+ inkscape:connector-curvature="0"
+ id="path4706"
+ d="m 200,-346.41016 20,-34.64102"
+ style="fill:none;fill-rule:evenodd;stroke:#1a1a1a;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ inkscape:transform-center-y="-363.73067" />
+ <path
+ inkscape:transform-center-y="-210"
+ style="fill:none;fill-rule:evenodd;stroke:#1a1a1a;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 346.41016,-200 34.64102,-20"
+ id="path4708"
+ inkscape:connector-curvature="0"
+ inkscape:transform-center-x="-363.73067" />
+ <path
+ inkscape:transform-center-x="-420"
+ inkscape:connector-curvature="0"
+ id="path4710"
+ d="M 400,-7.5688777e-7 440,1.1674235e-6"
+ style="fill:none;fill-rule:evenodd;stroke:#1a1a1a;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ inkscape:transform-center-y="-265.14989"
+ style="fill:#ffff00;fill-rule:evenodd;stroke:#1a1a1a;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -306.41778,-257.11504 -19.15111,-16.06969"
+ id="path4714"
+ inkscape:connector-curvature="0"
+ inkscape:transform-center-x="315.99334" />
+ <path
+ inkscape:transform-center-x="265.1499"
+ inkscape:connector-curvature="0"
+ id="path4725"
+ d="m -257.11505,-306.41777 -16.06969,-19.15112"
+ style="fill:#ffff00;fill-rule:evenodd;stroke:#1a1a1a;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ inkscape:transform-center-y="-315.99334" />
+ <path
+ inkscape:transform-center-y="-387.62322"
+ style="fill:#ffff00;fill-rule:evenodd;stroke:#1a1a1a;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -136.80807,-375.87705 -8.55049,-23.49232"
+ id="path4727"
+ inkscape:connector-curvature="0"
+ inkscape:transform-center-x="141.08332" />
+ <path
+ inkscape:transform-center-x="71.629882"
+ inkscape:connector-curvature="0"
+ id="path4729"
+ d="m -69.459281,-393.92311 -4.341191,-24.62019"
+ style="fill:#ffff00;fill-rule:evenodd;stroke:#1a1a1a;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ inkscape:transform-center-y="-406.23322" />
+ <path
+ inkscape:transform-center-x="-71.629877"
+ inkscape:transform-center-y="-406.23322"
+ style="fill:#ffff00;fill-rule:evenodd;stroke:#1a1a1a;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 69.459269,-393.92311 4.341217,-24.62019"
+ id="path4731"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4733"
+ d="m 136.80806,-375.87706 8.55051,-23.49231"
+ style="fill:#ffff00;fill-rule:evenodd;stroke:#1a1a1a;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ inkscape:transform-center-y="-387.62323"
+ inkscape:transform-center-x="-141.08331" />
+ <path
+ inkscape:transform-center-x="-265.1499"
+ inkscape:transform-center-y="-315.99336"
+ style="fill:#ffff00;fill-rule:evenodd;stroke:#1a1a1a;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 257.11506,-306.41779 16.06969,-19.1511"
+ id="path4735"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4737"
+ d="m 306.4178,-257.11505 19.15111,-16.06968"
+ style="fill:#ffff00;fill-rule:evenodd;stroke:#1a1a1a;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ inkscape:transform-center-y="-265.14991"
+ inkscape:transform-center-x="-315.99335" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4739"
+ d="m 0,-400 -12,-20 24,0 z"
+ style="fill:none;fill-rule:evenodd;stroke:#1a1a1a;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ </g>
+ <g
+ id="static">
+ <path
+ inkscape:transform-center-y="-390"
+ inkscape:transform-center-x="-0.0686585"
+ sodipodi:nodetypes="cccc"
+ inkscape:connector-curvature="0"
+ id="path4696"
+ d="M 0,-400 -11.862683,-380 12,-380 Z"
+ style="fill:#ffdd55;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:#fbff00;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 103.52762,-386.37033 -16.634855,16.24823 23.049585,6.17611 z"
+ id="path4741"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccc"
+ inkscape:transform-center-x="-98.417558"
+ inkscape:transform-center-y="-375.15816" />
+ <path
+ inkscape:transform-center-y="-375.17593"
+ inkscape:transform-center-x="98.284918"
+ sodipodi:nodetypes="cccc"
+ inkscape:connector-curvature="0"
+ id="path4743"
+ d="m -103.52762,-386.37033 -6.28209,22.3888 23.049582,-6.17612 z"
+ style="fill:#fbff00;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ d="M 30,0 150,0 M -30,0 -150,0 M 0,0 15,25 30,0 15,25 0,0 -15,25 -15,25 -30,0"
+ style="fill:none;fill-rule:evenodd;stroke:#ffdd55;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path4991"
+ inkscape:connector-curvature="0" />
+ </g>
+</svg>
diff --git a/mavigator-server/src/main/resources/assets/images/hud/hud.svg b/mavigator-server/src/main/resources/assets/images/hud/hud.svg
deleted file mode 100644
index 943d7a4..0000000
--- a/mavigator-server/src/main/resources/assets/images/hud/hud.svg
+++ /dev/null
@@ -1,182 +0,0 @@
-<?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="1200"
- height="1200"
- id="svg2"
- version="1.1"
- inkscape:version="0.48.5 r10040"
- sodipodi:docname="drawing.svg"
- viewBox="-600 600 1200 1200">
- <defs
- id="defs4" />
- <sodipodi:namedview
- id="base"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageopacity="0.0"
- inkscape:pageshadow="2"
- inkscape:zoom="0.25013902"
- inkscape:cx="1001.4796"
- inkscape:cy="-231.89709"
- inkscape:document-units="px"
- inkscape:current-layer="layer1"
- showgrid="true"
- showborder="true"
- inkscape:window-width="1920"
- inkscape:window-height="1033"
- inkscape:window-x="0"
- inkscape:window-y="27"
- inkscape:window-maximized="1">
- <inkscape:grid
- type="xygrid"
- id="grid2987"
- empspacing="5"
- visible="true"
- enabled="true"
- snapvisiblegridlinesonly="true" />
- </sodipodi:namedview>
- <metadata
- id="metadata7">
- <rdf:RDF>
- <cc:Work
- rdf:about="">
- <dc:format>image/svg+xml</dc:format>
- <dc:type
- rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g
- inkscape:label="Layer 1"
- inkscape:groupmode="layer"
- id="layer1">
- <rect
- style="opacity:0.6;fill:#808000;fill-opacity:0.48235294;stroke:none"
- id="rect2985"
- width="1200"
- height="1200"
- x="-600"
- y="600" />
- <path
- sodipodi:type="arc"
- style="opacity:0.6;fill:#808000;fill-opacity:0.48235294;stroke:none"
- id="path2989"
- sodipodi:cx="0"
- sodipodi:cy="1200"
- sodipodi:rx="440"
- sodipodi:ry="440"
- d="m 440,1200 a 440,440 0 1 1 -880,0 440,440 0 1 1 880,0 z" />
- <path
- inkscape:connector-curvature="0"
- id="path3782"
- d="m 380,1200 60,0"
- style="fill:none;stroke:#ffffff;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
- <path
- style="fill:none;stroke:#ffffff;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- d="m -440,1200 60,0"
- id="path3012"
- inkscape:connector-curvature="0" />
- <path
- style="fill:none;stroke:#ffffff;stroke-width:5;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- d="M 0,760.06247 0,815"
- id="path3786"
- inkscape:connector-curvature="0"
- inkscape:transform-center-y="-412.46877" />
- <path
- inkscape:transform-center-y="-357.20843"
- inkscape:connector-curvature="0"
- id="path3794"
- d="M -219.96877,819.00292 -192.5,866.58022"
- style="fill:none;stroke:#ffffff;stroke-width:5;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- inkscape:transform-center-x="206.23439" />
- <path
- inkscape:transform-center-y="-291.65947"
- inkscape:connector-curvature="0"
- id="path3796"
- d="m -311.08281,888.91719 38.8467,38.8467"
- style="fill:none;stroke:#ffffff;stroke-width:5;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- inkscape:transform-center-x="291.65946" />
- <path
- inkscape:transform-center-y="-291.65947"
- inkscape:connector-curvature="0"
- id="path3798"
- d="m 311.08281,888.91719 -38.8467,38.8467"
- style="fill:none;stroke:#ffffff;stroke-width:5;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- inkscape:transform-center-x="-291.65946" />
- <path
- inkscape:transform-center-y="-357.20843"
- inkscape:connector-curvature="0"
- id="path3800"
- d="M 219.96877,819.00292 192.5,866.58022"
- style="fill:none;stroke:#ffffff;stroke-width:5;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- inkscape:transform-center-x="-206.23439" />
- <path
- inkscape:transform-center-y="-406.20244"
- inkscape:connector-curvature="0"
- id="path3802"
- d="m 76.394345,766.74611 -9.539802,54.1029"
- style="fill:none;stroke:#ffffff;stroke-width:5;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- inkscape:transform-center-x="-71.624444" />
- <path
- inkscape:transform-center-x="-141.07262"
- style="fill:none;stroke:#ffffff;stroke-width:5;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- d="m 150.46749,786.59395 -18.78974,51.62438"
- id="path3804"
- inkscape:connector-curvature="0"
- inkscape:transform-center-y="-387.59386" />
- <path
- inkscape:transform-center-y="-406.20244"
- inkscape:connector-curvature="0"
- id="path3806"
- d="m -76.394345,766.74611 9.539802,54.1029"
- style="fill:none;stroke:#ffffff;stroke-width:5;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- inkscape:transform-center-x="71.624444" />
- <path
- inkscape:transform-center-x="141.07262"
- style="fill:none;stroke:#ffffff;stroke-width:5;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- d="m -150.46749,786.59395 18.78974,51.62438"
- id="path3808"
- inkscape:connector-curvature="0"
- inkscape:transform-center-y="-387.59386" />
- <path
- style="fill:#ffb380;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
- d="m -10,760 20,0 -10,40 -10,-40"
- id="path3810"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="cccc" />
- <path
- d="m 440,1200 a 440,440 0 1 1 -880,0 440,440 0 1 1 880,0 z"
- sodipodi:ry="440"
- sodipodi:rx="440"
- sodipodi:cy="1200"
- sodipodi:cx="0"
- id="path3812"
- style="opacity:0.6;fill:#808000;fill-opacity:0.48235294;stroke:none"
- sodipodi:type="arc"
- transform="translate(0,910)" />
- <rect
- style="opacity:0.6;fill:#ffb380;fill-opacity:0.48235294;stroke:#ffffff;stroke-width:5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0"
- id="rect3814"
- width="125"
- height="895"
- x="-597.5"
- y="752.5" />
- <rect
- y="752.5"
- x="472.5"
- height="895"
- width="125"
- id="rect3816"
- style="opacity:0.6;fill:#ffb380;fill-opacity:0.48235294;stroke:#ffffff;stroke-width:5;stroke-linejoin:round;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0" />
- </g>
-</svg>
diff --git a/mavigator-server/src/main/resources/assets/images/hud/overlay.html b/mavigator-server/src/main/resources/assets/images/hud/overlay.html
deleted file mode 100644
index 58b0ef7..0000000
--- a/mavigator-server/src/main/resources/assets/images/hud/overlay.html
+++ /dev/null
@@ -1,29 +0,0 @@
-<!DOCTYPE html>
-
-<html>
- <head>
- <style>
- html,body {
- margin: 0;
- width: 100%;
- height: 100%;
- }
- </style>
- </head>
- <body>
-
- <div style="width: 100%; height: 100%; display: flex; align-content: flex-start; align-items: stretch; flex-direction: row">
- <div style="flex: 1; background-color: yellow;">side menu</div>
-
- <div style="flex: 1 1 70%; background-color: red; position: relative;">
- <div style="background-color: green; z-index: 0; display: flex; align-content: center; align-items: stretch; flex-direction: column; position: absolute; left: 0; right: 0; top: 0; bottom: 0;">
- <object style="flex: 1 1 100%;" type="image/svg+xml" data="horizon.svg">level0</object>
- </div>
-
- <div style="z-index: 1; display: flex; align-content: center; align-items: stretch; flex-direction: column; position: absolute; left: 0; right: 0; top: 0; bottom: 0;">
- <object style="flex: 1 1 100%;" type="image/svg+xml" data="roll.svg">level0</object>
- </div>
- </div>
- </div>
- </body>
-</html>
diff --git a/mavigator-server/src/main/resources/assets/images/hud/roll.svg b/mavigator-server/src/main/resources/assets/images/hud/roll.svg
deleted file mode 100644
index 93a9f90..0000000
--- a/mavigator-server/src/main/resources/assets/images/hud/roll.svg
+++ /dev/null
@@ -1,143 +0,0 @@
-<?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="1200"
- height="1200"
- id="svg2"
- version="1.1"
- inkscape:version="0.48.5 r10040"
- sodipodi:docname="hud.svg"
- viewBox="-600 600 1200 1200">
- <defs
- id="defs4" />
- <sodipodi:namedview
- id="base"
- pagecolor="#ffffff"
- bordercolor="#666666"
- borderopacity="1.0"
- inkscape:pageopacity="0.0"
- inkscape:pageshadow="2"
- inkscape:zoom="0.70749999"
- inkscape:cx="1028.6796"
- inkscape:cy="816.93521"
- inkscape:document-units="px"
- inkscape:current-layer="layer1"
- showgrid="true"
- showborder="true"
- inkscape:window-width="1920"
- inkscape:window-height="1033"
- inkscape:window-x="0"
- inkscape:window-y="27"
- inkscape:window-maximized="1">
- <inkscape:grid
- type="xygrid"
- id="grid2987"
- empspacing="5"
- visible="true"
- enabled="true"
- snapvisiblegridlinesonly="true" />
- </sodipodi:namedview>
- <metadata
- id="metadata7">
- <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></dc:title>
- </cc:Work>
- </rdf:RDF>
- </metadata>
- <g
- inkscape:label="Layer 1"
- inkscape:groupmode="layer"
- id="layer1">
- <path
- inkscape:connector-curvature="0"
- id="path3782"
- d="m 380,1200 60,0"
- style="fill:none;stroke:#ffffff;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
- <path
- style="fill:none;stroke:#ffffff;stroke-width:5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- d="m -440,1200 60,0"
- id="path3012"
- inkscape:connector-curvature="0" />
- <path
- style="fill:none;stroke:#ffffff;stroke-width:5;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- d="M 0,760.06247 0,815"
- id="path3786"
- inkscape:connector-curvature="0"
- inkscape:transform-center-y="-412.46877" />
- <path
- inkscape:transform-center-y="-357.20843"
- inkscape:connector-curvature="0"
- id="path3794"
- d="M -219.96877,819.00292 -192.5,866.58022"
- style="fill:none;stroke:#ffffff;stroke-width:5;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- inkscape:transform-center-x="206.23439" />
- <path
- inkscape:transform-center-y="-291.65947"
- inkscape:connector-curvature="0"
- id="path3796"
- d="m -311.08281,888.91719 38.8467,38.8467"
- style="fill:none;stroke:#ffffff;stroke-width:5;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- inkscape:transform-center-x="291.65946" />
- <path
- inkscape:transform-center-y="-291.65947"
- inkscape:connector-curvature="0"
- id="path3798"
- d="m 311.08281,888.91719 -38.8467,38.8467"
- style="fill:none;stroke:#ffffff;stroke-width:5;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- inkscape:transform-center-x="-291.65946" />
- <path
- inkscape:transform-center-y="-357.20843"
- inkscape:connector-curvature="0"
- id="path3800"
- d="M 219.96877,819.00292 192.5,866.58022"
- style="fill:none;stroke:#ffffff;stroke-width:5;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- inkscape:transform-center-x="-206.23439" />
- <path
- inkscape:transform-center-y="-406.20244"
- inkscape:connector-curvature="0"
- id="path3802"
- d="m 76.394345,766.74611 -9.539802,54.1029"
- style="fill:none;stroke:#ffffff;stroke-width:5;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- inkscape:transform-center-x="-71.624444" />
- <path
- inkscape:transform-center-x="-141.07262"
- style="fill:none;stroke:#ffffff;stroke-width:5;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- d="m 150.46749,786.59395 -18.78974,51.62438"
- id="path3804"
- inkscape:connector-curvature="0"
- inkscape:transform-center-y="-387.59386" />
- <path
- inkscape:transform-center-y="-406.20244"
- inkscape:connector-curvature="0"
- id="path3806"
- d="m -76.394345,766.74611 9.539802,54.1029"
- style="fill:none;stroke:#ffffff;stroke-width:5;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- inkscape:transform-center-x="71.624444" />
- <path
- inkscape:transform-center-x="141.07262"
- style="fill:none;stroke:#ffffff;stroke-width:5;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
- d="m -150.46749,786.59395 18.78974,51.62438"
- id="path3808"
- inkscape:connector-curvature="0"
- inkscape:transform-center-y="-387.59386" />
- <path
- style="fill:#ffb380;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
- d="m -10,760 20,0 -10,40 -10,-40"
- id="path3810"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="cccc" />
- </g>
-</svg>
diff --git a/mavigator-server/src/main/resources/assets/stylesheets/main.css b/mavigator-server/src/main/resources/assets/stylesheets/main.css
index f254449..6529940 100644
--- a/mavigator-server/src/main/resources/assets/stylesheets/main.css
+++ b/mavigator-server/src/main/resources/assets/stylesheets/main.css
@@ -1,203 +1,146 @@
-html, body {
+html, body, .app {
width: 100%;
height: 100%;
}
body {
- background-color: #e6e6e6;
+ background-color: #e6e6e6;
}
.loader {
- width: 100%;
- font-size: 50px;
- text-align: center;
-}
-
-#vfd-dashboard {
- width: 100%;
- height: 100%;
-}
-
-#vfd-dashboard header {
- color: #eeeeee;
- background-color: #222222;
- padding-left: 8px;
- padding-right: 8px;
- padding-top: 3px;
- padding-bottom: 3px;
- margin-bottom: 3px;
- 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;
-}
-
-.d-row {
- flex-direction: row;
-}
-
-.d-above {
- flex: none;
-}
-
-.d-left {
- flex: 1 1 30%;
-}
-
-.d-main {
- flex: 1 1 70%;
-}
-
-.d-panel {
- margin: 3px;
- padding: 10px;
- background-color: white;
- border-radius: 3px;
-}
-
+ width: 100%;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
-/* Mode styles */
-.mode {
- display: inline-block;
- box-sizing: border-box;
- text-decoration: normal;
- margin-right: 5px;
+ font-size: 50px;
+ text-align: center;
}
-.mode.danger {
+@keyframes danger-blink {
+ 0% {
+ color: #222222;
+ text-shadow: none;
+ }
+ 50% {
color: #d9534f;
text-shadow: 0 0 5px #d9534f;
- animation: danger-blink 1s linear infinite;
- -webkit-animation: danger-blink 1s linear infinite;
-}
-
-.mode.warning {
- color: #f0ad4e;
- text-shadow: 0 0 5px #f0ad4e;
+ }
+ 100% {
+ color: #222222;
+ text-shadow: none;
+ }
}
-.mode.info {
- color: #5bc0de;
- text-shadow: 0 0 5px #5bc0de;
+@-webkit-keyframes danger-blink {
+ 0% {
+ color: #eeeeee;
+ text-shadow: none;
+ }
+ 50% {
+ color: #d9534f;
+ text-shadow: 0 0 5px #d9534f;
+ }
+ 100% {
+ color: #eeeeee;
+ text-shadow: none;
+ }
}
-.mode.success {
- color: #5cb85c;
- text-shadow: 0 0 5px #5cb85c;
+#map {
+ position: absolute;
+ top: auto;
+ left: auto;
+ right: 10px;
+ bottom: 10px;
}
-.mode.off {
- color: #eeeeee;
- text-shadow: none;
- animation: none;
- -webkit-animation: none;
+#motors {
+ position: absolute;
+ top: auto;
+ bottom: 10px;
+ left: 10px;
+ right: auto;
}
-/* TODO: Rules below are maybe obsolete and need to be reviewed */
.table-instrument {
- table-layout: fixed;
- width: 100%;
+ table-layout: fixed;
+ width: 100%;
}
.table-instrument td {
- width: 100%;
+ width: 100%;
}
.heartbeat {
- color: rgba(165, 25, 25, 1);
- animation: heartbeat 2s linear infinite;
- -webkit-animation: heartbeat 2s linear infinite;
+ color: rgba(165, 25, 25, 1);
+ animation: heartbeat 2s linear infinite;
+ -webkit-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);
+ }
}
@-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);
- }
+ 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% {
- color: #eeeeee;
- text-shadow: none;
- }
- 50% {
- color: #d9534f;
- text-shadow: 0 0 5px #d9534f;
- }
- 100% {
- color: #eeeeee;
- text-shadow: none;
- }
+ 0% {
+ color: #eeeeee;
+ text-shadow: none;
+ }
+ 50% {
+ color: #d9534f;
+ text-shadow: 0 0 5px #d9534f;
+ }
+ 100% {
+ color: #eeeeee;
+ text-shadow: none;
+ }
}
@-webkit-keyframes danger-blink {
- 0% {
- color: #eeeeee;
- text-shadow: none;
- }
- 50% {
- color: #d9534f;
- text-shadow: 0 0 5px #d9534f;
- }
- 100% {
- color: #eeeeee;
- text-shadow: none;
- }
-} \ No newline at end of file
+ 0% {
+ color: #eeeeee;
+ text-shadow: none;
+ }
+ 50% {
+ color: #d9534f;
+ text-shadow: 0 0 5px #d9534f;
+ }
+ 100% {
+ color: #eeeeee;
+ text-shadow: none;
+ }
+}
+*/
diff --git a/mavigator-server/src/main/resources/assets/stylesheets/reset.css b/mavigator-server/src/main/resources/assets/stylesheets/reset.css
new file mode 100644
index 0000000..af94440
--- /dev/null
+++ b/mavigator-server/src/main/resources/assets/stylesheets/reset.css
@@ -0,0 +1,48 @@
+/* http://meyerweb.com/eric/tools/css/reset/
+ v2.0 | 20110126
+ License: none (public domain)
+*/
+
+html, body, div, span, applet, object, iframe,
+h1, h2, h3, h4, h5, h6, p, blockquote, pre,
+a, abbr, acronym, address, big, cite, code,
+del, dfn, em, img, ins, kbd, q, s, samp,
+small, strike, strong, sub, sup, tt, var,
+b, u, i, center,
+dl, dt, dd, ol, ul, li,
+fieldset, form, label, legend,
+table, caption, tbody, tfoot, thead, tr, th, td,
+article, aside, canvas, details, embed,
+figure, figcaption, footer, header, hgroup,
+menu, nav, output, ruby, section, summary,
+time, mark, audio, video {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ font-size: 100%;
+ font: inherit;
+ vertical-align: baseline;
+}
+/* HTML5 display-role reset for older browsers */
+article, aside, details, figcaption, figure,
+footer, header, hgroup, menu, nav, section {
+ display: block;
+}
+body {
+ line-height: 1;
+}
+ol, ul {
+ list-style: none;
+}
+blockquote, q {
+ quotes: none;
+}
+blockquote:before, blockquote:after,
+q:before, q:after {
+ content: '';
+ content: none;
+}
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+} \ No newline at end of file
diff --git a/mavigator-server/src/main/scala/mavigator/Router.scala b/mavigator-server/src/main/scala/mavigator/Router.scala
index 07e40b0..389cb4d 100644
--- a/mavigator-server/src/main/scala/mavigator/Router.scala
+++ b/mavigator-server/src/main/scala/mavigator/Router.scala
@@ -22,11 +22,11 @@ object Router {
val socketUrl = "ws://localhost:8080/mavlink"
def route(implicit system: ActorSystem): Route = (
- path("dashboard" / IntNumber) { id =>
+ path("cockpit" / IntNumber) { id =>
get {
val html = mavigator.views.html.app(
"Mavigator",
- "mavigator_dashboard_Main",
+ "mavigator_cockpit_Main",
Map(
"socketUrl" -> socketUrl,
"remoteSystemId" -> "0",
diff --git a/mavigator-server/src/main/twirl/mavigator/views/app.scala.html b/mavigator-server/src/main/twirl/mavigator/views/app.scala.html
index 34d1fde..34359f8 100644
--- a/mavigator-server/src/main/twirl/mavigator/views/app.scala.html
+++ b/mavigator-server/src/main/twirl/mavigator/views/app.scala.html
@@ -13,9 +13,9 @@
<p>The error was: "<span id="scalajs-error-message"></span>"</p>
</div>
-<div id="@appId">
+<div id="@appId" class="app">
<div class="loader">
- <i class="fa fa-spinner fa-spin"></i>
+ <i class="fa fa-cog fa-spin"></i>
</div>
</div>
@@ -30,10 +30,12 @@
root0.removeChild(root0.firstChild);
}
- //run ScalaJS application
+ //run ScalaJS application
+ console.info("[@appId] Starting ScalaJS application...")
@{appId}().start({
root: root0,
- assetsBase: "/assets",
+ styleRoot: document.getElementById("app-styles"),
+ baseUrl: "/assets",
args: {@args.map{ case (key, value) =>
@key: "@value",
}}
@@ -48,6 +50,6 @@
});
</script>
-<script type="text/javascript" src="/assets/js/mavigator-dashboard-opt.js"></script>
+<script type="text/javascript" src="/assets/js/mavigator-cockpit-fastopt.js"></script>
}
diff --git a/mavigator-server/src/main/twirl/mavigator/views/index.scala.html b/mavigator-server/src/main/twirl/mavigator/views/index.scala.html
deleted file mode 100644
index 1bf892b..0000000
--- a/mavigator-server/src/main/twirl/mavigator/views/index.scala.html
+++ /dev/null
@@ -1,5 +0,0 @@
-@()
-
-@main("Index"){
-<h1>Hello world!</h1>
-}
diff --git a/mavigator-server/src/main/twirl/mavigator/views/main.scala.html b/mavigator-server/src/main/twirl/mavigator/views/main.scala.html
index e3e83b8..1e21b1b 100644
--- a/mavigator-server/src/main/twirl/mavigator/views/main.scala.html
+++ b/mavigator-server/src/main/twirl/mavigator/views/main.scala.html
@@ -5,15 +5,18 @@
<html lang="en">
<head>
<meta charset="utf-8">
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
+
+ <!-- Become fullscreen on mobile -->
<meta name="mobile-web-app-capable" content="yes">
+
<title>Mavigator - @title</title>
<link rel="shortcut icon" href="/assets/images/logo.svg">
<link rel="stylesheet" media="screen" href="/assets/lib/bootstrap-3.3.6-dist/css/bootstrap.min.css">
<link rel="stylesheet" media="screen" href="/assets/lib/font-awesome-4.5.0/css/font-awesome.min.css">
<link rel="stylesheet" media="screen" href="/assets/stylesheets/main.css">
+ <style id="app-styles"></style>
</head>
<body>
diff --git a/mavigator-uav/src/main/scala/mavigator/uav/MavlinkUtil.scala b/mavigator-uav/src/main/scala/mavigator/uav/MavlinkUtil.scala.disabled
index 0fb56b0..0fb56b0 100644
--- a/mavigator-uav/src/main/scala/mavigator/uav/MavlinkUtil.scala
+++ b/mavigator-uav/src/main/scala/mavigator/uav/MavlinkUtil.scala.disabled
diff --git a/mavigator-uav/src/main/scala/mavigator/uav/Multiplexer.scala b/mavigator-uav/src/main/scala/mavigator/uav/Multiplexer.scala.disabled
index 5e48ea1..5e48ea1 100644
--- a/mavigator-uav/src/main/scala/mavigator/uav/Multiplexer.scala
+++ b/mavigator-uav/src/main/scala/mavigator/uav/Multiplexer.scala.disabled
diff --git a/mavigator-uav/src/main/scala/mavigator/uav/Uav.scala b/mavigator-uav/src/main/scala/mavigator/uav/Uav.scala
index 06a8f00..8f8c083 100644
--- a/mavigator-uav/src/main/scala/mavigator/uav/Uav.scala
+++ b/mavigator-uav/src/main/scala/mavigator/uav/Uav.scala
@@ -32,7 +32,7 @@ class Uav(system: ExtendedActorSystem) extends Extension {
def connect(): Flow[ByteString, ByteString, NotUsed] = {
Flow.fromSinkAndSource(
Sink.ignore,
- (new MockConnection(0,0,1)).data
+ (new MockConnection(0,0,1)).data //TODO: use source instead of hardcoded value
)
}
diff --git a/mavigator-uav/src/main/scala/mavigator/uav/mock/RandomFlightPlan.scala b/mavigator-uav/src/main/scala/mavigator/uav/mock/RandomFlightPlan.scala
index 89e5a75..680de1e 100644
--- a/mavigator-uav/src/main/scala/mavigator/uav/mock/RandomFlightPlan.scala
+++ b/mavigator-uav/src/main/scala/mavigator/uav/mock/RandomFlightPlan.scala
@@ -54,9 +54,10 @@ class RandomFlightPlan {
0
)
+
def attitude = Attitude(
millis,
- (2 * math.Pi * time / 6).toFloat,
+ (math.sin(2 * math.Pi * time ) * math.Pi / 6).toFloat,
(math.sin(2 * math.Pi * time / 5) * math.Pi / 6).toFloat,
(2 * math.Pi * time / 4).toFloat,
0,
diff --git a/project/Dependencies.scala b/project/Dependencies.scala
index be6cbfe..ba39c94 100644
--- a/project/Dependencies.scala
+++ b/project/Dependencies.scala
@@ -19,8 +19,8 @@ object Dependencies {
val flowNative = "com.github.jodersky" % "flow-native" % FlowVersion % Runtime
val flowStream = "com.github.jodersky" % "flow-stream" % FlowVersion
- val jsDom = Def.setting{"org.scala-js" %%% "scalajs-dom" % "0.8.2"}
+ val jsDom = Def.setting{"org.scala-js" %%% "scalajs-dom" % "0.9.0"}
val scalatags = Def.setting{"com.lihaoyi" %%% "scalatags" % "0.5.4"}
- val scalarx = Def.setting{"com.lihaoyi" %%% "scalarx" % "0.2.8"}
+ val scalarx = Def.setting{"com.lihaoyi" %%% "scalarx" % "0.3.1"}
}
diff --git a/project/Js.scala b/project/Js.scala
index d665eab..9f85f3b 100644
--- a/project/Js.scala
+++ b/project/Js.scala
@@ -8,7 +8,7 @@ object Js {
def dependsOnJs(proj: Project): Seq[Setting[_]] = Seq(
resourceGenerators in Compile += Def.task{
- val js: File = (fullOptJS in (proj, Compile)).value.data
+ val js: File = (fastOptJS in (proj, Compile)).value.data
val map = js.getParentFile / (js.name + ".map")
val out = (resourceManaged in Compile).value / "assets" / "js"
diff --git a/project/plugins.sbt b/project/plugins.sbt
index 798db0c..301572f 100644
--- a/project/plugins.sbt
+++ b/project/plugins.sbt
@@ -2,19 +2,18 @@
* Additional resolvers
*/
-resolvers += "Typesafe repository" at "https://repo.typesafe.com/typesafe/releases/"
+//resolvers += "Typesafe repository" at "https://repo.typesafe.com/typesafe/releases/"
-resolvers += "jgit-repo" at "http://download.eclipse.org/jgit/maven"
+//resolvers += "jgit-repo" at "http://download.eclipse.org/jgit/maven"
resolvers += Resolver.jcenterRepo
-
/*
* Main plugins
*/
// add support for scalajs
-addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.6")
+addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.8")
// twirl html templating
addSbtPlugin("com.typesafe.sbt" % "sbt-twirl" % "1.1.1")