diff options
Diffstat (limited to 'ebus-scala')
-rwxr-xr-x[-rw-r--r--] | ebus-scala/Main.scala | 271 |
1 files changed, 130 insertions, 141 deletions
diff --git a/ebus-scala/Main.scala b/ebus-scala/Main.scala index be8daca..05f40c2 100644..100755 --- a/ebus-scala/Main.scala +++ b/ebus-scala/Main.scala @@ -1,3 +1,6 @@ +#!/bin/sh +exec scala "$0" "$@" +!# import Stream._ import java.io.{ InputStream, EOFException } import java.net.{ InetAddress, InetSocketAddress, Socket } @@ -15,6 +18,7 @@ class EbusDefinition(val ebusXmlFile: String) { } abstract class EbusField(val name : String, val offset : Int); + class Data2c(override val name: String, override val offset : Int) extends EbusField(name, offset) { } @@ -37,6 +41,7 @@ object EbusField { } + class EbusL2Packet(val source: Int, val destination: Int, val primaryCommand: Int, @@ -56,21 +61,6 @@ class EbusL2Packet(val source: Int, destination.toInt, primaryCommand.toInt, secondaryCommand.toInt, payloadLength.toInt, crc.toInt, p.toString, rawLength) } - - /** - * Die CRC-Prüfsumme wird vom jeweiligen Sender über der expandierten Bytesendefolge mit dem - * Generatorpolynom X8 +X7 + X4 + X3 + X +1 gebildet und als letztes Byte der Nachricht, - * sofern notwendig, sogar expandiert gesendet. - * - * Generatorpol. 11001101 - */ - def calcCrc: Int = { - //val foo = payload.reduce((a, b) => ((a.intValue + b.intValue) % 256).toChar) - //val sum = destination + primaryCommand + secondaryCommand + payloadLength + foo - - val crc = 1 + 1 - 1 - } } class EbusL2MasterSlavePacket(source: Int, @@ -94,7 +84,7 @@ class EbusL2MasterSlavePacket(source: Int, class FakePacket(val rawLength : Int); -class EbusParseException(val message : String, val data : List[Int]); +class EbusParseException(val message : String, val data : List[Int], val skipbytes : Int) extends Exception; object CRC { val crcTab = List[Int]( @@ -132,8 +122,14 @@ object CRC { } +object Ebus { + def SYN = 0xaa + def ACK_OK = 0x00 + def ACK_FAIL = 0xff +} object EbusL2Packet { + def PAYLOAD_LENGTH_MAX = 16 /** * Wird von {@link EbusReader} mit einer Streamposition aufgerufen. * Ausgehend von dieser Position wird versucht ein Paket aufzubauen, @@ -144,56 +140,55 @@ object EbusL2Packet { def apply(stream: Stream[Int]): Option[Any] = { var mystream = stream var rawLength = 0 - def readBytes(n:Int, msg : String = "") : List[Int] = { + 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)).foldLeft(""){(s1,s2) => s1+","+s2}) + println("RAW %15s: ".format(msg) + d.map((it) => "0x%02x".format(it)).reduceRight((s1,s2) => s1+","+s2)) return d } - val source :: destination :: primaryCommand :: secondaryCommand :: payloadLength :: _ = readBytes(5, "QQ|ZZ|PB|SB|NN") - - // Maximale payload Grösse = 16 Byte - if (payloadLength > 16) { - println("Erkannte payload Groesse zu gross: %d".format(payloadLength)) - return Some(new FakePacket(rawLength + 1)) - } else if (payloadLength == 0) { - // Unklar - println("Überspringe payloadLength mit Länge = 0") - return Some(new FakePacket(rawLength + 1)); - } - // "eBUS Spezifikation Physikalische Schicht – OSI 1 - // Verbindungsschicht – OSI 2 V.1.3.1" page 8 - val payload = Range(0,payloadLength).map({ i => + 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 - App.println("Invalid escape sequence (0xa9 0x%02x)".format(code)) - return None + throw new EbusParseException("Invalid escape sequence (0xa9 0x%02x)".format(code), + stream.slice(0, rawLength).toList, rawLength); } } } case value : Int => value } - }).toList - + + val source :: destination :: primaryCommand :: secondaryCommand :: payloadLength :: _ = readBytes(5, "QQ|ZZ|PB|SB|NN") + + // Maximale payload Grösse = 16 Byte + if (payloadLength > PAYLOAD_LENGTH_MAX) { + throw new EbusParseException("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", + 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) if (crc != crcCalc) { - println("CRC mismatch (read) 0x%02x != 0x%02x (calc)".format(crc, crcCalc)) - return Some(new FakePacket(rawLength + 1)) // + SYN + throw new EbusParseException("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 != 0xaa) { - App.println("bad SYN 0x%02x".format(syn)) - return None + if (syn != Ebus.SYN) { + throw new EbusParseException("Bad SYN: 0x%02x".format(syn), + stream.slice(0, rawLength).toList, rawLength); } return Some(new EbusL2Packet(source, destination, primaryCommand, secondaryCommand, @@ -204,56 +199,43 @@ object EbusL2Packet { val syn = readBytes(1, "SYN/NN2")(0) // Master-Master Packet ends here - if (syn == 0xaa) { - if (ack != 0x00) { - App.println("bad ACK=0x%02x".format(syn)) + if (syn == Ebus.SYN) { + if (ack != Ebus.ACK_OK) { + throw new EbusParseException("Bad ACK: 0x%02x".format(ack), + stream.slice(0, rawLength).toList, rawLength); } return Some(new EbusL2Packet(source, destination, primaryCommand, secondaryCommand, payloadLength, payload, crc, rawLength)) } + // otherwise this is a Master-Slave Telegramm // and syn is payloadSlave length val payloadSlaveLength = syn - if (payloadSlaveLength > 16) { - println("payloadSlaveLength > 16: 0x%02x".format(payloadSlaveLength)) - return None + if (payloadSlaveLength > PAYLOAD_LENGTH_MAX) { + throw new EbusParseException("payloadSlaveLength > 16: 0x%02x".format(payloadSlaveLength), + stream.slice(0, rawLength).toList, rawLength); } - val payloadSlave = Range(0,payloadSlaveLength).map({ i => - readBytes(1, "payloadSlave "+i)(0) match { - case 0xa9 => { - readBytes(1,"payloadSlave(esc) "+i)(0) match { - case 0x00 => 0xa9 - case 0x01 => 0xaa - case code : Int => { - // throw - App.println("Invalid escape sequence (0xa9 0x%02x)".format(code)) - return None - } - } - } - case value : Int => value - } - }).toList + val payloadSlave = Range(0,payloadSlaveLength).map(readPayloadByte).toList val crcSlave = readBytes(1,"crcSlave")(0) val crcSlaveCalc = CRC(List(payloadSlaveLength) ++ payloadSlave) if (crcSlave != crcSlaveCalc) { - println("CRC mismatch (read) 0x%02x != 0x%02x (calc)".format(crcSlave, crcSlaveCalc)) - return Some(new FakePacket(rawLength + 2)) // + ACK + SYN + throw new EbusParseException("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 != 0x00) { - App.println("Master-Slave ACK nicht 0x00 sondern 0x%02x".format(ackSlave)) - return None + if (ackSlave != Ebus.ACK_OK) { + throw new EbusParseException("Master-Slave ACK nicht 0x00 sondern 0x%02x".format(ackSlave), + stream.slice(0, rawLength).toList, rawLength); } val synSlave= readBytes(1,"SYNslave")(0) - if (synSlave != 0xaa) { - App.println("Master-Slave SYN nicht 0xaa sondern 0x%02x".format(synSlave)) - return None + if (synSlave != Ebus.SYN) { + throw new EbusParseException("Master-Slave SYN nicht 0xaa sondern 0x%02x".format(synSlave), + stream.slice(0, rawLength).toList, rawLength); } return Some(new EbusL2MasterSlavePacket(source, destination, @@ -269,28 +251,31 @@ class EbusReader(var stream : Stream[Int]) { def next() : EbusL2Packet = { while (true) { // Überspringe Synchronisationszeichen - var i = 0 - while (stream(0) == 0xaa) { - stream = stream.drop(1) - i = i +1 - } - println("Skipped " + i + " SYNs") - - EbusL2Packet(stream) match { - case Some(packet : EbusL2Packet) => { - stream = stream.drop(packet.rawLength) - return packet - } - case Some(packet : FakePacket) => { - println("Fake paket - forward %d bytes".format(packet.rawLength)) - stream = stream.drop(packet.rawLength) + stream = stream.dropWhile(_ == Ebus.SYN) + + try { + EbusL2Packet(stream) match { + case Some(packet : EbusL2Packet) => { + stream = stream.drop(packet.rawLength) + return packet + } + case Some(packet : FakePacket) => { + println("Fake paket - forward %d bytes".format(packet.rawLength)) + stream = stream.drop(packet.rawLength) + } + case None => { + // Es konnte kein Paket eingelesen werden + // verschiebe die Anfangsposition um ein Byte + App.println("Paket konnte nicht gelesen werden, verschiebe start um 1 byte") + stream = stream.drop(1) + } + } + } catch { + case exc : EbusParseException => { + 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)) } - case None => { - // Es konnte kein Paket eingelesen werden - // verschiebe die Anfangsposition um ein Byte - App.println("Paket konnte nicht gelesen werden, verschiebe start um 1 byte") - stream = stream.drop(1) - } } } return null; @@ -323,76 +308,80 @@ object App { var source: InputStream = null def println(msg : Any) { - Predef.println("[ebus] " + msg.toString) + Predef.println("[ebus]\t" + msg.toString) } def main(args: Array[String]): Unit = { - if (args.size == 1 && args(0) == "-") { - println("Use stdin as source") - source = System.in - } else if (args.size == 2) { - val addr = InetAddress.getByName(args(0)) - val sockAddr = new InetSocketAddress(addr, args(1).toInt) - val s = new Socket() - s.connect(sockAddr) - println("Connected to %s %d".format(args(0), args(1).toInt)) - source = s.getInputStream - } else if (args.size == 3 && args(0) == "dump") { - val addr = InetAddress.getByName(args(1)) - val sockAddr = new InetSocketAddress(addr, args(2).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) + 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) } - System.out.write(v) } - } else { - println("Missing Arguments") - println(" -") - println(" Read from stdin") - println("HOST PORT") - println(" Read from TCP Server") - println("dump HOST PORT") - println(" Dump TCP-Stream") - System.exit(1) + case _ => println( +"""Usage: PROGRAM <ARGUMENTS> +ARGUMENTS: +- + Parse Ebus from stdin +dump HOST PORT + Read and Dump TCP Connection +HOST PORT + Parse Ebus from TCP Connection +""") } - val read = (() => { + var stream: Stream[Int] = Stream.continually({ val value = source.read if (value == -1) - throw new EOFException("End of File reached (read returned -1)") + throw new EOFException("End of File reached (read returned -1)") value }) - - - var stream: Stream[Int] = Stream.continually(read()) // Synchronisiere - while (stream(0) != 0xaa && stream(1) != 0xaa) { + while (stream(0) != Ebus.SYN) { stream = stream.drop(1) } val reader = new EbusReader(stream) - while (true) { - try { - println(reader.next) - } catch { - case exc : EOFException => { - println("EOF") - source.close - exit(0) + (() => { + while (true) { + try { + println(reader.next) + } catch { + case exc : EOFException => { + println("EOF") + return; + } } } - } + })() println("exiting") source.close } } -App.main(Array("-")) + + +App.main(args) |