diff options
Diffstat (limited to 'mavigator-index/src/main/scala/vfd')
3 files changed, 175 insertions, 0 deletions
diff --git a/mavigator-index/src/main/scala/vfd/index/ActiveVehicle.scala b/mavigator-index/src/main/scala/vfd/index/ActiveVehicle.scala new file mode 100644 index 0000000..0eaeea3 --- /dev/null +++ b/mavigator-index/src/main/scala/vfd/index/ActiveVehicle.scala @@ -0,0 +1,44 @@ +package vfd.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" + } + +}
\ No newline at end of file diff --git a/mavigator-index/src/main/scala/vfd/index/Main.scala b/mavigator-index/src/main/scala/vfd/index/Main.scala new file mode 100644 index 0000000..d5b3f64 --- /dev/null +++ b/mavigator-index/src/main/scala/vfd/index/Main.scala @@ -0,0 +1,92 @@ +package vfd.index + +import org.mavlink.Parser +import org.mavlink.messages.Message +import org.mavlink.messages.Heartbeat + +import org.scalajs.dom +import org.scalajs.dom.html + +import rx._ + +import scala.scalajs.js +import scala.scalajs.js.annotation.JSExport + +import scalatags.JsDom.all._ + +@JSExport("Main") +object Main { + 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 _ => () + } + } + ) + + @JSExport + def main(root: html.Element, baseAssets: String, args: js.Dictionary[String]): Unit = { + + 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) + } + +}
\ No newline at end of file diff --git a/mavigator-index/src/main/scala/vfd/index/Util.scala b/mavigator-index/src/main/scala/vfd/index/Util.scala new file mode 100644 index 0000000..09793df --- /dev/null +++ b/mavigator-index/src/main/scala/vfd/index/Util.scala @@ -0,0 +1,39 @@ +package vfd.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) + } + +}
\ No newline at end of file |