diff options
6 files changed, 119 insertions, 56 deletions
diff --git a/mavigator-bindings/mavlink/common.xml b/mavigator-bindings/mavlink/common.xml index 163abe6..b91e165 100644 --- a/mavigator-bindings/mavlink/common.xml +++ b/mavigator-bindings/mavlink/common.xml @@ -3234,6 +3234,11 @@ <field type="float" name="size_x">Size in radians of target along x-axis</field> <field type="float" name="size_y">Size in radians of target along y-axis</field> </message> + <message id="150" name="Stability"> + <description>Stability issues</description> + <field type="uint8_t" name="stable">Is it stable?</field> + </message> + <!-- MESSAGE IDs 180 - 240: Space for custom messages in individual projectname_messages.xml files --> <message id="241" name="VIBRATION"> <description>Vibration levels and accelerometer clipping</description> diff --git a/mavigator-cockpit/src/main/scala/mavigator/cockpit/Instruments.scala b/mavigator-cockpit/src/main/scala/mavigator/cockpit/Instruments.scala index c8715f4..f6a32db 100644 --- a/mavigator-cockpit/src/main/scala/mavigator/cockpit/Instruments.scala +++ b/mavigator-cockpit/src/main/scala/mavigator/cockpit/Instruments.scala @@ -97,38 +97,36 @@ trait Instruments { page: Page => val overlayStyle = """ - |.hud-overlay { - | position: absolute; - | left: 0; - | right: 0; - | top: 0; - | bottom: 0; - | - | display: flex; - | flex-direction: row; - | justify-content: center; - | align-items: center; - |} - |.hud-overlay > * { - | flex: 1 1 0; - | width: 100%; - | height: 100%; - | max-width: 100%; - | max-height: 100%; - |}""".stripMargin - - - def mode(name: String, kind: String, on: Boolean = false) = { - div(`class` := s"mode $kind ${if (!on) "off"}")(name) + .hud-overlay { + position: absolute; + left: 0; + right: 0; + top: 0; + bottom: 0; + + display: flex; + flex-direction: row; + justify-content: center; + align-items: center; + } + .hud-overlay > * { + flex: 1 1 0; + width: 100%; + height: 100%; + max-width: 100%; + max-height: 100%; + }""" + + + def mode(name: String, level: String) = new Instrument[Boolean] { + override val element: html.Element = div(`class` := s"mode $level off")(name).render + def update(newValue: Boolean): Unit = { + val classes = element.classList + if (newValue) classes.add("off") else classes.remove("off") + } } - //TODO make these into real instruments and lazy vals - def modes = div(style := "float: right;")( - mode("LINK", "danger", true), - mode("BAT", "warning", true), - mode("GPS", "warning", true), - mode("STABILIZED", "info", true) - ) + val unstable = mode("UNSTABLE", "danger") val modeStyle = """ .mode { diff --git a/mavigator-cockpit/src/main/scala/mavigator/cockpit/Layout.scala b/mavigator-cockpit/src/main/scala/mavigator/cockpit/Layout.scala index 991ee9a..67db95b 100644 --- a/mavigator-cockpit/src/main/scala/mavigator/cockpit/Layout.scala +++ b/mavigator-cockpit/src/main/scala/mavigator/cockpit/Layout.scala @@ -13,7 +13,9 @@ trait Layout { self: Page with Instruments => def mcp = div(id := "mcp")( img(src := asset("images/logo-invert.svg"), style:="height: 20px; margin: 5px;"), span(`class`:="mode warning")("Demo System"), - modes + div(`style` := "float: right")( + unstable.element + ) ) /** Element to deisplay on heads-up display (main area). */ @@ -23,27 +25,26 @@ trait Layout { self: Page with Instruments => ) val layoutStyle = """ - |#cockpit { - | width: 100%; - | height: 100%; - | display: flex; - | flex-direction: column; - | justify-content: flex-start; - | align-items: stretch; - | - | background-color: pink; - |} - | - |#mcp { - | flex: 0 1 0; - | background-color: #222222; - |} - | - |#hud { - | flex: 1 1 auto; - | position: relative; - | background-color: lightblue; - |}""".stripMargin + #cockpit { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: stretch; + + background-color: pink; + } + + #mcp { + flex: 0 1 0; + background-color: #222222; + } + #hud { + flex: 1 1 auto; + position: relative; + background-color: lightblue; + }""" override def styles = Seq(layoutStyle) ++ instrumentStyles diff --git a/mavigator-cockpit/src/main/scala/mavigator/cockpit/MavlinkWebSockets.scala b/mavigator-cockpit/src/main/scala/mavigator/cockpit/MavlinkWebSockets.scala index b2ecf48..d4b0239 100644 --- a/mavigator-cockpit/src/main/scala/mavigator/cockpit/MavlinkWebSockets.scala +++ b/mavigator-cockpit/src/main/scala/mavigator/cockpit/MavlinkWebSockets.scala @@ -55,6 +55,11 @@ trait MavlinkWebSockets { self: Instruments => case a: Attitude => attitudeOverlay.update((a.pitch, a.roll)) horizonOverlay.update((a.pitch, a.roll)) + case Stability(v) => { + unstable.update(v != 0) + println("stability " + v) + } + case _ => () } diff --git a/mavigator-uav/src/main/resources/reference.conf b/mavigator-uav/src/main/resources/reference.conf index 282df90..bbd7f49 100644 --- a/mavigator-uav/src/main/resources/reference.conf +++ b/mavigator-uav/src/main/resources/reference.conf @@ -25,7 +25,7 @@ mavigator.uav { # Settings related to mock connections mock { # Mavlink system ID of the simulated UAV - remote_system_id = 42 + remote_system_id = 1 # Mavlink component ID of the simulated UAV remote_component_id = 0 # Divide simulated message frequency diff --git a/mavigator-uav/src/main/scala/mavigator/uav/Core.scala b/mavigator-uav/src/main/scala/mavigator/uav/Core.scala index 4418dae..02a8779 100644 --- a/mavigator-uav/src/main/scala/mavigator/uav/Core.scala +++ b/mavigator-uav/src/main/scala/mavigator/uav/Core.scala @@ -5,9 +5,12 @@ import akka.NotUsed import akka.actor.{ActorLogging, ActorRef, ActorSystem, Props} import akka.stream.Materializer import akka.stream.actor.{ActorPublisher, ActorPublisherMessage} -import akka.stream.scaladsl.{Flow, Sink, Source} +import akka.stream.scaladsl.{ Flow, RunnableGraph, Sink, Source } import akka.util.ByteString +import org.mavlink.Assembler +import org.mavlink.messages._ import org.reactivestreams.{Publisher, Subscriber} +import scala.concurrent.duration._ /** A core enables dynamic creation and removal of clients and backend connections. * It is essentially a dynamic stream multiplexer. */ @@ -31,8 +34,18 @@ class Core(implicit val system: ActorSystem, val materializer: Materializer) { Source.asSubscriber[ByteString] )((toClient, fromClient) => (toClient, fromClient)) - private lazy val runnable = clients.joinMat(backend){ case ((toClient, fromClient), (toBackend, fromBackend)) => - toClient + private lazy val runnable: RunnableGraph[Publisher[ByteString]] = { + val timer = Source.tick(2.seconds, 2.seconds, ()) + val generator: Source[ByteString, _] = timer.flatMapConcat{ _ => + Util.barrelRoll via Util.assembler + } + + val merged: Flow[ByteString, ByteString, _] = Util.merge(generator, backend) + + merged.joinMat(clients){case (_, (toClient, _)) => + toClient + } + } private lazy val clientPublisher: Publisher[ByteString] = { @@ -55,6 +68,47 @@ class Core(implicit val system: ActorSystem, val materializer: Materializer) { } +object Util { + import akka.stream.scaladsl._ + import akka.stream.FlowShape + + + def merge[A](preferred: Source[A, _], other: Flow[A, A, _]): Flow[A, A, _] = { + val graph = GraphDSL.create(preferred, other)((_, _)) { implicit builder => + (pref, flow) => + + import GraphDSL.Implicits._ + + val merge = builder.add(MergePreferred[A](1)) + + pref.out ~> merge.preferred + flow.out ~> merge.in(0) + + FlowShape(flow.in, merge.out) + } + Flow.fromGraph(graph) + } + + def assembler: Flow[Message, ByteString, _] = Flow[Message].map{ msg => + val as = new Assembler(1, 1) + val (id, payload) = Message.pack(msg) + val bytes = as.assemble(id, payload).toArray + ByteString(bytes) + } + + def barrelRoll(): Source[Message, _] = { + val angle: Source[Float, _] = + Source.tick(10.millis, 10.millis, 0.1f).scan(0.0f)(_+_).takeWhile(_ < 2 * math.Pi) + val attitude = angle.map{ a => + Attitude(0,a,0,0,0,0,0) + } + + Source.single(Stability(0)) concat attitude concat Source.single(Stability(1)) + } + +} + + object Core { private class BackendPublisher extends ActorPublisher[ByteString] with ActorLogging { |