From ffaec1ddc9118eb07a30b2d759f0204484fa9ec4 Mon Sep 17 00:00:00 2001 From: yvesf Date: Sun, 3 Jul 2011 11:38:28 +0200 Subject: ebus-scala --- ebus-scala/.classpath | 7 + ebus-scala/.gitignore | 1 + ebus-scala/.project | 18 +++ ebus-scala/.settings/org.eclipse.jdt.core.prefs | 12 ++ ebus-scala/src/App.scala | 194 ++++++++++++++++++++++++ 5 files changed, 232 insertions(+) create mode 100644 ebus-scala/.classpath create mode 100644 ebus-scala/.gitignore create mode 100644 ebus-scala/.project create mode 100644 ebus-scala/.settings/org.eclipse.jdt.core.prefs create mode 100644 ebus-scala/src/App.scala diff --git a/ebus-scala/.classpath b/ebus-scala/.classpath new file mode 100644 index 0000000..bc01bff --- /dev/null +++ b/ebus-scala/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/ebus-scala/.gitignore b/ebus-scala/.gitignore new file mode 100644 index 0000000..be9619d --- /dev/null +++ b/ebus-scala/.gitignore @@ -0,0 +1 @@ +.scala_dependencies diff --git a/ebus-scala/.project b/ebus-scala/.project new file mode 100644 index 0000000..a19eae3 --- /dev/null +++ b/ebus-scala/.project @@ -0,0 +1,18 @@ + + + ebus-scala + + + + + + org.scala-ide.sdt.core.scalabuilder + + + + + + org.scala-ide.sdt.core.scalanature + org.eclipse.jdt.core.javanature + + diff --git a/ebus-scala/.settings/org.eclipse.jdt.core.prefs b/ebus-scala/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..33e19bd --- /dev/null +++ b/ebus-scala/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,12 @@ +#Sat Jul 02 17:29:58 CEST 2011 +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/ebus-scala/src/App.scala b/ebus-scala/src/App.scala new file mode 100644 index 0000000..8bd27e9 --- /dev/null +++ b/ebus-scala/src/App.scala @@ -0,0 +1,194 @@ +import Stream._ +import java.io.{ InputStream, EOFException } +import java.net.{ InetAddress, InetSocketAddress, Socket } + +class EbusDefinition(val ebusXmlFile: String) { + import scala.xml.XML + import scala.xml.NodeSeq + + val xml = XML.loadFile(ebusXmlFile) + + def packetFromCommandId(primaryCommand: Char, secondaryCommand: Char): NodeSeq = { + val packet = (xml \ "packets" \ "packet").filter(n => + n.attribute("primary").get.text.toInt == primaryCommand && + n.attribute("secondary").get.text.toInt == secondaryCommand) + packet + } +} + +object EbusPacket { + /** + * 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 EbusPacket-Instanz als Option zurückgegen, + * sonst None + */ + def apply(stream: Stream[Int]): Option[EbusPacket] = { + try { + var rawLength = 0 + val source = stream.apply(0).toChar + val destination = stream.apply(1).toChar + val primaryCommand = stream.apply(2).toChar + val secondaryCommand = stream.apply(3).toChar + val payloadLength = stream.apply(4).toChar + rawLength = 5 + + var payload = new Array[Char](payloadLength) + for (i <- 0 until payloadLength) { + var it = stream.apply(rawLength).toChar + rawLength += 1 + // Handle escape sequences + // "eBUS Spezifikation Physikalische Schicht – OSI 1 + // Verbindungsschicht – OSI 2 V.1.3.1" auf Seite 8 + if (it == 0xa9.toChar) { + val itNext = stream.apply(rawLength).toChar + rawLength += 1 + if (itNext == 0x00.toChar) { + it = 0xa9.toChar + } else if (itNext == 0x01.toChar) { + it = 0xaa.toChar + } else { + println("Ignore escape sequence 0xa9 0x%02x".format(itNext)) + rawLength -= 1 + } + } + payload.update(i, it) + } + val crc = stream.apply(5 + payloadLength).toChar + rawLength += 1 + + return Some(new EbusPacket(source, destination, + primaryCommand, secondaryCommand, + payloadLength, payload, crc, rawLength)) + } catch { + case exc: IndexOutOfBoundsException => { + println(exc.toString) + exc.printStackTrace + return None + } + } + } +} + +class EbusPacket(val source: Char, + val destination: Char, + val primaryCommand: Char, + val secondaryCommand: Char, + val payloadLength: Char, + val payload: Array[Char], + val crc: Char, + val rawLength: Int) { + val length = 6 + payload.size + + 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) + + "Ebus: d=%02x pC=%02x sC=%02x pL=%02x crc=%02x p=%s".format( + destination.toInt, primaryCommand.toInt, secondaryCommand.toInt, payloadLength.toInt, + crc.toInt, p.toString) + } + + /** + * 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 + */ + private def calcCrc: Char = { + val sum = destination + primaryCommand + secondaryCommand + payloadLength + + payload.reduce((a, b) => ((a.intValue + b.intValue) % 256).toChar) + val crc = 1 + 1 + 1 + } +} + +object EbusReader { + def apply(streamI: Stream[Int]): EbusPacket = { + var stream = streamI + while (true) { + // Überspringe Synchronisationszeichen + stream = stream.dropWhile(v => v.toChar == 0xaa.toChar) + + EbusPacket(stream) match { + case Some(packet) => { + stream = stream.drop(packet.rawLength) + return packet + } + case None => { + // Es konnte kein Paket eingelesen werden + // verschieben die Anfangsposition um ein Byte + stream = stream.drop(1) + println("skip 1 byte") + } + } + } + null + } +} + +object App { + val ebusDefinition = new EbusDefinition("/home/yvesf/vcs/ebus/ebus/ebus-xml/ebus.xml") + var source: InputStream = null + + def main(args: Array[String]): Unit = { + if (args.size == 1) { + 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) + System.exit(1) + print(v.toChar) + } + } else { + println("Missing Arguments") + println(" Read from stdin: -") + println(" Read from TCP: HOST PORT") + println(" Read from TCP and dump to stdout: dump HOST PORT") + System.exit(1) + } + + val read = (() => { + val value = source.read + if (value == -1) + throw new EOFException("End of File reached (read returned -1)") + value + }) + val stream: Stream[Int] = Stream.continually(read()) + + while (true) { + EbusReader(stream) match { + case x: EbusPacket => { + println(x) + val p = ebusDefinition.packetFromCommandId(x.primaryCommand, x.secondaryCommand) + if (p.size == 1) + println(p(0).attribute("name").get) + } + case null => { + println("null") + } + } + } + println("exiting") + source.close + } +} -- cgit v1.2.1