package vfd.dashboard.ui import rx._ import scalatags.JsDom.all._ import vfd.dashboard.Environment import vfd.dashboard.MavlinkSocket import vfd.dashboard.ui.instruments._ import org.mavlink.messages._ import vfd.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&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 }