aboutsummaryrefslogtreecommitdiff
path: root/mavigator-cockpit/src/main/scala/mavigator/index
diff options
context:
space:
mode:
Diffstat (limited to 'mavigator-cockpit/src/main/scala/mavigator/index')
-rw-r--r--mavigator-cockpit/src/main/scala/mavigator/index/ActiveVehicle.scala44
-rw-r--r--mavigator-cockpit/src/main/scala/mavigator/index/Main.scala97
-rw-r--r--mavigator-cockpit/src/main/scala/mavigator/index/Util.scala39
3 files changed, 180 insertions, 0 deletions
diff --git a/mavigator-cockpit/src/main/scala/mavigator/index/ActiveVehicle.scala b/mavigator-cockpit/src/main/scala/mavigator/index/ActiveVehicle.scala
new file mode 100644
index 0000000..870d3be
--- /dev/null
+++ b/mavigator-cockpit/src/main/scala/mavigator/index/ActiveVehicle.scala
@@ -0,0 +1,44 @@
+package mavigator.index
+
+import org.mavlink.enums.MavAutopilot
+import org.mavlink.enums.MavState
+import org.mavlink.enums.MavType
+import org.mavlink.messages.Heartbeat
+
+case class ActiveVehicle(systemId: Int, vehicleType: String, autopilot: String, state: String)
+
+object ActiveVehicle {
+
+ def fromHeartbeat(id: Int, hb: Heartbeat) = ActiveVehicle(
+ id,
+ vehicleType(hb.`type`),
+ autopilot(hb.autopilot),
+ state(hb.systemStatus))
+
+ def vehicleType(tpe: Int) = tpe match {
+ case MavType.MavTypeGeneric => "Generic"
+ case MavType.MavTypeQuadrotor => "Quadcopter"
+ case MavType.MavTypeFixedWing => "Fixed Wing"
+ case _ => "Other"
+ }
+
+ def autopilot(tpe: Int) = tpe match {
+ case MavAutopilot.MavAutopilotGeneric => "Generic"
+ case MavAutopilot.MavAutopilotInvalid => "Invalid"
+ case MavAutopilot.MavAutopilotPixhawk => "Pixhawk"
+ case _ => "Other"
+ }
+
+ def state(s: Int) = s match {
+ case MavState.MavStateActive => "Active"
+ case MavState.MavStateBoot => "Booting"
+ case MavState.MavStateCalibrating => "Calibrating"
+ case MavState.MavStateCritical => "Critical"
+ case MavState.MavStateEmergency => "Emergency"
+ case MavState.MavStatePoweroff => "Poweroff"
+ case MavState.MavStateStandby => "Standby"
+ case MavState.MavStateUninit => "Uninitialized"
+ case _ => "Unknown"
+ }
+
+}
diff --git a/mavigator-cockpit/src/main/scala/mavigator/index/Main.scala b/mavigator-cockpit/src/main/scala/mavigator/index/Main.scala
new file mode 100644
index 0000000..7724f89
--- /dev/null
+++ b/mavigator-cockpit/src/main/scala/mavigator/index/Main.scala
@@ -0,0 +1,97 @@
+package mavigator.index
+
+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._
+
+import org.mavlink.Parser
+import org.mavlink.messages.Message
+import org.mavlink.messages.Heartbeat
+
+import org.scalajs.dom
+
+import rx._
+
+
+@JSExport("mavigator_index_Main")
+object Main extends Application {
+ import Util._
+
+ val active = Var(Set.empty[ActiveVehicle])
+
+ 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)
+ case _ => ()
+ }
+ }
+ )
+
+ override def main(args: Map[String, String])(implicit env: Environment): Unit = {
+ val root = env.root
+ val connection = new dom.WebSocket(args("socketUrl"))
+
+ connection.binaryType = "arraybuffer"
+ 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.Event) => {
+ while (root.firstChild != null) {
+ root.removeChild(root.firstChild);
+ }
+ root.appendChild(
+ div(`class`:="alert alert-danger")(
+ "Connection to server unexpectedly closed. Check server logs for errors."
+ ).render
+ )
+
+ }
+
+ 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"))
+ )
+ }
+ )
+ }
+ ),
+ i(`class`:="fa fa-spinner fa-spin")(),
+ " Listening for heartbeats..."
+ ).render)
+
+ }
+}
diff --git a/mavigator-cockpit/src/main/scala/mavigator/index/Util.scala b/mavigator-cockpit/src/main/scala/mavigator/index/Util.scala
new file mode 100644
index 0000000..16f3d5d
--- /dev/null
+++ b/mavigator-cockpit/src/main/scala/mavigator/index/Util.scala
@@ -0,0 +1,39 @@
+package mavigator.index
+
+import scala.language.implicitConversions
+
+import org.scalajs.dom.html
+
+import rx._
+
+import scala.util.Try
+import scala.util.Success
+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
+ *
+ * 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)
+ }
+
+}