Skip to content
This repository was archived by the owner on Aug 18, 2020. It is now read-only.

Commit 4fbb87a

Browse files
committed
Merge branch 'serial-connector'
2 parents 671fb63 + 74174e9 commit 4fbb87a

File tree

5 files changed

+176
-0
lines changed

5 files changed

+176
-0
lines changed

build.sbt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ libraryDependencies ++= Seq(
5757
resolvers += "jcenter-bintray" at "http://jcenter.bintray.com"
5858
libraryDependencies += "net.dv8tion" % "JDA" % "4.ALPHA.0_82"
5959

60+
//Serial Communication
61+
libraryDependencies += "com.fazecast" % "jSerialComm" % "[2.0.0,3.0.0)"
62+
6063
// ---------------------------------------------------------------------------------------------------------------------
6164
// PLUGIN FRAMEWORK DEFINITIONS
6265
// ---------------------------------------------------------------------------------------------------------------------
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
package org.codeoverflow.chatoverflow.requirement.service.serial
2+
3+
import java.io.{InputStream, PrintStream}
4+
5+
import com.fazecast.jSerialComm.{SerialPort, SerialPortInvalidPortException}
6+
import org.codeoverflow.chatoverflow.WithLogger
7+
import org.codeoverflow.chatoverflow.connector.Connector
8+
9+
/**
10+
* The serial connector allows to communicate with a device connected to the pcs serial port (like an Arduino)
11+
*
12+
* @param sourceIdentifier r the unique source identifier to identify this connector
13+
*/
14+
class SerialConnector(override val sourceIdentifier: String) extends Connector(sourceIdentifier) with WithLogger {
15+
16+
override protected var optionalCredentialKeys: List[String] = List("baudRate")
17+
override protected var requiredCredentialKeys: List[String] = List("port")
18+
19+
private var serialPort: Option[SerialPort] = None
20+
private var out: Option[PrintStream] = None
21+
private var in: Option[InputStream] = None
22+
private val serialPortInputListener = new SerialPortInputListener
23+
24+
/**
25+
* @throws java.lang.IllegalStateException if the serial port is not available yet
26+
* @return print stream that outputs to the port
27+
*/
28+
@throws(classOf[IllegalStateException])
29+
def getPrintStream: PrintStream = {
30+
if (serialPort.isEmpty) throw new IllegalStateException("Serial port is not available yet")
31+
out.get
32+
}
33+
34+
/**
35+
* @throws java.lang.IllegalStateException if the serial port is not available yet
36+
* @return a inputstream that receives all data from the port
37+
*/
38+
@throws(classOf[IllegalStateException])
39+
def getInputStream: InputStream = {
40+
if (serialPort.isEmpty) throw new IllegalStateException("Serial port is not available yet")
41+
in.get
42+
}
43+
44+
/**
45+
* Adds a new input listener that receives all data
46+
* @param listener a listener that handles incoming data in a byte array
47+
* @throws java.lang.IllegalStateException if the serial port is not available yet
48+
*/
49+
@throws(classOf[IllegalStateException])
50+
def addInputListener(listener: Array[Byte] => Unit): Unit = {
51+
if (serialPort.isEmpty) throw new IllegalStateException("Serial port is not available yet")
52+
serialPortInputListener.addDataAvailableListener(_ => {
53+
val buffer = new Array[Byte](serialPort.get.bytesAvailable())
54+
serialPort.get.readBytes(buffer, buffer.length) //FIXME DOES IT CRASH?
55+
listener(buffer)
56+
})
57+
}
58+
59+
/**
60+
* Opens a connection with the serial port
61+
*/
62+
override def start(): Boolean = {
63+
//TODO Test if connector is working this way or if it requires an actor
64+
try {
65+
serialPort = Some(SerialPort.getCommPort(credentials.get.getValue("port").get))
66+
credentials.get.getValue("baudRate") match {
67+
case Some(baudRate) if baudRate.matches("\\s*\\d+\\s*") => serialPort.get.setBaudRate(baudRate.trim.toInt)
68+
case Some(ivalidBaudrate) =>
69+
logger error s"Invalid baud rate: $ivalidBaudrate"
70+
return false
71+
case None => //Do nothing
72+
}
73+
logger info s"Waiting for serial port to open..."
74+
if (serialPort.get.openPort(1000)) {
75+
Thread.sleep(1500)//Sleep to wait for
76+
serialPort.get.setComPortTimeouts(SerialPort.TIMEOUT_READ_SEMI_BLOCKING, 0, 0)
77+
out = Some(new PrintStream(serialPort.get.getOutputStream, true, "US-ASCII"))
78+
in = Some(serialPort.get.getInputStream)
79+
serialPort.get.addDataListener(serialPortInputListener)
80+
logger info "Opened serial port!"
81+
true
82+
} else {
83+
logger error s"Could not open serial port $sourceIdentifier"
84+
false
85+
}
86+
} catch {
87+
case e: SerialPortInvalidPortException =>
88+
logger error s"Source identifier $sourceIdentifier is invalid: ${e.getMessage}"
89+
false
90+
}
91+
}
92+
93+
/**
94+
* Closes the connection with the port
95+
*/
96+
override def stop(): Boolean = {
97+
serialPort.foreach(_.closePort())
98+
true
99+
}
100+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package org.codeoverflow.chatoverflow.requirement.service.serial
2+
3+
import com.fazecast.jSerialComm.{SerialPort, SerialPortDataListener, SerialPortEvent}
4+
5+
import scala.collection.mutable.ListBuffer
6+
7+
class SerialPortInputListener extends SerialPortDataListener {
8+
9+
private val listeners = ListBuffer[SerialPortEvent => Unit]()
10+
11+
override def getListeningEvents: Int = SerialPort.LISTENING_EVENT_DATA_AVAILABLE
12+
13+
override def serialEvent(event: SerialPortEvent): Unit = {
14+
if (event.getEventType == SerialPort.LISTENING_EVENT_DATA_AVAILABLE) {
15+
listeners.foreach(listener => listener(event))
16+
}
17+
}
18+
19+
def addDataAvailableListener(listener: SerialPortEvent => Unit): Unit = listeners += listener
20+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package org.codeoverflow.chatoverflow.requirement.service.serial.impl
2+
3+
import java.io.InputStream
4+
import java.nio.charset.StandardCharsets
5+
import java.util.function.Consumer
6+
7+
import org.codeoverflow.chatoverflow.WithLogger
8+
import org.codeoverflow.chatoverflow.api.io.input.SerialInput
9+
import org.codeoverflow.chatoverflow.registry.Impl
10+
import org.codeoverflow.chatoverflow.requirement.InputImpl
11+
import org.codeoverflow.chatoverflow.requirement.service.serial.SerialConnector
12+
13+
import scala.collection.mutable.ListBuffer
14+
15+
@Impl(impl = classOf[SerialInput], connector = classOf[SerialConnector])
16+
class SerialInputImpl extends InputImpl[SerialConnector] with SerialInput with WithLogger {
17+
18+
private val stringListeners = ListBuffer[Consumer[String]]()
19+
private val byteListeners = ListBuffer[Consumer[Array[Byte]]]()
20+
21+
override def start(): Boolean = {
22+
sourceConnector.get.addInputListener(onIncomingData)
23+
true
24+
}
25+
26+
private def onIncomingData(bytes: Array[Byte]): Unit = {
27+
byteListeners.foreach(l => l.accept(bytes))
28+
stringListeners.foreach(l => l.accept(new String(bytes, StandardCharsets.US_ASCII)))
29+
}
30+
31+
override def registerStringListener(consumer: Consumer[String]): Unit = stringListeners += consumer
32+
33+
override def registerDataListener(consumer: Consumer[Array[Byte]]): Unit = byteListeners += consumer
34+
35+
override def getInputStream: InputStream = sourceConnector.get.getInputStream
36+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package org.codeoverflow.chatoverflow.requirement.service.serial.impl
2+
3+
import java.io.PrintStream
4+
5+
import org.codeoverflow.chatoverflow.WithLogger
6+
import org.codeoverflow.chatoverflow.api.io.output.SerialOutput
7+
import org.codeoverflow.chatoverflow.registry.Impl
8+
import org.codeoverflow.chatoverflow.requirement.OutputImpl
9+
import org.codeoverflow.chatoverflow.requirement.service.serial.SerialConnector
10+
11+
@Impl(impl = classOf[SerialOutput], connector = classOf[SerialConnector])
12+
class SerialOutputImpl extends OutputImpl[SerialConnector] with SerialOutput with WithLogger {
13+
14+
override def start(): Boolean = true
15+
16+
override def getPrintStream: PrintStream = sourceConnector.get.getPrintStream
17+
}

0 commit comments

Comments
 (0)