From 3ee2a9071f6226bdd797d0cafd61044c1f3a139e Mon Sep 17 00:00:00 2001 From: Yves Fischer Date: Sat, 24 Dec 2011 01:45:46 +0100 Subject: ebus-scala: restructered files --- ebus-scala/.settings/org.eclipse.jdt.core.prefs | 12 - ebus-scala/Main.scala | 407 --------------------- ebus-scala/run.sh | 4 - .../src/main/scala/org/xapek/ebus/Ebus.scala | 315 ++++++++++++++++ .../src/main/scala/org/xapek/ebus/Main.scala | 97 +++++ 5 files changed, 412 insertions(+), 423 deletions(-) delete mode 100644 ebus-scala/.settings/org.eclipse.jdt.core.prefs delete mode 100755 ebus-scala/Main.scala delete mode 100755 ebus-scala/run.sh create mode 100755 ebus-scala/src/main/scala/org/xapek/ebus/Ebus.scala create mode 100644 ebus-scala/src/main/scala/org/xapek/ebus/Main.scala (limited to 'ebus-scala') diff --git a/ebus-scala/.settings/org.eclipse.jdt.core.prefs b/ebus-scala/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index 33e19bd..0000000 --- a/ebus-scala/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,12 +0,0 @@ -#Sat Jul 02 17:29:58 CEST 2011 -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 -org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.6 -org.eclipse.jdt.core.compiler.debug.lineNumber=generate -org.eclipse.jdt.core.compiler.debug.localVariable=generate -org.eclipse.jdt.core.compiler.debug.sourceFile=generate -org.eclipse.jdt.core.compiler.problem.assertIdentifier=error -org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.source=1.6 diff --git a/ebus-scala/Main.scala b/ebus-scala/Main.scala deleted file mode 100755 index da54e71..0000000 --- a/ebus-scala/Main.scala +++ /dev/null @@ -1,407 +0,0 @@ -import Stream._ -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(name: String, offset : Int) extends EbusField(name, offset) { -} - -object EbusField { -/* 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)) - } - null - } - def buildField(field :Node) : Option[EbusField] = { - if (field.label == "data2c") { - val name = field.attribute("name").get.text - val offset = field.attribute("offset").get.text.toInt - return Some(new Data2c(name, offset)) - } - return None - }*/ -} - - -/// 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 - 243, 104, 94, 197, 43, 176, 134, 29, 234, 113, //20 - 71, 220, 100, 255, 201, 82, 165, 62, 8, 147, //30 - 125, 230, 208, 75, 188, 39, 17, 138, 86, 205, - 251, 96, 151, 12, 58, 161, 79, 212, 226, 121, - 142, 21, 35, 184, 200, 83, 101, 254, 9, 146, - 164, 63, 209, 74, 124, 231, 16, 139, 189, 38, - 250, 97, 87, 204, 59, 160, 150, 13, 227, 120, - 78, 213, 34, 185, 143, 20, 172, 55, 1, 154, - 109, 246, 192, 91, 181, 46, 24, 131, 116, 239, //100 - 217, 66, 158, 5, 51, 168, 95, 196, 242, 105, - 135, 28, 42, 177, 70, 221, 235, 112, 11, 144, - 166, 61, 202, 81, 103, 252, 18, 137, 191, 36, - 211, 72, 126, 229, 57, 162, 148, 15, 248, 99, - 85, 206, 32, 187, 141, 22, 225, 122, 76, 215, - 111, 244, 194, 89, 174, 53, 3, 152, 118, 237, - 219, 64, 183, 44, 26, 129, 93, 198, 240, 107, - 156, 7, 49, 170, 68, 223, 233, 114, 133, 30, - 40, 179, 195, 88, 110, 245, 2, 153, 175, 52, - 218, 65, 119, 236, 27, 128, 182, 45, 241, 106, //200 - 92, 199, 48, 171, 157, 6, 232, 115, 69, 222, - 41, 178, 132, 31, 167, 60, 10, 145, 102, 253, - 203, 80, 190, 37, 19, 136, 127, 228, 210, 73, - 149, 14, 56, 163, 84, 207, 249, 98, 140, 23, - 33, 186, 77, 214, 224, 123) - - 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) - } -}; - -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 - /** - * Wird von {@link EbusReader} mit einer Streamposition aufgerufen. - * Ausgehend von dieser Position wird versucht ein Paket aufzubauen, - * und anschliesend die Checksumme geprüft. - * Im Erfolgsfall wird eine EbusL2Packet-Instanz als Option zurückgegen, - * sonst None - */ - def apply(stream: Stream[Int]): EbusL2Packet = { - var mystream = stream - var rawLength = 0 - def readBytes(n:Int, msg : String) : List[Int] = { - val d = mystream.slice(rawLength,rawLength+n).toList - rawLength = rawLength + n - println("RAW %15s: ".format(msg) + d.map((it) => "0x%02x".format(it)).reduceRight((s1,s2) => s1+","+s2)) - return d - } - - def readPayloadByte(i:Int) : Int = - readBytes(1, "payload " + i)(0) match { - case 0xa9 => { - readBytes(1, "payload esc "+i)(0) match { - case 0x00 => 0xa9 - case 0x01 => 0xaa - case code : Int => { - throw new EbusL2ParseException("Invalid escape sequence (0xa9 0x%02x)".format(code), - stream.slice(0, rawLength).toList, rawLength); - } - } - } - case value : Int => value - } - - 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 EbusL2ParseException("Erkannte payload Groesse zu gross: %d".format(payloadLength), - stream.slice(0, rawLength).toList, rawLength); - } else if (payloadLength == 0) { - // Unklar - 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 = Ebus.crc(List(source, destination, primaryCommand, secondaryCommand, payloadLength) ++ payload) - if (crc != crcCalc) { - throw new EbusL2ParseException("CRC mismatch (read) 0x%02x != 0x%02x (calc)".format(crc, crcCalc), - stream.slice(0, rawLength).toList, rawLength + 1); - } - - // Broadcast Packet ends here - if (destination == 0xfe) { - val syn = readBytes(1,"SYN(broadcast)")(0) - if (syn != Ebus.SYN) { - throw new EbusL2ParseException("Bad SYN: 0x%02x".format(syn), - stream.slice(0, rawLength).toList, rawLength); - } - return new EbusL2MasterMasterPacket(l2Header, payload, crc, rawLength) - } - - val ack = readBytes(1,"ACK")(0) - - val syn = readBytes(1, "SYN/NN2")(0) - // Master-Master Packet ends here - if (syn == Ebus.SYN) { - if (ack != Ebus.ACK_OK) { - throw new EbusL2ParseException("Bad ACK: 0x%02x".format(ack), - stream.slice(0, rawLength).toList, 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 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 = Ebus.crc(List(payloadSlaveLength) ++ payloadSlave) - if (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 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 EbusL2ParseException("Master-Slave SYN nicht 0xaa sondern 0x%02x".format(synSlave), - stream.slice(0, rawLength).toList, rawLength); - } - - return new EbusL2MasterSlavePacket(l2Header, payload, crc, - payloadSlaveLength, payloadSlave, - crcSlave, rawLength); - } -} - -class EbusReader(var stream : Stream[Int]) { - def next() : EbusL2Packet = { - while (true) { - // Überspringe Synchronisationszeichen - stream = stream.dropWhile(_ == Ebus.SYN) - - try { - val packet = EbusL2Packet(stream) - stream = stream.drop(packet.rawLength) - return packet - } catch { - 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)) - } - } - } - return null; - } -} - -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 = EbusDefinition.fromFile("../ebus-xml/ebus.xml") - var source: InputStream = null - - def println(msg : Any) { - Predef.println("[ebus]\t" + msg.toString) - } - - def main(args: Array[String]): Unit = { - args.toList match { - case "-" :: List() => { - source = System.in - } - case host :: port :: List() => { - val addr = InetAddress.getByName(host) - val sockAddr = new InetSocketAddress(addr, port.toInt) - val s = new Socket() - s.connect(sockAddr) - println("Connected to %s %d".format(args(0), args(1).toInt)) - source = s.getInputStream - } - case "dump" :: host :: port :: List() => { - val addr = InetAddress.getByName(host) - val sockAddr = new InetSocketAddress(addr, port.toInt) - val s = new Socket() - s.connect(sockAddr) - System.err.println("Connected to %s %d".format(args(1), args(2).toInt)) - while (true) { - val v = s.getInputStream.read - if (v == -1) { - println("Fehler: read == -1") - System.exit(1) - } - System.out.write(v) - } - } - case _ => { - println( -"""Usage: PROGRAM -ARGUMENTS: -- - Parse Ebus from stdin -dump HOST PORT - Read and Dump TCP Connection -HOST PORT - Parse Ebus from TCP Connection -""") - exit(1) - } - } - - var stream: Stream[Int] = Stream.continually({ - val value = source.read - if (value == -1) - throw new EOFException("End of File reached (read returned -1)") - value - }) - - - // Synchronisiere - while (stream(0) != Ebus.SYN) { - stream = stream.drop(1) - } - - val reader = new EbusReader(stream) - (() => { - while (true) { - try { - 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") - return; - } - } - } - })() - println("exiting") - source.close - } -} - diff --git a/ebus-scala/run.sh b/ebus-scala/run.sh deleted file mode 100755 index 6b137eb..0000000 --- a/ebus-scala/run.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -exec scala "$0" "$@" -#! -App.main(args) diff --git a/ebus-scala/src/main/scala/org/xapek/ebus/Ebus.scala b/ebus-scala/src/main/scala/org/xapek/ebus/Ebus.scala new file mode 100755 index 0000000..3db87ac --- /dev/null +++ b/ebus-scala/src/main/scala/org/xapek/ebus/Ebus.scala @@ -0,0 +1,315 @@ +package org.xapek.ebus + +import Stream._ +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(name: String, offset : Int) extends EbusField(name, offset) { +} + +object EbusField { +/* def getFields(ebusDefinition : EbusDefinition, primaryCommand: Char, secondaryCommand: Char): List[EbusField] = { + val packetDef = ebusDefinition.packet(primaryCommand, secondaryCommand) + for (fieldDef <- (packetDef \ "fields").head.child) { + println(buildField(fieldDef)) + } + null + } + def buildField(field :Node) : Option[EbusField] = { + if (field.label == "data2c") { + val name = field.attribute("name").get.text + val offset = field.attribute("offset").get.text.toInt + return Some(new Data2c(name, offset)) + } + return None + }*/ +} + + +/// Ebus Protocol Parser +object EbusProtocol { + 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 + 243, 104, 94, 197, 43, 176, 134, 29, 234, 113, //20 + 71, 220, 100, 255, 201, 82, 165, 62, 8, 147, //30 + 125, 230, 208, 75, 188, 39, 17, 138, 86, 205, + 251, 96, 151, 12, 58, 161, 79, 212, 226, 121, + 142, 21, 35, 184, 200, 83, 101, 254, 9, 146, + 164, 63, 209, 74, 124, 231, 16, 139, 189, 38, + 250, 97, 87, 204, 59, 160, 150, 13, 227, 120, + 78, 213, 34, 185, 143, 20, 172, 55, 1, 154, + 109, 246, 192, 91, 181, 46, 24, 131, 116, 239, //100 + 217, 66, 158, 5, 51, 168, 95, 196, 242, 105, + 135, 28, 42, 177, 70, 221, 235, 112, 11, 144, + 166, 61, 202, 81, 103, 252, 18, 137, 191, 36, + 211, 72, 126, 229, 57, 162, 148, 15, 248, 99, + 85, 206, 32, 187, 141, 22, 225, 122, 76, 215, + 111, 244, 194, 89, 174, 53, 3, 152, 118, 237, + 219, 64, 183, 44, 26, 129, 93, 198, 240, 107, + 156, 7, 49, 170, 68, 223, 233, 114, 133, 30, + 40, 179, 195, 88, 110, 245, 2, 153, 175, 52, + 218, 65, 119, 236, 27, 128, 182, 45, 241, 106, //200 + 92, 199, 48, 171, 157, 6, 232, 115, 69, 222, + 41, 178, 132, 31, 167, 60, 10, 145, 102, 253, + 203, 80, 190, 37, 19, 136, 127, 228, 210, 73, + 149, 14, 56, 163, 84, 207, 249, 98, 140, 23, + 33, 186, 77, 214, 224, 123) + + 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) + } +}; + +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 + /** + * Wird von {@link EbusReader} mit einer Streamposition aufgerufen. + * Ausgehend von dieser Position wird versucht ein Paket aufzubauen, + * und anschliesend die Checksumme geprüft. + * Im Erfolgsfall wird eine EbusL2Packet-Instanz als Option zurückgegen, + * sonst None + */ + def apply(stream: Stream[Int]): EbusL2Packet = { + var mystream = stream + var rawLength = 0 + def readBytes(n:Int, msg : String) : List[Int] = { + val d = mystream.slice(rawLength,rawLength+n).toList + rawLength = rawLength + n + println("RAW %15s: ".format(msg) + d.map((it) => "0x%02x".format(it)).reduceRight((s1,s2) => s1+","+s2)) + return d + } + + def readPayloadByte(i:Int) : Int = + readBytes(1, "payload " + i)(0) match { + case 0xa9 => { + readBytes(1, "payload esc "+i)(0) match { + case 0x00 => 0xa9 + case 0x01 => 0xaa + case code : Int => { + throw new EbusL2ParseException("Invalid escape sequence (0xa9 0x%02x)".format(code), + stream.slice(0, rawLength).toList, rawLength); + } + } + } + case value : Int => value + } + + 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 EbusL2ParseException("Erkannte payload Groesse zu gross: %d".format(payloadLength), + stream.slice(0, rawLength).toList, rawLength); + } else if (payloadLength == 0) { + // Unklar + 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 = EbusProtocol.crc(List(source, destination, primaryCommand, secondaryCommand, payloadLength) ++ payload) + if (crc != crcCalc) { + throw new EbusL2ParseException("CRC mismatch (read) 0x%02x != 0x%02x (calc)".format(crc, crcCalc), + stream.slice(0, rawLength).toList, rawLength + 1); + } + + // Broadcast Packet ends here + if (destination == 0xfe) { + val syn = readBytes(1,"SYN(broadcast)")(0) + if (syn != EbusProtocol.SYN) { + throw new EbusL2ParseException("Bad SYN: 0x%02x".format(syn), + stream.slice(0, rawLength).toList, rawLength); + } + return new EbusL2MasterMasterPacket(l2Header, payload, crc, rawLength) + } + + val ack = readBytes(1,"ACK")(0) + + val syn = readBytes(1, "SYN/NN2")(0) + // Master-Master Packet ends here + if (syn == EbusProtocol.SYN) { + if (ack != EbusProtocol.ACK_OK) { + throw new EbusL2ParseException("Bad ACK: 0x%02x".format(ack), + stream.slice(0, rawLength).toList, 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 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 = EbusProtocol.crc(List(payloadSlaveLength) ++ payloadSlave) + if (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 != EbusProtocol.ACK_OK) { + 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 != EbusProtocol.SYN) { + throw new EbusL2ParseException("Master-Slave SYN nicht 0xaa sondern 0x%02x".format(synSlave), + stream.slice(0, rawLength).toList, rawLength); + } + + return new EbusL2MasterSlavePacket(l2Header, payload, crc, + payloadSlaveLength, payloadSlave, + crcSlave, rawLength); + } +} + +class EbusReader(var stream : Stream[Int]) { + def next() : EbusL2Packet = { + while (true) { + // Überspringe Synchronisationszeichen + stream = stream.dropWhile(_ == EbusProtocol.SYN) + + try { + val packet = EbusL2Packet(stream) + stream = stream.drop(packet.rawLength) + return packet + } catch { + case exc : EbusL2ParseException => { + stream = stream.drop(exc.skipbytes) + println("Exception: " + exc.message + "\n\tData: " + + exc.data.map((it) => "0x%02x".format(it)).reduceRight((s1,s2) => s1+","+s2)) + } + } + } + return null; + } +} + +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) {}; + diff --git a/ebus-scala/src/main/scala/org/xapek/ebus/Main.scala b/ebus-scala/src/main/scala/org/xapek/ebus/Main.scala new file mode 100644 index 0000000..fc5022e --- /dev/null +++ b/ebus-scala/src/main/scala/org/xapek/ebus/Main.scala @@ -0,0 +1,97 @@ +package org.xapek.ebus + +import java.io.{InputStream, EOFException} +import java.net.{InetAddress, InetSocketAddress, Socket} +import Stream._ + + +object App { + val ebusDefinition = EbusDefinition.fromFile("../ebus-xml/ebus.xml") + var source: InputStream = null + + def println(msg : Any) { + Predef.println("[ebus]\t" + msg.toString) + } + + def main(args: Array[String]): Unit = { + args.toList match { + case "-" :: List() => { + source = System.in + } + case host :: port :: List() => { + val addr = InetAddress.getByName(host) + val sockAddr = new InetSocketAddress(addr, port.toInt) + val s = new Socket() + s.connect(sockAddr) + println("Connected to %s %d".format(args(0), args(1).toInt)) + source = s.getInputStream + } + case "dump" :: host :: port :: List() => { + val addr = InetAddress.getByName(host) + val sockAddr = new InetSocketAddress(addr, port.toInt) + val s = new Socket() + s.connect(sockAddr) + System.err.println("Connected to %s %d".format(args(1), args(2).toInt)) + while (true) { + val v = s.getInputStream.read + if (v == -1) { + println("Fehler: read == -1") + System.exit(1) + } + System.out.write(v) + } + } + case _ => { + println( +"""Usage: PROGRAM +ARGUMENTS: +- + Parse Ebus from stdin +dump HOST PORT + Read and Dump TCP Connection +HOST PORT + Parse Ebus from TCP Connection +""") + exit(1) + } + } + + var stream: Stream[Int] = Stream.continually({ + val value = source.read + if (value == -1) + throw new EOFException("End of File reached (read returned -1)") + value + }) + + + // Synchronisiere + while (stream(0) != EbusProtocol.SYN) { + stream = stream.drop(1) + } + + val reader = new EbusReader(stream) + (() => { + while (true) { + try { + 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") + return; + } + } + } + })() + println("exiting") + source.close + } +} + -- cgit v1.2.1