From 47e2208c7c0c8788ddd137358cbbdb74b6884906 Mon Sep 17 00:00:00 2001 From: Jakob Odersky Date: Sun, 15 Jan 2017 18:26:24 -0800 Subject: Remove references to flow --- CHANGELOG.md | 4 ++ Documentation/developer.md | 18 +++--- Documentation/manual.md | 34 +++++----- build.sbt | 10 +-- core/build.sbt | 2 +- core/src/main/scala/akka/serial/Serial.scala | 8 +-- .../main/scala/akka/serial/SerialOperator.scala | 2 +- core/src/test/resources/reference.conf | 3 + .../test/scala/akka/serial/SerialManagerSpec.scala | 2 + project/Dependencies.scala | 2 +- samples/README.md | 2 +- .../akka/serial/samples/terminalstream/Main.scala | 66 +++++++++++++++++++ .../flow/samples/terminalstream/Main.scala | 66 ------------------- .../serial/samples/terminal/ConsoleReader.scala | 29 +++++++++ .../scala/akka/serial/samples/terminal/Main.scala | 30 +++++++++ .../akka/serial/samples/terminal/Terminal.scala | 75 ++++++++++++++++++++++ .../flow/samples/terminal/ConsoleReader.scala | 29 --------- .../ch/jodersky/flow/samples/terminal/Main.scala | 30 --------- .../jodersky/flow/samples/terminal/Terminal.scala | 75 ---------------------- .../scala/akka/serial/samples/watcher/main.scala | 50 +++++++++++++++ .../ch/jodersky/flow/samples/watcher/main.scala | 50 --------------- stream/build.sbt | 2 +- .../serial/stream/impl/SerialConnectionLogic.scala | 10 +-- stream/src/test/resources/application.conf | 3 + .../test/scala/akka/serial/stream/SerialSpec.scala | 2 +- sync/build.sbt | 2 +- .../test/scala/akka/serial/PseudoTerminal.scala | 2 +- 27 files changed, 311 insertions(+), 297 deletions(-) create mode 100644 core/src/test/resources/reference.conf create mode 100644 samples/terminal-stream/src/main/scala/akka/serial/samples/terminalstream/Main.scala delete mode 100644 samples/terminal-stream/src/main/scala/ch/jodersky/flow/samples/terminalstream/Main.scala create mode 100644 samples/terminal/src/main/scala/akka/serial/samples/terminal/ConsoleReader.scala create mode 100644 samples/terminal/src/main/scala/akka/serial/samples/terminal/Main.scala create mode 100644 samples/terminal/src/main/scala/akka/serial/samples/terminal/Terminal.scala delete mode 100644 samples/terminal/src/main/scala/ch/jodersky/flow/samples/terminal/ConsoleReader.scala delete mode 100644 samples/terminal/src/main/scala/ch/jodersky/flow/samples/terminal/Main.scala delete mode 100644 samples/terminal/src/main/scala/ch/jodersky/flow/samples/terminal/Terminal.scala create mode 100644 samples/watcher/src/main/scala/akka/serial/samples/watcher/main.scala delete mode 100644 samples/watcher/src/main/scala/ch/jodersky/flow/samples/watcher/main.scala create mode 100644 stream/src/test/resources/application.conf diff --git a/CHANGELOG.md b/CHANGELOG.md index 90d5e47..ee0f076 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# (unreleased) +- Rename project to akka-serial +- Move non-akka-dependent serial code into separate project, "akka-serial-sync" + # Version 3.0.4 - Implement unit tests - Upgrade Akka to 2.4.14 diff --git a/Documentation/developer.md b/Documentation/developer.md index 62f64d4..e7c581f 100644 --- a/Documentation/developer.md +++ b/Documentation/developer.md @@ -7,7 +7,7 @@ title: Developer Guide {:toc} # Building from Source -A complete build of flow involves two parts +A complete build of akka-serial involves two parts 1. Building Scala sources (the front-end), resulting in a platform independent artifact (i.e. a jar file). @@ -19,7 +19,7 @@ Both steps are independent, their only interaction being a header file generated Run `sbt core/package` in the base directory. This simply compiles Scala sources as with any standard sbt project and packages the resulting class files into a jar. ## Building Native Sources -The back-end is managed by CMake and all relevant files are contained in `flow-native/src`. +The back-end is managed by CMake and all relevant files are contained in `native/src`. ### Build Process Several steps are involved in producing the native library: @@ -45,9 +45,9 @@ Several steps are involved in producing the native library: - put into a "fat" jar, useful for dependency management with sbt (see next section) ### Creating a Fat Jar -The native library produced in the previous step may be bundled into a "fat" jar so that it can be included in SBT projects through its regular dependency mechanisms. In this process, sbt basically acts as a wrapper script around CMake, calling the native build process and packaging generated libraries. Running `sbt native/package` produces the fat jar in `flow-native/target`. +The native library produced in the previous step may be bundled into a "fat" jar so that it can be included in sbt projects through its regular dependency mechanisms. In this process, sbt basically acts as a wrapper script around CMake, calling the native build process and packaging generated libraries. Running `sbt native/package` produces the fat jar in `native/target`. -Note: an important feature of fat jars is to include native libraries for several platforms. To copy binaries compiled on other platforms to the fat jar, place them in a subfolder of `flow-native/lib_native`. The subfolder should have the name `$(arch)-$(kernel)`, where `arch` and `kernel` are, respectively, the lower-case values returned by `uname -m` and `uname -s`. +Note: an important feature of fat jars is to include native libraries for several platforms. To copy binaries compiled on other platforms to the fat jar, place them in a subfolder of `native/lib_native`. The subfolder should have the name `$(arch)-$(kernel)`, where `arch` and `kernel` are, respectively, the lower-case values returned by `uname -m` and `uname -s`. ### Note About Versioning The project and package versions follow a [semantic](http://semver.org/) pattern: `M.m.p`, where @@ -58,11 +58,11 @@ The project and package versions follow a [semantic](http://semver.org/) pattern - `p` is the patch number, representing internal modifications such as bug-fixes -Usually (following most Linux distribution's conventions), shared libraries produced by a project `name` of version `M.m.p` are named `libname.so.M.m.p`. However, since when accessing shared libraries through the JVM, only the `name` can be specified and no particular version, the convention adopted by flow is to append `M` to the library name and always keep the major version at zero. E.g. `libflow.so.3.1.2` becomes `libflow3.so.0.1.2`. +Usually (following most Linux distribution's conventions), shared libraries produced by a project `name` of version `M.m.p` are named `libname.so.M.m.p`. However, since when accessing shared libraries through the JVM, only the `name` can be specified and no particular version, the convention adopted by akka-serial is to append `M` to the library name and always keep the major version at zero. E.g. `libakkaserial.so.3.1.2` becomes `libakakserial3.so.0.1.2`. # Testing -`flow-samples` directory contains fully functional application examples of flow. To run an example, change to the base directory of flow and run sbt samples/run. -All projects, including samples, can be listed by running sbt projects. +The `samples` directory contains fully functional application examples of akka-serial. To run an example, change to the base directory of akka-serial and run sbt samples/run. +All projects, including samples, can be listed by running `sbt projects`. To be able connect you can use real device (arduino) burned with sample-echo (`dev/arduino-terminal`) code, or create Virtual Serial Port pair @@ -101,11 +101,11 @@ socat -d -d pty,raw,echo=0 "exec:/bin/cat,pty,raw,echo=0" ``` # Publishing and Releasing -Releases are handled automatically by the continuous integration and deployment system (Travis CI). A release will be performed for every annotated Git tag that is pushed to the main repository. +Releases are handled automatically by the continuous integration and deployment system, Travis CI. A release will be performed for every annotated Git tag that is pushed to the main repository. Here are a couple of observations on the release process: -- During a release, only readily available libraries in `lib_native` are packaged into the fat jar, no local native compilation is performed. The rationale behind this is that while native libraries rarely change, they are still tied to the version of libc of the compiling system. Since the releases are mostly done on a development machine (cutting-edge OS), compiling native libraries locally could break compatibility with older systems. +- During a release, only readily available libraries in `native/lib_native` are packaged into the fat jar, no local native compilation is performed. The rationale behind this is that while native libraries rarely change, they are still tied to the version of libc of the compiling system. Since the releases are mostly done on a development machine with a cutting-edge OS, compiling native libraries locally could break compatibility with older systems. - The website is not automatically updated. After creating a new release: diff --git a/Documentation/manual.md b/Documentation/manual.md index 0a65e42..f51689f 100644 --- a/Documentation/manual.md +++ b/Documentation/manual.md @@ -7,13 +7,13 @@ title: User Guide {:toc} # Getting Started -Flow uses sbt as build system. To get started, include a dependency to flow in your project: +akka-serial uses sbt as build system. To get started, include a dependency to akka-serial in your project: ~~~scala -libraryDependencies += "ch.jodersky" %% "flow-core" % "@version@" +libraryDependencies += "ch.jodersky" %% "akka-serial-core" % "@version@" ~~~ -Next, you need to include flow's native library that supports communication for serial devices. +Next, you need to include akka-serial's native library that supports communication for serial devices. ## Including Native Library There are two options to include the native library: @@ -28,19 +28,19 @@ It is recommended that you use the first option for testing purposes or end-user In case your kernel/architecture combination is present in the "supported platforms" table in the [downloads section]({{site.url}}/downloads/), add a second dependency to your project: ~~~scala -libraryDependencies += "ch.jodersky" % "flow-native" % "@version@" % "runtime" +libraryDependencies += "ch.jodersky" % "akka-serial-native" % "@version@" % "runtime" ~~~ This will add a jar to your classpath containing native libraries for various platforms. At run-time, the correct library for the current platform is selected, extracted and loaded. This solution enables running applications seamlessly, as if they were pure JVM applications. ### Maximum Portability -Start by obtaining a copy of the native library, either by [building flow](./developer) or by [downloading]({{site.url}}/downloads/) a native archive. In order to work with this version of flow, native libraries need to be of major version @native_major@ and minor version greater or equal to @native_minor@. +Start by obtaining a copy of the native library, either by [building akka-serial](./developer) or by [downloading]({{site.url}}/downloads/) a native archive. In order to work with this version of akka-serial, native libraries need to be of major version @native_major@ and minor version greater or equal to @native_minor@. -Then, for every end-user application that relies on flow, manually add the native library for the current platform to the JVM's library path. This can be achieved through various ways, notably: +Then, for every end-user application that relies on akka-serial, manually add the native library for the current platform to the JVM's library path. This can be achieved through various ways, notably: - Per application: - Run your program with the command-line option ```-Djava.library.path=".:"```. E.g. ```java -Djava.library.path=".:/home/" -jar your-app.jar``` + Run your program with the command-line option ```-Djava.library.path=".:"```. E.g. ```java -Djava.library.path=".:/home/" -jar your-app.jar``` - System- or user-wide: @@ -49,15 +49,15 @@ Then, for every end-user application that relies on flow, manually add the nativ --- # Communication Protocol -The following is a general guide on the usage of flow. If you prefer a complete example, check out the code contained in the [flow-samples](https://github.com/jodersky/flow/tree/v@version@/flow-samples) directory. +The following is a general guide on the usage of akak-serial. If you prefer a complete example, check out the code contained in the [samples](https://github.com/jodersky/akka-serial/tree/v@version@/samples) directory. -Flow's API follows that of an actor based system, where each actor is assigned specific functions involved in serial communication. The two main actor types are: +akka-serial's API follows that of an actor based system, where each actor is assigned specific functions involved in serial communication. The two main actor types are: 1. Serial "manager". The manager is a singleton actor that is instantiated once per actor system, a reference to it may be obtained with `IO(Serial)`. It is typically used to open serial ports (see following section). 2. Serial "operators". Operators are created once per open serial port and serve as an intermediate between client code and native code dealing with serial data transmission and reception. They isolate the user from threading issues and enable the reactive dispatch of incoming data. A serial operator is said to be "associated" to its underlying open serial port. -The messages understood by flow's actors are all contained in the `ch.jodersky.akka.serial.Serial` object. They are well documented and should serve as the entry point when searching the API documentation. +The messages understood by akka-serial's actors are all contained in the `akka.serial.Serial` object. They are well documented and should serve as the entry point when searching the API documentation. ## Opening a Port A serial port is opened by sending an `Open` message to the serial manager. The response varies on the outcome of opening the underlying serial port. @@ -67,7 +67,7 @@ A serial port is opened by sending an `Open` message to the serial manager. The 2. In case of success, the sender is notified with an `Opened` message. This message is sent from an operator actor, spawned by the serial manager. It is useful to capture the sender (i.e. the operator) of this message as all further communication with the newly opened port must pass through the operator. ~~~scala -import ch.jodersky.akka.serial.{ Serial, SerialSettings, AccessDeniedException } +import akka.serial.{ Serial, SerialSettings, AccessDeniedException } val port = "/dev/ttyXXX" val settings = SerialSettings( @@ -138,7 +138,7 @@ The opposite is not true by default, i.e. if the operator crashes (this can happ --- # Watching Ports -As of version 2.2.0, flow can watch directories for new files. On most unix systems this can be used for watching for new serial ports in `/dev/`. +akka-serial can watch directories for new files. On most unix systems this can be used for watching for new serial ports in `/dev/`. Watching happens through a message-based, publish-subscribe protocol as explained in the sections below. ## Subscribing @@ -183,13 +183,13 @@ Note that the manager has a deathwatch on every subscribed client. Hence, should --- # Stream Support -Flow provides support for Akka streams and thus can be interfaced with reactive-streams. Support is implemented in a separate module, which needs to be added as a library dependency: +akka-serial provides support for Akka streams and thus can be interfaced with reactive-streams. Support is implemented in a separate module, which needs to be added as a library dependency: ~~~scala -libraryDependencies += "ch.jodersky" %% "flow-stream" % "@version@" +libraryDependencies += "ch.jodersky" %% "akka-serial-stream" % "@version@" ~~~ -The main entry point for serial streaming is `ch.jodersky.akka.serial.stream.Serial`. Its API is also well documented and should serve as the starting point when searching documentation on serial streaming. +The main entry point for serial streaming is `akka.serial.stream.Serial`. Its API is also well documented and should serve as the starting point when searching documentation on serial streaming. ## Opening a Port Connection is established by materializing a `Flow[ByteString, ByteString, Future[Connection]]` obtained by calling `Serial().open()` @@ -217,6 +217,6 @@ Note that backpressure is only available for writing, to add backpressure on the The underlying serial port is closed when its materialized serial flow is closed. ## Errors and Resource Handling -Any errors described in flow-core can also be encountered in flow-streaming. When thrown, they will be wrapped as the cause of a `StreamSerialException` and cause the the serial `Flow` stage to fail. +Any errors described in akka-serial-core can also be encountered in akka-serial-stream. When thrown, they will be wrapped as the cause of a `StreamSerialException` and cause the the serial `Flow` stage to fail. -As with flow-core, native resources are handled by underlying Akka mechanisms and any crashes in user code will automatically case the resources to be freed. +As with akka-serial-core, native resources are handled by underlying Akka mechanisms and any crashes in user code will automatically case the resources to be freed. diff --git a/build.sbt b/build.sbt index 9d2b5bf..1b6262d 100644 --- a/build.sbt +++ b/build.sbt @@ -9,11 +9,13 @@ scalacOptions in ThisBuild ++= Seq( "-target:jvm-1.8" ) fork in ThisBuild := true +connectInput in run in ThisBuild := true +outputStrategy in run in ThisBuild := Some(StdoutOutput) // Publishing organization in ThisBuild := "ch.jodersky" licenses in ThisBuild := Seq(("BSD New", url("http://opensource.org/licenses/BSD-3-Clause"))) -homepage in ThisBuild := Some(url("https://jodersky.github.io/flow")) +homepage in ThisBuild := Some(url("https://jodersky.github.io/akka-serial")) publishMavenStyle in ThisBuild := true publishTo in ThisBuild := { val nexus = "https://oss.sonatype.org/" @@ -21,8 +23,8 @@ publishTo in ThisBuild := { } pomExtra in ThisBuild := { - git@github.com:jodersky/flow.git - scm:git:git@github.com:jodersky/flow.git + git@github.com:jodersky/akka-serial.git + scm:git:git@github.com:jodersky/akka-serial.git @@ -45,7 +47,7 @@ lazy val native = (project in file("native")) lazy val stream = (project in file("stream")) .settings(name := "akka-serial-stream") - .dependsOn(core, sync % "test->test", native % "test->runtime") + .dependsOn(core, sync % "test->test") lazy val sync = (project in file("sync")) .settings(name := "akka-serial-sync") diff --git a/core/build.sbt b/core/build.sbt index d1f1dc6..78673ce 100644 --- a/core/build.sbt +++ b/core/build.sbt @@ -1,4 +1,4 @@ -import flow.Dependencies +import akkaserial.Dependencies libraryDependencies += Dependencies.akkaActor libraryDependencies += Dependencies.akkaTestKit % "test" diff --git a/core/src/main/scala/akka/serial/Serial.scala b/core/src/main/scala/akka/serial/Serial.scala index 75ed888..a8e564a 100644 --- a/core/src/main/scala/akka/serial/Serial.scala +++ b/core/src/main/scala/akka/serial/Serial.scala @@ -3,16 +3,16 @@ package akka.serial import akka.actor.ExtensionKey import akka.util.ByteString -/** Defines messages used by flow's serial IO layer. */ +/** Defines messages used by akka-serial's serial IO layer. */ object Serial extends ExtensionKey[SerialExt] { - /** Base trait for any flow-related messages. */ + /** Base trait for any akka-serial-related messages. */ sealed trait Message - /** A message extending this trait is to be viewed as a command, an out-bound message issued by the client to flow's API. */ + /** A message extending this trait is to be viewed as a command, an out-bound message issued by the client to akka-serial's API. */ trait Command extends Message - /** A message extending this trait is to be viewed as an event, an in-bound message issued by flow to the client. */ + /** A message extending this trait is to be viewed as an event, an in-bound message issued by akka-serial to the client. */ trait Event extends Message /** A command has failed. */ diff --git a/core/src/main/scala/akka/serial/SerialOperator.scala b/core/src/main/scala/akka/serial/SerialOperator.scala index a0e4b5c..cb5b46d 100644 --- a/core/src/main/scala/akka/serial/SerialOperator.scala +++ b/core/src/main/scala/akka/serial/SerialOperator.scala @@ -1,6 +1,6 @@ package akka.serial -import akka.actor.{ Actor, ActorLogging, ActorRef, Props, Terminated } +import akka.actor.{ Actor, ActorRef, Props, Terminated } import akka.util.ByteString import java.nio.ByteBuffer diff --git a/core/src/test/resources/reference.conf b/core/src/test/resources/reference.conf new file mode 100644 index 0000000..bdf80e2 --- /dev/null +++ b/core/src/test/resources/reference.conf @@ -0,0 +1,3 @@ +# Don't fill test output with log messages +akka.stdout-loglevel = "OFF" +akka.loglevel = "OFF" \ No newline at end of file diff --git a/core/src/test/scala/akka/serial/SerialManagerSpec.scala b/core/src/test/scala/akka/serial/SerialManagerSpec.scala index 7683635..eab5013 100644 --- a/core/src/test/scala/akka/serial/SerialManagerSpec.scala +++ b/core/src/test/scala/akka/serial/SerialManagerSpec.scala @@ -30,6 +30,8 @@ class SerialManagerSpec "fail opening a non-existing port" in { val cmd = Serial.Open("nonexistent", SerialSettings(115200)) manager ! cmd + + //manager.context.set assert(expectMsgType[Serial.CommandFailed].command == cmd) } diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 0e7d1ac..4ba7c12 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -1,4 +1,4 @@ -package flow +package akkaserial import sbt._ diff --git a/samples/README.md b/samples/README.md index 5460b4d..d51b9c2 100644 --- a/samples/README.md +++ b/samples/README.md @@ -1,3 +1,3 @@ -This directory contains fully functional application examples of flow. To run an example, change to the base directory of flow (this parent's directory) and run `sbt samples/run`. +This directory contains fully functional application examples of akka-serial. To run an example, change to the base directory of akka-serial (this parent's directory) and run `sbt samples/run`. All projects, including samples, can be listed by running `sbt projects`. diff --git a/samples/terminal-stream/src/main/scala/akka/serial/samples/terminalstream/Main.scala b/samples/terminal-stream/src/main/scala/akka/serial/samples/terminalstream/Main.scala new file mode 100644 index 0000000..de626a3 --- /dev/null +++ b/samples/terminal-stream/src/main/scala/akka/serial/samples/terminalstream/Main.scala @@ -0,0 +1,66 @@ +package akka.serial +package samples.terminalstream + +import scala.concurrent.Future +import scala.concurrent.duration._ +import scala.io.StdIn + +import akka.actor.ActorSystem +import akka.stream.ActorMaterializer +import akka.stream.scaladsl.{Flow, Keep, Sink, Source} +import akka.util.ByteString + +import stream.Serial + +object Main { + + final val Delay = FiniteDuration(500, MILLISECONDS) + + implicit val system = ActorSystem("terminal-stream") + implicit val materializer = ActorMaterializer() + + def ask(label: String, default: String) = { + print(label + " [" + default.toString + "]: ") + val in = StdIn.readLine() + println("") + if (in.isEmpty) default else in + } + + def main(args: Array[String]): Unit = { + import system.dispatcher + + val port = ask("Device", "/dev/ttyACM0") + val baud = ask("Baud rate", "115200").toInt + val cs = ask("Char size", "8").toInt + val tsb = ask("Use two stop bits", "false").toBoolean + val parity = Parity(ask("Parity (0=None, 1=Odd, 2=Even)", "0").toInt) + val settings = SerialSettings(baud, cs, tsb, parity) + + val serial: Flow[ByteString, ByteString, Future[Serial.Connection]] = + Serial().open(port, settings) + + val printer: Sink[ByteString, _] = Sink.foreach[ByteString]{data => + println("server says: " + data.decodeString("UTF-8")) + } + + val ticker: Source[ByteString, _] = Source.tick(Delay, Delay, ()).scan(0){case (x, _) => + x + 1 + }.map{ x => + println(x) + ByteString(x.toString) + } + + val connection: Future[Serial.Connection] = ticker.viaMat(serial)(Keep.right).to(printer).run() + + connection map { conn => + println("Connected to " + conn.port) + StdIn.readLine("Press enter to exit") + } recover { case err => + println("Cannot connect: " + err) + } andThen { case _ => + system.terminate() + } + + } + +} diff --git a/samples/terminal-stream/src/main/scala/ch/jodersky/flow/samples/terminalstream/Main.scala b/samples/terminal-stream/src/main/scala/ch/jodersky/flow/samples/terminalstream/Main.scala deleted file mode 100644 index a752e22..0000000 --- a/samples/terminal-stream/src/main/scala/ch/jodersky/flow/samples/terminalstream/Main.scala +++ /dev/null @@ -1,66 +0,0 @@ -akka.serial -package samples.terminalstream - -import scala.concurrent.Future -import scala.concurrent.duration._ -import scala.io.StdIn - -import akka.actor.ActorSystem -import akka.stream.ActorMaterializer -import akka.stream.scaladsl.{Flow, Keep, Sink, Source} -import akka.util.ByteString - -import stream.Serial - -object Main { - - final val Delay = FiniteDuration(500, MILLISECONDS) - - implicit val system = ActorSystem("terminal-stream") - implicit val materializer = ActorMaterializer() - - def ask(label: String, default: String) = { - print(label + " [" + default.toString + "]: ") - val in = StdIn.readLine() - println("") - if (in.isEmpty) default else in - } - - def main(args: Array[String]): Unit = { - import system.dispatcher - - val port = ask("Device", "/dev/ttyACM0") - val baud = ask("Baud rate", "115200").toInt - val cs = ask("Char size", "8").toInt - val tsb = ask("Use two stop bits", "false").toBoolean - val parity = Parity(ask("Parity (0=None, 1=Odd, 2=Even)", "0").toInt) - val settings = SerialSettings(baud, cs, tsb, parity) - - val serial: Flow[ByteString, ByteString, Future[Serial.Connection]] = - Serial().open(port, settings) - - val printer: Sink[ByteString, _] = Sink.foreach[ByteString]{data => - println("server says: " + data.decodeString("UTF-8")) - } - - val ticker: Source[ByteString, _] = Source.tick(Delay, Delay, ()).scan(0){case (x, _) => - x + 1 - }.map{ x => - println(x) - ByteString(x.toString) - } - - val connection: Future[Serial.Connection] = ticker.viaMat(serial)(Keep.right).to(printer).run() - - connection map { conn => - println("Connected to " + conn.port) - StdIn.readLine("Press enter to exit") - } recover { case err => - println("Cannot connect: " + err) - } andThen { case _ => - system.terminate() - } - - } - -} diff --git a/samples/terminal/src/main/scala/akka/serial/samples/terminal/ConsoleReader.scala b/samples/terminal/src/main/scala/akka/serial/samples/terminal/ConsoleReader.scala new file mode 100644 index 0000000..cc14dd4 --- /dev/null +++ b/samples/terminal/src/main/scala/akka/serial/samples/terminal/ConsoleReader.scala @@ -0,0 +1,29 @@ +package akka.serial +package samples.terminal + +import akka.actor.Actor +import scala.io.StdIn + +class ConsoleReader extends Actor { + import context._ + import ConsoleReader._ + + def receive = { + case Read => + StdIn.readLine() match { + case ":q" | null => parent ! EOT + case s => { + parent ! ConsoleInput(s) + } + } + } + +} + +object ConsoleReader { + + case object Read + case object EOT + case class ConsoleInput(in: String) + +} diff --git a/samples/terminal/src/main/scala/akka/serial/samples/terminal/Main.scala b/samples/terminal/src/main/scala/akka/serial/samples/terminal/Main.scala new file mode 100644 index 0000000..0226e72 --- /dev/null +++ b/samples/terminal/src/main/scala/akka/serial/samples/terminal/Main.scala @@ -0,0 +1,30 @@ +package akka.serial +package samples.terminal + +import akka.actor.ActorSystem +import scala.io.StdIn + +object Main { + + def ask(label: String, default: String) = { + print(label + " [" + default.toString + "]: ") + val in = StdIn.readLine() + println("") + if (in.isEmpty) default else in + } + + def main(args: Array[String]): Unit = { + val port = ask("Device", "/dev/ttyACM0") + val baud = ask("Baud rate", "115200").toInt + val cs = ask("Char size", "8").toInt + val tsb = ask("Use two stop bits", "false").toBoolean + val parity = Parity(ask("Parity (0=None, 1=Odd, 2=Even)", "0").toInt) + val settings = SerialSettings(baud, cs, tsb, parity) + + println("Starting terminal system, enter :q to exit.") + Serial.debug(true) + val system = ActorSystem("akka-serial") + val terminal = system.actorOf(Terminal(port, settings), name = "terminal") + system.registerOnTermination(println("Stopped terminal system.")) + } +} diff --git a/samples/terminal/src/main/scala/akka/serial/samples/terminal/Terminal.scala b/samples/terminal/src/main/scala/akka/serial/samples/terminal/Terminal.scala new file mode 100644 index 0000000..55b0422 --- /dev/null +++ b/samples/terminal/src/main/scala/akka/serial/samples/terminal/Terminal.scala @@ -0,0 +1,75 @@ +package akka.serial +package samples.terminal + +import akka.actor.{ Actor, ActorLogging, ActorRef, Props, Terminated, actorRef2Scala } +import akka.io.IO +import akka.util.ByteString + +class Terminal(port: String, settings: SerialSettings) extends Actor with ActorLogging { + import Terminal._ + import context._ + + val reader = actorOf(Props[ConsoleReader]) + + log.info(s"Requesting manager to open port: ${port}, baud: ${settings.baud}") + IO(Serial) ! Serial.Open(port, settings) + + override def postStop() = { + system.terminate() + } + + def receive = { + case Serial.CommandFailed(cmd, reason) => { + log.error(s"Connection failed, stopping terminal. Reason: ${reason}") + context stop self + } + case Serial.Opened(port) => { + log.info(s"Port ${port} is now open.") + val operator = sender + context become opened(operator) + context watch operator + reader ! ConsoleReader.Read + } + } + + def opened(operator: ActorRef): Receive = { + + case Serial.Received(data) => { + log.info(s"Received data: ${formatData(data)}") + } + + case Terminal.Wrote(data) => log.info(s"Wrote data: ${formatData(data)}") + + case Serial.Closed => { + log.info("Operator closed normally, exiting terminal.") + context unwatch operator + context stop self + } + + case Terminated(`operator`) => { + log.error("Operator crashed, exiting terminal.") + context stop self + } + + case ConsoleReader.EOT => { + log.info("Initiating close.") + operator ! Serial.Close + } + + case ConsoleReader.ConsoleInput(input) => { + val data = ByteString(input.getBytes) + operator ! Serial.Write(data, length => Wrote(data.take(length))) + reader ! ConsoleReader.Read + } + } + +} + +object Terminal { + case class Wrote(data: ByteString) extends Serial.Event + + def apply(port: String, settings: SerialSettings) = Props(classOf[Terminal], port, settings) + + private def formatData(data: ByteString) = data.mkString("[", ",", "]") + " " + (new String(data.toArray, "UTF-8")) + +} diff --git a/samples/terminal/src/main/scala/ch/jodersky/flow/samples/terminal/ConsoleReader.scala b/samples/terminal/src/main/scala/ch/jodersky/flow/samples/terminal/ConsoleReader.scala deleted file mode 100644 index cc14dd4..0000000 --- a/samples/terminal/src/main/scala/ch/jodersky/flow/samples/terminal/ConsoleReader.scala +++ /dev/null @@ -1,29 +0,0 @@ -package akka.serial -package samples.terminal - -import akka.actor.Actor -import scala.io.StdIn - -class ConsoleReader extends Actor { - import context._ - import ConsoleReader._ - - def receive = { - case Read => - StdIn.readLine() match { - case ":q" | null => parent ! EOT - case s => { - parent ! ConsoleInput(s) - } - } - } - -} - -object ConsoleReader { - - case object Read - case object EOT - case class ConsoleInput(in: String) - -} diff --git a/samples/terminal/src/main/scala/ch/jodersky/flow/samples/terminal/Main.scala b/samples/terminal/src/main/scala/ch/jodersky/flow/samples/terminal/Main.scala deleted file mode 100644 index b7e371d..0000000 --- a/samples/terminal/src/main/scala/ch/jodersky/flow/samples/terminal/Main.scala +++ /dev/null @@ -1,30 +0,0 @@ -package akka.serial -package samples.terminal - -import akka.actor.ActorSystem -import scala.io.StdIn - -object Main { - - def ask(label: String, default: String) = { - print(label + " [" + default.toString + "]: ") - val in = StdIn.readLine() - println("") - if (in.isEmpty) default else in - } - - def main(args: Array[String]): Unit = { - val port = ask("Device", "/dev/ttyACM0") - val baud = ask("Baud rate", "115200").toInt - val cs = ask("Char size", "8").toInt - val tsb = ask("Use two stop bits", "false").toBoolean - val parity = Parity(ask("Parity (0=None, 1=Odd, 2=Even)", "0").toInt) - val settings = SerialSettings(baud, cs, tsb, parity) - - println("Starting terminal system, enter :q to exit.") - Serial.debug(true) - val system = ActorSystem("flow") - val terminal = system.actorOf(Terminal(port, settings), name = "terminal") - system.registerOnTermination(println("Stopped terminal system.")) - } -} diff --git a/samples/terminal/src/main/scala/ch/jodersky/flow/samples/terminal/Terminal.scala b/samples/terminal/src/main/scala/ch/jodersky/flow/samples/terminal/Terminal.scala deleted file mode 100644 index 55b0422..0000000 --- a/samples/terminal/src/main/scala/ch/jodersky/flow/samples/terminal/Terminal.scala +++ /dev/null @@ -1,75 +0,0 @@ -package akka.serial -package samples.terminal - -import akka.actor.{ Actor, ActorLogging, ActorRef, Props, Terminated, actorRef2Scala } -import akka.io.IO -import akka.util.ByteString - -class Terminal(port: String, settings: SerialSettings) extends Actor with ActorLogging { - import Terminal._ - import context._ - - val reader = actorOf(Props[ConsoleReader]) - - log.info(s"Requesting manager to open port: ${port}, baud: ${settings.baud}") - IO(Serial) ! Serial.Open(port, settings) - - override def postStop() = { - system.terminate() - } - - def receive = { - case Serial.CommandFailed(cmd, reason) => { - log.error(s"Connection failed, stopping terminal. Reason: ${reason}") - context stop self - } - case Serial.Opened(port) => { - log.info(s"Port ${port} is now open.") - val operator = sender - context become opened(operator) - context watch operator - reader ! ConsoleReader.Read - } - } - - def opened(operator: ActorRef): Receive = { - - case Serial.Received(data) => { - log.info(s"Received data: ${formatData(data)}") - } - - case Terminal.Wrote(data) => log.info(s"Wrote data: ${formatData(data)}") - - case Serial.Closed => { - log.info("Operator closed normally, exiting terminal.") - context unwatch operator - context stop self - } - - case Terminated(`operator`) => { - log.error("Operator crashed, exiting terminal.") - context stop self - } - - case ConsoleReader.EOT => { - log.info("Initiating close.") - operator ! Serial.Close - } - - case ConsoleReader.ConsoleInput(input) => { - val data = ByteString(input.getBytes) - operator ! Serial.Write(data, length => Wrote(data.take(length))) - reader ! ConsoleReader.Read - } - } - -} - -object Terminal { - case class Wrote(data: ByteString) extends Serial.Event - - def apply(port: String, settings: SerialSettings) = Props(classOf[Terminal], port, settings) - - private def formatData(data: ByteString) = data.mkString("[", ",", "]") + " " + (new String(data.toArray, "UTF-8")) - -} diff --git a/samples/watcher/src/main/scala/akka/serial/samples/watcher/main.scala b/samples/watcher/src/main/scala/akka/serial/samples/watcher/main.scala new file mode 100644 index 0000000..d9a0d4e --- /dev/null +++ b/samples/watcher/src/main/scala/akka/serial/samples/watcher/main.scala @@ -0,0 +1,50 @@ +package akka.serial +package samples.watcher + +import akka.actor.{ Actor, ActorLogging, ActorSystem, Props } +import akka.io.IO +import scala.io.StdIn + +class Watcher extends Actor with ActorLogging { + import context._ + + val ports = List( + "/dev/ttyUSB\\d+", + "/dev/ttyACM\\d+", + "/dev/cu\\d+", + "/dev/ttyS\\d+" + ) + + override def preStart() = { + val cmd = Serial.Watch() + IO(Serial) ! cmd //watch for new devices + log.info(s"Watching ${cmd.directory} for new devices.") + } + + def receive = { + + case Serial.CommandFailed(w: Serial.Watch, err) => + log.error(err, s"Could not get a watch on ${w.directory}.") + context stop self + + case Serial.Connected(path) => + log.info(s"New device: ${path}") + ports.find(path matches _) match { + case Some(port) => log.info(s"Device is a serial device.") + case None => log.warning(s"Device is NOT serial device.") + } + + } + +} + +object Main { + + def main(args: Array[String]): Unit = { + val system = ActorSystem("akka-serial") + val watcher = system.actorOf(Props(classOf[Watcher]), name = "watcher") + StdIn.readLine() + system.terminate() + } + +} diff --git a/samples/watcher/src/main/scala/ch/jodersky/flow/samples/watcher/main.scala b/samples/watcher/src/main/scala/ch/jodersky/flow/samples/watcher/main.scala deleted file mode 100644 index 901f27b..0000000 --- a/samples/watcher/src/main/scala/ch/jodersky/flow/samples/watcher/main.scala +++ /dev/null @@ -1,50 +0,0 @@ -package akka.serial -package samples.watcher - -import akka.actor.{ Actor, ActorLogging, ActorSystem, Props } -import akka.io.IO -import scala.io.StdIn - -class Watcher extends Actor with ActorLogging { - import context._ - - val ports = List( - "/dev/ttyUSB\\d+", - "/dev/ttyACM\\d+", - "/dev/cu\\d+", - "/dev/ttyS\\d+" - ) - - override def preStart() = { - val cmd = Serial.Watch() - IO(Serial) ! cmd //watch for new devices - log.info(s"Watching ${cmd.directory} for new devices.") - } - - def receive = { - - case Serial.CommandFailed(w: Serial.Watch, err) => - log.error(err, s"Could not get a watch on ${w.directory}.") - context stop self - - case Serial.Connected(path) => - log.info(s"New device: ${path}") - ports.find(path matches _) match { - case Some(port) => log.info(s"Device is a serial device.") - case None => log.warning(s"Device is NOT serial device.") - } - - } - -} - -object Main { - - def main(args: Array[String]): Unit = { - val system = ActorSystem("flow") - val watcher = system.actorOf(Props(classOf[Watcher]), name = "watcher") - StdIn.readLine() - system.terminate() - } - -} diff --git a/stream/build.sbt b/stream/build.sbt index c9aa7eb..e66f1d5 100644 --- a/stream/build.sbt +++ b/stream/build.sbt @@ -1,4 +1,4 @@ -import flow.Dependencies +import akkaserial.Dependencies libraryDependencies += Dependencies.akkaActor libraryDependencies += Dependencies.akkaStream diff --git a/stream/src/main/scala/akka/serial/stream/impl/SerialConnectionLogic.scala b/stream/src/main/scala/akka/serial/stream/impl/SerialConnectionLogic.scala index 0f81597..a650c82 100644 --- a/stream/src/main/scala/akka/serial/stream/impl/SerialConnectionLogic.scala +++ b/stream/src/main/scala/akka/serial/stream/impl/SerialConnectionLogic.scala @@ -143,11 +143,11 @@ private[stream] class SerialConnectionLogic( if (isAvailable(out)) { push(out, data) } else if (failOnOverflow) { - /* Note that the native backend does not provide any way of informing about - * dropped serial data. However, in most cases, a computer capable of running flow - * is also capable of processing incoming serial data at typical baud rates. - * Hence packets will usually only be dropped if an application that uses flow - * backpressures, which can however be detected here. */ + /* Note that the native backend does not provide any way of informing about dropped serial + * data. However, in most cases, a computer capable of running akka-serial is also capable + * of processing incoming serial data at typical baud rates. Hence packets will usually + * only be dropped if an application that uses akka-serial backpressures, which can + * however be detected here. */ failStage(new StreamSerialException("Incoming serial data was dropped.")) } diff --git a/stream/src/test/resources/application.conf b/stream/src/test/resources/application.conf new file mode 100644 index 0000000..bdf80e2 --- /dev/null +++ b/stream/src/test/resources/application.conf @@ -0,0 +1,3 @@ +# Don't fill test output with log messages +akka.stdout-loglevel = "OFF" +akka.loglevel = "OFF" \ No newline at end of file diff --git a/stream/src/test/scala/akka/serial/stream/SerialSpec.scala b/stream/src/test/scala/akka/serial/stream/SerialSpec.scala index 0803732..7a64383 100644 --- a/stream/src/test/scala/akka/serial/stream/SerialSpec.scala +++ b/stream/src/test/scala/akka/serial/stream/SerialSpec.scala @@ -12,7 +12,7 @@ import org.scalatest._ class SerialSpec extends WordSpec with BeforeAndAfterAll with PseudoTerminal { - implicit val system = ActorSystem("flow-test") + implicit val system = ActorSystem("akka-serial-test") implicit val materializer = ActorMaterializer() override def afterAll { diff --git a/sync/build.sbt b/sync/build.sbt index 2e58d91..547a3fd 100644 --- a/sync/build.sbt +++ b/sync/build.sbt @@ -1,4 +1,4 @@ -import flow.Dependencies +import akkaserial.Dependencies libraryDependencies += Dependencies.scalatest % "test" diff --git a/sync/src/test/scala/akka/serial/PseudoTerminal.scala b/sync/src/test/scala/akka/serial/PseudoTerminal.scala index 37eaf91..3e9e9fe 100644 --- a/sync/src/test/scala/akka/serial/PseudoTerminal.scala +++ b/sync/src/test/scala/akka/serial/PseudoTerminal.scala @@ -12,7 +12,7 @@ trait PseudoTerminal { final val SetupTimeout = 100.milliseconds def withEcho[A](action: (String, SerialSettings) => A): A = { - val dir = Files.createTempDirectory("flow-pty").toFile + val dir = Files.createTempDirectory("akka-serial-pty").toFile val pty = new File(dir, "pty") val socat = try { -- cgit v1.2.3