From c25d4d9c49fab29abea0d85b4f4190d5bc7330c1 Mon Sep 17 00:00:00 2001 From: Yves Fischer Date: Sat, 17 Dec 2011 21:47:25 +0100 Subject: ebus-scala cleanup, fixup - master-slave packets --- ebus-scala/Main.scala | 343 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 218 insertions(+), 125 deletions(-) diff --git a/ebus-scala/Main.scala b/ebus-scala/Main.scala index e91de0a..070240f 100644 --- a/ebus-scala/Main.scala +++ b/ebus-scala/Main.scala @@ -45,8 +45,6 @@ class EbusL2Packet(val source: Int, val payload: List[Int], val crc: Int, val rawLength: Int) { - val length = 6 + payload.size - override def toString: String = { val p = if (payload.size == 0) "" @@ -54,9 +52,9 @@ class EbusL2Packet(val source: Int, 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".format( + "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) + crc.toInt, p.toString, rawLength) } /** @@ -75,7 +73,65 @@ class EbusL2Packet(val source: Int, } } -class FakePacket(val length : Int); +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 FakePacket(val rawLength : Int); + +class EbusParseException(val message : String, val data : List[Int]); + +object CRC { + 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 apply(data : List[Int]) : Int = { + // initialCrc=0 + return data.foldLeft(0){ (crc,byte) => crcTab(crc) ^ byte} + } +} + + object EbusL2Packet { /** @@ -86,105 +142,158 @@ object EbusL2Packet { * sonst None */ def apply(stream: Stream[Int]): Option[Any] = { - try { - var rawLength = 0 - val source = stream(0) - val destination = stream(1) - val primaryCommand = stream(2) - val secondaryCommand = stream(3) - val payloadLength = stream(4) - rawLength = 5 - - println("source=0x%02x destination=0x%02x primaryCommand=0x%02x secondaryCommand=0x%02x payloadLength=0x%02x".format( - source, destination, primaryCommand, secondaryCommand, payloadLength)) - - // Maximale payload Grösse = 16 Byte - if (payloadLength > 16) { - println("Erkannte payload Groesse zu gross: %d".format(payloadLength)) + var mystream = stream + var rawLength = 0 + def readBytes(n:Int) : List[Int] = { + val d = mystream.slice(rawLength,rawLength+n).toList + rawLength = rawLength + n + println("RAW: " + d.map((it) => "0x%02x".format(it)).foldLeft(""){(s1,s2) => s1+","+s2}) + return d + } + val source :: destination :: primaryCommand :: secondaryCommand :: payloadLength :: _ = readBytes(5) + + println("source=0x%02x destination=0x%02x primaryCommand=0x%02x secondaryCommand=0x%02x payloadLength=0x%02x".format( + source, destination, primaryCommand, secondaryCommand, payloadLength)) + + // 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)); + } else { + println("Payload = " + payloadLength); + } + + // "eBUS Spezifikation Physikalische Schicht – OSI 1 + // Verbindungsschicht – OSI 2 V.1.3.1" page 8 + val payload = Range(0,payloadLength).map({ i => + readBytes(1)(0) match { + case 0xa9 => { + readBytes(1)(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 + println("Payload: " + payload.map((it) => "0x%02x".format(it)).foldLeft(""){(s1,s2) => s1+","+s2}) + + val crc = readBytes(1)(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 + } + + // Broadcast Packet ends here + if (destination == 0xfe) { + val syn = readBytes(1)(0) + if (syn != 0xaa) { + App.println("bad SYN 0x%02x".format(syn)) return None - } else { - println("Payload = " + payloadLength); } + return Some(new EbusL2Packet(source, destination, + primaryCommand, secondaryCommand, + payloadLength, payload, crc, rawLength)) + } - var payload = List[Int]() - for (i <- 0 until payloadLength) { - var it = stream(rawLength) - rawLength += 1 - // Handle escape sequences - // "eBUS Spezifikation Physikalische Schicht – OSI 1 - // Verbindungsschicht – OSI 2 V.1.3.1" auf Seite 8 - if (it == 0xa9) { - val itNext = stream.apply(rawLength) - rawLength += 1 - if (itNext == 0x00) { - it = 0xa9 - } else if (itNext == 0x01) { - it = 0xaa - } else { - App.println("Ignore escape sequence 0xa9 0x%02x".format(itNext)) - rawLength -= 1 - } - } - payload = payload :+ it - println("Payload: " + payload.map((it) => "0x%02x".format(it)).foldLeft(""){(s1,s2) => s1+","+s2}) + val ack = readBytes(1)(0) + println("ACK=0x%02x".format(ack)) + + val syn = readBytes(1)(0) + // Master-Master Packet ends here + if (syn == 0xaa) { + if (ack != 0x00) { + App.println("bad ACK=0x%02x".format(syn)) } - val crc = stream.apply(rawLength + 0) - println("CRC=0x%x".format(crc)) - val ack = stream.apply(rawLength + 1) - println("ACK=0x%02x".format(ack)) - if (destination == 0xfe) { - // Broadcast - if (ack != 0xaa) { - println("ACK Bei Broadcast Paket unerwertet nicht 0xaa sondern 0x%02x".format(ack)) - } - } else { - if (ack != 0x00) { - println("ACK unerwartet nicht 0x00 sondern 0x%02x".format(ack)) + 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 + println("payloadSlaveLength = 0x%02x".format(payloadSlaveLength)) + if (payloadSlaveLength > 16) { + println("payloadSlaveLength > 16: 0x%02x".format(payloadSlaveLength)) + return None + } + + val payloadSlave = Range(0,payloadSlaveLength).map({ i => + readBytes(1)(0) match { + case 0xa9 => { + readBytes(1)(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 } - rawLength += 0 - - if (stream.apply(rawLength + 4) == 0xaa) { - // Broadcast oder Master-Master - return Some(new EbusL2Packet(source, destination, - primaryCommand, secondaryCommand, - payloadLength, payload, crc, rawLength)) - } else { - // Master-Slave - return Some(new FakePacket(rawLength)) - } - } catch { - case exc: IndexOutOfBoundsException => { - App.println(exc.toString) - exc.printStackTrace - return None - } + }).toList + println("PayloadSlave: " + payloadSlave.map((it) => "0x%02x".format(it)).foldLeft(""){(s1,s2) => s1+","+s2}) + + val crcSlave = readBytes(1)(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 + } + + val ackSlave = readBytes(1)(0) + App.println("ACKslave = 0x%02x".format(ackSlave)) + if (ackSlave != 0x00) { + App.println("Master-Slave ACK nicht 0x00 sondern 0x%02x".format(ackSlave)) + return None + } + + val synSlave= readBytes(1)(0) + if (synSlave != 0xaa) { + App.println("Master-Slave SYN nicht 0xaa sondern 0x%02x".format(synSlave)) + return None } + + return Some(new EbusL2MasterSlavePacket(source, destination, + primaryCommand, secondaryCommand, + payloadLength, payload, crc, + payloadSlaveLength, payloadSlave, crcSlave, rawLength)); } } -object EbusReader { - def apply(streamI: Stream[Int]) : EbusL2Packet = { - var stream = streamI +class EbusReader(var stream : Stream[Int]) { + def next() : EbusL2Packet = { while (true) { - // Synchronisiere - while (stream(0) != 0xaa && stream(1) != 0xaa) { - stream = stream.drop(1) - } // Überspringe Synchronisationszeichen - while (stream(0).toChar == 0xaa) { + 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.length)) - stream = stream.drop(packet.length) + println("Fake paket - forward %d bytes".format(packet.rawLength)) + stream = stream.drop(packet.rawLength) } case None => { // Es konnte kein Paket eingelesen werden @@ -194,33 +303,30 @@ object EbusReader { } } } - null + return null; } } -object EbusL7Packet { - def apply(l2packet : EbusL2Packet, ebusDefinition : EbusDefinition) : Option[EbusL7Packet] = { - val packetDef = ebusDefinition.packetFromCommandId( - l2packet.primaryCommand, l2packet.secondaryCommand) +// 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 - } +// 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) - } -} +// override def toString : String = { +// "EbusL7: name=%s".format(name) +// } +// } object App { val ebusDefinition = new EbusDefinition("/home/yvesf/vcs/ebus/ebus/ebus-xml/ebus.xml") @@ -276,29 +382,16 @@ object App { var stream: Stream[Int] = Stream.continually(read()) + + // Synchronisiere + while (stream(0) != 0xaa && stream(1) != 0xaa) { + stream = stream.drop(1) + } + + val reader = new EbusReader(stream) while (true) { try { - EbusReader(stream) match { - case l2packet : EbusL2Packet => { - println("Read l2 packaet: " + l2packet); - /* - EbusL7Packet(l2packet, ebusDefinition) match { - case Some(l7packet) => { - println(l7packet) - } - case _ => {} - } - */ - println(l2packet) - - // Spule den Stream zur naechstmoeglichen Paketposition - stream = stream.drop(l2packet.rawLength) - } - case null => { - println("null") - System.exit(1) - } - } + println(reader.next) } catch { case exc : EOFException => { println("EOF") -- cgit v1.2.1