diff options
Diffstat (limited to 'ebus-scala/src/main/scala/org')
-rwxr-xr-x | ebus-scala/src/main/scala/org/xapek/ebus/Ebus.scala | 159 | ||||
-rw-r--r-- | ebus-scala/src/main/scala/org/xapek/ebus/Layer2.scala | 251 | ||||
-rw-r--r-- | ebus-scala/src/main/scala/org/xapek/ebus/Main.scala | 140 |
3 files changed, 0 insertions, 550 deletions
diff --git a/ebus-scala/src/main/scala/org/xapek/ebus/Ebus.scala b/ebus-scala/src/main/scala/org/xapek/ebus/Ebus.scala deleted file mode 100755 index 5b61a5b..0000000 --- a/ebus-scala/src/main/scala/org/xapek/ebus/Ebus.scala +++ /dev/null @@ -1,159 +0,0 @@ -package org.xapek.ebus - -// This file contains the Ebus Layer 7 Implementation. -// It depends on Layer 2 and the Ebus-Definition XML - -import Stream._ -import scala.xml.{XML, Elem, Node, NodeSeq} - -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) -} - -class EbusL7Packet(val l2Packet: EbusL2Packet, val name: String, val fields: List[EbusL7Field[Any]]) { - override def toString(): String = "EbusL7Packet: name=%s Fields: %s %s".format( - name, - fields.map(_.toString).toString, - l2Packet.toString) -} - -object EbusDefinition { - def fromFile(ebusXMLPath : String) = { - new EbusDefinition(XML.loadFile(ebusXMLPath)) - } - private def parseHexNode(n: Node):Int = Integer.parseInt(n.text,16) -} - -class EbusDefinition(val xml: Elem) { - def packet(l2Packet: EbusL2Packet): Option[EbusL7Packet] = { - packet(l2Packet.l2Header.primaryCommand, l2Packet.l2Header.secondaryCommand).map(node => - new EbusL7Packet(l2Packet, node.attribute("name").get.head.text, fields(node))) - } - def fields(packet: Node) : List[EbusL7Field[Any]] = { - val fields = (packet \ "fields"); - val foo = fields.headOption.map(fields => - fields.child.map(child => EbusL7Field(child)) - ).getOrElse(Seq[EbusL7Field[Any]]()).filterNot(_ == null).toList - - println(foo) - foo - } - def packet(primaryCommand: Int, secondaryCommand: Int): Option[Node] = { - (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 - } - def device(address: Int): Option[EbusDevice] = { - (xml \ "devices" \ "device").filter(n => - n.attribute("address").flatMap(_.headOption.map(EbusDefinition.parseHexNode)) == Some(address) - ).headOption.map(EbusDevice(_)) - } -} - - - -object EbusL7Field { - def apply(fieldNode: Node): EbusL7Field[Any] = { - val name = fieldNode.attribute("name").headOption.map(_.text).getOrElse("") - val offset = Integer.parseInt(fieldNode.attribute("offset").headOption.map(_.text).getOrElse("-1")) - fieldNode.label match { - case "data1b" => new EbusL7FieldData1b(name, offset) - case "data1c" => new EbusL7FieldData1c(name, offset) - case "#PCDATA" => null - case _ => { - // Ignore unknown fieldtypes - null - } - } - } -} -abstract class EbusL7Field[+T](val name: String, val offset: Int) { - def value(payload: List[Int]): T - override def toString(): String = { - "%s: name=%s offset=%d".format(this.getClass().getSimpleName(), name, offset) - } -} - -class EbusL7FieldData1b(name: String, offset: Int) extends EbusL7Field[Int](name, offset) { - def value(payload: List[Int]): Int = { - /** Beispiel für die Berechnung: - * if ((x & 80h) == 80h) // y negativ - * y = - [dez(!x) + 1] - * else - * y = dez(x) - */ - Some(payload(offset)).map(dataByte => - if ( (dataByte & 0x80) == 0x80 ) {// Wert negativ - (-1) * (0xff ^ dataByte + 1) - } else { - dataByte - }).get - } -} - -class EbusL7FieldData1c(name: String, offset: Int) extends EbusL7Field[Double](name, offset) { - def value(payload: List[Int]): Double = { - payload(offset) / 2.0 - } -} - -// Bit -object EbusDecoderBit { - def value(payload: List[Int], offset: Int): Int = { - if ( payload(offset) == 0x1) - 1 - else - 0 - } -} - -object EbusDecoderWord { - def value(payload: List[Int], offset: Int): Int = { - val lb :: hb :: _ = payload.slice(offset, offset+2) - lb + (hb << 8) - } -} - -object EbusDecoderBCD { - def value(payload: List[Int], offset: Int): Int = { - Some(payload(offset)).map( byte => - (byte >> 4) * 10 + (byte & 0xf) - ).get - } -} - -/// Ebus Protocol Parser - -abstract class EbusL7Reader(val stream: Stream[Int], val ebusDefinition: EbusDefinition) { - def l2Reader = new EbusL2Reader(stream) { - override def onNewPacket(packet: EbusL2Packet): Unit = { - EbusL7Reader.this.process(packet) - } - override def onException(exc: Exception): Unit = { - EbusL7Reader.this.onException(exc) - } - } - - def run = l2Reader.run - - def process(l2Packet: EbusL2Packet): Unit = { - println(ebusDefinition.packet(l2Packet)) - } - - def onNewPacket(packet: EbusL7Packet): Unit - def onException(exc: Exception): Unit -} - -class EbusL7ParseException(val message: String, - val l2Packet: EbusL2Packet) extends Exception; diff --git a/ebus-scala/src/main/scala/org/xapek/ebus/Layer2.scala b/ebus-scala/src/main/scala/org/xapek/ebus/Layer2.scala deleted file mode 100644 index 7c0f1c6..0000000 --- a/ebus-scala/src/main/scala/org/xapek/ebus/Layer2.scala +++ /dev/null @@ -1,251 +0,0 @@ -package org.xapek.ebus - -// This file contains the Ebus Layer 2 Implementation. Layer 2 does -// not depend on the Ebus-Definition XML -// -// -// For reference see: -// -// - "eBUS Spezifikation Physikalische Schicht – OSI 1 -// Verbindungsschicht – OSI 2 V.1.3.1" -// -// - http://www.mikrocontroller.net/topic/75698 - -import Stream._ - - -object EbusL2Protocol { - 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} - } -} - -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 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 = EbusL2Protocol.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 != EbusL2Protocol.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 == EbusL2Protocol.SYN) { - if (ack != EbusL2Protocol.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 = EbusL2Protocol.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 != EbusL2Protocol.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 != EbusL2Protocol.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); - } -} - - -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; - -class EbusL2Reader(var stream : Stream[Int]) { - def onNewPacket(packet: EbusL2Packet): Unit = {} - - def onException(exc: Exception): Unit = { - throw exc - } - - def run(): Unit = { - // Überspringe Angeschnittene Pakete Synchronisieren auf neues - // Paket - while (stream(0) != EbusL2Protocol.SYN) { - stream = stream.drop(1) - } - - while (true) { - // Überspringe Synchronisationszeichen - stream = stream.dropWhile(_ == EbusL2Protocol.SYN) - - try { - val packet = EbusL2Packet(stream) - stream = stream.drop(packet.rawLength) - - onNewPacket(packet) - } catch { - case exc : EbusL2ParseException => { - stream = stream.drop(exc.skipbytes) - onException(exc) - } - } - } - } -} diff --git a/ebus-scala/src/main/scala/org/xapek/ebus/Main.scala b/ebus-scala/src/main/scala/org/xapek/ebus/Main.scala deleted file mode 100644 index 4643bcd..0000000 --- a/ebus-scala/src/main/scala/org/xapek/ebus/Main.scala +++ /dev/null @@ -1,140 +0,0 @@ -package org.xapek.ebus - -import java.io.{InputStream, EOFException} -import java.net.{InetAddress, InetSocketAddress, Socket} -import Stream._ - -object Main { - val ebusDefinition = EbusDefinition.fromFile("../ebus-xml/ebus.xml") - - def getStream(source: InputStream):Stream[Int] = { - Stream.continually({ - val value = source.read - if (value == -1) - throw new EOFException("End of File reached (read returned -1)") - value - }) - } - - def layer2(source: InputStream): Unit = { - var stream = getStream(source) - val reader = new EbusL2Reader(stream) { - override def onNewPacket(packet: EbusL2Packet): Unit = { - println(packet) - } - override def onException(exc: Exception): Unit = { - exc match { - case exc: EbusL2ParseException => - println("Exception: " + exc.message + "\n\tData: " + - exc.data.map((it) => "0x%02x".format(it)).reduceRight((s1,s2) => s1+","+s2)) - case exc => - throw exc - } - } - } - while (true) { - try { - reader.run - } catch { - case exc: EOFException => { - source.close - return - } - case exc=> { - exc.printStackTrace() - source.close - return - } - } - } - } - - def layer7(source: InputStream, ebusDefinition: EbusDefinition): Unit = { - var stream = getStream(source) - val reader = new EbusL7Reader(stream, ebusDefinition) { - override def onNewPacket(packet: EbusL7Packet): Unit = { - println(packet) - } - override def onException(exc: Exception): Unit = { - exc match { - case exc: EbusL2ParseException => - println("L2 Exception: " + exc.message + "\n\tData: " + - exc.data.map((it) => "0x%02x".format(it)).reduceRight((s1,s2) => s1+","+s2)) - case exc: EbusL7ParseException => - println("L7 Exception: " + exc.message + "\n\tL2 Packet: " + exc.l2Packet) - case exc => - exc.printStackTrace - } - } - } - while (true) { - try { - reader.run - } catch { - case exc: EOFException => { - source.close - return - } - case exc=> { - exc.printStackTrace() - source.close - return - } - } - } - - } - - def connect(host: String, port: Int): Socket = { - val addr = InetAddress.getByName(host) - val s = new Socket - s.connect(new InetSocketAddress(addr, port)) - println("Connected to %s:%d".format(host, port)) - s - } - - def main(args: Array[String]): Unit = { - args.toList match { - case "layer2" :: "-" :: List() => { - println("Read from stdin") - layer2(System.in) - } - case "layer2" :: host :: port :: List() => { - layer2(connect(host, port.toInt).getInputStream) - } - case "layer7" :: "-" :: List() => { - println("Read from stdin") - layer7(System.in, ebusDefinition) - } - case "dump" :: host :: port :: List() => { - val s = connect(host, port.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> - ARGUMENTS: - > layer2 - - Parse Ebus Layer 2 from stdin - - > layer2 HOST PORT - Parse Ebus Layer 2 from TCP Connection - - > layer7 - - Parse Ebus Layer 2+7 from stdin - - > dump HOST PORT - Read and Dump TCP Connection - """) - } - } - } -} - |