aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakob Odersky <jakob@odersky.com>2016-04-18 03:15:43 -0700
committerJakob Odersky <jakob@odersky.com>2016-04-18 03:15:43 -0700
commit05f2776ccb9989dbc359276c5c36c46d8282fc8f (patch)
tree931bd6728602694ebefbc52351287d0b08657aba
parentea468fe2af5dbc674c08e2f2c1d6766e6596f9a1 (diff)
downloadmavigator-05f2776ccb9989dbc359276c5c36c46d8282fc8f.tar.gz
mavigator-05f2776ccb9989dbc359276c5c36c46d8282fc8f.tar.bz2
mavigator-05f2776ccb9989dbc359276c5c36c46d8282fc8f.zip
Implement serial backend
-rw-r--r--build.sbt6
-rw-r--r--mavigator-server/build.sbt45
-rw-r--r--mavigator-server/src/main/resources/assets/images/hud/attitude.svg972
-rw-r--r--mavigator-server/src/main/resources/assets/images/hud/horizon.svg51
-rw-r--r--mavigator-server/src/main/scala/mavigator/Main.scala40
-rw-r--r--mavigator-server/src/main/scala/mavigator/Router.scala120
-rw-r--r--mavigator-uav/build.sbt1
-rw-r--r--mavigator-uav/src/main/resources/reference.conf23
-rw-r--r--mavigator-uav/src/main/scala/mavigator/uav/Backend.scala8
-rw-r--r--mavigator-uav/src/main/scala/mavigator/uav/Core.scala100
-rw-r--r--mavigator-uav/src/main/scala/mavigator/uav/Multiplexer.scala.disabled20
-rw-r--r--mavigator-uav/src/main/scala/mavigator/uav/Uav.scala36
-rw-r--r--mavigator-uav/src/main/scala/mavigator/uav/mock/MockBackend.scala (renamed from mavigator-uav/src/main/scala/mavigator/uav/mock/MockConnection.scala)37
-rw-r--r--mavigator-uav/src/main/scala/mavigator/uav/serial/SerialBackend.scala58
-rw-r--r--project/Dependencies.scala6
-rw-r--r--project/MavigatorBuild.scala3
-rw-r--r--project/build.properties2
-rw-r--r--project/plugins.sbt12
18 files changed, 1298 insertions, 242 deletions
diff --git a/build.sbt b/build.sbt
index 64a248e..69aecca 100644
--- a/build.sbt
+++ b/build.sbt
@@ -1,2 +1,6 @@
+import mavigator.MavigatorBuild
+
+MavigatorBuild.defaultSettings
+
//goto main project on load
-//onLoad in Global := (Command.process("project mavigator-main", _: State)) compose (onLoad in Global).value
+//onLoad in Global := (Command.process("project mavigator-server", _: State)) compose (onLoad in Global).value
diff --git a/mavigator-server/build.sbt b/mavigator-server/build.sbt
index 89bb0cd..10b59d0 100644
--- a/mavigator-server/build.sbt
+++ b/mavigator-server/build.sbt
@@ -7,7 +7,50 @@ MavigatorBuild.defaultSettings
libraryDependencies ++= Seq(
Dependencies.akkaHttp,
Dependencies.akkaHttpCore,
- Dependencies.akkaStream
+ Dependencies.akkaStream,
+ Dependencies.flowNative //FIXME runtime dependencies from uav are not included, is this an sbt bug?
)
Js.dependsOnJs(MavigatorBuild.cockpit)
+
+fork in run := true
+connectInput in run := true
+cancelable in Global := true
+
+
+/*
+ * Deployment configuration
+ */
+enablePlugins(UniversalPlugin, DebianPlugin, DockerPlugin)
+enablePlugins(JavaServerAppPackaging)
+
+
+name in Universal := "mavigator"
+packageName in Universal := "mavigator"
+executableScriptName in Universal := "mavigator"
+
+name in Linux := (name in Universal).value
+packageName in Linux := (packageName in Universal).value
+executableScriptName in Linux := (executableScriptName in Universal).value
+
+maintainer in Linux := "Jakob Odersky <jakob@odersky.com>"
+packageSummary in Linux := "Virtual cockpit for drones."
+packageDescription in Linux := "Compatible with devices using the MAVLink protocol."
+
+version in Debian := version.value
+debianPackageDependencies in Debian ++= Seq(
+ "java8-runtime-headless",
+ "bash (>= 2.05a-11)"
+)
+
+import com.typesafe.sbt.packager.archetypes.ServerLoader
+serverLoading in Debian := ServerLoader.Systemd
+
+
+name in Docker := "mavigator"
+packageName in Docker := "mavigator"
+executableScriptName := "mavigator"
+maintainer in Docker := "Jakob Odersky <jakob@odersky.com>"
+
+dockerBaseImage := "java:8"
+dockerExposedPorts += 8080
diff --git a/mavigator-server/src/main/resources/assets/images/hud/attitude.svg b/mavigator-server/src/main/resources/assets/images/hud/attitude.svg
index eb7dd70..04d5c71 100644
--- a/mavigator-server/src/main/resources/assets/images/hud/attitude.svg
+++ b/mavigator-server/src/main/resources/assets/images/hud/attitude.svg
@@ -20,12 +20,13 @@
id="defs4">
<clipPath
clipPathUnits="userSpaceOnUse"
- id="clipPath4472">
+ id="clipPath4744">
<circle
- id="circle4474"
- cx="0"
+ r="380"
cy="0"
- r="380" />
+ cx="0"
+ id="circle4746"
+ style="fill:#000080" />
</clipPath>
</defs>
<sodipodi:namedview
@@ -35,9 +36,9 @@
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
- inkscape:zoom="1"
- inkscape:cx="415.60712"
- inkscape:cy="547.77206"
+ inkscape:zoom="0.70710678"
+ inkscape:cx="229.45655"
+ inkscape:cy="461.66458"
inkscape:document-units="px"
inkscape:current-layer="svg2"
showgrid="true"
@@ -61,7 +62,7 @@
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
- showguides="false">
+ showguides="true">
<inkscape:grid
type="xygrid"
id="grid4686" />
@@ -84,7 +85,7 @@
clip-path="none">
<g
id="g4476"
- clip-path="url(#clipPath4472)">
+ clip-path="url(#clipPath4744)">
<g
id="pitch">
<path
@@ -109,11 +110,6 @@
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
- id="path5032"
- d="m -30,-350 60,0"
- style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
- <path
- inkscape:connector-curvature="0"
id="path5034"
d="m -50,-200 100,0"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
@@ -138,11 +134,6 @@
d="m -30,250 60,0"
style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
- style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
- d="m -30,350 60,0"
- id="path5044"
- inkscape:connector-curvature="0" />
- <path
inkscape:connector-curvature="0"
id="path5046"
d="m -50,100 100,0"
@@ -152,11 +143,6 @@
d="m -50,200 100,0"
id="path5048"
inkscape:connector-curvature="0" />
- <path
- inkscape:connector-curvature="0"
- id="path5050"
- d="m -50,300 100,0"
- style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
@@ -214,17 +200,6 @@
y="204.55017">20</tspan></text>
<text
xml:space="preserve"
- style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
- x="50"
- y="304.55017"
- id="text5078"
- sodipodi:linespacing="125%"><tspan
- sodipodi:role="line"
- id="tspan5080"
- x="50"
- y="304.55017">30</tspan></text>
- <text
- xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:end;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
x="-50"
y="-295.44983"
@@ -278,17 +253,6 @@
id="tspan5100"
x="-50"
y="204.55017">20</tspan></text>
- <text
- xml:space="preserve"
- style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:end;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
- x="-50"
- y="304.55017"
- id="text5102"
- sodipodi:linespacing="125%"><tspan
- sodipodi:role="line"
- x="-50"
- y="304.55017"
- id="tspan5106">30</tspan></text>
<path
inkscape:connector-curvature="0"
id="path5110"
@@ -397,26 +361,6 @@
d="m -10,-290 20,0"
style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
- style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
- d="m -10,-340 20,0"
- id="path4263"
- inkscape:connector-curvature="0" />
- <path
- inkscape:connector-curvature="0"
- id="path4265"
- d="m -10,-330 20,0"
- style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
- <path
- style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
- d="m -10,-320 20,0"
- id="path4267"
- inkscape:connector-curvature="0" />
- <path
- inkscape:connector-curvature="0"
- id="path4269"
- d="m -10,-310 20,0"
- style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
- <path
inkscape:connector-curvature="0"
id="path4273"
d="m -10,-170 20,0"
@@ -578,24 +522,895 @@
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
- id="path4337"
+ id="path4369"
+ d="m -30,-350 60,0"
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4371"
+ d="m -50,-400 100,0"
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text4373"
+ y="-395.44983"
+ x="50"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ y="-395.44983"
+ x="50"
+ id="tspan4375"
+ sodipodi:role="line">40</tspan></text>
+ <text
+ sodipodi:linespacing="125%"
+ id="text4377"
+ y="-395.44983"
+ x="-50"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:end;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ y="-395.44983"
+ x="-50"
+ id="tspan4379"
+ sodipodi:role="line">40</tspan></text>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,-370 20,0"
+ id="path4381"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4383"
+ d="m -10,-380 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4385"
+ d="m -10,-360 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,-340 20,0"
+ id="path4387"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4389"
+ d="m -10,-330 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,-320 20,0"
+ id="path4391"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4393"
+ d="m -10,-310 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,-390 20,0"
+ id="path4395"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -30,-450 60,0"
+ id="path4397"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -50,-500 100,0"
+ id="path4399"
+ inkscape:connector-curvature="0" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="50"
+ y="-495.44983"
+ id="text4401"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4403"
+ x="50"
+ y="-495.44983">50</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:end;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="-50"
+ y="-495.44983"
+ id="text4405"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4407"
+ x="-50"
+ y="-495.44983">50</tspan></text>
+ <path
+ inkscape:connector-curvature="0"
+ id="path4409"
+ d="m -10,-470 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,-480 20,0"
+ id="path4411"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,-460 20,0"
+ id="path4413"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4415"
+ d="m -10,-440 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,-430 20,0"
+ id="path4417"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4419"
+ d="m -10,-420 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,-410 20,0"
+ id="path4421"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4423"
+ d="m -10,-490 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4425"
+ d="m -30,-550 60,0"
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4427"
+ d="m -50,-600 100,0"
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text4429"
+ y="-595.44983"
+ x="50"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ y="-595.44983"
+ x="50"
+ id="tspan4431"
+ sodipodi:role="line">60</tspan></text>
+ <text
+ sodipodi:linespacing="125%"
+ id="text4433"
+ y="-595.44983"
+ x="-50"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:end;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ y="-595.44983"
+ x="-50"
+ id="tspan4435"
+ sodipodi:role="line">60</tspan></text>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,-570 20,0"
+ id="path4437"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4439"
+ d="m -10,-580 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4441"
+ d="m -10,-560 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,-540 20,0"
+ id="path4443"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4445"
+ d="m -10,-530 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,-520 20,0"
+ id="path4447"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4449"
+ d="m -10,-510 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,-590 20,0"
+ id="path4451"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -30,-650 60,0"
+ id="path4453"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -50,-700 100,0"
+ id="path4455"
+ inkscape:connector-curvature="0" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="50"
+ y="-695.44983"
+ id="text4457"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4459"
+ x="50"
+ y="-695.44983">70</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:end;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="-50"
+ y="-695.44983"
+ id="text4461"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4463"
+ x="-50"
+ y="-695.44983">70</tspan></text>
+ <path
+ inkscape:connector-curvature="0"
+ id="path4465"
+ d="m -10,-670 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,-680 20,0"
+ id="path4467"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,-660 20,0"
+ id="path4469"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4471"
+ d="m -10,-640 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,-630 20,0"
+ id="path4473"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4475"
+ d="m -10,-620 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,-610 20,0"
+ id="path4477"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4479"
+ d="m -10,-690 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4481"
+ d="m -30,-750 60,0"
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4483"
+ d="m -50,-800 100,0"
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text4485"
+ y="-795.44983"
+ x="50"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ y="-795.44983"
+ x="50"
+ id="tspan4487"
+ sodipodi:role="line">80</tspan></text>
+ <text
+ sodipodi:linespacing="125%"
+ id="text4489"
+ y="-795.44983"
+ x="-50"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:end;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ y="-795.44983"
+ x="-50"
+ id="tspan4491"
+ sodipodi:role="line">80</tspan></text>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,-770 20,0"
+ id="path4493"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4495"
+ d="m -10,-780 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4497"
+ d="m -10,-760 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,-740 20,0"
+ id="path4499"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4501"
+ d="m -10,-730 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,-720 20,0"
+ id="path4503"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4505"
+ d="m -10,-710 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,-790 20,0"
+ id="path4507"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -30,-850 60,0"
+ id="path4509"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -50,-900 100,0"
+ id="path4511"
+ inkscape:connector-curvature="0" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="50"
+ y="-895.44983"
+ id="text4513"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4515"
+ x="50"
+ y="-895.44983">90</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:end;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="-50"
+ y="-895.44983"
+ id="text4517"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4519"
+ x="-50"
+ y="-895.44983">90</tspan></text>
+ <path
+ inkscape:connector-curvature="0"
+ id="path4521"
+ d="m -10,-870 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,-880 20,0"
+ id="path4523"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,-860 20,0"
+ id="path4525"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4527"
+ d="m -10,-840 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,-830 20,0"
+ id="path4529"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4531"
+ d="m -10,-820 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,-810 20,0"
+ id="path4533"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4535"
+ d="m -10,-890 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -30,350 60,0"
+ id="path4565"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4567"
+ d="m -50,300 100,0"
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text4569"
+ y="304.55017"
+ x="50"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ y="304.55017"
+ x="50"
+ id="tspan4571"
+ sodipodi:role="line">30</tspan></text>
+ <text
+ sodipodi:linespacing="125%"
+ id="text4573"
+ y="304.55017"
+ x="-50"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:end;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ y="304.55017"
+ x="-50"
+ id="tspan4575"
+ sodipodi:role="line">30</tspan></text>
+ <path
+ inkscape:connector-curvature="0"
+ id="path4577"
d="m -10,330 20,0"
style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -10,320 20,0"
- id="path4339"
+ id="path4579"
inkscape:connector-curvature="0" />
<path
style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m -10,340 20,0"
- id="path4341"
+ id="path4581"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4583"
+ d="m -10,360 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,370 20,0"
+ id="path4585"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
- id="path4351"
+ id="path4587"
+ d="m -10,380 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,390 20,0"
+ id="path4589"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4591"
d="m -10,310 20,0"
style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4593"
+ d="m -30,450 60,0"
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -50,400 100,0"
+ id="path4595"
+ inkscape:connector-curvature="0" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="50"
+ y="404.55017"
+ id="text4597"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4599"
+ x="50"
+ y="404.55017">40</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:end;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="-50"
+ y="404.55017"
+ id="text4601"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4603"
+ x="-50"
+ y="404.55017">40</tspan></text>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,430 20,0"
+ id="path4605"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4607"
+ d="m -10,420 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4609"
+ d="m -10,440 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,460 20,0"
+ id="path4611"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4613"
+ d="m -10,470 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,480 20,0"
+ id="path4615"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4617"
+ d="m -10,490 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,410 20,0"
+ id="path4619"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -30,550 60,0"
+ id="path4621"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4623"
+ d="m -50,500 100,0"
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text4625"
+ y="504.55017"
+ x="50"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ y="504.55017"
+ x="50"
+ id="tspan4627"
+ sodipodi:role="line">50</tspan></text>
+ <text
+ sodipodi:linespacing="125%"
+ id="text4629"
+ y="504.55017"
+ x="-50"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:end;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ y="504.55017"
+ x="-50"
+ id="tspan4631"
+ sodipodi:role="line">50</tspan></text>
+ <path
+ inkscape:connector-curvature="0"
+ id="path4633"
+ d="m -10,530 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,520 20,0"
+ id="path4635"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,540 20,0"
+ id="path4637"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4639"
+ d="m -10,560 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,570 20,0"
+ id="path4641"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4643"
+ d="m -10,580 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,590 20,0"
+ id="path4645"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4647"
+ d="m -10,510 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4649"
+ d="m -30,650 60,0"
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -50,600 100,0"
+ id="path4651"
+ inkscape:connector-curvature="0" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="50"
+ y="604.55017"
+ id="text4653"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4655"
+ x="50"
+ y="604.55017">60</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:end;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="-50"
+ y="604.55017"
+ id="text4657"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4659"
+ x="-50"
+ y="604.55017">60</tspan></text>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,630 20,0"
+ id="path4661"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4663"
+ d="m -10,620 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4665"
+ d="m -10,640 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,660 20,0"
+ id="path4667"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4669"
+ d="m -10,670 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,680 20,0"
+ id="path4671"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4673"
+ d="m -10,690 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,610 20,0"
+ id="path4675"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -30,750 60,0"
+ id="path4677"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4679"
+ d="m -50,700 100,0"
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text4681"
+ y="704.55017"
+ x="50"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ y="704.55017"
+ x="50"
+ id="tspan4683"
+ sodipodi:role="line">70</tspan></text>
+ <text
+ sodipodi:linespacing="125%"
+ id="text4685"
+ y="704.55017"
+ x="-50"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:end;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ y="704.55017"
+ x="-50"
+ id="tspan4687"
+ sodipodi:role="line">70</tspan></text>
+ <path
+ inkscape:connector-curvature="0"
+ id="path4689"
+ d="m -10,730 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,720 20,0"
+ id="path4691"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,740 20,0"
+ id="path4693"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4695"
+ d="m -10,760 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,770 20,0"
+ id="path4697"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4699"
+ d="m -10,780 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,790 20,0"
+ id="path4701"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4703"
+ d="m -10,710 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4705"
+ d="m -30,850 60,0"
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -50,800 100,0"
+ id="path4707"
+ inkscape:connector-curvature="0" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="50"
+ y="804.55017"
+ id="text4709"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4711"
+ x="50"
+ y="804.55017">80</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:end;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="-50"
+ y="804.55017"
+ id="text4713"
+ sodipodi:linespacing="125%"><tspan
+ sodipodi:role="line"
+ id="tspan4715"
+ x="-50"
+ y="804.55017">80</tspan></text>
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,830 20,0"
+ id="path4717"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4719"
+ d="m -10,820 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4721"
+ d="m -10,840 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,860 20,0"
+ id="path4723"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4726"
+ d="m -10,870 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,880 20,0"
+ id="path4728"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4730"
+ d="m -10,890 20,0"
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <path
+ style="fill:none;fill-rule:evenodd;stroke:#999999;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m -10,810 20,0"
+ id="path4732"
+ inkscape:connector-curvature="0" />
+ <path
+ inkscape:connector-curvature="0"
+ id="path4734"
+ d="m -50,900 100,0"
+ style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
+ <text
+ sodipodi:linespacing="125%"
+ id="text4736"
+ y="904.55017"
+ x="-50"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:end;letter-spacing:0px;word-spacing:0px;text-anchor:end;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ y="904.55017"
+ x="-50"
+ id="tspan4738"
+ sodipodi:role="line">90</tspan></text>
+ <text
+ sodipodi:linespacing="125%"
+ id="text4740"
+ y="904.55017"
+ x="50"
+ style="font-style:normal;font-weight:normal;font-size:12.5px;line-height:125%;font-family:sans-serif;text-align:start;letter-spacing:0px;word-spacing:0px;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ y="904.55017"
+ x="50"
+ id="tspan4742"
+ sodipodi:role="line">90</tspan></text>
</g>
</g>
<text
@@ -783,6 +1598,12 @@
<g
id="static">
<path
+ sodipodi:nodetypes="cc"
+ inkscape:connector-curvature="0"
+ id="path4757"
+ style="fill:none;fill-rule:evenodd;stroke:#ffdd55;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ d="m 385,0 70,0" />
+ <path
inkscape:transform-center-y="-390"
inkscape:transform-center-x="-0.0686585"
sodipodi:nodetypes="cccc"
@@ -807,9 +1628,16 @@
d="m -103.52762,-386.37033 -6.28209,22.3888 23.049582,-6.17612 z"
style="fill:#fbff00;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
<path
- d="M 30,0 150,0 M -30,0 -150,0 M 0,0 15,25 30,0 15,25 0,0 -15,25 -15,25 -30,0"
+ d="M 30,0 60,0 M -30,0 -60,0 M 0,0 15,15 30,0 15,15 0,0 -15,15 -15,15 -30,0"
style="fill:none;fill-rule:evenodd;stroke:#ffdd55;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path4991"
- inkscape:connector-curvature="0" />
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccccccc" />
+ <path
+ d="m -455,0 70,0"
+ style="fill:none;fill-rule:evenodd;stroke:#ffdd55;stroke-width:3;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ id="path4759"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
</g>
</svg>
diff --git a/mavigator-server/src/main/resources/assets/images/hud/horizon.svg b/mavigator-server/src/main/resources/assets/images/hud/horizon.svg
index 8cf0150..36d0300 100644
--- a/mavigator-server/src/main/resources/assets/images/hud/horizon.svg
+++ b/mavigator-server/src/main/resources/assets/images/hud/horizon.svg
@@ -9,13 +9,13 @@
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
- width="1200"
- height="1200"
+ width="1000"
+ height="1000"
id="svg2"
version="1.1"
- inkscape:version="0.48.5 r10040"
+ inkscape:version="0.91 r13725"
sodipodi:docname="horizon.svg"
- viewBox="-600 -600 1200 1200"
+ viewBox="-500 -500 1000 1000"
preserveAspectRatio="none">
<defs
id="defs4" />
@@ -26,15 +26,15 @@
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
- inkscape:zoom="0.35374999"
- inkscape:cx="356.86783"
- inkscape:cy="585.56122"
+ inkscape:zoom="0.176875"
+ inkscape:cx="417.16915"
+ inkscape:cy="602.15446"
inkscape:document-units="px"
- inkscape:current-layer="horizon"
+ inkscape:current-layer="svg2"
showgrid="true"
showborder="true"
inkscape:window-width="1920"
- inkscape:window-height="1033"
+ inkscape:window-height="1034"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1">
@@ -58,20 +58,23 @@
</rdf:RDF>
</metadata>
<g
- id="horizon">
- <rect
- style="fill:#c88f00;fill-opacity:1;stroke:none"
- id="rect3000"
- width="1800"
- height="1200"
- x="-900"
- y="0" />
- <rect
- y="-1200"
- x="-900"
- height="1200"
- width="1800"
- id="rect3770"
- style="fill:#aaeeff;fill-opacity:1;stroke:none" />
+ id="roll">
+ <g
+ id="pitch">
+ <rect
+ y="0"
+ x="-1000"
+ height="2000"
+ width="2000"
+ id="ground"
+ style="fill:#c88f00;fill-opacity:1;stroke:none" />
+ <rect
+ style="fill:#aaeeff;fill-opacity:1;stroke:none"
+ id="sky"
+ width="2000"
+ height="2000"
+ x="-1000"
+ y="-2000" />
+ </g>
</g>
</svg>
diff --git a/mavigator-server/src/main/scala/mavigator/Main.scala b/mavigator-server/src/main/scala/mavigator/Main.scala
index 6ea894e..3ad1460 100644
--- a/mavigator-server/src/main/scala/mavigator/Main.scala
+++ b/mavigator-server/src/main/scala/mavigator/Main.scala
@@ -1,11 +1,12 @@
package mavigator
+import scala.concurrent.{Await, TimeoutException}
+import scala.concurrent.duration._
+
import akka.actor._
import akka.http.scaladsl._
-import akka.http.scaladsl.server._
import akka.stream._
-import scala.concurrent.Await
-import scala.concurrent.duration.Duration
+import mavigator.uav.Uav
object Main {
@@ -15,30 +16,33 @@ object Main {
def main(args: Array[String]): Unit = {
import system.dispatcher
- system.log.info("System started.")
+ val route = Router.route
- val router = Router.route
+ system.log.info(s"Initializing UAV connection backend...")
+ Uav().init()
- system.log.info(s"Starting server")
- val binding = Http(system).bindAndHandle(router, "0.0.0.0", 8080)
+ system.log.info(s"Starting server...")
+ val binding = Http(system).bindAndHandle(route, "::", 8080)
for (b <- binding) {
val addr = b.localAddress.getHostString()
val port = b.localAddress.getPort()
system.log.info(s"Server is listening on $addr:$port")
}
-
- scala.io.StdIn.readLine()
-
- binding.flatMap{b =>
- system.log.info("Shutting down server...")
- b.unbind()
- }.onComplete{ _ =>
- system.log.info("Server shut down")
- system.terminate()
- }
- Await.result(system.whenTerminated, Duration.Inf)
+ sys.addShutdownHook { stop() }
+ }
+ def stop() = {
+ system.log.info("Stopping server...")
+ system.terminate()
+
+ try {
+ Await.result(system.whenTerminated, 2.seconds)
+ System.err.println("Bye.")
+ } catch {
+ case ex: TimeoutException =>
+ System.err.println("Shutdown is taking too long, exiting now")
+ }
}
}
diff --git a/mavigator-server/src/main/scala/mavigator/Router.scala b/mavigator-server/src/main/scala/mavigator/Router.scala
index 389cb4d..226d6c1 100644
--- a/mavigator-server/src/main/scala/mavigator/Router.scala
+++ b/mavigator-server/src/main/scala/mavigator/Router.scala
@@ -1,80 +1,92 @@
package mavigator
import akka.actor._
-import akka.stream._
-import akka.stream.scaladsl._
-import akka.http.scaladsl._
+import akka.http.scaladsl.marshalling.{Marshaller, ToEntityMarshaller}
+import akka.http.scaladsl.model.MediaTypes._
import akka.http.scaladsl.model._
+import akka.http.scaladsl.model.Uri.Path
import akka.http.scaladsl.model.ws._
import akka.http.scaladsl.server._
-import uav.Uav
import akka.util._
-
-import akka.http.scaladsl.marshalling.{Marshaller, ToEntityMarshaller}
-import akka.http.scaladsl.model.MediaTypes._
-import akka.http.scaladsl.model.MediaType
-import play.twirl.api.{ Xml, Txt, Html }
+import akka.stream.scaladsl._
+import play.twirl.api.Html
+import uav.Uav
object Router {
import Directives._
- val socketUrl = "ws://localhost:8080/mavlink"
+ final val SocketEndpoint = "mavlink"
+
+ def withSocketUri: Directive1[Uri] = extractUri.map { uri =>
+ uri.withScheme("ws").withPath(Path.Empty / SocketEndpoint)
+ }
def route(implicit system: ActorSystem): Route = (
- path("cockpit" / IntNumber) { id =>
+ path("whoami") {
get {
- val html = mavigator.views.html.app(
- "Mavigator",
- "mavigator_cockpit_Main",
- Map(
- "socketUrl" -> socketUrl,
- "remoteSystemId" -> "0",
- "systemId" -> "0",
- "componentId" -> "0"
- )
- )
- complete(html)
+ withSocketUri { sock =>
+ complete(sock.toString)
+ }
}
} ~
- path("mavlink") {
- get {
- val fromWebSocket = Flow[Message].collect{
- case BinaryMessage.Strict(data) => data
+ path("cockpit" / IntNumber) { id =>
+ get {
+ withSocketUri { socket =>
+ val html = mavigator.views.html.app(
+ "Mavigator",
+ "mavigator_cockpit_Main",
+ Map(
+ "socketUrl" -> socket.toString,
+ "remoteSystemId" -> id.toString
+ )
+ )
+ complete(html)
+ }
}
+ } ~
+ path(SocketEndpoint) {
+ get {
+ val fromWebSocket = Flow[Message].collect{
+ case BinaryMessage.Strict(data) => data
+ }
- val toWebSocket = Flow[ByteString].map{bytes =>
- BinaryMessage(bytes)
- }
+ val toWebSocket = Flow[ByteString].map{bytes =>
+ BinaryMessage(bytes)
+ }
- val backend = Uav().connect()
+ val backend = Uav().connect()
- handleWebSocketMessages(fromWebSocket via backend via toWebSocket)
- }
- } ~
- pathPrefix("assets") {
- get {
- encodeResponse {
- getFromResourceDirectory("assets")
+ handleWebSocketMessages(fromWebSocket via backend via toWebSocket)
+ }
+ } ~
+ pathEndOrSingleSlash {
+ get {
+ withSocketUri { socket =>
+ val html = mavigator.views.html.app(
+ "Index",
+ "mavigator_index_Main",
+ Map(
+ "socketUrl" -> socket.toString
+ )
+ )
+ complete(html)
+ }
+ }
+ } ~
+ pathPrefix("assets") {
+ get {
+ encodeResponse {
+ getFromResourceDirectory("assets")
+ }
}
}
- } ~
- pathEndOrSingleSlash {
- get {
- val html = mavigator.views.html.app(
- "Index",
- "mavigator_index_Main",
- Map(
- "socketUrl" -> socketUrl
- )
- )
- complete(html)
- }
- }
)
- implicit val twirlHtml : ToEntityMarshaller[Html] = Marshaller.StringMarshaller.wrap(`text/html`){(h: Html) =>
- h.toString
- }
-
+ /** Enables completing requests with html. */
+ implicit val twirlHtml : ToEntityMarshaller[Html] =
+ Marshaller.StringMarshaller.wrap(`text/html`){ h: Html =>
+ h.toString
+ }
+
}
diff --git a/mavigator-uav/build.sbt b/mavigator-uav/build.sbt
index abe935b..c2b97c6 100644
--- a/mavigator-uav/build.sbt
+++ b/mavigator-uav/build.sbt
@@ -7,5 +7,6 @@ libraryDependencies ++= Seq(
Dependencies.akkaStream,
Dependencies.flow,
Dependencies.flowNative,
+ Dependencies.flowStream,
Dependencies.reactiveStreams
)
diff --git a/mavigator-uav/src/main/resources/reference.conf b/mavigator-uav/src/main/resources/reference.conf
index 3b43d37..5e66de3 100644
--- a/mavigator-uav/src/main/resources/reference.conf
+++ b/mavigator-uav/src/main/resources/reference.conf
@@ -1,30 +1,25 @@
# Settings related to the connection with a UAV
mavigator.uav {
+
# The type of connection to use
# 'mock' or 'serial'
- type = mock
-
- # Mavlink component ID used by this connection,
- # in case it needs to inject messages. I.e. heartbeats
- # will originate from this ID.
- component_id = 1
-
- # Interval in milliseconds between heartbeat messages injected by
- # the connection
- # 0 = no heartbeats injected
- heartbeat = 2000
+ type = serial
# Settings related to serial connections
serial {
# Serial port
- port = "/dev/ttyUSB0"
+ port = "/dev/ttyACM0"
# Baud rate (b/s)
- baud = 115200
+ baud = 57600
# Use two stop bits
two_stop_bits = false
# Parity check
# 0 = None, 1 = Odd, 2 = Even
parity = 0
+
+ # Delay between detection of serial port and attempt to open it.
+ # Set this to provide time for the device to initialise.
+ connection_delay = 1000
}
# Settings related to mock connections
@@ -32,7 +27,7 @@ mavigator.uav {
# Mavlink system ID of the simulated UAV
remote_system_id = 42
# Mavlink component ID of the simulated UAV
- remote_system_id = 0
+ remote_component_id = 0
# Divide simulated message frequency
prescaler = 1
}
diff --git a/mavigator-uav/src/main/scala/mavigator/uav/Backend.scala b/mavigator-uav/src/main/scala/mavigator/uav/Backend.scala
new file mode 100644
index 0000000..84b9673
--- /dev/null
+++ b/mavigator-uav/src/main/scala/mavigator/uav/Backend.scala
@@ -0,0 +1,8 @@
+package mavigator
+package uav
+
+trait Backend {
+
+ def init(core: Core): Unit
+
+}
diff --git a/mavigator-uav/src/main/scala/mavigator/uav/Core.scala b/mavigator-uav/src/main/scala/mavigator/uav/Core.scala
new file mode 100644
index 0000000..4418dae
--- /dev/null
+++ b/mavigator-uav/src/main/scala/mavigator/uav/Core.scala
@@ -0,0 +1,100 @@
+package mavigator
+package uav
+
+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.util.ByteString
+import org.reactivestreams.{Publisher, Subscriber}
+
+/** A core enables dynamic creation and removal of clients and backend connections.
+ * It is essentially a dynamic stream multiplexer. */
+class Core(implicit val system: ActorSystem, val materializer: Materializer) {
+ import Core._
+
+ /** The actor responsible for forwarding messages from the backend
+ * to this core. */
+ private lazy val backendPublisherActor: ActorRef = system.actorOf(BackendPublisher())
+
+ /** Publisher that forwards messages received from the backend. */
+ private lazy val backendPublisher: Publisher[ByteString] = ActorPublisher(backendPublisherActor)
+
+ private lazy val backend = Flow.fromSinkAndSourceMat(
+ Sink.ignore, //FIXME: this will need to be changed for controlling the uav from the browser
+ Source.fromPublisher(backendPublisher)
+ )((toBackend, fromBackend) => (toBackend, fromBackend))
+
+ private lazy val clients = Flow.fromSinkAndSourceMat(
+ Sink.asPublisher[ByteString](true),
+ Source.asSubscriber[ByteString]
+ )((toClient, fromClient) => (toClient, fromClient))
+
+ private lazy val runnable = clients.joinMat(backend){ case ((toClient, fromClient), (toBackend, fromBackend)) =>
+ toClient
+ }
+
+ private lazy val clientPublisher: Publisher[ByteString] = {
+ system.log.info("Core started.")
+ runnable.run()
+ }
+
+ def setBackend(): Flow[ByteString, ByteString, NotUsed] = {
+ val sink = Sink.actorRef(backendPublisherActor, BackendPublisher.BackendComplete)
+ val source = Source.empty[ByteString] //FIXME: this will need to be changed for controlling the uav from the browser
+ Flow.fromSinkAndSource(sink, source)
+ }
+
+ def connect(): Flow[ByteString, ByteString, NotUsed] = {
+ Flow.fromSinkAndSource(
+ Sink.ignore,
+ Source.fromPublisher(clientPublisher)
+ )
+ }
+
+}
+
+object Core {
+
+ private class BackendPublisher extends ActorPublisher[ByteString] with ActorLogging {
+ import akka.stream.actor.ActorPublisherMessage._
+
+ override def preStart() = {
+ log.info("Starting backend publisher actor...")
+ }
+
+ private var fromBackend: ByteString = null
+
+ def receive = {
+
+ case msg: ByteString =>
+ fromBackend = msg
+ deliver()
+
+ case BackendPublisher.BackendComplete =>
+ sys.error("Backend completed normally, this should not happen.")
+
+ // subscriber requests more
+ case ActorPublisherMessage.Request(_) =>
+ deliver()
+
+ //subscriber cancels
+ case ActorPublisherMessage.Cancel =>
+ sys.error("Subscriber cancelled backend stream, this should not happen.")
+
+ }
+
+ def deliver() = if (fromBackend != null && totalDemand > 0 && isActive) {
+ onNext(fromBackend)
+ fromBackend = null
+ }
+
+ }
+
+ private object BackendPublisher {
+ case object BackendComplete
+ def apply(): Props = Props(classOf[BackendPublisher])
+ }
+
+}
diff --git a/mavigator-uav/src/main/scala/mavigator/uav/Multiplexer.scala.disabled b/mavigator-uav/src/main/scala/mavigator/uav/Multiplexer.scala.disabled
deleted file mode 100644
index 5e48ea1..0000000
--- a/mavigator-uav/src/main/scala/mavigator/uav/Multiplexer.scala.disabled
+++ /dev/null
@@ -1,20 +0,0 @@
-package mavigator
-package uav
-
-import akka.stream.scaladsl._
-import akka.stream._
-import org.reactivestreams._
-
-private[uav] class Multiplexer[In, Out](service: Flow[In, Out, _])(implicit materializer: Materializer) {
-
- private val endpoint: Flow[Out, In, (Publisher[Out], Subscriber[In])] = Flow.fromSinkAndSourceMat(
- Sink.asPublisher[Out](fanout = true),
- Source.asSubscriber[In])((pub, sub) => (pub, sub))
-
- private lazy val (publisher, subscriber) = (service.joinMat(endpoint)(Keep.right)).run()
-
- def connect(client: Flow[Out, In, _]) = {
- Source.fromPublisher(publisher).via(client).to(Sink.ignore).run()
- }
-
-}
diff --git a/mavigator-uav/src/main/scala/mavigator/uav/Uav.scala b/mavigator-uav/src/main/scala/mavigator/uav/Uav.scala
index 8f8c083..1387983 100644
--- a/mavigator-uav/src/main/scala/mavigator/uav/Uav.scala
+++ b/mavigator-uav/src/main/scala/mavigator/uav/Uav.scala
@@ -2,40 +2,38 @@ package mavigator
package uav
import java.lang.IllegalArgumentException
-import mock._
+
import akka._
import akka.actor._
-import akka.util._
+import akka.stream.ActorMaterializer
import akka.stream.scaladsl._
+import akka.util._
+import mock._
+import serial._
+
+//TODO: the whole backend system feels hacky, it probably needs a major redesign
class Uav(system: ExtendedActorSystem) extends Extension {
+ private val materializer = ActorMaterializer()(system)
+
private lazy val config = system.settings.config.getConfig("mavigator.uav")
private lazy val tpe = config.getString("type")
- private lazy val componentId = config.getInt("componentId").toByte
- private lazy val heartbeat = config.getInt("heartbeat")
- private lazy val connection = config.getConfig(tpe)
- lazy val source = tpe match {
- case "mock" =>
- new MockConnection(
- connection.getInt("remote_system_id").toByte,
- componentId,
- connection.getDouble("prescaler")
- )
-
- case "serial" => ???
+ private lazy val core = new Core()(system, materializer)
+ lazy val backend: Backend = tpe match {
+ case "mock" => MockBackend
+ case "serial" => SerialBackend
case _ => throw new IllegalArgumentException(s"Unsupported connection type: $tpe")
}
- def connect(): Flow[ByteString, ByteString, NotUsed] = {
- Flow.fromSinkAndSource(
- Sink.ignore,
- (new MockConnection(0,0,1)).data //TODO: use source instead of hardcoded value
- )
+ def init(): Unit = {
+ backend.init(core)
}
+ def connect(): Flow[ByteString, ByteString, NotUsed] = core.connect()
+
}
object Uav extends ExtensionId[Uav] with ExtensionIdProvider {
diff --git a/mavigator-uav/src/main/scala/mavigator/uav/mock/MockConnection.scala b/mavigator-uav/src/main/scala/mavigator/uav/mock/MockBackend.scala
index 58b4977..0b89fbd 100644
--- a/mavigator-uav/src/main/scala/mavigator/uav/mock/MockConnection.scala
+++ b/mavigator-uav/src/main/scala/mavigator/uav/mock/MockBackend.scala
@@ -10,15 +10,16 @@ import akka.stream.Attributes._
import akka.stream.scaladsl._
import akka.util._
import org.mavlink._
-import org.mavlink.messages.{Heartbeat, Message}
+import org.mavlink.messages.Message
-class MockConnection(
+/** A test connection that produces random MAVLink messages. */
+class MockBackend(
remoteSystemId: Byte,
remoteComponentId: Byte,
prescaler: Double
) {
- import MockConnection._
+ import MockBackend._
private lazy val assembler = new Assembler(remoteSystemId, remoteComponentId)
@@ -35,7 +36,7 @@ class MockConnection(
delayed(0.1)(_.distance)
)
- val data: Source[ByteString, NotUsed] = messages.map{ message =>
+ private val data: Source[ByteString, NotUsed] = messages.map{ message =>
val (messageId, payload) = Message.pack(message)
val packet = assembler.assemble(messageId, payload)
ByteString(packet.toArray)
@@ -43,7 +44,7 @@ class MockConnection(
}
-object MockConnection {
+object MockBackend extends Backend {
final val ClockTick: FiniteDuration = 0.02.seconds
@@ -52,7 +53,7 @@ object MockConnection {
Source.fromGraph(GraphDSL.create() { implicit b =>
val clock = Source.tick(ClockTick, ClockTick, plan) map { plan =>
- plan.tick(0.01)
+ plan.tick(ClockTick.toMillis / 1000.0)
plan
}
val bcast = b.add(Broadcast[RandomFlightPlan](messages.length))
@@ -67,4 +68,28 @@ object MockConnection {
})
}
+
+ override def init(core: Core) = {
+ import core.materializer
+ import core.system
+
+ system.log.info("Initializing mock backend...")
+
+ val config = system.settings.config.getConfig("mavigator.uav.mock")
+
+ val mock = new MockBackend(
+ config.getInt("remote_system_id").toByte,
+ config.getInt("remote_component_id").toByte,
+ config.getDouble("prescaler")
+ )
+
+ val mockFlow = Flow.fromSinkAndSource(
+ Sink.ignore,
+ mock.data
+ )
+
+ (mockFlow join core.setBackend()).run()
+
+ }
+
}
diff --git a/mavigator-uav/src/main/scala/mavigator/uav/serial/SerialBackend.scala b/mavigator-uav/src/main/scala/mavigator/uav/serial/SerialBackend.scala
new file mode 100644
index 0000000..a52d238
--- /dev/null
+++ b/mavigator-uav/src/main/scala/mavigator/uav/serial/SerialBackend.scala
@@ -0,0 +1,58 @@
+package mavigator
+package uav
+package serial
+
+import scala.concurrent.Future
+import scala.concurrent.duration._
+import scala.util.{Failure, Success}
+
+import akka.NotUsed
+import akka.stream.scaladsl.{Flow, Keep}
+import akka.util.ByteString
+import com.github.jodersky.flow.{Parity, SerialSettings}
+import com.github.jodersky.flow.stream.Serial
+import com.github.jodersky.flow.stream.Serial.Connection
+
+object SerialBackend extends Backend {
+
+ override def init(core: Core): Unit = {
+ import core.materializer
+ import core.system
+ import core.system.dispatcher
+
+ system.log.info("Initializing serial backend...")
+
+ val conf = system.settings.config.getConfig("mavigator.uav.serial")
+ val port = conf.getString("port")
+ val serialSettings = SerialSettings(
+ baud = conf.getInt("baud"),
+ twoStopBits = conf.getBoolean("two_stop_bits"),
+ parity = Parity(conf.getInt("parity"))
+ )
+
+ val connectionDelay = conf.getInt("connection_delay").millis
+
+ system.log.info("Waiting for serial device on " + port + "...")
+ Serial().watch(Set(port)).map{ port =>
+ system.log.info("Serial device connected on port " + port)
+ port
+ }.delay(connectionDelay).runForeach{ port =>
+ system.log.info("Opening serial port " + port)
+
+ val backend: Flow[ByteString, ByteString, NotUsed] = core.setBackend()
+
+ val uav: Flow[ByteString, ByteString, Future[Connection]] = Serial().open(port, serialSettings)
+
+ val connection = uav.joinMat(backend)(Keep.left).run().onComplete{
+ case Success(connection) =>
+ system.log.info("Successfully opened serial port " + connection.port)
+ case Failure(ex) =>
+ system.log.error(ex, "Error occurred while trying to open " + port)
+ }
+
+ }
+
+
+ }
+
+}
diff --git a/project/Dependencies.scala b/project/Dependencies.scala
index ba39c94..60e7410 100644
--- a/project/Dependencies.scala
+++ b/project/Dependencies.scala
@@ -6,7 +6,7 @@ import org.scalajs.sbtplugin.ScalaJSPlugin.autoImport._
object Dependencies {
- final val AkkaVersion = "2.4.2-RC1"
+ final val AkkaVersion = "2.4.4"
val akkaActor = "com.typesafe.akka" %% "akka-actor" % AkkaVersion
val akkaHttp = "com.typesafe.akka" %% "akka-http-experimental" % AkkaVersion
val akkaHttpCore = "com.typesafe.akka" %% "akka-http-core" % AkkaVersion
@@ -14,10 +14,10 @@ object Dependencies {
val reactiveStreams = "org.reactivestreams" % "reactive-streams" % "1.0.0"
- final val FlowVersion = "2.5.0-M2"
+ final val FlowVersion = "2.6.0"
val flow = "com.github.jodersky" %% "flow-core" % FlowVersion
val flowNative = "com.github.jodersky" % "flow-native" % FlowVersion % Runtime
- val flowStream = "com.github.jodersky" % "flow-stream" % FlowVersion
+ val flowStream = "com.github.jodersky" %% "flow-stream" % FlowVersion
val jsDom = Def.setting{"org.scala-js" %%% "scalajs-dom" % "0.9.0"}
val scalatags = Def.setting{"com.lihaoyi" %%% "scalatags" % "0.5.4"}
diff --git a/project/MavigatorBuild.scala b/project/MavigatorBuild.scala
index e46d5f2..f6c0884 100644
--- a/project/MavigatorBuild.scala
+++ b/project/MavigatorBuild.scala
@@ -8,7 +8,8 @@ object MavigatorBuild extends Build {
// settings common to all projects
val defaultSettings = Seq(
- scalaVersion := "2.11.7",
+ resolvers += Resolver.jcenterRepo,
+ scalaVersion := "2.11.8",
scalacOptions ++= Seq("-feature", "-deprecation")
)
diff --git a/project/build.properties b/project/build.properties
index 817bc38..43b8278 100644
--- a/project/build.properties
+++ b/project/build.properties
@@ -1 +1 @@
-sbt.version=0.13.9
+sbt.version=0.13.11
diff --git a/project/plugins.sbt b/project/plugins.sbt
index 301572f..611b313 100644
--- a/project/plugins.sbt
+++ b/project/plugins.sbt
@@ -2,14 +2,10 @@
* Additional resolvers
*/
-//resolvers += "Typesafe repository" at "https://repo.typesafe.com/typesafe/releases/"
-
-//resolvers += "jgit-repo" at "http://download.eclipse.org/jgit/maven"
-
resolvers += Resolver.jcenterRepo
/*
- * Main plugins
+ * Main plugins (required for building)
*/
// add support for scalajs
@@ -19,11 +15,11 @@ addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.8")
addSbtPlugin("com.typesafe.sbt" % "sbt-twirl" % "1.1.1")
// generate MAVLink protocol bindings
-addSbtPlugin("com.github.jodersky" % "sbt-mavlink" % "0.5.2")
+addSbtPlugin("com.github.jodersky" % "sbt-mavlink" % "0.7.0")
/*
- * Utility or meta plugins
+ * Utility or meta plugins (non-essential, can be disabled)
*/
// automate publishing documentation
@@ -35,4 +31,4 @@ addSbtPlugin("com.github.jodersky" % "sbt-mavlink" % "0.5.2")
// generate documentation for all projects
//addSbtPlugin("com.eed3si9n" % "sbt-unidoc" % "0.3.3")
-addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.0.0")
+addSbtPlugin("com.typesafe.sbt" % "sbt-native-packager" % "1.1.0-RC3")