aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakob Odersky <jodersky@gmail.com>2014-10-09 15:53:24 +0200
committerJakob Odersky <jodersky@gmail.com>2014-10-09 15:54:48 +0200
commit0226a83f40e0126f8e75e155609f3f295661a3a5 (patch)
treef63aa06c616f3c7627e056b4fcde8809760a5090
parent7f127610f7d5062eb6d6f9aa4f6eeb36f64cb51d (diff)
downloadmavigator-0226a83f40e0126f8e75e155609f3f295661a3a5.tar.gz
mavigator-0226a83f40e0126f8e75e155609f3f295661a3a5.tar.bz2
mavigator-0226a83f40e0126f8e75e155609f3f295661a3a5.zip
tidy up frontend
-rw-r--r--project/Build.scala1
-rw-r--r--project/Dependencies.scala3
-rw-r--r--project/util.scala2
-rw-r--r--vfd-backend/app/controllers/Application.scala12
-rw-r--r--vfd-backend/app/views/index.scala.html41
-rw-r--r--vfd-backend/app/views/main.scala.html6
-rw-r--r--vfd-backend/public/audio/alarm.mp3bin0 -> 204029 bytes
-rw-r--r--vfd-backend/public/audio/alarm.oggbin0 -> 138838 bytes
-rw-r--r--vfd-backend/public/images/instruments/altitude.svg13
-rw-r--r--vfd-backend/public/images/instruments/attitude.svg57
-rw-r--r--vfd-backend/public/images/leds/led.svg293
-rw-r--r--vfd-backend/public/stylesheets/main.css8
-rw-r--r--vfd-frontend/src/main/scala/Frontend.scala41
-rw-r--r--vfd-frontend/src/main/scala/vfd/frontend/Frontend.scala99
-rw-r--r--vfd-frontend/src/main/scala/vfd/frontend/ui/Components.scala67
-rw-r--r--vfd-frontend/src/main/scala/vfd/frontend/ui/Panels.scala116
-rw-r--r--vfd-frontend/src/main/scala/vfd/frontend/util/Application.scala11
-rw-r--r--vfd-frontend/src/main/scala/vfd/frontend/util/Framework.scala58
-rw-r--r--vfd-uav/src/main/scala/vfd/uav/DummyConnection.scala13
19 files changed, 729 insertions, 112 deletions
diff --git a/project/Build.scala b/project/Build.scala
index f4a7834..678c8d2 100644
--- a/project/Build.scala
+++ b/project/Build.scala
@@ -60,6 +60,7 @@ object ApplicationBuild extends Build {
libraryDependencies ++= Seq(
rx,
dom,
+ tag,
pickling
)
)
diff --git a/project/Dependencies.scala b/project/Dependencies.scala
index ae56455..2afcbb1 100644
--- a/project/Dependencies.scala
+++ b/project/Dependencies.scala
@@ -13,7 +13,8 @@ object Dependencies {
val jquery = "org.webjars" % "jquery" % "2.1.1"
val dom = "org.scala-lang.modules.scalajs" %%%! "scalajs-dom" % "0.6"
- val rx = "com.scalarx" %%%! "scalarx" % "0.2.5"
+ val tag = "com.scalatags" %%%! "scalatags" % "0.4.1"
+ val rx = "com.scalarx" %%%! "scalarx" % "0.2.6"
val pickling = "org.scalajs" %%%! "scalajs-pickling" % "0.3.1"
val picklingPlay = "org.scalajs" %% "scalajs-pickling-play-json" % "0.3.1"
diff --git a/project/util.scala b/project/util.scala
index 595d7fe..1415ba8 100644
--- a/project/util.scala
+++ b/project/util.scala
@@ -25,7 +25,7 @@ package object util {
}
outFiles
}.taskValue,
- playMonitoredFiles ++= (watchSources in reference).value.map(_.getCanonicalPath)
+ playMonitoredFiles += (scalaSource in (reference, Compile)).value.getCanonicalPath
)
}
diff --git a/vfd-backend/app/controllers/Application.scala b/vfd-backend/app/controllers/Application.scala
index 6bd066d..efdf1bd 100644
--- a/vfd-backend/app/controllers/Application.scala
+++ b/vfd-backend/app/controllers/Application.scala
@@ -14,19 +14,19 @@ import plugins.UavPlugin
object Application extends Controller {
+ implicit val dataFrameFormat = spicklerFormat[DataFrame]
+ implicit val dataFrameFormatter = FrameFormatter.jsonFrame[DataFrame]
+
def use[A <: Plugin](implicit app: Application, m: Manifest[A]) = {
app.plugin[A].getOrElse(throw new RuntimeException(m.runtimeClass.toString + " plugin should be available at this point"))
}
- def index() = Action {
- Ok(views.html.index())
+ def index = Action { implicit request =>
+ Ok(views.html.index(routes.Application.socket.webSocketURL()))
}
-
- implicit val dataFrameFormat = spicklerFormat[DataFrame]
- implicit val dataFrameFormatter = FrameFormatter.jsonFrame[DataFrame]
- def socket = WebSocket.acceptWithActor[String, DataFrame] { request =>
+ def socket = WebSocket.acceptWithActor[String, DataFrame] { implicit request =>
out => use[UavPlugin].register(out)
}
} \ No newline at end of file
diff --git a/vfd-backend/app/views/index.scala.html b/vfd-backend/app/views/index.scala.html
index 654c2a1..da7a79f 100644
--- a/vfd-backend/app/views/index.scala.html
+++ b/vfd-backend/app/views/index.scala.html
@@ -1,45 +1,12 @@
-@main("Main"){
-
- <div class="row">
- <div class="col-lg-2">
- <div style="width: 100%; height: 450px; background-color: #000000; display: table;">
- <div style="display: table-cell; text-align: center; vertical-align: middle;">no feed</div>
- </div>
- </div>
- <div class="col-lg-6">
- @panels.pfd()
- <!-- <div style="width: 100%; height: 450px; background-color: #000000; display: table;">
- <div style="display: table-cell; text-align: center; vertical-align: middle;">no feed</div>
- </div> -->
- </div>
- <div class="col-lg-4">
- station
- <ul>
- <li>server link</li>
- <li>uav link</li>
- </ul>
- </div>
- </div>
- <div class="row">
- <div class="col-lg-4">
- uav
- </div>
- <div class="col-lg-4">
-
- </div>
- <div class="col-lg-4">
- @panels.eicas()
- </div>
- </div>
-
+@(socket: String)
+@main("Main"){
-
+ <div id="app"></div>
<script type="text/javascript">
-
window.onload = function () {
- var frontend = new Frontend('#attitude', '#heading', '#altitude')
+ var frontend = new Frontend('app', '@routes.Assets.at("")', '@socket')
frontend.main()
}
</script>
diff --git a/vfd-backend/app/views/main.scala.html b/vfd-backend/app/views/main.scala.html
index db894d2..f65ca66 100644
--- a/vfd-backend/app/views/main.scala.html
+++ b/vfd-backend/app/views/main.scala.html
@@ -51,10 +51,8 @@
</div><!-- /.container-fluid -->
</nav>
</header>
-
- <div class="container-fluid">
- @content
- </div>
+
+ @content
<script type="text/javascript" src="@routes.Assets.at("lib/vfd-frontend-fastopt.js")"></script>
<script type="text/javascript" src="@routes.Assets.at("lib/jquery/jquery.js")"></script>
diff --git a/vfd-backend/public/audio/alarm.mp3 b/vfd-backend/public/audio/alarm.mp3
new file mode 100644
index 0000000..97c83d5
--- /dev/null
+++ b/vfd-backend/public/audio/alarm.mp3
Binary files differ
diff --git a/vfd-backend/public/audio/alarm.ogg b/vfd-backend/public/audio/alarm.ogg
new file mode 100644
index 0000000..19a1717
--- /dev/null
+++ b/vfd-backend/public/audio/alarm.ogg
Binary files differ
diff --git a/vfd-backend/public/images/instruments/altitude.svg b/vfd-backend/public/images/instruments/altitude.svg
index 24146a1..2bfb306 100644
--- a/vfd-backend/public/images/instruments/altitude.svg
+++ b/vfd-backend/public/images/instruments/altitude.svg
@@ -26,7 +26,7 @@
inkscape:pageopacity="0.0"
inkscape:pageshadow="0"
inkscape:zoom="5.6568542"
- inkscape:cx="68.511673"
+ inkscape:cx="68.865226"
inkscape:cy="45.38396"
inkscape:document-units="px"
inkscape:current-layer="layer1"
@@ -106,6 +106,17 @@
style="fill:#1a1a1a;fill-opacity:1;stroke:none"
inkscape:connector-curvature="0" />
</g>
+ <text
+ xml:space="preserve"
+ style="font-size:10px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#f2f2f2;fill-opacity:1;stroke:none;font-family:Arial;-inkscape-font-specification:Arial"
+ x="-16.982601"
+ y="-14.119686"
+ id="altitude"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan3120"
+ x="-16.982601"
+ y="-14.119686">0000.00</tspan></text>
<g
id="dial">
<g
diff --git a/vfd-backend/public/images/instruments/attitude.svg b/vfd-backend/public/images/instruments/attitude.svg
index 7f0452c..09464d1 100644
--- a/vfd-backend/public/images/instruments/attitude.svg
+++ b/vfd-backend/public/images/instruments/attitude.svg
@@ -20,17 +20,17 @@
id="defs4">
<clipPath
clipPathUnits="userSpaceOnUse"
- id="clipPath3341">
+ id="clipPath3999">
<path
- sodipodi:type="arc"
- style="fill:#00ff00;fill-opacity:1;stroke:none"
- id="path3343"
- sodipodi:cx="55"
- sodipodi:cy="55"
- sodipodi:rx="50"
- sodipodi:ry="50"
+ transform="translate(-55,-55)"
d="M 105,55 A 50,50 0 1 1 5,55 50,50 0 1 1 105,55 z"
- transform="translate(-55,-55)" />
+ sodipodi:ry="50"
+ sodipodi:rx="50"
+ sodipodi:cy="55"
+ sodipodi:cx="55"
+ id="path4002"
+ style="fill:#00ff00;fill-opacity:1;stroke:none"
+ sodipodi:type="arc" />
</clipPath>
</defs>
<sodipodi:namedview
@@ -40,9 +40,9 @@
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
- inkscape:zoom="5.6568542"
- inkscape:cx="116.6015"
- inkscape:cy="56.323079"
+ inkscape:zoom="2.8284271"
+ inkscape:cx="-34.071266"
+ inkscape:cy="22.322089"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
@@ -87,7 +87,7 @@
id="layer1">
<g
id="roll"
- clip-path="url(#clipPath3341)">
+ clip-path="url(#clipPath3999)">
<g
id="pitch">
<rect
@@ -353,6 +353,37 @@
id="tspan3974"
x="-21.544434"
y="-28.301392">30</tspan></text>
+ <g
+ id="runway">
+ <path
+ inkscape:transform-center-x="-25"
+ inkscape:transform-center-y="43.30127"
+ inkscape:connector-curvature="0"
+ id="path3046"
+ d="M 50,86.60254 0,0"
+ style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.16695975;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none" />
+ <path
+ style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.16695975;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ d="M 86.60254,50 0,0"
+ id="path3977"
+ inkscape:connector-curvature="0"
+ inkscape:transform-center-y="25"
+ inkscape:transform-center-x="-43.30127" />
+ <path
+ inkscape:transform-center-x="25"
+ style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.16695975;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ d="M -50,86.60254 1.2675457e-8,-4.7305491e-8"
+ id="path3979"
+ inkscape:connector-curvature="0"
+ inkscape:transform-center-y="43.30127" />
+ <path
+ inkscape:transform-center-y="25"
+ inkscape:connector-curvature="0"
+ id="path3981"
+ d="M -86.60254,50 2.3652725e-8,-4.0967757e-8"
+ style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.16695975;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none"
+ inkscape:transform-center-x="43.30127" />
+ </g>
</g>
<path
sodipodi:type="star"
diff --git a/vfd-backend/public/images/leds/led.svg b/vfd-backend/public/images/leds/led.svg
new file mode 100644
index 0000000..74cc4ff
--- /dev/null
+++ b/vfd-backend/public/images/leds/led.svg
@@ -0,0 +1,293 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="64"
+ height="64"
+ id="svg2985"
+ version="1.1"
+ inkscape:version="0.48.5 r10040"
+ sodipodi:docname="led.svg"
+ viewBox="0 0 64 64">
+ <defs
+ id="defs2987">
+ <linearGradient
+ id="linearGradient3803"
+ inkscape:collect="always">
+ <stop
+ id="stop3805"
+ offset="0"
+ style="stop-color:#cfcfcf;stop-opacity:0.51141554" />
+ <stop
+ id="stop3807"
+ offset="1"
+ style="stop-color:#ffffff;stop-opacity:0.04109589" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3891">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.26940638"
+ offset="0"
+ id="stop3893" />
+ <stop
+ style="stop-color:#ffffff;stop-opacity:0.15525115"
+ offset="1"
+ id="stop3895" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3883">
+ <stop
+ style="stop-color:#000000;stop-opacity:0.02283105"
+ offset="0"
+ id="stop3885" />
+ <stop
+ style="stop-color:#000000;stop-opacity:1"
+ offset="1"
+ id="stop3887" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3849">
+ <stop
+ id="stop3851"
+ offset="0"
+ style="stop-color:#00ac00;stop-opacity:1" />
+ <stop
+ id="stop3853"
+ offset="1"
+ style="stop-color:#8cff00;stop-opacity:0;" />
+ </linearGradient>
+ <linearGradient
+ id="linearGradient3807">
+ <stop
+ style="stop-color:#000000;stop-opacity:0"
+ offset="0"
+ id="stop3809" />
+ <stop
+ style="stop-color:#000000;stop-opacity:0.5"
+ offset="1"
+ id="stop3811" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3787">
+ <stop
+ style="stop-color:#b3b3b3;stop-opacity:1;"
+ offset="0"
+ id="stop3789" />
+ <stop
+ style="stop-color:#cccccc;stop-opacity:1"
+ offset="1"
+ id="stop3791" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ id="linearGradient3777">
+ <stop
+ style="stop-color:#ffffff;stop-opacity:1"
+ offset="0"
+ id="stop3779" />
+ <stop
+ style="stop-color:#b3b3b3;stop-opacity:1"
+ offset="1"
+ id="stop3781" />
+ </linearGradient>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3777"
+ id="linearGradient3783"
+ x1="16.608555"
+ y1="18.651081"
+ x2="75.365089"
+ y2="69.463295"
+ gradientUnits="userSpaceOnUse" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3787"
+ id="radialGradient3793"
+ cx="48.149883"
+ cy="41.854801"
+ fx="48.149883"
+ fy="41.854801"
+ r="27.765808"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.0026007,0.01478583,-0.01619592,1.0982174,0.55265642,-4.8228038)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3891"
+ id="linearGradient3901"
+ x1="10.083722"
+ y1="34.833542"
+ x2="44.591557"
+ y2="14.1497"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1088977,0,0,1.1088977,-3.2150185,-2.383994)" />
+ <radialGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3807"
+ id="radialGradient3794"
+ gradientUnits="userSpaceOnUse"
+ cx="48.149883"
+ cy="41.854801"
+ fx="48.149883"
+ fy="41.854801"
+ r="27.765808"
+ gradientTransform="matrix(1.7287032,0,0,1.7287032,-35.086972,-30.499726)" />
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient3803"
+ id="linearGradient3801"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="matrix(1.1088977,0,0,1.1088977,-3.2150185,-2.383994)"
+ x1="-11.46802"
+ y1="62.658627"
+ x2="53.882328"
+ y2="18.582912" />
+ </defs>
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="13.34375"
+ inkscape:cx="25.462406"
+ inkscape:cy="33.437936"
+ inkscape:current-layer="svg2985"
+ showgrid="true"
+ inkscape:document-units="px"
+ inkscape:grid-bbox="true"
+ inkscape:window-width="1920"
+ inkscape:window-height="1029"
+ inkscape:window-x="0"
+ inkscape:window-y="27"
+ inkscape:window-maximized="1" />
+ <metadata
+ id="metadata2990">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ id="layer1"
+ inkscape:label="background"
+ inkscape:groupmode="layer"
+ style="display:inline"
+ sodipodi:insensitive="true">
+ <path
+ sodipodi:type="arc"
+ style="fill:#b3b3b3;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ id="path2993"
+ sodipodi:cx="48.149883"
+ sodipodi:cy="41.854801"
+ sodipodi:rx="27.765808"
+ sodipodi:ry="27.765808"
+ d="m 75.915691,41.854801 a 27.765808,27.765808 0 1 1 -55.531616,0 27.765808,27.765808 0 1 1 55.531616,0 z"
+ transform="matrix(-1.1268556,0,0,1.1268556,86.070613,-15.351671)" />
+ <path
+ transform="matrix(-1.0565207,0,0,1.0565207,82.725544,-12.366264)"
+ d="m 75.915691,41.854801 a 27.765808,27.765808 0 1 1 -55.531616,0 27.765808,27.765808 0 1 1 55.531616,0 z"
+ sodipodi:ry="27.765808"
+ sodipodi:rx="27.765808"
+ sodipodi:cy="41.854801"
+ sodipodi:cx="48.149883"
+ id="path3771"
+ style="fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:none"
+ sodipodi:type="arc" />
+ <path
+ transform="matrix(-1.1268556,0,0,1.1268556,86.070613,-15.351671)"
+ d="m 75.915691,41.854801 a 27.765808,27.765808 0 1 1 -55.531616,0 27.765808,27.765808 0 1 1 55.531616,0 z"
+ sodipodi:ry="27.765808"
+ sodipodi:rx="27.765808"
+ sodipodi:cy="41.854801"
+ sodipodi:cx="48.149883"
+ id="path3785"
+ style="fill:url(#radialGradient3793);fill-opacity:1;fill-rule:nonzero;stroke:none"
+ sodipodi:type="arc" />
+ <path
+ sodipodi:type="arc"
+ style="fill:#808080;fill-opacity:1;fill-rule:nonzero;stroke:url(#linearGradient3783)"
+ id="path3775"
+ sodipodi:cx="48.149883"
+ sodipodi:cy="41.854801"
+ sodipodi:rx="27.765808"
+ sodipodi:ry="27.765808"
+ d="m 75.915691,41.854801 a 27.765808,27.765808 0 1 1 -55.531616,0 27.765808,27.765808 0 1 1 55.531616,0 z"
+ transform="matrix(-1.0565207,0,0,1.0565207,82.725544,-12.366264)" />
+ <path
+ transform="matrix(-1.0445488,0,0,1.0445488,82.149098,-11.865182)"
+ d="m 75.915691,41.854801 a 27.765808,27.765808 0 1 1 -55.531616,0 27.765808,27.765808 0 1 1 55.531616,0 z"
+ sodipodi:ry="27.765808"
+ sodipodi:rx="27.765808"
+ sodipodi:cy="41.854801"
+ sodipodi:cx="48.149883"
+ id="path3795"
+ style="fill:#46ff00;fill-opacity:0;fill-rule:nonzero;stroke:none"
+ sodipodi:type="arc" />
+ </g>
+ <g
+ inkscape:groupmode="layer"
+ id="layer2"
+ inkscape:label="color"
+ style="display:inline">
+ <path
+ sodipodi:type="arc"
+ style="fill:#782121;fill-opacity:1;fill-rule:nonzero;stroke:none;display:inline"
+ id="light"
+ sodipodi:cx="48.149883"
+ sodipodi:cy="41.854801"
+ sodipodi:rx="27.765808"
+ sodipodi:ry="27.765808"
+ d="m 75.915691,41.854801 a 27.765808,27.765808 0 1 1 -55.531616,0 27.765808,27.765808 0 1 1 55.531616,0 z"
+ transform="matrix(-1.0445488,0,0,1.0445488,82.149098,-11.865182)">
+ <animate
+ attributeType="XML"
+ attributeName="fill"
+ dur="2s"
+ repeatCount="indefinite"
+ id="animate40"
+ values="#782121, #ff0000, #782121" />
+ </path>
+ </g>
+ <g
+ inkscape:groupmode="layer"
+ id="layer3"
+ inkscape:label="reflection"
+ style="display:inline"
+ sodipodi:insensitive="true">
+ <path
+ transform="matrix(-1.0445488,0,0,1.0445488,82.149098,-11.865182)"
+ d="m 75.915691,41.854801 a 27.765808,27.765808 0 1 1 -55.531616,0 27.765808,27.765808 0 1 1 55.531616,0 z"
+ sodipodi:ry="27.765808"
+ sodipodi:rx="27.765808"
+ sodipodi:cy="41.854801"
+ sodipodi:cx="48.149883"
+ id="path3805"
+ style="fill:url(#radialGradient3794);fill-opacity:1;fill-rule:nonzero;stroke:none;display:inline"
+ sodipodi:type="arc" />
+ <path
+ style="fill:url(#linearGradient3801);fill-opacity:1;fill-rule:nonzero;stroke:none;display:inline"
+ d="m 13.661019,17.992002 c -2.716552,0 -5.3380492,0.375822 -7.8315903,1.074244 -1.8971123,3.856752 -2.9801626,8.198554 -2.9801626,12.786977 0,16.017772 12.9868349,29.004607 29.0046069,29.004607 2.703688,0 5.313926,-0.382194 7.796937,-1.074245 1.910078,-3.867248 3.014816,-8.21652 3.014816,-12.82163 0,-16.017772 -12.986835,-28.969953 -29.004607,-28.969953 z"
+ id="path3815"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:url(#linearGradient3901);fill-opacity:1;fill-rule:nonzero;stroke:none;display:inline"
+ d="m 31.853873,2.8486171 c -4.086863,0 -7.978121,0.8657411 -11.504814,2.3910608 2.453382,-0.6830778 5.034928,-1.0742447 7.692978,-1.0742447 16.52263,0 29.905586,14.0815598 29.905586,31.4303198 0,5.007819 -1.133611,9.729358 -3.118775,13.930528 3.772658,-4.895422 6.029632,-11.014661 6.029632,-17.673058 0,-16.017771 -12.986835,-29.0046059 -29.004607,-29.0046059 z"
+ id="path3857"
+ inkscape:connector-curvature="0" />
+ </g>
+</svg>
diff --git a/vfd-backend/public/stylesheets/main.css b/vfd-backend/public/stylesheets/main.css
index 815d2b9..f335da8 100644
--- a/vfd-backend/public/stylesheets/main.css
+++ b/vfd-backend/public/stylesheets/main.css
@@ -3,6 +3,14 @@
src: url('../fonts/ds-digi.ttf')
}
+html {
+ height: 100%;
+}
+
+body {
+ min-height: 100%;
+}
+
body {
background-color: #e6e6e6;
}
diff --git a/vfd-frontend/src/main/scala/Frontend.scala b/vfd-frontend/src/main/scala/Frontend.scala
deleted file mode 100644
index dcb6a8d..0000000
--- a/vfd-frontend/src/main/scala/Frontend.scala
+++ /dev/null
@@ -1,41 +0,0 @@
-
-
-import scala.scalajs.js
-import js.annotation.JSExport
-import org.scalajs.dom
-import org.scalajs.spickling._
-import org.scalajs.spickling.jsany._
-
-@JSExport
-class Frontend {
- PicklerRegistry.register[vfd.uav.DataFrame]
-
- lazy val attitude = dom.document.getElementById("attitude").asInstanceOf[dom.HTMLObjectElement].contentDocument
- lazy val heading = dom.document.getElementById("heading").asInstanceOf[dom.HTMLObjectElement].contentDocument
- lazy val altitude = dom.document.getElementById("altitude").asInstanceOf[dom.HTMLObjectElement].contentDocument
-
- @JSExport
- def main() = {
- var roll = attitude.getElementById("roll");
- var pitch = attitude.getElementById("pitch");
- var heading = this.heading.getElementById("heading");
- var altitude = this.altitude.getElementById("hand")
-
- val connection = new dom.WebSocket("ws://localhost:9000/socket");
-
- connection.onmessage = (e: dom.MessageEvent) => {
-
- val data = PicklerRegistry.unpickle(js.JSON.parse(e.data.asInstanceOf[String]).asInstanceOf[js.Any] ).asInstanceOf[vfd.uav.DataFrame]
- //Console.println(d.roll)
-
-
-
-
- roll.setAttribute("transform", "rotate(" + data.roll.toDouble + ")");
- pitch.setAttribute("transform", "translate(0, " + data.pitch.toDouble + ")");
- heading.setAttribute("transform", "rotate(" + data.heading.toDouble + ")");
- altitude.setAttribute("transform", "rotate(" + data.altitude.toDouble * 36 + ")")
- }
- }
-
-} \ No newline at end of file
diff --git a/vfd-frontend/src/main/scala/vfd/frontend/Frontend.scala b/vfd-frontend/src/main/scala/vfd/frontend/Frontend.scala
new file mode 100644
index 0000000..ffae846
--- /dev/null
+++ b/vfd-frontend/src/main/scala/vfd/frontend/Frontend.scala
@@ -0,0 +1,99 @@
+package vfd.frontend
+
+
+import scala.scalajs.js
+import js.annotation.JSExport
+
+import org.scalajs.dom
+import org.scalajs.spickling._
+import org.scalajs.spickling.jsany._
+
+import scalatags.JsDom.all._
+
+import rx._
+
+import vfd.frontend.ui._
+import vfd.frontend.util.Application
+import vfd.uav.DataFrame
+
+@JSExport
+class Frontend(rootId: String, assetsBase: String, socketUrl: String) {
+
+ lazy val root = dom.document.getElementById(rootId)
+ implicit lazy val app = new Application(root, assetsBase)
+
+ PicklerRegistry.register[vfd.uav.DataFrame]
+
+ @JSExport
+ def main() = {
+ val connection = new dom.WebSocket(socketUrl);
+ val input: Var[DataFrame] = Var(DataFrame(0,0,0,0,0))
+
+ connection.onmessage = (e: dom.MessageEvent) => {
+ val json = js.JSON.parse(e.data.asInstanceOf[String]).asInstanceOf[js.Any]
+ val frame = PicklerRegistry.unpickle(json).asInstanceOf[vfd.uav.DataFrame]
+ input() = frame
+ }
+
+ val element = div(`class` := "container-fluid")(
+ div(`class` := "row")(
+ div(`class` := "col-xs-12")(
+ div(`class` := "panel panel-default")(
+ div(`class` := "panel-body")(
+ Panels.autopilot
+ )
+ )
+ )
+ ),
+ div(`class` := "row")(
+ div(`class` := "col-xs-4")(
+ div(`class` := "panel panel-default")(
+ div(`class` := "panel-body")(
+ Panels.secondary(input)
+ )
+ )
+ ),
+ div(`class` := "col-xs-5")(
+ div(`class` := "panel panel-default")(
+ div(`class` := "panel-body")(
+ Panels.primary(input)
+ )
+ )
+ ),
+ div(`class` := "col-xs-3")(
+ div(`class` := "panel panel-default")(
+ div(`class` := "panel-body")(
+ Panels.eicas()
+ )
+ )
+ )
+ )
+ )
+
+ root.appendChild(element.render)
+
+ }
+
+
+
+
+
+/*
+ def alert() = {
+ val image = "/assets/images/leds/led.svg"
+ val off = "#782121"
+ val on = "#ff0000"
+ val controls = div(
+ `object`("data".attr := image, `type` := "image/svg+xml", width:= "32px")(
+ "Cannot load"
+ ),
+ audio(
+ "autoplay".attr:="true",
+ source(src:="/assets/audio/alarm.ogg", `type`:="audio/ogg")
+ )
+
+ ).render
+ controls
+ }*/
+
+} \ No newline at end of file
diff --git a/vfd-frontend/src/main/scala/vfd/frontend/ui/Components.scala b/vfd-frontend/src/main/scala/vfd/frontend/ui/Components.scala
new file mode 100644
index 0000000..959ca8a
--- /dev/null
+++ b/vfd-frontend/src/main/scala/vfd/frontend/ui/Components.scala
@@ -0,0 +1,67 @@
+package vfd.frontend.ui
+
+import rx._
+import scalatags.JsDom.all._
+import vfd.frontend.util.Application
+import org.scalajs.dom.HTMLElement
+
+object Components {
+
+ def led(color: Rx[String], size: String)(implicit app: Application) = {
+ val elem = `object`(`type` := "image/svg+xml", "data".attr := app.asset("leds/led.svg"), width := size)(
+ "Error loading image."
+ ).render
+
+ Obs(color, skipInitial = true) {
+ val svg = elem.contentDocument
+ svg.getElementById("light").setAttribute("fill", color())
+ }
+ elem
+ }
+
+ private def instrument(name: String)(implicit app: Application) = {
+ val path = app.asset("images/instruments/" + name + ".svg")
+ `object`(`type` := "image/svg+xml", "data".attr := path, width := "100%")(
+ "Error loading image " + name
+ ).render
+ }
+
+ private def frame(elem: HTMLElement, size: String) = {
+ div(style := s"width: $size; height: $size; display: inline-block;" )(
+ elem
+ )
+ }
+
+ def attitude(pitchRoll: Rx[(Double, Double)], size: String)(implicit app: Application) = {
+ val inst = instrument("attitude")
+ Obs(pitchRoll, skipInitial = true){
+ val svg = inst.contentDocument
+ val pitch = svg.getElementById("pitch")
+ val roll = svg.getElementById("roll")
+ pitch.setAttribute("transform", "translate(0, " + pitchRoll()._1 / math.Pi * 180 + ")");
+ roll.setAttribute("transform", "rotate(" + pitchRoll()._2 / math.Pi * 180 + ")");
+ }
+ frame(inst, size)
+ }
+
+ def altitude(value: Rx[Double], size: String)(implicit app: Application) = {
+ val inst = instrument("altitude")
+ Obs(value, skipInitial = true){
+ val svg = inst.contentDocument
+ // 36deg === 1m
+ svg.getElementById("hand").setAttribute("transform", "rotate(" + value() * 36 + ")");
+ }
+ frame(inst, size)
+ }
+
+ def heading(value: Rx[Double], size: String)(implicit app: Application) = {
+ val inst = instrument("heading")
+ Obs(value, skipInitial = true){
+ val svg = inst.contentDocument
+ // 1deg === 1deg
+ svg.getElementById("heading").setAttribute("transform", "rotate(" + value() / math.Pi * 180 + ")");
+ }
+ frame(inst, size)
+ }
+
+} \ No newline at end of file
diff --git a/vfd-frontend/src/main/scala/vfd/frontend/ui/Panels.scala b/vfd-frontend/src/main/scala/vfd/frontend/ui/Panels.scala
new file mode 100644
index 0000000..8c5e38d
--- /dev/null
+++ b/vfd-frontend/src/main/scala/vfd/frontend/ui/Panels.scala
@@ -0,0 +1,116 @@
+package vfd.frontend.ui
+
+import rx._
+import rx.ops._
+import scalatags.JsDom.all._
+import vfd.uav.DataFrame
+import vfd.frontend.util.Application
+import vfd.frontend.util.Framework._
+
+
+object Panels {
+
+ def primary(input: Rx[DataFrame])(implicit app: Application) = div(
+ Components.heading(input map (_.heading), "33%"),
+ Components.attitude(input map (i => (i.pitch, i.roll)), "33%"),
+ Components.altitude(input map (_.altitude), "33%")
+ )
+
+ def secondary(input: Rx[DataFrame])(implicit app: Application) = div(
+ iframe(
+ width:="100%",
+ height:="350px",
+ "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&amp;layer=mapnik"
+ ),
+ table(`class`:="table")(
+ tr(
+ td("UAV Position"),
+ td("N13.1234 E1234.23465")
+ ),
+ tr(
+ td("Base Position"),
+ td("N13.1234 E1234.23465")
+ ),
+ tr(
+ td("Distance to UAV"),
+ td("200 m")
+ ),
+ tr(
+ td("Total flight distance"),
+ td("12.3 km")
+ ),
+ tr(
+ td("Groundspeed"),
+ td("23 km/h")
+ ),
+ tr(
+ td("---"),
+ td("")
+ ),
+ tr(
+ td("Below"),
+ td("180 cm")
+ )
+ )
+ )
+
+ def eicas()(implicit app: Application) = {
+ table(`class`:="table")(
+ tr(
+ td("Link Server"),
+ td("3"),
+ td("ms"),
+ td(img(src:="/assets/images/leds/red-off.svg",width:="16px"))
+ ),
+ tr(
+ td("Link UAV"),
+ td("-80"),
+ td("dB(m)"),
+ td(img(src:="/assets/images/leds/red-on.svg",width:="16px"))
+ ),
+ tr(
+ td("---"),
+ td(""),
+ td(""),
+ td("")
+ ),
+ tr(
+ td("Charge"),
+ td("4.800"),
+ td("Ah"),
+ td(img(src:="/assets/images/leds/red-off.svg",width:="16px"))
+ ),
+ tr(
+ td("Current"),
+ td("80"),
+ td("A"),
+ td(img(src:="/assets/images/leds/yellow-on.svg",width:="16px"))
+ ),
+ tr(
+ td("Endurance"),
+ td("14"),
+ td("min"),
+ td(img(src:="/assets/images/leds/none.svg",width:="16px"))
+ ),
+ tr(
+ td("GPS"),
+ td("5"),
+ td("satellites"),
+ td(img(src:="/assets/images/leds/none.svg",width:="16px"))
+ )
+ )
+ }
+
+ def autopilot = div(`class` := "btn-group")(
+ button(`type`:="button", `class`:="btn btn-default")("Auto"),
+ button(`type`:="button", `class`:="btn btn-default")("Position"),
+ button(`type`:="button", `class`:="btn btn-default")("Attitude"),
+ button(`type`:="button", `class`:="btn btn-default")(
+ span(`class`:="label label-default")("Manual")
+ )
+ )
+} \ No newline at end of file
diff --git a/vfd-frontend/src/main/scala/vfd/frontend/util/Application.scala b/vfd-frontend/src/main/scala/vfd/frontend/util/Application.scala
new file mode 100644
index 0000000..3b4e733
--- /dev/null
+++ b/vfd-frontend/src/main/scala/vfd/frontend/util/Application.scala
@@ -0,0 +1,11 @@
+package vfd.frontend.util
+
+import org.scalajs.dom.HTMLElement
+
+class Application(element: HTMLElement, assetsBase: String) {
+
+ def root = element
+
+ def asset(file: String): String = assetsBase + "/" + file
+
+} \ No newline at end of file
diff --git a/vfd-frontend/src/main/scala/vfd/frontend/util/Framework.scala b/vfd-frontend/src/main/scala/vfd/frontend/util/Framework.scala
new file mode 100644
index 0000000..76dceea
--- /dev/null
+++ b/vfd-frontend/src/main/scala/vfd/frontend/util/Framework.scala
@@ -0,0 +1,58 @@
+package vfd.frontend.util
+
+import scala.collection.{SortedMap, mutable}
+import scalatags.JsDom.all._
+import scala.util.{Failure, Success, Random}
+import rx._
+import rx.core.{Propagator, Obs}
+import org.scalajs.dom
+import org.scalajs.dom.{Element, DOMParser}
+import scala.scalajs.js
+import scala.language.implicitConversions
+
+/**
+ * A minimal binding between Scala.Rx and Scalatags and Scala-Js-Dom
+ * taken from https://github.com/lihaoyi/workbench-example-app/blob/todomvc/src/main/scala/example/Framework.scala, by Li Haoyi
+ */
+object Framework {
+
+ /**
+ * Wraps reactive strings in spans, so they can be referenced/replaced
+ * when the Rx changes.
+ */
+ implicit def RxStr[T](r: Rx[T])(implicit f: T => Frag): Frag = {
+ rxMod(Rx(span(r())))
+ }
+
+ /**
+ * 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 <: dom.HTMLElement](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)
+ }
+
+ implicit def RxAttrValue[T: AttrValue] = new AttrValue[Rx[T]]{
+ def apply(t: Element, a: Attr, r: Rx[T]): Unit = {
+ Obs(r){ implicitly[AttrValue[T]].apply(t, a, r())}
+ }
+ }
+
+ implicit def RxStyleValue[T: StyleValue] = new StyleValue[Rx[T]]{
+ def apply(t: Element, s: Style, r: Rx[T]): Unit = {
+ Obs(r){ implicitly[StyleValue[T]].apply(t, s, r())}
+ }
+ }
+}
diff --git a/vfd-uav/src/main/scala/vfd/uav/DummyConnection.scala b/vfd-uav/src/main/scala/vfd/uav/DummyConnection.scala
index bf8714f..991a303 100644
--- a/vfd-uav/src/main/scala/vfd/uav/DummyConnection.scala
+++ b/vfd-uav/src/main/scala/vfd/uav/DummyConnection.scala
@@ -9,17 +9,14 @@ class DummyConnection extends Actor with Connection {
import context._
var time = 0.0
- val messageInterval = FiniteDuration(20, MILLISECONDS)
+ val messageInterval = FiniteDuration(50, MILLISECONDS)
def flightData(time: Double) = {
- val speed = 5.0 / 1000
- val roll = 5.0/180*math.Pi
- val pitch = 10.0/180*math.Pi
Connection.NewDataFrame(DataFrame(
- roll,
- pitch,
- (roll * time * speed) % math.Pi,
- (pitch * time * speed),
+ math.sin(time/6000) * math.Pi,
+ math.sin(time/5050) * math.Pi/4,
+ time/5000 * 2 * math.Pi,
+ time/1000,
22
))
}