summaryrefslogtreecommitdiff
path: root/ebus-scala/Main.scala
diff options
context:
space:
mode:
Diffstat (limited to 'ebus-scala/Main.scala')
-rwxr-xr-xebus-scala/Main.scala273
1 files changed, 156 insertions, 117 deletions
diff --git a/ebus-scala/Main.scala b/ebus-scala/Main.scala
index b329ce4..63ec98e 100755
--- a/ebus-scala/Main.scala
+++ b/ebus-scala/Main.scala
@@ -2,29 +2,71 @@
exec scala "$0" "$@"
!#
import Stream._
-import java.io.{ InputStream, EOFException }
-import java.net.{ InetAddress, InetSocketAddress, Socket }
-import scala.xml.{XML, Node, NodeSeq}
-
-class EbusDefinition(val ebusXmlFile: String) {
- val xml = XML.loadFile(ebusXmlFile)
-
- def packetFromCommandId(primaryCommand: Int, secondaryCommand: Int): NodeSeq = {
- val packet = (xml \ "packets" \ "packet").filter(n =>
- n.attribute("primary").get.text.toInt == primaryCommand &&
- n.attribute("secondary").get.text.toInt == secondaryCommand)
- packet
+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(override val name: String, override val offset : Int) extends EbusField(name, offset) {
+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.packetFromCommandId(primaryCommand, secondaryCommand)
+/* 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))
}
@@ -37,54 +79,15 @@ object EbusField {
return Some(new Data2c(name, offset))
}
return None
- }
+ }*/
}
-
-class EbusL2Packet(val source: Int,
- val destination: Int,
- val primaryCommand: Int,
- val secondaryCommand: Int,
- val payloadLength: Int,
- val payload: List[Int],
- val crc: Int,
- val rawLength: Int) {
- override def toString: String = {
- val p = if (payload.size == 0)
- ""
- else
- 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 rawL=%d".format(
- destination.toInt, primaryCommand.toInt, secondaryCommand.toInt, payloadLength.toInt,
- crc.toInt, p.toString, rawLength)
- }
-}
-
-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 EbusParseException(val message : String, val data : List[Int], val skipbytes : Int) extends Exception;
-
-object CRC {
+/// 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
@@ -113,19 +116,63 @@ object CRC {
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}
+ 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)
+ }
+};
-object Ebus {
- def SYN = 0xaa
- def ACK_OK = 0x00
- def ACK_FAIL = 0xff
+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
/**
@@ -152,7 +199,7 @@ object EbusL2Packet {
case 0x00 => 0xa9
case 0x01 => 0xaa
case code : Int => {
- throw new EbusParseException("Invalid escape sequence (0xa9 0x%02x)".format(code),
+ throw new EbusL2ParseException("Invalid escape sequence (0xa9 0x%02x)".format(code),
stream.slice(0, rawLength).toList, rawLength);
}
}
@@ -161,23 +208,24 @@ object EbusL2Packet {
}
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 EbusParseException("Erkannte payload Groesse zu gross: %d".format(payloadLength),
+ throw new EbusL2ParseException("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",
+ 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 = CRC(List(source, destination, primaryCommand, secondaryCommand, payloadLength) ++ payload)
+ val crcCalc = Ebus.crc(List(source, destination, primaryCommand, secondaryCommand, payloadLength) ++ payload)
if (crc != crcCalc) {
- throw new EbusParseException("CRC mismatch (read) 0x%02x != 0x%02x (calc)".format(crc, crcCalc),
+ throw new EbusL2ParseException("CRC mismatch (read) 0x%02x != 0x%02x (calc)".format(crc, crcCalc),
stream.slice(0, rawLength).toList, rawLength + 1);
}
@@ -185,12 +233,10 @@ object EbusL2Packet {
if (destination == 0xfe) {
val syn = readBytes(1,"SYN(broadcast)")(0)
if (syn != Ebus.SYN) {
- throw new EbusParseException("Bad SYN: 0x%02x".format(syn),
+ throw new EbusL2ParseException("Bad SYN: 0x%02x".format(syn),
stream.slice(0, rawLength).toList, rawLength);
}
- return new EbusL2Packet(source, destination,
- primaryCommand, secondaryCommand,
- payloadLength, payload, crc, rawLength)
+ return new EbusL2MasterMasterPacket(l2Header, payload, crc, rawLength)
}
val ack = readBytes(1,"ACK")(0)
@@ -199,52 +245,47 @@ object EbusL2Packet {
// Master-Master Packet ends here
if (syn == Ebus.SYN) {
if (ack != Ebus.ACK_OK) {
- throw new EbusParseException("Bad ACK: 0x%02x".format(ack),
+ throw new EbusL2ParseException("Bad ACK: 0x%02x".format(ack),
stream.slice(0, rawLength).toList, rawLength);
}
- return new EbusL2Packet(source, destination,
- primaryCommand, secondaryCommand,
- payloadLength, payload, crc, 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 EbusParseException("payloadSlaveLength > 16: 0x%02x".format(payloadSlaveLength),
+ 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 = CRC(List(payloadSlaveLength) ++ payloadSlave)
+ val crcSlaveCalc = Ebus.crc(List(payloadSlaveLength) ++ payloadSlave)
if (crcSlave != crcSlaveCalc) {
- throw new EbusParseException("CRC mismatch (read) 0x%02x != 0x%02x (calc)".format(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 EbusParseException("Master-Slave ACK nicht 0x00 sondern 0x%02x".format(ackSlave),
+ 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 EbusParseException("Master-Slave SYN nicht 0xaa sondern 0x%02x".format(synSlave),
+ throw new EbusL2ParseException("Master-Slave SYN nicht 0xaa sondern 0x%02x".format(synSlave),
stream.slice(0, rawLength).toList, rawLength);
}
- return new EbusL2MasterSlavePacket(source, destination,
- primaryCommand, secondaryCommand,
- payloadLength, payload, crc,
- payloadSlaveLength, payloadSlave, crcSlave, rawLength);
+ return new EbusL2MasterSlavePacket(l2Header, payload, crc,
+ payloadSlaveLength, payloadSlave,
+ crcSlave, rawLength);
}
}
-
-
class EbusReader(var stream : Stream[Int]) {
def next() : EbusL2Packet = {
while (true) {
@@ -256,7 +297,7 @@ class EbusReader(var stream : Stream[Int]) {
stream = stream.drop(packet.rawLength)
return packet
} catch {
- case exc : EbusParseException => {
+ 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))
@@ -267,29 +308,18 @@ class EbusReader(var stream : Stream[Int]) {
}
}
-// 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
-// }
-
-// override def toString : String = {
-// "EbusL7: name=%s".format(name)
-// }
-// }
+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 = new EbusDefinition("/home/yvesf/vcs/ebus/ebus/ebus-xml/ebus.xml")
+ val ebusDefinition = EbusDefinition.fromFile("/home/yvesf/vcs/ebus/ebus/ebus-xml/ebus.xml")
var source: InputStream = null
def println(msg : Any) {
@@ -324,7 +354,8 @@ object App {
System.out.write(v)
}
}
- case _ => println(
+ case _ => {
+ println(
"""Usage: PROGRAM <ARGUMENTS>
ARGUMENTS:
-
@@ -334,6 +365,8 @@ dump HOST PORT
HOST PORT
Parse Ebus from TCP Connection
""")
+ exit(1)
+ }
}
var stream: Stream[Int] = Stream.continually({
@@ -353,7 +386,15 @@ HOST PORT
(() => {
while (true) {
try {
- println(reader.next)
+ 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")
@@ -367,6 +408,4 @@ HOST PORT
}
}
-
-
App.main(args)