aboutsummaryrefslogtreecommitdiff
path: root/project
diff options
context:
space:
mode:
authorJakob Odersky <jodersky@gmail.com>2014-12-16 00:09:12 +0100
committerJakob Odersky <jodersky@gmail.com>2014-12-16 00:09:12 +0100
commit458971f834a3af0dbf2fffe527352fa11e7d8168 (patch)
tree226675f8402f2c099cb15b21cab28eb8784f1c96 /project
parent84c641d12187183466df936eaa7c1637d861cf62 (diff)
downloadmavigator-458971f834a3af0dbf2fffe527352fa11e7d8168.tar.gz
mavigator-458971f834a3af0dbf2fffe527352fa11e7d8168.tar.bz2
mavigator-458971f834a3af0dbf2fffe527352fa11e7d8168.zip
generate mavlink files in build
Diffstat (limited to 'project')
-rw-r--r--project/Build.scala20
-rw-r--r--project/build.properties2
-rw-r--r--project/mavlink-library/src/main/scala/com/github/jodersky/mavlink/Main.scala41
-rw-r--r--project/mavlink-library/src/main/scala/com/github/jodersky/mavlink/parsing/Crc.scala24
-rw-r--r--project/mavlink-library/src/main/scala/com/github/jodersky/mavlink/parsing/FieldTypes.scala27
-rw-r--r--project/mavlink-library/src/main/scala/com/github/jodersky/mavlink/parsing/Name.scala14
-rw-r--r--project/mavlink-library/src/main/scala/com/github/jodersky/mavlink/parsing/parsing.scala85
-rw-r--r--project/mavlink-library/src/main/twirl/org/mavlink/Crc.scala.txt23
-rw-r--r--project/mavlink-library/src/main/twirl/org/mavlink/Packet.scala.txt59
-rw-r--r--project/mavlink-library/src/main/twirl/org/mavlink/Parser.scala.txt128
-rw-r--r--project/mavlink-library/src/main/twirl/org/mavlink/_header.scala.txt5
-rw-r--r--project/mavlink-library/src/main/twirl/org/mavlink/messages/Message.scala.txt70
-rw-r--r--project/mavlink-library/src/main/twirl/org/mavlink/messages/_message_class.scala.txt14
-rw-r--r--project/mavlink-plugin/src/main/scala/com/github/jodersky/sbt/SbtMavlink.scala24
-rw-r--r--project/mavlink-plugin/src/main/scala/com/github/jodersky/sbt/mavlink/MavlinkKeys.scala14
-rw-r--r--project/plugins.sbt7
-rw-r--r--project/project/BuildBuild.scala35
-rw-r--r--project/project/plugins.sbt1
18 files changed, 572 insertions, 21 deletions
diff --git a/project/Build.scala b/project/Build.scala
index 2728c48..4ca4b3b 100644
--- a/project/Build.scala
+++ b/project/Build.scala
@@ -7,18 +7,16 @@ import scala.scalajs.sbtplugin.ScalaJSPlugin
import scala.scalajs.sbtplugin.ScalaJSPlugin.ScalaJSKeys._
import Dependencies._
+import com.github.jodersky.sbt.mavlink.MavlinkKeys._
+import com.github.jodersky.sbt.SbtMavlink
object ApplicationBuild extends Build {
//settings common to all projects
val common = Seq(
scalaVersion := "2.11.4",
- scalacOptions ++= Seq("-feature", "-deprecation")
- )
-
- //settings for js/jvm projects using shared sources
- val shared = Seq(
- unmanagedSourceDirectories in Compile += (scalaSource in (mavlink, Compile)).value
+ scalacOptions ++= Seq("-feature", "-deprecation"),
+ mavlinkDialect := file(".") / "concise.xml"
)
lazy val root = Project("root", file(".")).aggregate(
@@ -27,15 +25,10 @@ object ApplicationBuild extends Build {
frontend
)
- lazy val mavlink = (
- Project("vfd-mavlink", file("vfd-mavlink"))
- settings(common: _*)
- )
-
lazy val uav = (
Project("vfd-uav", file("vfd-uav"))
+ enablePlugins(SbtMavlink)
settings(common: _*)
- settings(shared: _*)
settings(
libraryDependencies ++= Seq(
akkaActor,
@@ -48,6 +41,7 @@ object ApplicationBuild extends Build {
lazy val backend = (
Project("vfd-backend", file("vfd-backend"))
enablePlugins(PlayScala)
+ enablePlugins(SbtMavlink)
settings(common: _*)
settings(
resolvers += Resolver.url("scala-js-releases", url("http://dl.bintray.com/content/scala-js/scala-js-releases"))(Resolver.ivyStylePatterns),
@@ -64,8 +58,8 @@ object ApplicationBuild extends Build {
lazy val frontend = (
Project("vfd-frontend", file("vfd-frontend"))
settings(ScalaJSPlugin.scalaJSSettings: _*)
+ enablePlugins(SbtMavlink)
settings(common: _*)
- settings(shared: _*)
settings(
libraryDependencies ++= Seq(
rx,
diff --git a/project/build.properties b/project/build.properties
index be6c454..748703f 100644
--- a/project/build.properties
+++ b/project/build.properties
@@ -1 +1 @@
-sbt.version=0.13.5
+sbt.version=0.13.7
diff --git a/project/mavlink-library/src/main/scala/com/github/jodersky/mavlink/Main.scala b/project/mavlink-library/src/main/scala/com/github/jodersky/mavlink/Main.scala
new file mode 100644
index 0000000..5f77c1a
--- /dev/null
+++ b/project/mavlink-library/src/main/scala/com/github/jodersky/mavlink/Main.scala
@@ -0,0 +1,41 @@
+package com.github.jodersky.mavlink
+
+import scala.xml.XML
+import com.github.jodersky.mavlink.parsing.Protocol
+import java.io.FileWriter
+import java.io.BufferedWriter
+import scalax.file.Path
+import java.io.File
+
+object Main {
+
+ def prettify(str: String) = str.replaceAll("(\\s*\n)(\\s*\n)+", "\n\n")
+
+ private def processTemplates(protocol: Protocol, rootOut: Path) = {
+ val root = rootOut / Path.fromString("org/mavlink")
+ val mappings: List[(String, Path)] = List(
+ org.mavlink.txt.Crc().body -> Path("Crc.scala"),
+ org.mavlink.txt.Packet(protocol.messages).body -> Path("Packet.scala"),
+ org.mavlink.txt.Parser().body -> Path("Parser.scala"),
+ org.mavlink.messages.txt.Message(protocol.messages).body -> Path.fromString("messages/messages.scala")
+ )
+
+ for ((str, file) <- mappings) yield {
+ val out = root / file
+ out.createFile(true, false)
+ out.write(prettify(str))
+ out
+ }
+ }
+
+ def run(dialect: File, out: File) = {
+ val xml = XML.loadFile(dialect)
+ val protocol = Protocol.parse(xml)
+ processTemplates(protocol, Path(out)).map(_.fileOption.get)
+ }
+
+ def main(args: Array[String]): Unit = {
+ run(new File(args(0)), new File(args(1)))
+ }
+
+} \ No newline at end of file
diff --git a/project/mavlink-library/src/main/scala/com/github/jodersky/mavlink/parsing/Crc.scala b/project/mavlink-library/src/main/scala/com/github/jodersky/mavlink/parsing/Crc.scala
new file mode 100644
index 0000000..c54b7e9
--- /dev/null
+++ b/project/mavlink-library/src/main/scala/com/github/jodersky/mavlink/parsing/Crc.scala
@@ -0,0 +1,24 @@
+package com.github.jodersky.mavlink.parsing
+
+case class Crc(val crc: Int = 0xffff) extends AnyVal {
+
+ def accumulate(datum: Byte): Crc = {
+ val d = datum & 0xff
+ var tmp = d ^ (crc & 0xff)
+ tmp ^= (tmp << 4) & 0xff;
+ Crc(
+ ((crc >> 8) & 0xff) ^ (tmp << 8) ^ (tmp << 3) ^ ((tmp >> 4) & 0xff))
+ }
+
+ def accumulate(data: Seq[Byte]): Crc = {
+ var next = this
+ for (d <- data) {
+ next = next.accumulate(d)
+ }
+ next
+ }
+
+ def lsb: Byte = crc.toByte
+ def msb: Byte = (crc >> 8).toByte
+
+} \ No newline at end of file
diff --git a/project/mavlink-library/src/main/scala/com/github/jodersky/mavlink/parsing/FieldTypes.scala b/project/mavlink-library/src/main/scala/com/github/jodersky/mavlink/parsing/FieldTypes.scala
new file mode 100644
index 0000000..0cf29b4
--- /dev/null
+++ b/project/mavlink-library/src/main/scala/com/github/jodersky/mavlink/parsing/FieldTypes.scala
@@ -0,0 +1,27 @@
+package com.github.jodersky.mavlink.parsing
+
+import scala.xml.Node
+
+object FieldTypes {
+
+ sealed trait Type {
+ def width: Int
+ def scalaType: String
+ def nativeType: String
+ }
+
+ case class IntType(signed: Boolean, width: Int, scalaType: String, nativeType: String) extends Type
+
+ def parse(xml: Node): Type = xml.text match {
+ case "int8_t" => IntType(true, 1, "Byte", xml.text)
+ case "int16_t" => IntType(true, 2, "Short", xml.text)
+ case "int32_t" => IntType(true, 4, "Int", xml.text)
+ case "int64_t" => IntType(true, 8, "Long", xml.text)
+ case "uint8_t" => IntType(false, 1, "Byte", xml.text)
+ case "uint16_t" => IntType(false, 2, "Short", xml.text)
+ case "uint32_t" => IntType(false, 4, "Int", xml.text)
+ case "uint64_t" => IntType(false, 8, "Long", xml.text)
+ case _ => Parser.fatal("unsupported type \"" + xml.text + "\"", xml)
+ }
+
+} \ No newline at end of file
diff --git a/project/mavlink-library/src/main/scala/com/github/jodersky/mavlink/parsing/Name.scala b/project/mavlink-library/src/main/scala/com/github/jodersky/mavlink/parsing/Name.scala
new file mode 100644
index 0000000..6f5b6a7
--- /dev/null
+++ b/project/mavlink-library/src/main/scala/com/github/jodersky/mavlink/parsing/Name.scala
@@ -0,0 +1,14 @@
+package com.github.jodersky.mavlink.parsing
+
+object Name {
+
+ def words(raw: String) = raw.split("\\s+|_")
+
+ def className(raw: String): String = words(raw.toLowerCase()).map(_.capitalize).mkString("")
+
+ def varName(raw: String) = {
+ val (head, tail) = className(raw).splitAt(1)
+ head.toLowerCase() + tail
+ }
+
+} \ No newline at end of file
diff --git a/project/mavlink-library/src/main/scala/com/github/jodersky/mavlink/parsing/parsing.scala b/project/mavlink-library/src/main/scala/com/github/jodersky/mavlink/parsing/parsing.scala
new file mode 100644
index 0000000..e14e0c3
--- /dev/null
+++ b/project/mavlink-library/src/main/scala/com/github/jodersky/mavlink/parsing/parsing.scala
@@ -0,0 +1,85 @@
+package com.github.jodersky.mavlink.parsing
+
+import scala.xml.Node
+import scala.util.Try
+import FieldTypes._
+import Parser._
+import Name._
+
+object Parser {
+ def fatal(message: String, xml: Node) = throw new RuntimeException("Cannot parse message definition: " + message + " " + xml)
+}
+
+case class Protocol(version: String, enums: Seq[Enum], messages: Seq[Message])
+object Protocol {
+ def parse(xml: Node) = {
+ val root = xml \\ "mavlink"
+ val version = (root \ "version").text
+ val enums = (root \ "enums" \ "_") map Enum.parse
+ val messages = (root \ "messages" \ "_") map Message.parse
+ Protocol(version, enums, messages)
+ }
+}
+
+case class Enum(name: String, entries: Seq[EnumEntry]) {
+ def scalaName = className(name)
+}
+object Enum {
+ def parse(xml: Node) = {
+ val name = (xml \ "@name").headOption.map(_.text).getOrElse(fatal("no name defined", xml))
+ val entries = (xml \ "entry") map EnumEntry.parse
+ Enum(name, entries)
+ }
+}
+
+case class EnumEntry(value: Int, name: String, description: String) {
+ def scalaName = className(name)
+}
+object EnumEntry {
+ def parse(xml: Node) = {
+ val valueString = (xml \ "@value").headOption.map(_.text).getOrElse(fatal("no value defined", xml))
+ val value = Try { Integer.parseInt(valueString) }.getOrElse(fatal("value must be an integer", xml))
+ val name = (xml \ "@name").headOption.map(_.text).getOrElse(fatal("no name defined", xml))
+ val description = (xml \ "description").text
+ EnumEntry(value, name, description)
+ }
+}
+
+case class Message(id: Byte, name: String, description: String, fields: Seq[Field]) {
+ def scalaName = className(name)
+ def orderedFields = fields.sortBy(_.tpe.width)(Ordering[Int].reverse)
+
+ lazy val checksum = {
+ var c = new Crc()
+ c = c.accumulate((name + " ").getBytes)
+ for (field <- orderedFields) {
+ c = c.accumulate((field.tpe.nativeType + " ").getBytes)
+ c = c.accumulate((field.name + " ").getBytes)
+ }
+ (c.lsb ^ c.msb).toByte
+ }
+}
+object Message {
+ def parse(xml: Node): Message = {
+ val idString = (xml \ "@id").headOption.map(_.text).getOrElse(fatal("no id defined", xml))
+ val id = Try { Integer.parseInt(idString).toByte }.getOrElse(fatal("id must be an integer", xml))
+ val name = (xml \ "@name").headOption.map(_.text).getOrElse(fatal("no name defined", xml))
+ val description = (xml \ "description").text
+ val fields = (xml \ "field") map Field.parse
+ Message(id, name, description, fields)
+ }
+}
+
+case class Field(tpe: Type, name: String, enum: Option[String], description: String) {
+ def scalaName = varName(name)
+}
+object Field {
+ def parse(xml: Node): Field = {
+ val tpeNode = (xml \ "@type").headOption.getOrElse(fatal("no type defined", xml))
+ val tpe = FieldTypes.parse(tpeNode)
+ val name = (xml \ "@name").headOption.map(_.text).getOrElse(fatal("no name defined", xml))
+ val enum = (xml \ "@enum").headOption.map(_.text)
+ val description = (xml).text
+ Field(tpe, name, enum, description)
+ }
+} \ No newline at end of file
diff --git a/project/mavlink-library/src/main/twirl/org/mavlink/Crc.scala.txt b/project/mavlink-library/src/main/twirl/org/mavlink/Crc.scala.txt
new file mode 100644
index 0000000..61b5c6f
--- /dev/null
+++ b/project/mavlink-library/src/main/twirl/org/mavlink/Crc.scala.txt
@@ -0,0 +1,23 @@
+@()
+
+package org.mavlink
+
+/**
+ * X.25 CRC calculation for MAVlink messages. The checksum must be initialized,
+ * updated with witch field of the message, and then finished with the message
+ * id.
+ */
+case class Crc(val crc: Int = 0xffff) extends AnyVal {
+
+ def accumulate(datum: Byte): Crc = {
+ val d = datum & 0xff
+ var tmp = d ^ (crc & 0xff)
+ tmp ^= (tmp << 4) & 0xff;
+ Crc(
+ ((crc >> 8) & 0xff) ^ (tmp << 8) ^ (tmp << 3) ^ ((tmp >> 4) & 0xff))
+ }
+
+ def lsb: Byte = crc.toByte
+ def msb: Byte = (crc >> 8).toByte
+
+} \ No newline at end of file
diff --git a/project/mavlink-library/src/main/twirl/org/mavlink/Packet.scala.txt b/project/mavlink-library/src/main/twirl/org/mavlink/Packet.scala.txt
new file mode 100644
index 0000000..6f96b40
--- /dev/null
+++ b/project/mavlink-library/src/main/twirl/org/mavlink/Packet.scala.txt
@@ -0,0 +1,59 @@
+@(messages: Seq[parsing.Message])
+
+@messageCrcs() = @{
+ for (i <- 0 to 255) yield {
+ val crc = messages.find(_.id == i).map(_.checksum).getOrElse(0)
+ crc.toString
+ }
+}
+
+package org.mavlink
+
+case class Packet(
+ seq: Byte,
+ systemId: Byte,
+ componentId: Byte,
+ messageId: Byte,
+ payload: Seq[Byte]
+) {
+
+ lazy val crc = {
+ var c = new Crc()
+ c = c.accumulate(payload.length.toByte)
+ c = c.accumulate(seq)
+ c = c.accumulate(systemId)
+ c = c.accumulate(componentId)
+ c = c.accumulate(messageId)
+ for (p <- payload) {
+ c = c.accumulate(p)
+ }
+ c = c.accumulate(Packet.extraCrc(messageId))
+ c
+ }
+
+ def toSeq: Seq[Byte] = Seq(
+ Packet.Stx,
+ payload.length.toByte,
+ seq,
+ systemId,
+ componentId,
+ messageId
+ ) ++ payload ++ Seq(
+ crc.lsb,
+ crc.msb
+ )
+
+}
+
+object Packet {
+
+ final val Stx: Byte = (0xfe).toByte
+ final val MaxPayloadLength: Int = @messages.map(_.fields.map(_.tpe.width).sum).max
+
+ final val ExtraCrcs: Seq[Byte] = @messageCrcs().mkString("Array[Byte](", ", ", ")")
+
+ def extraCrc(id: Byte) = ExtraCrcs(id & 0xff)
+
+ final val Empty = Packet(0, 0, 0, -1, Array(0: Byte))
+
+} \ No newline at end of file
diff --git a/project/mavlink-library/src/main/twirl/org/mavlink/Parser.scala.txt b/project/mavlink-library/src/main/twirl/org/mavlink/Parser.scala.txt
new file mode 100644
index 0000000..f0e30dc
--- /dev/null
+++ b/project/mavlink-library/src/main/twirl/org/mavlink/Parser.scala.txt
@@ -0,0 +1,128 @@
+@()
+
+package org.mavlink
+
+import scala.collection.mutable.ArrayBuffer
+
+object Parser {
+
+ object ParseStates {
+ sealed trait State
+ case object Idle extends State
+ case object GotStx extends State
+ case object GotLength extends State
+ case object GotSeq extends State
+ case object GotSysId extends State
+ case object GotCompId extends State
+ case object GotMsgId extends State
+ case object GotCrc1 extends State
+ case object GotPayload extends State
+ }
+
+ object ParseErrors {
+ sealed trait ParseError
+ case object CrcError extends ParseError
+ case object OverflowError extends ParseError
+ }
+}
+
+class Parser(receiver: Packet => Unit, error: Parser.ParseErrors.ParseError => Unit = _ => ()) {
+ import Parser._
+
+ private var state: ParseStates.State = ParseStates.Idle
+
+ private object inbound {
+ var length: Int = 0
+ var seq: Byte = 0
+ var systemId: Byte = 0
+ var componentId: Byte = 0
+ var messageId: Byte = 0
+ var payload = new ArrayBuffer[Byte]
+ var crc: Crc = new Crc()
+ }
+
+ def push(c: Byte): Unit = {
+ import ParseStates._
+
+ state match {
+ case Idle =>
+ if (c == Packet.Stx) {
+ state = GotStx
+ }
+
+ case GotStx =>
+ inbound.crc = new Crc()
+ inbound.length = (c & 0xff)
+ inbound.crc = inbound.crc.accumulate(c)
+ state = GotLength
+
+ case GotLength =>
+ inbound.seq = c;
+ inbound.crc = inbound.crc.accumulate(c)
+ state = GotSeq
+
+ case GotSeq =>
+ inbound.systemId = c
+ inbound.crc = inbound.crc.accumulate(c)
+ state = GotSysId
+
+ case GotSysId =>
+ inbound.componentId = c
+ inbound.crc = inbound.crc.accumulate(c)
+ state = GotCompId
+
+ case GotCompId =>
+ inbound.messageId = c
+ inbound.crc = inbound.crc.accumulate(c)
+ if (inbound.length == 0) {
+ state = GotPayload
+ } else {
+ state = GotMsgId
+ inbound.payload.clear()
+ }
+
+ case GotMsgId =>
+ inbound.payload += c
+ inbound.crc = inbound.crc.accumulate(c)
+ if(inbound.payload.length >= Packet.MaxPayloadLength) {
+ state = Idle
+ error(ParseErrors.OverflowError)
+ }
+ if (inbound.payload.length >= inbound.length) {
+ state = GotPayload
+ }
+
+ case GotPayload =>
+ inbound.crc = inbound.crc.accumulate(Packet.extraCrc(inbound.messageId))
+ if (c != inbound.crc.lsb) {
+ state = Idle
+ if (c == Packet.Stx) {
+ state = GotStx
+ }
+ error(ParseErrors.CrcError)
+ } else {
+ state = GotCrc1
+ }
+
+ case GotCrc1 =>
+ if (c != inbound.crc.msb) {
+ state = Idle
+ if (c == Packet.Stx) {
+ state = GotStx
+ }
+ error(ParseErrors.CrcError)
+ } else {
+ val packet = Packet(
+ inbound.seq,
+ inbound.systemId,
+ inbound.componentId,
+ inbound.messageId,
+ inbound.payload)
+ state = Idle
+ inbound.payload = new ArrayBuffer[Byte]()
+ receiver(packet)
+ }
+ }
+ }
+
+}
diff --git a/project/mavlink-library/src/main/twirl/org/mavlink/_header.scala.txt b/project/mavlink-library/src/main/twirl/org/mavlink/_header.scala.txt
new file mode 100644
index 0000000..77ad6d2
--- /dev/null
+++ b/project/mavlink-library/src/main/twirl/org/mavlink/_header.scala.txt
@@ -0,0 +1,5 @@
+@()
+
+/**
+ * This file has been machine generated.
+ */ \ No newline at end of file
diff --git a/project/mavlink-library/src/main/twirl/org/mavlink/messages/Message.scala.txt b/project/mavlink-library/src/main/twirl/org/mavlink/messages/Message.scala.txt
new file mode 100644
index 0000000..1507df4
--- /dev/null
+++ b/project/mavlink-library/src/main/twirl/org/mavlink/messages/Message.scala.txt
@@ -0,0 +1,70 @@
+@(messages: Seq[parsing.Message])
+
+@extract(field: parsing.Field, offset: Int) = {
+ @field.tpe.width match {
+ case 1 => {payload(@offset)}
+ case 2 => {((payload(@offset)) | ((payload(@{offset+1}) & 0xff) << 8)).toShort}
+ case 4 => {(payload(@offset)) | ((payload(@{offset+1}) & 0xff) << 8) | ((payload(@{offset+2}) & 0xff) << 16) | ((payload(@{offset+3}) & 0xff) << 24)}
+ case 8 => {payload(@offset) |
+ ((payload(@{offset+2}) & 0xffl) << 8) |
+ ((payload(@{offset+3}) & 0xffl) << 16) |
+ ((payload(@{offset+4}) & 0xffl) << 24) |
+ ((payload(@{offset+5}) & 0xffl) << 32) |
+ ((payload(@{offset+6}) & 0xffl) << 40) |
+ ((payload(@{offset+7}) & 0xffl) << 48) |
+ ((payload(@{offset+8}) & 0xffl) << 56)}
+ }
+}
+
+@insert(field: parsing.Field, offset: Int) = {
+ @defining("m." + field.scalaName) { attr =>
+ @if(field.tpe.width >= 1) { arr(@offset) = (@attr & 0xff).toByte }
+ @if(field.tpe.width >= 2) { arr(@{offset+1}) = ((@attr >> 8) & 0xff).toByte }
+ @if(field.tpe.width >= 3) { arr(@{offset+2}) = ((@attr >> 16) & 0xff).toByte }
+ @if(field.tpe.width >= 4) { arr(@{offset+3}) = ((@attr >> 24) & 0xff).toByte }
+ @if(field.tpe.width >= 5) { arr(@{offset+4}) = ((@attr >> 32) & 0xff).toByte }
+ @if(field.tpe.width >= 6) { arr(@{offset+5}) = ((@attr >> 40) & 0xff).toByte }
+ @if(field.tpe.width >= 7) { arr(@{offset+6}) = ((@attr >> 48) & 0xff).toByte }
+ @if(field.tpe.width >= 8) { arr(@{offset+7}) = ((@attr >> 56) & 0xff).toByte }
+ }
+}
+
+@org.mavlink.txt._header()
+
+package org.mavlink.messages
+
+sealed trait Message
+@for(message <- messages) {
+ @_message_class(message)
+}
+case class Unknown(id: Byte, payload: Seq[Byte]) extends Message
+
+object Message {
+ def unpack(id: Byte, payload: Seq[Byte]) = id match {
+ @for(message <- messages) {
+ case @message.id =>
+ @defining(message.orderedFields){ ordered =>
+ @defining(ordered.map(_.tpe.width).scanLeft(0)(_ + _)){ offsets =>
+ @for((field, offset) <- ordered zip offsets) {
+ val @field.scalaName: @field.tpe.scalaType = @extract(field, offset)
+ }}}
+ @{message.scalaName}@message.fields.map(_.scalaName).mkString("(", ", ", ")")
+ }
+
+ case id => Unknown(id, payload)
+ }
+
+ def pack(message: Message): (Byte, Seq[Byte]) = message match {
+ @for(message <- messages) {
+ case m: @message.scalaName =>
+ val arr = new Array[Byte](@message.fields.map(_.tpe.width).sum)
+ @defining(message.orderedFields) { ordered =>
+ @defining(ordered.map(_.tpe.width).scanLeft(0)(_ + _)){ offsets =>
+ @for((field, offset) <- ordered zip offsets) {
+ @insert(field, offset)
+ }
+ (@message.id, arr)
+ }}}
+ case u: Unknown => (u.id, u.payload)
+ }
+} \ No newline at end of file
diff --git a/project/mavlink-library/src/main/twirl/org/mavlink/messages/_message_class.scala.txt b/project/mavlink-library/src/main/twirl/org/mavlink/messages/_message_class.scala.txt
new file mode 100644
index 0000000..78c8326
--- /dev/null
+++ b/project/mavlink-library/src/main/twirl/org/mavlink/messages/_message_class.scala.txt
@@ -0,0 +1,14 @@
+@(message: parsing.Message)
+
+@arguments(fields: Seq[parsing.Field]) = @{
+ fields.map(f => f.scalaName + ": " + f.tpe.scalaType)
+}
+
+@comment(paragraphs: Seq[String]) = @{
+ paragraphs.mkString("/**\n * ", "\n * ", "\n */")
+}
+
+@defining(message.description.grouped(120).toSeq){intro =>
+@defining(message.fields.map(field => "@param " + field.name + " " + field.description)){ fields =>
+@comment(intro ++ fields)}}
+case class @{message.scalaName}@arguments(message.fields).mkString("(", ", ", ")") extends Message \ No newline at end of file
diff --git a/project/mavlink-plugin/src/main/scala/com/github/jodersky/sbt/SbtMavlink.scala b/project/mavlink-plugin/src/main/scala/com/github/jodersky/sbt/SbtMavlink.scala
new file mode 100644
index 0000000..58692cd
--- /dev/null
+++ b/project/mavlink-plugin/src/main/scala/com/github/jodersky/sbt/SbtMavlink.scala
@@ -0,0 +1,24 @@
+package com.github.jodersky.sbt
+
+import sbt._
+import Keys._
+import plugins._
+import mavlink.MavlinkKeys._
+import sbt.Project.Initialize
+import com.github.jodersky.mavlink.Main
+
+object SbtMavlink extends AutoPlugin {
+
+ override def requires = JvmPlugin
+
+ lazy val generate = Def.task[Seq[File]] {
+ streams.value.log.info("Generating mavlink files...")
+ Main.run((mavlinkDialect in Compile).value, (mavlinkTarget in Compile).value).map(_.getAbsoluteFile)
+ }
+
+ override val projectSettings: Seq[Setting[_]] = Seq(
+ mavlinkTarget in Compile := (sourceManaged in Compile).value,
+ mavlinkGenerate in Compile := generate.value,
+ sourceGenerators in Compile += (mavlinkGenerate in Compile).taskValue
+ )
+}
diff --git a/project/mavlink-plugin/src/main/scala/com/github/jodersky/sbt/mavlink/MavlinkKeys.scala b/project/mavlink-plugin/src/main/scala/com/github/jodersky/sbt/mavlink/MavlinkKeys.scala
new file mode 100644
index 0000000..5cbffd4
--- /dev/null
+++ b/project/mavlink-plugin/src/main/scala/com/github/jodersky/sbt/mavlink/MavlinkKeys.scala
@@ -0,0 +1,14 @@
+package com.github.jodersky.sbt
+package mavlink
+
+import sbt._
+import sbt.Keys._
+import java.io.File
+
+object MavlinkKeys {
+
+ lazy val mavlinkDialect = settingKey[File]("Dialect definition from which to generate files.")
+ lazy val mavlinkTarget = settingKey[File]("Target directory to contain all generated mavlink files.")
+ lazy val mavlinkGenerate = taskKey[Seq[File]]("Generate mavlink files.")
+
+} \ No newline at end of file
diff --git a/project/plugins.sbt b/project/plugins.sbt
deleted file mode 100644
index 2c768c0..0000000
--- a/project/plugins.sbt
+++ /dev/null
@@ -1,7 +0,0 @@
-resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/"
-
-// The Play plugin
-addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.3.4")
-
-// Scala.js plugin
-addSbtPlugin("org.scala-lang.modules.scalajs" % "scalajs-sbt-plugin" % "0.5.5") \ No newline at end of file
diff --git a/project/project/BuildBuild.scala b/project/project/BuildBuild.scala
new file mode 100644
index 0000000..201507e
--- /dev/null
+++ b/project/project/BuildBuild.scala
@@ -0,0 +1,35 @@
+import sbt._
+import sbt.Keys._
+import play.twirl.sbt.SbtTwirl
+import play.twirl.sbt.Import._
+
+object BuildBuild extends Build {
+
+ lazy val root = (
+ Project("root", file("."))
+ settings(
+ addSbtPlugin("com.typesafe.play" %% "sbt-plugin" % "2.3.4"),
+ addSbtPlugin("org.scala-lang.modules.scalajs" % "scalajs-sbt-plugin" % "0.5.5")
+ )
+ dependsOn(mavlinkPlugin)
+ )
+
+ lazy val mavlinkLibrary = (
+ Project("mavlink-library", file("mavlink-library"))
+ enablePlugins(SbtTwirl)
+ settings(
+ libraryDependencies += "com.github.scala-incubator.io" %% "scala-io-file" % "0.4.2",
+ TwirlKeys.templateImports += "com.github.jodersky.mavlink._"
+ )
+ )
+
+ lazy val mavlinkPlugin = (
+ Project("mavlink-plugin", file("mavlink-plugin"))
+ settings(
+ sbtPlugin := true
+ )
+ dependsOn(mavlinkLibrary)
+ )
+
+}
+
diff --git a/project/project/plugins.sbt b/project/project/plugins.sbt
new file mode 100644
index 0000000..7b458b6
--- /dev/null
+++ b/project/project/plugins.sbt
@@ -0,0 +1 @@
+addSbtPlugin("com.typesafe.sbt" % "sbt-twirl" % "1.0.4") \ No newline at end of file