From 494b80da610fe2b4cb1790ae237437a17ac6ef62 Mon Sep 17 00:00:00 2001 From: Jakob Odersky Date: Sat, 2 Mar 2013 12:06:03 +0100 Subject: simplify scala implementation --- .../main/scala/com/github/jodersky/ace/Arq.scala | 83 ++++++++++++++++++++++ .../scala/com/github/jodersky/ace/Framer.scala | 83 ++++++++++++++++++++++ .../com/github/jodersky/ace/PhysicalLayer.scala | 5 ++ .../com/github/jodersky/ace/ReactiveLayer.scala | 38 ++++++++++ .../github/jodersky/ace/SimpleActionLayer.scala | 8 +++ .../com/github/jodersky/ace/protocol/Arq.scala | 83 ---------------------- .../com/github/jodersky/ace/protocol/Framer.scala | 83 ---------------------- .../jodersky/ace/protocol/ReactiveLayer.scala | 38 ---------- .../jodersky/ace/protocol/SimpleActionLayer.scala | 8 --- .../com/github/jodersky/ace/serial/Serial.scala | 15 ---- .../jodersky/ace/serial/SerialProvider.scala | 7 -- .../main/scala/com/github/jodersky/ace/Main.scala | 18 ++--- .../jodersky/ace/jssc/JSSCSerialProvider.scala | 48 ------------- .../com/github/jodersky/ace/jssc/Serial.scala | 47 ++++++++++++ .../com/github/jodersky/ace/jssc/package.scala | 7 -- 15 files changed, 273 insertions(+), 298 deletions(-) create mode 100644 scala/ace/src/main/scala/com/github/jodersky/ace/Arq.scala create mode 100644 scala/ace/src/main/scala/com/github/jodersky/ace/Framer.scala create mode 100644 scala/ace/src/main/scala/com/github/jodersky/ace/PhysicalLayer.scala create mode 100644 scala/ace/src/main/scala/com/github/jodersky/ace/ReactiveLayer.scala create mode 100644 scala/ace/src/main/scala/com/github/jodersky/ace/SimpleActionLayer.scala delete mode 100644 scala/ace/src/main/scala/com/github/jodersky/ace/protocol/Arq.scala delete mode 100644 scala/ace/src/main/scala/com/github/jodersky/ace/protocol/Framer.scala delete mode 100644 scala/ace/src/main/scala/com/github/jodersky/ace/protocol/ReactiveLayer.scala delete mode 100644 scala/ace/src/main/scala/com/github/jodersky/ace/protocol/SimpleActionLayer.scala delete mode 100644 scala/ace/src/main/scala/com/github/jodersky/ace/serial/Serial.scala delete mode 100644 scala/ace/src/main/scala/com/github/jodersky/ace/serial/SerialProvider.scala delete mode 100644 scala/jssc/src/main/scala/com/github/jodersky/ace/jssc/JSSCSerialProvider.scala create mode 100644 scala/jssc/src/main/scala/com/github/jodersky/ace/jssc/Serial.scala delete mode 100644 scala/jssc/src/main/scala/com/github/jodersky/ace/jssc/package.scala diff --git a/scala/ace/src/main/scala/com/github/jodersky/ace/Arq.scala b/scala/ace/src/main/scala/com/github/jodersky/ace/Arq.scala new file mode 100644 index 0000000..f7c4376 --- /dev/null +++ b/scala/ace/src/main/scala/com/github/jodersky/ace/Arq.scala @@ -0,0 +1,83 @@ +package com.github.jodersky.ace + +import scala.concurrent._ +import scala.concurrent.ExecutionContext.Implicits.global +import scala.concurrent.duration._ +import java.io.IOException +import scala.collection.mutable.HashMap +import scala.collection.mutable.Queue +import scala.util.Success + +class Arq(timeout: Int, maxResends: Int = 5, maxMessageBuffer: Int = 10) extends ReactiveLayer[Seq[Int], Seq[Int]] { + import Arq._ + + case class OpenMessage(data: Seq[Int], promise: Promise[Seq[Int]]) + + // a map containing yet to be acknowledged messages + private val openMessages = HashMap[Int, OpenMessage]() + + // received message sequences + private val receivedSequences = Queue[Int]() + + protected def receive(frameData: Seq[Int]) = { + val sequence = frameData(SequenceOffset) + val command = frameData(CommandOffset) + val message = frameData.drop(MessageOffset) + + command match { + + case Ack => { + openMessages.get(sequence) map { + case OpenMessage(data, promise) => promise.complete(Success(data)) + } + } + + case Data => { + sendToLower(ack(sequence)) + + if (!(receivedSequences contains sequence)) { + if (receivedSequences.size > maxMessageBuffer) receivedSequences.dequeue + receivedSequences enqueue sequence + notifyHigher(message) + } + } + + } + } + + def send(message: Seq[Int]) = { + val promise = Promise[Seq[Int]] + val sequence = nextSequence() + + val frameData = Seq(sequence, Data) ++ message + + def send(n: Int): Future[Seq[Int]] = + sendToLower(frameData) map { frameData => + Await.result(promise.future, timeout.milliseconds) + } recoverWith { + case t: TimeoutException if (n < maxResends) => send(n + 1) + } + + if (openMessages.size >= maxMessageBuffer) Future.failed(new IOException("too many open requests")) + else { + openMessages += (sequence -> OpenMessage(message, promise)) + send(0) andThen { case successOrFailure => (openMessages -= sequence) } + } + } + +} + +object Arq { + final val MaxSequence = 255 + final val Data = 0x05 + final val Ack = 0x06 + + final val SequenceOffset = 0 + final val CommandOffset = 1 + final val MessageOffset = 2 + + private[this] var seq = 0 + private def nextSequence() = { seq += 1; if (seq > MaxSequence) seq = 0; seq } + + def ack(seq: Int) = Seq(seq, Ack) +} \ No newline at end of file diff --git a/scala/ace/src/main/scala/com/github/jodersky/ace/Framer.scala b/scala/ace/src/main/scala/com/github/jodersky/ace/Framer.scala new file mode 100644 index 0000000..8546c48 --- /dev/null +++ b/scala/ace/src/main/scala/com/github/jodersky/ace/Framer.scala @@ -0,0 +1,83 @@ +package com.github.jodersky.ace + +import scala.collection.mutable.ArrayBuffer +import scala.concurrent.Future +import scala.concurrent.ExecutionContext.Implicits.global + +/** A framer takes bytes (as unsigned integers) and creates frames. + * Note that the input type of this reactive layer is also a sequence of + * integers for performance reasons (i.e. a future will not be created for every byte sent). + */ +class Framer extends ReactiveLayer[Seq[Int], Seq[Int]] { + import Framer._ + + private var state: State = Waiting + private val buffer = new ArrayBuffer[Int] + + protected def receive(bytes: Seq[Int]) = bytes foreach receive + + protected def receive(byte: Int): Unit = { + + state match { + case Escaping => { + buffer += byte + state = Receiving + } + case Waiting => if (byte == Start) { + buffer.clear() + state = Receiving + } + case Receiving => byte match { + case Escape => state = Escaping + case Start => buffer.clear() + case Stop => { + state = Waiting + if (checksum(buffer.init) == buffer.last) + notifyHigher(buffer.init) + } + case datum => buffer += datum + } + } + } + + def send(data: Seq[Int]): Future[Seq[Int]] = { + val buffer = new ArrayBuffer[Int] + + buffer += Start + data foreach { byte => + byte match { + case Start | Stop | Escape => { + buffer += Escape + buffer += byte + } + case _ => buffer += byte + } + } + val c = checksum(data) + c match { + case Start | Stop | Escape => { + buffer += Escape + buffer += c + } + case _ => buffer += c + } + buffer += Stop + sendToLower(buffer) map (_ => data) + } +} + +object Framer { + sealed trait State + case object Waiting extends State + case object Receiving extends State + case object Escaping extends State + + final val Escape = 0x10 + final val Start = 0x02 + final val Stop = 0x03 + + def checksum(unsignedData: Seq[Int]) = { + unsignedData.fold(0)(_ ^ _) + } + +} \ No newline at end of file diff --git a/scala/ace/src/main/scala/com/github/jodersky/ace/PhysicalLayer.scala b/scala/ace/src/main/scala/com/github/jodersky/ace/PhysicalLayer.scala new file mode 100644 index 0000000..61ebd88 --- /dev/null +++ b/scala/ace/src/main/scala/com/github/jodersky/ace/PhysicalLayer.scala @@ -0,0 +1,5 @@ +package com.github.jodersky.ace + +trait PhysicalLayer extends ReactiveLayer[Nothing, Seq[Int]] { + protected def receive(nothing: Nothing) = throw new UnsupportedOperationException("A receive function cannot be called on the lowest layer.") +} \ No newline at end of file diff --git a/scala/ace/src/main/scala/com/github/jodersky/ace/ReactiveLayer.scala b/scala/ace/src/main/scala/com/github/jodersky/ace/ReactiveLayer.scala new file mode 100644 index 0000000..9d426d3 --- /dev/null +++ b/scala/ace/src/main/scala/com/github/jodersky/ace/ReactiveLayer.scala @@ -0,0 +1,38 @@ +package com.github.jodersky.ace + +import scala.concurrent.Future + +/** Represents a layer in a reactive protocol. + * @tparam L data type this layer receives from or writes to a lower layer + * @tparam T data type this layer sends to a higher layer or receives from a higher */ +trait ReactiveLayer[L, T] { + private var lowerLayer: Option[ReactiveLayer[_, L]] = None + private var higherLayer: Option[ReactiveLayer[T, _]] = None + + /** Notifies a higher layer that data is available. */ + protected def notifyHigher(data: T): Unit = higherLayer match { + case Some(higher) => higher.receive(data) + case None => throw new UnsupportedOperationException("Higher layer doesn't exist.") + } + + /** Sends data to a lower layer. */ + protected def sendToLower(l: L): Future[L] = lowerLayer match { + case Some(lower) => lower.send(l) + case None => Future.failed(new UnsupportedOperationException("Lower layer doesn't exist.")) + } + + /** Connects this layer with a higher layer, effectively linking calls + * `notifyHigher` to `higher.receive` and `higher.sendToLower` to `send`. */ + def connect[A](higher: ReactiveLayer[T, A]) = { + this.higherLayer = Some(higher) + higher.lowerLayer = Some(this) + higher + } + + /** Called from lower layer. */ + protected def receive(data: L): Unit + + /** Send data to this layer. + * @return a future value containing the data sent, or an error */ + def send(data: T): Future[T] +} \ No newline at end of file diff --git a/scala/ace/src/main/scala/com/github/jodersky/ace/SimpleActionLayer.scala b/scala/ace/src/main/scala/com/github/jodersky/ace/SimpleActionLayer.scala new file mode 100644 index 0000000..4026c08 --- /dev/null +++ b/scala/ace/src/main/scala/com/github/jodersky/ace/SimpleActionLayer.scala @@ -0,0 +1,8 @@ +package com.github.jodersky.ace + +class SimpleActionLayer[A](action: A => Unit) extends ReactiveLayer[A, A] { + protected def receive(data: A) = action(data) + + def send(data: A) = sendToLower(data) + +} \ No newline at end of file diff --git a/scala/ace/src/main/scala/com/github/jodersky/ace/protocol/Arq.scala b/scala/ace/src/main/scala/com/github/jodersky/ace/protocol/Arq.scala deleted file mode 100644 index 4a4ed8d..0000000 --- a/scala/ace/src/main/scala/com/github/jodersky/ace/protocol/Arq.scala +++ /dev/null @@ -1,83 +0,0 @@ -package com.github.jodersky.ace.protocol - -import scala.concurrent._ -import scala.concurrent.ExecutionContext.Implicits.global -import scala.concurrent.duration._ -import java.io.IOException -import scala.collection.mutable.HashMap -import scala.collection.mutable.Queue -import scala.util.Success - -class Arq(timeout: Int, maxResends: Int = 5, maxMessageBuffer: Int = 10) extends ReactiveLayer[Seq[Int], Seq[Int]] { - import Arq._ - - case class OpenMessage(data: Seq[Int], promise: Promise[Seq[Int]]) - - // a map containing yet to be acknowledged messages - private val openMessages = HashMap[Int, OpenMessage]() - - // received message sequences - private val receivedSequences = Queue[Int]() - - protected def receive(frameData: Seq[Int]) = { - val sequence = frameData(SequenceOffset) - val command = frameData(CommandOffset) - val message = frameData.drop(MessageOffset) - - command match { - - case Ack => { - openMessages.get(sequence) map { - case OpenMessage(data, promise) => promise.complete(Success(data)) - } - } - - case Data => { - sendToLower(ack(sequence)) - - if (!(receivedSequences contains sequence)) { - if (receivedSequences.size > maxMessageBuffer) receivedSequences.dequeue - receivedSequences enqueue sequence - notifyHigher(message) - } - } - - } - } - - def send(message: Seq[Int]) = { - val promise = Promise[Seq[Int]] - val sequence = nextSequence() - - val frameData = Seq(sequence, Data) ++ message - - def send(n: Int): Future[Seq[Int]] = - sendToLower(frameData) map { frameData => - Await.result(promise.future, timeout.milliseconds) - } recoverWith { - case t: TimeoutException if (n < maxResends) => send(n + 1) - } - - if (openMessages.size >= maxMessageBuffer) Future.failed(new IOException("too many open requests")) - else { - openMessages += (sequence -> OpenMessage(message, promise)) - send(0) andThen { case successOrFailure => (openMessages -= sequence) } - } - } - -} - -object Arq { - final val MaxSequence = 255 - final val Data = 0x05 - final val Ack = 0x06 - - final val SequenceOffset = 0 - final val CommandOffset = 1 - final val MessageOffset = 2 - - private[this] var seq = 0 - private def nextSequence() = { seq += 1; if (seq > MaxSequence) seq = 0; seq } - - def ack(seq: Int) = Seq(seq, Ack) -} \ No newline at end of file diff --git a/scala/ace/src/main/scala/com/github/jodersky/ace/protocol/Framer.scala b/scala/ace/src/main/scala/com/github/jodersky/ace/protocol/Framer.scala deleted file mode 100644 index 5558db3..0000000 --- a/scala/ace/src/main/scala/com/github/jodersky/ace/protocol/Framer.scala +++ /dev/null @@ -1,83 +0,0 @@ -package com.github.jodersky.ace.protocol - -import scala.collection.mutable.ArrayBuffer -import scala.concurrent.Future -import scala.concurrent.ExecutionContext.Implicits.global - -/** A framer takes bytes (as unsigned integers) and creates frames. - * Note that the input type of this reactive layer is also a sequence of - * integers for performance reasons (i.e. a future will not be created for every byte sent). - */ -class Framer extends ReactiveLayer[Seq[Int], Seq[Int]] { - import Framer._ - - private var state: State = Waiting - private val buffer = new ArrayBuffer[Int] - - protected def receive(bytes: Seq[Int]) = bytes foreach receive - - protected def receive(byte: Int): Unit = { - - state match { - case Escaping => { - buffer += byte - state = Receiving - } - case Waiting => if (byte == Start) { - buffer.clear() - state = Receiving - } - case Receiving => byte match { - case Escape => state = Escaping - case Start => buffer.clear() - case Stop => { - state = Waiting - if (checksum(buffer.init) == buffer.last) - notifyHigher(buffer.init) - } - case datum => buffer += datum - } - } - } - - def send(data: Seq[Int]): Future[Seq[Int]] = { - val buffer = new ArrayBuffer[Int] - - buffer += Start - data foreach { byte => - byte match { - case Start | Stop | Escape => { - buffer += Escape - buffer += byte - } - case _ => buffer += byte - } - } - val c = checksum(data) - c match { - case Start | Stop | Escape => { - buffer += Escape - buffer += c - } - case _ => buffer += c - } - buffer += Stop - sendToLower(buffer) map (_ => data) - } -} - -object Framer { - sealed trait State - case object Waiting extends State - case object Receiving extends State - case object Escaping extends State - - final val Escape = 0x10 - final val Start = 0x02 - final val Stop = 0x03 - - def checksum(unsignedData: Seq[Int]) = { - unsignedData.fold(0)(_ ^ _) - } - -} \ No newline at end of file diff --git a/scala/ace/src/main/scala/com/github/jodersky/ace/protocol/ReactiveLayer.scala b/scala/ace/src/main/scala/com/github/jodersky/ace/protocol/ReactiveLayer.scala deleted file mode 100644 index c9531c0..0000000 --- a/scala/ace/src/main/scala/com/github/jodersky/ace/protocol/ReactiveLayer.scala +++ /dev/null @@ -1,38 +0,0 @@ -package com.github.jodersky.ace.protocol - -import scala.concurrent.Future - -/** Represents a layer in a reactive protocol. - * @tparam L data type this layer receives from or writes to a lower layer - * @tparam T data type this layer sends to a higher layer or receives from a higher */ -trait ReactiveLayer[L, T] { - private var lowerLayer: Option[ReactiveLayer[_, L]] = None - private var higherLayer: Option[ReactiveLayer[T, _]] = None - - /** Notifies a higher layer that data is available. */ - protected def notifyHigher(data: T): Unit = higherLayer match { - case Some(higher) => higher.receive(data) - case None => throw new UnsupportedOperationException("Higher layer doesn't exist.") - } - - /** Sends data to a lower layer. */ - protected def sendToLower(l: L): Future[L] = lowerLayer match { - case Some(lower) => lower.send(l) - case None => Future.failed(new UnsupportedOperationException("Lower layer doesn't exist.")) - } - - /** Connects this layer with a higher layer, effectively linking calls - * `notifyHigher` to `higher.receive` and `higher.sendToLower` to `send`. */ - def connect[A](higher: ReactiveLayer[T, A]) = { - this.higherLayer = Some(higher) - higher.lowerLayer = Some(this) - higher - } - - /** Called from lower layer. */ - protected def receive(data: L): Unit - - /** Send data to this layer. - * @return a future value containing the data sent, or an error */ - def send(data: T): Future[T] -} \ No newline at end of file diff --git a/scala/ace/src/main/scala/com/github/jodersky/ace/protocol/SimpleActionLayer.scala b/scala/ace/src/main/scala/com/github/jodersky/ace/protocol/SimpleActionLayer.scala deleted file mode 100644 index 81e39cb..0000000 --- a/scala/ace/src/main/scala/com/github/jodersky/ace/protocol/SimpleActionLayer.scala +++ /dev/null @@ -1,8 +0,0 @@ -package com.github.jodersky.ace.protocol - -class SimpleActionLayer[A](action: A => Unit) extends ReactiveLayer[A, A] { - protected def receive(data: A) = action(data) - - def send(data: A) = sendToLower(data) - -} \ No newline at end of file diff --git a/scala/ace/src/main/scala/com/github/jodersky/ace/serial/Serial.scala b/scala/ace/src/main/scala/com/github/jodersky/ace/serial/Serial.scala deleted file mode 100644 index b688ec8..0000000 --- a/scala/ace/src/main/scala/com/github/jodersky/ace/serial/Serial.scala +++ /dev/null @@ -1,15 +0,0 @@ -package com.github.jodersky.ace.serial - -import com.github.jodersky.ace.protocol.ReactiveLayer - -trait Serial extends ReactiveLayer[Nothing, Seq[Int]] { - protected def receive(nothing: Nothing) = throw new UnsupportedOperationException("A receive function cannot be called on the lowest layer.") - - def begin(): Unit - def close(): Unit - -} - -object Serial { - def open(port: String, baudRate: Int)(implicit provider: SerialProvider) = provider.open(port, baudRate) -} \ No newline at end of file diff --git a/scala/ace/src/main/scala/com/github/jodersky/ace/serial/SerialProvider.scala b/scala/ace/src/main/scala/com/github/jodersky/ace/serial/SerialProvider.scala deleted file mode 100644 index 9c7b95c..0000000 --- a/scala/ace/src/main/scala/com/github/jodersky/ace/serial/SerialProvider.scala +++ /dev/null @@ -1,7 +0,0 @@ -package com.github.jodersky.ace.serial - -trait SerialProvider { - - def open(port: String, baudRate: Int): Serial - -} \ No newline at end of file diff --git a/scala/jssc/src/main/scala/com/github/jodersky/ace/Main.scala b/scala/jssc/src/main/scala/com/github/jodersky/ace/Main.scala index 55c23b9..1a673ec 100644 --- a/scala/jssc/src/main/scala/com/github/jodersky/ace/Main.scala +++ b/scala/jssc/src/main/scala/com/github/jodersky/ace/Main.scala @@ -1,29 +1,29 @@ package com.github.jodersky.ace -import com.github.jodersky.ace.protocol._ - +import com.github.jodersky.ace._ +import com.github.jodersky.ace.jssc._ import scala.concurrent.ExecutionContext.Implicits.global +import _root_.jssc.SerialPort + object Main { + + def main(args: Array[String]): Unit = { - import com.github.jodersky.ace.jssc._ - - val s = serial.Serial.open("/dev/ttyACM0", 9600) + val serial = new Serial("/dev/ttyACM0") val framer = new Framer val arq = new Arq(200) val app = new SimpleActionLayer((s: Seq[Int]) => println(s)) - s connect framer connect arq connect app - s.begin() + serial connect framer connect arq connect app + serial.begin() while (true) { app.send(Console.readLine.getBytes().map(_.toInt)).map(sent => Console.println("> " + sent)) } - - () } diff --git a/scala/jssc/src/main/scala/com/github/jodersky/ace/jssc/JSSCSerialProvider.scala b/scala/jssc/src/main/scala/com/github/jodersky/ace/jssc/JSSCSerialProvider.scala deleted file mode 100644 index 3bfd650..0000000 --- a/scala/jssc/src/main/scala/com/github/jodersky/ace/jssc/JSSCSerialProvider.scala +++ /dev/null @@ -1,48 +0,0 @@ -package com.github.jodersky.ace.jssc - -import scala.concurrent._ -import scala.concurrent.ExecutionContext.Implicits.global -import com.github.jodersky.ace.serial._ -import com.github.jodersky.ace.serial.Serial -import jssc.SerialPortEvent -import jssc.SerialPort -import jssc.SerialPortEventListener -import java.io.IOException - -object JSSCSerialProvider extends SerialProvider { - - def open(port: String, baudRate: Int) = new Serial { - val serialPort = new SerialPort(port); - serialPort.openPort() - serialPort.setParams( - baudRate, - SerialPort.DATABITS_8, - SerialPort.STOPBITS_1, - SerialPort.PARITY_NONE) - - val listener = new SerialPortEventListener { - override def serialEvent(event: SerialPortEvent) = { - if (event.isRXCHAR()) { - val bytes = serialPort.readBytes - if (bytes != null) notifyHigher(bytes.map(_ & 0xff)) - } - } - } - - def send(data: Seq[Int]) = future { - serialPort.writeBytes(data.toArray.map(_.toByte)) - } map { success => - if (success) data - else throw new IOException("Could not write to serial port.") - } - - def close() = serialPort.closePort() - - def begin() = { - val mask = SerialPort.MASK_RXCHAR + SerialPort.MASK_CTS + SerialPort.MASK_DSR - serialPort.setEventsMask(mask) - serialPort.addEventListener(listener) - } - } - -} \ No newline at end of file diff --git a/scala/jssc/src/main/scala/com/github/jodersky/ace/jssc/Serial.scala b/scala/jssc/src/main/scala/com/github/jodersky/ace/jssc/Serial.scala new file mode 100644 index 0000000..508a79e --- /dev/null +++ b/scala/jssc/src/main/scala/com/github/jodersky/ace/jssc/Serial.scala @@ -0,0 +1,47 @@ +package com.github.jodersky.ace.jssc + +import com.github.jodersky.ace.PhysicalLayer +import jssc.SerialPortEvent +import jssc.SerialPort +import jssc.SerialPortEventListener +import scala.concurrent._ +import scala.concurrent.ExecutionContext.Implicits.global +import java.io.IOException + +class Serial( + portName: String, + baudRate: Int = 9600, + dataBits: Int = SerialPort.DATABITS_8, + stopBits: Int = SerialPort.STOPBITS_1, + parity: Int = SerialPort.PARITY_NONE) extends PhysicalLayer { + + private val port = new SerialPort(portName) + + private val listener = new SerialPortEventListener { + override def serialEvent(event: SerialPortEvent) = { + if (event.isRXCHAR()) { + val bytes = port.readBytes + if (bytes != null) notifyHigher(bytes.map(_ & 0xff)) + } + } + } + + def send(data: Seq[Int]) = future { + port.writeBytes(data.toArray.map(_.toByte)) + } map { success => + if (success) data + else throw new IOException("Could not write to serial port.") + } + + def begin() { + val mask = SerialPort.MASK_RXCHAR + SerialPort.MASK_CTS + SerialPort.MASK_DSR + + port.openPort() + port.setParams(baudRate, dataBits, stopBits, parity) + port.setEventsMask(mask) + port.addEventListener(listener) + } + + def close() = port.closePort() + +} \ No newline at end of file diff --git a/scala/jssc/src/main/scala/com/github/jodersky/ace/jssc/package.scala b/scala/jssc/src/main/scala/com/github/jodersky/ace/jssc/package.scala deleted file mode 100644 index 891e588..0000000 --- a/scala/jssc/src/main/scala/com/github/jodersky/ace/jssc/package.scala +++ /dev/null @@ -1,7 +0,0 @@ -package com.github.jodersky.ace - -package object jssc { - - implicit val provider = JSSCSerialProvider - -} \ No newline at end of file -- cgit v1.2.3