aboutsummaryrefslogtreecommitdiff
path: root/Documentation
diff options
context:
space:
mode:
authorJakob Odersky <jodersky@gmail.com>2015-05-06 11:13:50 +0200
committerJakob Odersky <jodersky@gmail.com>2015-05-06 12:13:11 +0200
commit57d0893e17b12bf5f8bb10aeb6386dd25d708571 (patch)
treed1d7a98a9f756cc791832fa10a5e653313faa6d8 /Documentation
parent5e611b35a891ab35d15a5eb8fcb8fcee6de1bb08 (diff)
downloadakka-serial-57d0893e17b12bf5f8bb10aeb6386dd25d708571.tar.gz
akka-serial-57d0893e17b12bf5f8bb10aeb6386dd25d708571.tar.bz2
akka-serial-57d0893e17b12bf5f8bb10aeb6386dd25d708571.zip
update documentation to reflect build refatorings
Diffstat (limited to 'Documentation')
-rw-r--r--Documentation/basics.md86
-rw-r--r--Documentation/building.md26
2 files changed, 112 insertions, 0 deletions
diff --git a/Documentation/basics.md b/Documentation/basics.md
new file mode 100644
index 0000000..1982f66
--- /dev/null
+++ b/Documentation/basics.md
@@ -0,0 +1,86 @@
+# Introduction
+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 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 the serial "manager" and serial "operators".
+
+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).
+
+Serial 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 `com.github.jodersky.flow.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.
+ 1. In case of failure, the serial manager will respond with a `CommandFailed` message to the original sender. The message contains details on the reason to why the opening failed.
+ 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 (=operator) of this message as all further communication with the newly opened port must pass through the operator.
+
+```scala
+val port = "/dev/ttyXXX"
+val settings = SerialSettings(
+ baud = 115200,
+ characterSize = 8,
+ twoStopBits = false,
+ parity = Parity.None
+)
+
+IO(Serial) ! Serial.Open(port, settings)
+
+def receive = {
+ case CommandFailed(cmd: Open, reason: AccessDeniedException) => println("you're not allowed to open that port!")
+ case CommandFailed(cmd: Open, reason) => println("could not open port for some other reason: " + reason.getMessage)
+ case Opened(settings) => {
+ val operator = sender
+ //do stuff with the operator, e.g. context become opened(op)
+ }
+}
+```
+
+
+# Communicating
+
+## Writing data
+Writing data is as simple as sending a `Write` message containing data to an operator. The type of data is an instance of `akka.util.ByteString`:
+```scala
+operator ! Write(data)
+```
+
+To receive an acknowledgement when data has been sent (this means queued in the kernel buffer; no guarantees can be made on the actual transmission of the data), the sender may add an additional `ack` parameter to a `Write` message. The `ack` parameter is of type `Int => Serial.Event`, i.e. a function that takes the number of actual bytes written and returns an event.
+
+```scala
+
+case class MyPacketAck(wrote: Int) extends Serial.Event
+
+operator ! Write(data, MyPacketAck(_))
+operator ! Write(data, MyPacketAck(_))
+operator ! Write(data, n => MyPacketAck(n))
+
+def receive = {
+ case MyPacketAck(x) => println("Wrote " + x + " bytes of data")
+}
+
+```
+
+## Receiving data
+The actor that opened a serial port (refered to as the client), exclusively receives incomming messages from the operator. These messages are in the form of `ByteStrings` and wrapped in a `Received` object.
+
+```scala
+def receive = {
+ case Received(data) => println("got data: " + data.toString)
+}
+```
+
+
+#Closing a port
+A port is closed by sending a `Close` message to its operator:
+```scala
+operator ! Serial.Close
+```
+The operator will close the underlying serial port and respond with a final `Closed` message before terminating.
+
+
+# Resources and error handling
+The operator has a deathwatch on the client actor that opened the port, this means that if the latter crashes, the operator closes the port and equally terminates, freeing any allocated resources.
+
+The opposite is not true by default, i.e. if the operator crashes (this can happen for example on IO errors) it dies silently and the client is not informed. Therefore, it is recommended that the client keep a deathwatch on the operator.
+
+
diff --git a/Documentation/building.md b/Documentation/building.md
new file mode 100644
index 0000000..6283851
--- /dev/null
+++ b/Documentation/building.md
@@ -0,0 +1,26 @@
+# Building from source
+A complete build of flow involves two parts
+
+ 1. building scala sources (the front-end)
+ 2. building native sources (the back-end)
+
+As any java or scala project, the first part results in a platform independent artifact. However, the second part yields a binary that may only be used on systems resembling the platform for which it was compiled. Both steps are independent, their only interaction being the header files generated by javah (see `sbt javah` for details), and may therefore be built in part and in any order.
+
+## Compiling and packaging java/scala sources
+Run `sbt main/packageBin` in the base directory. This simply compiles any scala and java sources as with any standard sbt project and produces a jar ready for being used.
+
+## Compiling and linking native sources
+The back-end is managed by GNU Autotools and all relevant files are contained in 'flow-native'. The first time, run `./bootstrap`, then `./configure && make` to compile the back-end. After completing this step, native libraries for the different platforms are available to be copied and included in end-user applications or installed on the system. To copy the binaries to a local directory, run ```DESTDIR=`pwd`/<directory> make install```. To install them system-wide, simply run `make install` as an administrator.
+
+## Creating a fat jar
+The native binaries produced in the previous step may be bundled in a "fat" jar so that they can be included in sbt projects through its regular dependency mechanisms. In this process, sbt basically acts as a wrapper script around autotools, calling the native build process and packaging generated binaries. Running `sbt native/packageBin` in the base directory produces the fat jar in 'flow-native-sbt/target'.
+
+Note: an important feature of fat jars is to include binaries for several platforms. To copy binaries compiled on other platforms to the fat jar, place them in a subfolder of 'flow-native-sbt/lib_native'. The subfolder should have the name `$(os.name)-$(os.arch)`, where `os.name` and `os.arch` are the java system properties of the respective platforms.
+
+# Note about versioning
+A versioning quirk to be aware of is when building native sources. In this case, the project and package versions follow a sematic pattern: `M.m.p`, where
+ - `M` is the major version, representing backwards incompatible changes
+ - `m` is the minor version, indicating backwards compatible changes such as new feature additions
+ - `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.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.3.1.2` becomes `libflow3.0.1.2`.