From 937bec66df1a3edfe819fb68e3ad85f60f92c1bb Mon Sep 17 00:00:00 2001 From: Jakob Odersky Date: Tue, 16 Dec 2014 18:24:55 +0100 Subject: add animations and reenable showing messages --- concise.xml | 13 + .../public/images/instruments/altimeter.svg | 1292 ++++++++++++++++++++ vfd-backend/public/images/instruments/altitude.svg | 1292 -------------------- vfd-backend/public/images/instruments/attitude.svg | 569 --------- vfd-backend/public/images/instruments/basic.svg | 313 +++++ vfd-backend/public/images/instruments/compass.svg | 1034 ++++++++++++++++ vfd-backend/public/images/instruments/heading.svg | 1034 ---------------- vfd-backend/public/images/instruments/horizon.svg | 569 +++++++++ .../src/main/scala/vfd/frontend/Main.scala | 3 +- .../main/scala/vfd/frontend/ui/Components.scala | 62 +- .../vfd/frontend/ui/panels/Communication.scala | 27 +- .../scala/vfd/frontend/ui/panels/Primary.scala | 12 +- .../src/main/scala/vfd/uav/MockConnection.scala | 20 +- 13 files changed, 3311 insertions(+), 2929 deletions(-) create mode 100644 vfd-backend/public/images/instruments/altimeter.svg delete mode 100644 vfd-backend/public/images/instruments/altitude.svg delete mode 100644 vfd-backend/public/images/instruments/attitude.svg create mode 100644 vfd-backend/public/images/instruments/basic.svg create mode 100644 vfd-backend/public/images/instruments/compass.svg delete mode 100644 vfd-backend/public/images/instruments/heading.svg create mode 100644 vfd-backend/public/images/instruments/horizon.svg diff --git a/concise.xml b/concise.xml index 9be16f7..4d8d174 100644 --- a/concise.xml +++ b/concise.xml @@ -66,6 +66,19 @@ System ID Component ID + + Status of motors + m0 + m1 + m2 + m3 + + + The attitude in the aeronautical frame (right-handed, Z-down, X-front, Y-right). + Roll angle + Pitch angle + Yaw angle + The RAW values of the RC channels sent to the MAV to override info received from the RC radio. A value of UINT16_MAX means no change to that channel. A value of 0 means control of that channel should be released back to the RC radio. The standard PPM modulation is as follows: 1000 microseconds: 0%, 2000 microseconds: 100%. Individual receivers/transmitters might violate this specification. System ID diff --git a/vfd-backend/public/images/instruments/altimeter.svg b/vfd-backend/public/images/instruments/altimeter.svg new file mode 100644 index 0000000..2bfb306 --- /dev/null +++ b/vfd-backend/public/images/instruments/altimeter.svg @@ -0,0 +1,1292 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + 0000.00 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + + + + + + ALT + x10 m + + diff --git a/vfd-backend/public/images/instruments/altitude.svg b/vfd-backend/public/images/instruments/altitude.svg deleted file mode 100644 index 2bfb306..0000000 --- a/vfd-backend/public/images/instruments/altitude.svg +++ /dev/null @@ -1,1292 +0,0 @@ - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - 0000.00 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - - - - - - ALT - x10 m - - diff --git a/vfd-backend/public/images/instruments/attitude.svg b/vfd-backend/public/images/instruments/attitude.svg deleted file mode 100644 index 09464d1..0000000 --- a/vfd-backend/public/images/instruments/attitude.svg +++ /dev/null @@ -1,569 +0,0 @@ - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - 10 - 20 - 30 - 20 - 30 - 10 - 10 - 10 - 20 - 30 - 20 - 30 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vfd-backend/public/images/instruments/basic.svg b/vfd-backend/public/images/instruments/basic.svg new file mode 100644 index 0000000..7712ece --- /dev/null +++ b/vfd-backend/public/images/instruments/basic.svg @@ -0,0 +1,313 @@ + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 100 + 0 + 0 + % + 50 + + + + + diff --git a/vfd-backend/public/images/instruments/compass.svg b/vfd-backend/public/images/instruments/compass.svg new file mode 100644 index 0000000..83cf17b --- /dev/null +++ b/vfd-backend/public/images/instruments/compass.svg @@ -0,0 +1,1034 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + N + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 3 + 6 + E + 12 + 15 + S + 21 + 24 + W + 30 + 33 + + + + + + + + + + + + + diff --git a/vfd-backend/public/images/instruments/heading.svg b/vfd-backend/public/images/instruments/heading.svg deleted file mode 100644 index 83cf17b..0000000 --- a/vfd-backend/public/images/instruments/heading.svg +++ /dev/null @@ -1,1034 +0,0 @@ - - - - - - - - - - - - image/svg+xml - - - - - - - - - - N - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 3 - 6 - E - 12 - 15 - S - 21 - 24 - W - 30 - 33 - - - - - - - - - - - - - diff --git a/vfd-backend/public/images/instruments/horizon.svg b/vfd-backend/public/images/instruments/horizon.svg new file mode 100644 index 0000000..363b962 --- /dev/null +++ b/vfd-backend/public/images/instruments/horizon.svg @@ -0,0 +1,569 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + 10 + 20 + 30 + 20 + 30 + 10 + 10 + 10 + 20 + 30 + 20 + 30 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vfd-frontend/src/main/scala/vfd/frontend/Main.scala b/vfd-frontend/src/main/scala/vfd/frontend/Main.scala index 5c22a4b..446d56d 100644 --- a/vfd-frontend/src/main/scala/vfd/frontend/Main.scala +++ b/vfd-frontend/src/main/scala/vfd/frontend/Main.scala @@ -64,7 +64,8 @@ object Main { socket.stats.packets, socket.stats.crcErrors, socket.stats.overflows, - socket.stats.wrongIds)))))) + socket.stats.wrongIds, + message)))))) env.root.appendChild(element.render) } diff --git a/vfd-frontend/src/main/scala/vfd/frontend/ui/Components.scala b/vfd-frontend/src/main/scala/vfd/frontend/ui/Components.scala index a340c15..4c17ecf 100644 --- a/vfd-frontend/src/main/scala/vfd/frontend/ui/Components.scala +++ b/vfd-frontend/src/main/scala/vfd/frontend/ui/Components.scala @@ -1,7 +1,6 @@ package vfd.frontend.ui import org.scalajs.dom.HTMLElement - import rx.Obs import rx.Rx import rx.Rx @@ -19,17 +18,6 @@ import vfd.frontend.util.Environment object Components { - def led(color: Rx[String], size: String)(implicit app: Environment) = { - val elem = `object`(`type` := "image/svg+xml", "data".attr := app.asset("leds/led.svg"), width := size)( - "Error loading image.").render - - Obs(color, skipInitial = true) { - val svg = elem.contentDocument - svg.getElementById("light").setAttribute("fill", color()) - } - elem - } - private def instrument(name: String)(implicit app: Environment) = { val path = app.asset("images/instruments/" + name + ".svg") `object`(`type` := "image/svg+xml", "data".attr := path, width := "100%")( @@ -40,21 +28,34 @@ object Components { div(style := s"width: $size; height: $size; display: inline-block;")( elem) } + + def led(color: Rx[String], size: String)(implicit app: Environment) = { + val elem = `object`(`type` := "image/svg+xml", "data".attr := app.asset("leds/led.svg"), width := size)( + "Error loading image.").render + + Obs(color, skipInitial = true) { + val svg = elem.contentDocument + svg.getElementById("light").setAttribute("fill", color()) + } + elem + } - def attitude(pitchRoll: Rx[(Double, Double)], size: String)(implicit app: Environment) = { - val inst = instrument("attitude") + def horizon(pitchRoll: Rx[(Double, Double)], size: String)(implicit app: Environment) = { + val inst = instrument("horizon") Obs(pitchRoll, skipInitial = true) { val svg = inst.contentDocument val pitch = svg.getElementById("pitch") val roll = svg.getElementById("roll") - pitch.setAttribute("transform", "translate(0, " + pitchRoll()._1 / math.Pi * 180 + ")"); - roll.setAttribute("transform", "rotate(" + pitchRoll()._2 / math.Pi * 180 + ")"); + pitch.style.transition = "transform 250ms ease-out" + roll.style.transition = "transform 250ms ease-out" + pitch.style.transform = "translate(0px, " + pitchRoll()._1 + "px)" + roll.style.transform = "rotate(" + pitchRoll()._2 + "deg)" } frame(inst, size) } - def altitude(value: Rx[Double], size: String)(implicit app: Environment) = { - val inst = instrument("altitude") + def altimeter(value: Rx[Double], size: String)(implicit app: Environment) = { + val inst = instrument("altimeter") Obs(value, skipInitial = true) { val svg = inst.contentDocument // 36deg === 1m @@ -63,14 +64,29 @@ object Components { frame(inst, size) } - def heading(value: Rx[Double], size: String)(implicit app: Environment) = { - val inst = instrument("heading") + def compass(value: Rx[Double], size: String)(implicit app: Environment) = { + val inst = instrument("compass") Obs(value, skipInitial = true) { val svg = inst.contentDocument - // 1deg === 1deg - svg.getElementById("heading").setAttribute("transform", "rotate(" + value() / math.Pi * 180 + ")"); + val heading = svg.getElementById("heading") + heading.style.transition = "transform 250ms ease-out" + heading.style.transform = "rotate(" + value() + "deg)" } frame(inst, size) } + + def basic(value: Rx[Double], size: String)(implicit app: Environment) = { + val inst = instrument("basic") + Obs(value, skipInitial = true) { + val svg = inst.contentDocument + val hand = svg.getElementById("hand") + hand.style.transform = "rotate(" + value() * 270 / 100 + "deg)"; + hand.style.transition = "transform 250ms ease-out" + svg.getElementById("unit").textContent = "%" + svg.getElementById("value").textContent = value().toString + } + frame(inst, size) + } + +} -} \ No newline at end of file diff --git a/vfd-frontend/src/main/scala/vfd/frontend/ui/panels/Communication.scala b/vfd-frontend/src/main/scala/vfd/frontend/ui/panels/Communication.scala index b93ad27..ba0d490 100644 --- a/vfd-frontend/src/main/scala/vfd/frontend/ui/panels/Communication.scala +++ b/vfd-frontend/src/main/scala/vfd/frontend/ui/panels/Communication.scala @@ -15,10 +15,31 @@ import scalatags.JsDom.all.td import scalatags.JsDom.all.tr import vfd.frontend.util.Environment import vfd.frontend.util.Framework.RxStr +import vfd.frontend.ui.Components +import rx.core.Var +import rx.core.Obs +import org.mavlink.messages._ object Communication { - def apply(packets: Rx[Int], crcs: Rx[Int], overflows: Rx[Int], wrongIds: Rx[Int])(implicit app: Environment) = { + def apply(packets: Rx[Int], crcs: Rx[Int], overflows: Rx[Int], wrongIds: Rx[Int], message: Rx[Message])(implicit app: Environment) = { + + val m0 = Var(0.0) + val m1 = Var(0.0) + val m2 = Var(0.0) + val m3 = Var(0.0) + + Obs(message) { + message() match { + case Motor(_m0, _m1, _m2, _m3) => + m0() = _m0 + m1() = _m1 + m2() = _m2 + m3() = _m3 + case _ => () + } + } + div( "Link Status", table(`class` := "table table-condensed")( @@ -42,7 +63,9 @@ object Communication { td("OFLW"), td(overflows), td("BID"), - td(wrongIds))))) + td(wrongIds)))), + div( + Components.basic(m0, "25%"),Components.basic(m1, "25%"),Components.basic(m2, "25%"),Components.basic(m3, "25%"))) } } \ No newline at end of file diff --git a/vfd-frontend/src/main/scala/vfd/frontend/ui/panels/Primary.scala b/vfd-frontend/src/main/scala/vfd/frontend/ui/panels/Primary.scala index 738a84a..4517ea0 100644 --- a/vfd-frontend/src/main/scala/vfd/frontend/ui/panels/Primary.scala +++ b/vfd-frontend/src/main/scala/vfd/frontend/ui/panels/Primary.scala @@ -1,7 +1,6 @@ package vfd.frontend.ui.panels -import org.mavlink.messages.Message - +import org.mavlink.messages._ import rx.core.Obs import rx.core.Rx import rx.core.Var @@ -18,14 +17,17 @@ object Primary { Obs(message) { message() match { + case Attitude(roll, pitch, yaw) => + pitchRoll() = (roll, pitch) + heading() = yaw case _ => () } } div( - Components.heading(heading, "33%"), - Components.attitude(pitchRoll, "33%"), - Components.altitude(altitude, "33%")) + Components.compass(heading, "33%"), + Components.horizon(pitchRoll, "33%"), + Components.altimeter(altitude, "33%")) } } \ No newline at end of file diff --git a/vfd-uav/src/main/scala/vfd/uav/MockConnection.scala b/vfd-uav/src/main/scala/vfd/uav/MockConnection.scala index 126fa9a..d427aef 100644 --- a/vfd-uav/src/main/scala/vfd/uav/MockConnection.scala +++ b/vfd-uav/src/main/scala/vfd/uav/MockConnection.scala @@ -1,15 +1,15 @@ package vfd.uav import java.util.concurrent.TimeUnit.MILLISECONDS - import scala.concurrent.duration.FiniteDuration import scala.util.Random - import Connection.Received import akka.actor.Actor import akka.actor.ActorLogging import akka.actor.Props import akka.util.ByteString +import org.mavlink.messages._ +import org.mavlink.Packet class MockConnection extends Actor with ActorLogging with Connection { import Connection._ @@ -36,7 +36,21 @@ object MockConnection { object MockPackets { - def random() = Random.nextInt(2) match { + private implicit class RichMessage(val message: Message) extends AnyVal { + def bytes: Array[Byte] = { + val (id, payload) = Message.pack(message) + Packet(5, 42, 1, id, payload).toSeq.toArray + } + } + + def random(): Array[Byte] = Random.nextInt(4) match { + case 0 => randomInvalid() + case 1 => Heartbeat(0).bytes + case 2 => Motor(Random.nextInt(101).toByte, Random.nextInt(101).toByte, Random.nextInt(101).toByte, Random.nextInt(101).toByte).bytes + case 3 => Attitude((Random.nextInt(160) - 80).toShort, (Random.nextInt(160) - 80).toShort.toShort, Random.nextInt(360).toShort).bytes + } + + def randomInvalid() = Random.nextInt(2) match { case 0 => invalidCrc case 1 => invalidOverflow } -- cgit v1.2.3