summaryrefslogtreecommitdiff
path: root/ebus-scala/src/main/scala/org
diff options
context:
space:
mode:
Diffstat (limited to 'ebus-scala/src/main/scala/org')
-rwxr-xr-xebus-scala/src/main/scala/org/xapek/ebus/Ebus.scala352
-rw-r--r--ebus-scala/src/main/scala/org/xapek/ebus/Layer2.scala251
-rw-r--r--ebus-scala/src/main/scala/org/xapek/ebus/Main.scala181
3 files changed, 461 insertions, 323 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
index 3db87ac..5b61a5b 100755
--- a/ebus-scala/src/main/scala/org/xapek/ebus/Ebus.scala
+++ b/ebus-scala/src/main/scala/org/xapek/ebus/Ebus.scala
@@ -1,9 +1,11 @@
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}
-/// Ebus Bus Definition
object EbusDevice {
def apply(node : Node): EbusDevice = {
new EbusDevice(
@@ -17,37 +19,40 @@ class EbusDevice(val address:Int, val busType :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 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)
}
-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))
}
+ private def parseHexNode(n: Node):Int = Integer.parseInt(n.text,16)
}
-class EbusDefinition(val xml : Elem) {
- def packet(primaryCommand: Int, secondaryCommand: Int): Option[EbusPacket] = {
+
+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.map(EbusPacket(_))
+ ).headOption
}
def device(address: Int): Option[EbusDevice] = {
(xml \ "devices" \ "device").filter(n =>
@@ -56,260 +61,99 @@ class EbusDefinition(val xml : Elem) {
}
}
-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))
+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
}
- 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}
+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)
}
}
-// 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)
+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
}
-};
+}
-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 EbusL7FieldData1c(name: String, offset: Int) extends EbusL7Field[Double](name, offset) {
+ def value(payload: List[Int]): Double = {
+ payload(offset) / 2.0
}
}
-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);
- }
+// Bit
+object EbusDecoderBit {
+ def value(payload: List[Int], offset: Int): Int = {
+ if ( payload(offset) == 0x1)
+ 1
+ else
+ 0
+ }
+}
- // 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)
- }
+object EbusDecoderWord {
+ def value(payload: List[Int], offset: Int): Int = {
+ val lb :: hb :: _ = payload.slice(offset, offset+2)
+ lb + (hb << 8)
+ }
+}
- val ack = readBytes(1,"ACK")(0)
+object EbusDecoderBCD {
+ def value(payload: List[Int], offset: Int): Int = {
+ Some(payload(offset)).map( byte =>
+ (byte >> 4) * 10 + (byte & 0xf)
+ ).get
+ }
+}
- 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)
- }
+/// Ebus Protocol Parser
- // 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);
+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)
}
-
- 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);
+ override def onException(exc: Exception): Unit = {
+ EbusL7Reader.this.onException(exc)
}
+ }
- 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);
- }
+ def run = l2Reader.run
- 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);
+ def process(l2Packet: EbusL2Packet): Unit = {
+ println(ebusDefinition.packet(l2Packet))
}
-}
-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;
- }
+ def onNewPacket(packet: EbusL7Packet): Unit
+ def onException(exc: Exception): Unit
}
-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) {};
-
+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
new file mode 100644
index 0000000..7c0f1c6
--- /dev/null
+++ b/ebus-scala/src/main/scala/org/xapek/ebus/Layer2.scala
@@ -0,0 +1,251 @@
+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
index fc5022e..4643bcd 100644
--- a/ebus-scala/src/main/scala/org/xapek/ebus/Main.scala
+++ b/ebus-scala/src/main/scala/org/xapek/ebus/Main.scala
@@ -4,94 +4,137 @@ import java.io.{InputStream, EOFException}
import java.net.{InetAddress, InetSocketAddress, Socket}
import Stream._
-
-object App {
+object Main {
val ebusDefinition = EbusDefinition.fromFile("../ebus-xml/ebus.xml")
- var source: InputStream = null
-
- def println(msg : Any) {
- Predef.println("[ebus]\t" + msg.toString)
+
+ 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 "-" :: List() => {
- source = System.in
+ case "layer2" :: "-" :: List() => {
+ println("Read from stdin")
+ layer2(System.in)
+ }
+ case "layer2" :: host :: port :: List() => {
+ layer2(connect(host, port.toInt).getInputStream)
}
- 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 "layer7" :: "-" :: List() => {
+ println("Read from stdin")
+ layer7(System.in, ebusDefinition)
}
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))
+ val s = connect(host, port.toInt)
while (true) {
val v = s.getInputStream.read
- if (v == -1) {
- println("Fehler: read == -1")
- System.exit(1)
- }
+ if (v == -1) {
+ println("Fehler: read == -1")
+ System.exit(1)
+ }
System.out.write(v)
}
}
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
-""")
- exit(1)
- }
- }
+ """Usage: PROGRAM <ARGUMENTS>
+ ARGUMENTS:
+ > layer2 -
+ Parse Ebus Layer 2 from stdin
- var stream: Stream[Int] = Stream.continually({
- val value = source.read
- if (value == -1)
- throw new EOFException("End of File reached (read returned -1)")
- value
- })
-
+ > layer2 HOST PORT
+ Parse Ebus Layer 2 from TCP Connection
- // Synchronisiere
- while (stream(0) != EbusProtocol.SYN) {
- stream = stream.drop(1)
- }
+ > layer7 -
+ Parse Ebus Layer 2+7 from stdin
- 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;
- }
- }
+ > dump HOST PORT
+ Read and Dump TCP Connection
+ """)
}
- })()
- println("exiting")
- source.close
+ }
}
}