From b36e11d9567d6abf0d90f4fa97767c241258331a Mon Sep 17 00:00:00 2001 From: Yves Fischer Date: Sun, 18 Dec 2011 22:59:14 +0100 Subject: scala: refactor for L7 Parsing --- ebus-scala/Main.scala | 273 ++++++++++++++++++++++++++++---------------------- 1 file changed, 156 insertions(+), 117 deletions(-) (limited to 'ebus-scala/Main.scala') diff --git a/ebus-scala/Main.scala b/ebus-scala/Main.scala index b329ce4..63ec98e 100755 --- a/ebus-scala/Main.scala +++ b/ebus-scala/Main.scala @@ -2,29 +2,71 @@ exec scala "$0" "$@" !# import Stream._ -import java.io.{ InputStream, EOFException } -import java.net.{ InetAddress, InetSocketAddress, Socket } -import scala.xml.{XML, Node, NodeSeq} - -class EbusDefinition(val ebusXmlFile: String) { - val xml = XML.loadFile(ebusXmlFile) - - def packetFromCommandId(primaryCommand: Int, secondaryCommand: Int): NodeSeq = { - val packet = (xml \ "packets" \ "packet").filter(n => - n.attribute("primary").get.text.toInt == primaryCommand && - n.attribute("secondary").get.text.toInt == secondaryCommand) - packet +import java.io.{InputStream, EOFException} +import java.net.{InetAddress, InetSocketAddress, Socket} +import scala.xml.{XML, Elem, Node, NodeSeq} + +/// Ebus Bus Definition +object EbusDevice { + def apply(node : Node): EbusDevice = { + new EbusDevice( + Integer.parseInt(node.attribute("address").head.text, 16), + node.attribute("type").head.text, + node.attribute("name").head.text) + } +} +class EbusDevice(val address:Int, val busType :String, + val name: String) { + override def toString() : String = "EbusDevice: address=0x%02x type=%s name=%s".format( + address, busType, name) +} + +object EbusPacket { + def apply(node : Node): EbusPacket = { + new EbusPacket( + EbusDefinition.parseHexNode(node.attribute("primary").get.head), + EbusDefinition.parseHexNode(node.attribute("secondary").get.head), + node.attribute("name").get.head.text, + List[EbusField]()) + } +} + +class EbusPacket(val primary: Int, val secondary: Int, + val name: String, val fields: List[EbusField]) { + override def toString(): String = { + "EbusPacket: primary=0x%02x secondary=0x%02x name=%s".format( + primary, secondary, name) + } +}; + +object EbusDefinition { + def parseHexNode(n: Node):Int = Integer.parseInt(n.text,16) + def fromFile(ebusXMLPath : String) = { + new EbusDefinition(XML.loadFile(ebusXMLPath)) + } +} +class EbusDefinition(val xml : Elem) { + def packet(primaryCommand: Int, secondaryCommand: Int): Option[EbusPacket] = { + (xml \ "packets" \ "packet").filter(n => + n.attribute("primary").flatMap(_.headOption.map(EbusDefinition.parseHexNode)) == Some(primaryCommand) && + n.attribute("secondary").flatMap(_.headOption.map(EbusDefinition.parseHexNode)) == Some(secondaryCommand) + ).headOption.map(EbusPacket(_)) + } + def device(address: Int): Option[EbusDevice] = { + (xml \ "devices" \ "device").filter(n => + n.attribute("address").flatMap(_.headOption.map(EbusDefinition.parseHexNode)) == Some(address) + ).headOption.map(EbusDevice(_)) } } abstract class EbusField(val name : String, val offset : Int); -class Data2c(override val name: String, override val offset : Int) extends EbusField(name, offset) { +class Data2c(name: String, offset : Int) extends EbusField(name, offset) { } object EbusField { - def getFields(ebusDefinition : EbusDefinition, primaryCommand: Char, secondaryCommand: Char): List[EbusField] = { - val packetDef = ebusDefinition.packetFromCommandId(primaryCommand, secondaryCommand) +/* def getFields(ebusDefinition : EbusDefinition, primaryCommand: Char, secondaryCommand: Char): List[EbusField] = { + val packetDef = ebusDefinition.packet(primaryCommand, secondaryCommand) for (fieldDef <- (packetDef \ "fields").head.child) { App.println(buildField(fieldDef)) } @@ -37,54 +79,15 @@ object EbusField { return Some(new Data2c(name, offset)) } return None - } + }*/ } - -class EbusL2Packet(val source: Int, - val destination: Int, - val primaryCommand: Int, - val secondaryCommand: Int, - val payloadLength: Int, - val payload: List[Int], - val crc: Int, - val rawLength: Int) { - override def toString: String = { - val p = if (payload.size == 0) - "" - else - payload.map(a => "%02x".format(a.toInt)). - reduceLeft((a: Any, b: String) => a + " " + b) - - "L2Packet: d=%02x pC=%02x sC=%02x pL=%02x crc=%02x p=%s rawL=%d".format( - destination.toInt, primaryCommand.toInt, secondaryCommand.toInt, payloadLength.toInt, - crc.toInt, p.toString, rawLength) - } -} - -class EbusL2MasterSlavePacket(source: Int, - destination: Int, - primaryCommand: Int, - secondaryCommand: Int, - payloadLength: Int, - payload: List[Int], - crc: Int, - val payloadSlaveLength : Int, - val payloadSlave : List[Int], - val crcSlave : Int, - rawLength: Int) extends EbusL2Packet(source, - destination, - primaryCommand, - secondaryCommand, - payloadLength, - payload, - crc, - rawLength); - -class EbusParseException(val message : String, val data : List[Int], val skipbytes : Int) extends Exception; - -object CRC { +/// Ebus Protocol Parser +object Ebus { + def SYN = 0xaa + def ACK_OK = 0x00 + def ACK_FAIL = 0xff val crcTab = List[Int]( 0, 155, 173, 54, 193, 90, 108, 247, 25, 130, //0 -9 180, 47, 216, 67, 117, 238, 50, 169, 159, 4, //10-19 @@ -113,19 +116,63 @@ object CRC { 149, 14, 56, 163, 84, 207, 249, 98, 140, 23, 33, 186, 77, 214, 224, 123) - def apply(data : List[Int]) : Int = { - // initialCrc=0 - return data.foldLeft(0){ (crc,byte) => crcTab(crc) ^ byte} + def crc(data: List[Int]): Int = { + data.foldLeft(0){ (crc,byte) => crcTab(crc) ^ byte} } } +// TODO buildFrom(List(,,,)) +class EbusL2Header(val source: Int, val destination: Int, + val primaryCommand: Int, val secondaryCommand: Int, + val payloadLength: Int) { + override def toString: String = { + "L2Header: QQ=0x%02x ZZ=0x%02x PB=0x%02x SB=0x%02x NN=0x%02x".format( + source, destination, primaryCommand, secondaryCommand, payloadLength) + } +}; -object Ebus { - def SYN = 0xaa - def ACK_OK = 0x00 - def ACK_FAIL = 0xff +abstract class EbusL2Packet(val l2Header : EbusL2Header, + val payload: List[Int], + val crc: Int, + val rawLength: Int) { + override def toString: String = { + val p = payload.map(a => "%02x".format(a.toInt)).reduceLeft((a: Any, b: String) => a + " " + b) + "L2Packet: (%s) payload=%s CRC=0x%02x rawLength=%d".format( + l2Header.toString, p, crc, rawLength) + } } +class EbusL2MasterMasterPacket(l2Header : EbusL2Header, + payload: List[Int], + crc: Int, + rawLength : Int) extends EbusL2Packet(l2Header, + payload, + crc, + rawLength); + +class EbusL2BroadcastPacket(l2Header : EbusL2Header, + payload: List[Int], + crc: Int, + rawLength : Int) extends EbusL2Packet(l2Header, + payload, + crc, + rawLength); + +class EbusL2MasterSlavePacket(l2Header: EbusL2Header, + payload: List[Int], + crc: Int, + val payloadSlaveLength: Int, + val payloadSlave: List[Int], + val crcSlave: Int, + rawLength: Int) extends EbusL2Packet(l2Header, + payload, + crc, + rawLength); + +class EbusL2ParseException(val message: String, + val data: List[Int], + val skipbytes: Int) extends Exception; + object EbusL2Packet { def PAYLOAD_LENGTH_MAX = 16 /** @@ -152,7 +199,7 @@ object EbusL2Packet { case 0x00 => 0xa9 case 0x01 => 0xaa case code : Int => { - throw new EbusParseException("Invalid escape sequence (0xa9 0x%02x)".format(code), + throw new EbusL2ParseException("Invalid escape sequence (0xa9 0x%02x)".format(code), stream.slice(0, rawLength).toList, rawLength); } } @@ -161,23 +208,24 @@ object EbusL2Packet { } val source :: destination :: primaryCommand :: secondaryCommand :: payloadLength :: _ = readBytes(5, "QQ|ZZ|PB|SB|NN") + val l2Header = new EbusL2Header(source, destination, primaryCommand, secondaryCommand, payloadLength) // Maximale payload Grösse = 16 Byte if (payloadLength > PAYLOAD_LENGTH_MAX) { - throw new EbusParseException("Erkannte payload Groesse zu gross: %d".format(payloadLength), + throw new EbusL2ParseException("Erkannte payload Groesse zu gross: %d".format(payloadLength), stream.slice(0, rawLength).toList, rawLength); } else if (payloadLength == 0) { // Unklar - throw new EbusParseException("Skip packet with payloadLength = 0", + throw new EbusL2ParseException("Skip packet with payloadLength = 0", stream.slice(0, rawLength).toList, rawLength + 1); } val payload = Range(0,payloadLength).map(readPayloadByte).toList val crc = readBytes(1,"CRC")(0) - val crcCalc = CRC(List(source, destination, primaryCommand, secondaryCommand, payloadLength) ++ payload) + val crcCalc = Ebus.crc(List(source, destination, primaryCommand, secondaryCommand, payloadLength) ++ payload) if (crc != crcCalc) { - throw new EbusParseException("CRC mismatch (read) 0x%02x != 0x%02x (calc)".format(crc, crcCalc), + throw new EbusL2ParseException("CRC mismatch (read) 0x%02x != 0x%02x (calc)".format(crc, crcCalc), stream.slice(0, rawLength).toList, rawLength + 1); } @@ -185,12 +233,10 @@ object EbusL2Packet { if (destination == 0xfe) { val syn = readBytes(1,"SYN(broadcast)")(0) if (syn != Ebus.SYN) { - throw new EbusParseException("Bad SYN: 0x%02x".format(syn), + throw new EbusL2ParseException("Bad SYN: 0x%02x".format(syn), stream.slice(0, rawLength).toList, rawLength); } - return new EbusL2Packet(source, destination, - primaryCommand, secondaryCommand, - payloadLength, payload, crc, rawLength) + return new EbusL2MasterMasterPacket(l2Header, payload, crc, rawLength) } val ack = readBytes(1,"ACK")(0) @@ -199,52 +245,47 @@ object EbusL2Packet { // Master-Master Packet ends here if (syn == Ebus.SYN) { if (ack != Ebus.ACK_OK) { - throw new EbusParseException("Bad ACK: 0x%02x".format(ack), + throw new EbusL2ParseException("Bad ACK: 0x%02x".format(ack), stream.slice(0, rawLength).toList, rawLength); } - return new EbusL2Packet(source, destination, - primaryCommand, secondaryCommand, - payloadLength, payload, crc, rawLength) + return new EbusL2MasterMasterPacket(l2Header, payload, crc, rawLength) } // otherwise this is a Master-Slave Telegramm // and syn is payloadSlave length val payloadSlaveLength = syn if (payloadSlaveLength > PAYLOAD_LENGTH_MAX) { - throw new EbusParseException("payloadSlaveLength > 16: 0x%02x".format(payloadSlaveLength), + throw new EbusL2ParseException("payloadSlaveLength > 16: 0x%02x".format(payloadSlaveLength), stream.slice(0, rawLength).toList, rawLength); } val payloadSlave = Range(0,payloadSlaveLength).map(readPayloadByte).toList val crcSlave = readBytes(1,"crcSlave")(0) - val crcSlaveCalc = CRC(List(payloadSlaveLength) ++ payloadSlave) + val crcSlaveCalc = Ebus.crc(List(payloadSlaveLength) ++ payloadSlave) if (crcSlave != crcSlaveCalc) { - throw new EbusParseException("CRC mismatch (read) 0x%02x != 0x%02x (calc)".format(crcSlave, crcSlaveCalc), + throw new EbusL2ParseException("CRC mismatch (read) 0x%02x != 0x%02x (calc)".format(crcSlave, crcSlaveCalc), stream.slice(0, rawLength).toList, rawLength); } val ackSlave = readBytes(1,"ACKslave")(0) if (ackSlave != Ebus.ACK_OK) { - throw new EbusParseException("Master-Slave ACK nicht 0x00 sondern 0x%02x".format(ackSlave), + throw new EbusL2ParseException("Master-Slave ACK nicht 0x00 sondern 0x%02x".format(ackSlave), stream.slice(0, rawLength).toList, rawLength); } val synSlave= readBytes(1,"SYNslave")(0) if (synSlave != Ebus.SYN) { - throw new EbusParseException("Master-Slave SYN nicht 0xaa sondern 0x%02x".format(synSlave), + throw new EbusL2ParseException("Master-Slave SYN nicht 0xaa sondern 0x%02x".format(synSlave), stream.slice(0, rawLength).toList, rawLength); } - return new EbusL2MasterSlavePacket(source, destination, - primaryCommand, secondaryCommand, - payloadLength, payload, crc, - payloadSlaveLength, payloadSlave, crcSlave, rawLength); + return new EbusL2MasterSlavePacket(l2Header, payload, crc, + payloadSlaveLength, payloadSlave, + crcSlave, rawLength); } } - - class EbusReader(var stream : Stream[Int]) { def next() : EbusL2Packet = { while (true) { @@ -256,7 +297,7 @@ class EbusReader(var stream : Stream[Int]) { stream = stream.drop(packet.rawLength) return packet } catch { - case exc : EbusParseException => { + case exc : EbusL2ParseException => { stream = stream.drop(exc.skipbytes) App.println("Exception: " + exc.message + "\n\tData: " + exc.data.map((it) => "0x%02x".format(it)).reduceRight((s1,s2) => s1+","+s2)) @@ -267,29 +308,18 @@ class EbusReader(var stream : Stream[Int]) { } } -// object EbusL7Packet { -// def apply(l2packet : EbusL2Packet, ebusDefinition : EbusDefinition) : Option[EbusL7Packet] = { -// val packetDef = ebusDefinition.packetFromCommandId( -// l2packet.primaryCommand, l2packet.secondaryCommand) - -// if (packetDef.isEmpty) -// return None -// else -// return Some(new EbusL7Packet(l2packet, packetDef)) -// } -// } -// class EbusL7Packet(val l2packet : EbusL2Packet, val packetDefinition : NodeSeq) { -// def name : String = { -// packetDefinition.map({p :Node => p.attributes.apply("name")}).mkString -// } - -// override def toString : String = { -// "EbusL7: name=%s".format(name) -// } -// } +abstract class EbusL7Packet(val l2Packet: EbusL2Packet) {}; + +class EbusL7MasterMasterPacket(l2Packet: EbusL2MasterMasterPacket) extends EbusL7Packet(l2Packet) {}; +class EbusL7MasterSlavePacket(l2Packet: EbusL2MasterSlavePacket) extends EbusL7Packet(l2Packet) {}; + +class EbusL7BroadcastPacket(l2Packet: EbusL2BroadcastPacket) extends EbusL7Packet(l2Packet) {}; + + +/// Application object App { - val ebusDefinition = new EbusDefinition("/home/yvesf/vcs/ebus/ebus/ebus-xml/ebus.xml") + val ebusDefinition = EbusDefinition.fromFile("/home/yvesf/vcs/ebus/ebus/ebus-xml/ebus.xml") var source: InputStream = null def println(msg : Any) { @@ -324,7 +354,8 @@ object App { System.out.write(v) } } - case _ => println( + case _ => { + println( """Usage: PROGRAM ARGUMENTS: - @@ -334,6 +365,8 @@ dump HOST PORT HOST PORT Parse Ebus from TCP Connection """) + exit(1) + } } var stream: Stream[Int] = Stream.continually({ @@ -353,7 +386,15 @@ HOST PORT (() => { while (true) { try { - println(reader.next) + val packet = reader.next + val sourceDevice = ebusDefinition.device(packet.l2Header.source) + val destDevice = ebusDefinition.device(packet.l2Header.destination) + val packetDef = ebusDefinition.packet(packet.l2Header.primaryCommand, + packet.l2Header.secondaryCommand) + println(sourceDevice) + println(destDevice) + println(packetDef) + //println(packet) } catch { case exc : EOFException => { println("EOF") @@ -367,6 +408,4 @@ HOST PORT } } - - App.main(args) -- cgit v1.2.1