Thursday, May 6, 2010

Read/Write binary data with Scala

In my project to communicate between my notebook and an Arduino over XBees I need to send binary commands to the XBee. The protocol is defined approx. like this: every packet start with 0x7E and then two bytes containing the length of the packed then the payload and afterwards a checksum.
So I decided to write a little library that allows me to parse and compose these commands in a natural way. The parsing should use pattern matching.

To define a structure use something like this:

val myPattern = <<(byte, integer, integer, byte)>>

('<<' is a imported function, the '>>' is just decoration, that returns itself)

then you can use

val bytes: Seq[Byte] = myPattern(12, 33321, 24432, 0)

to generate a binary representation (i.e 0x12, 0x00, 0x00,  0x82, 0x29, ...). To a byte sequence use

bytes match {
case myPattern((a,b,c,d), rest) =>
a should be(12)
b should be(33321)
c should be(24432)
d should be(0)
rest should be(Nil)
}

The variable "Rest" in the example will contain everything in the Seq[Byte] after the matched sequence.


It also allows to check for fixed values (in the example above 0x7E in the first byte):

val myPattern2 = <<(fix_byte(0x7E), integer, integer, byte) drop1
myPattern2(1234, 4321, 12) //will be 0x7E, 0x00, 0x00, 0x04, 0xD2


And you can map a pattern to some class:

case class Color(red: Byte, green: Byte, blue: Byte)

val rgbPattern = <<(byte, byte, byte)>>>(
(c: Color) => (color.red, color.green, color.blue),
t => Some(Color(t._1, t._2, t._3))
)

But what makes it really powerful is the ability to nest those patterns

val colorfulPattern = <<(fix_byte(0x7E), rgbPattern, rgbPattern, integer) drop1
val data = (0x7E :: 33 :: 21 :: 11 :: 0 :: 100 :: 12 :: 0 :: 0 :: 0 :: 4 :: Nil).map(_.toByte)
data match {
case colorfulPattern((foregroundColor, backgroundColor, pixelsToFill), _) =>
foregroundColor should be(Color(33,21,11))
backgroundColor should be(Color(0, 100, 12))
pixelsToFill should be(4)
}

so it's pretty easy to define complex structures pretty fast, easy and reusable. The framework also provides possibilities to parse/serialize lists (even of "complex" objects) and optional values.


Just check out the code a GitHub and feel free to use it and contribute to it!

No comments:

Post a Comment