diff options
Diffstat (limited to 'mavigator-cockpit/src/main/scala/mavigator/index')
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) + } + +} |