Split StreamInterface from SerialInterface, so it can be a TCPInterface baseclass

pull/276/head^2
Kevin Hester 2021-03-29 19:10:52 +08:00
rodzic d510576fa8
commit 1ebc710006
2 zmienionych plików z 144 dodań i 104 usunięć

Wyświetl plik

@ -16,19 +16,19 @@ import com.hoho.android.usbserial.driver.UsbSerialProber
import com.hoho.android.usbserial.util.SerialInputOutputManager
class SerialInterface(private val service: RadioInterfaceService, val address: String) : Logging,
IRadioInterface, SerialInputOutputManager.Listener {
/**
* An interface that assumes we are talking to a meshtastic device via USB serial
*/
class SerialInterface(service: RadioInterfaceService, address: String) :
StreamInterface(service, address), Logging, SerialInputOutputManager.Listener {
companion object : Logging {
private const val START1 = 0x94.toByte()
private const val START2 = 0xc3.toByte()
private const val MAX_TO_FROM_RADIO_SIZE = 512
/**
* according to https://stackoverflow.com/questions/12388914/usb-device-access-pop-up-suppression/15151075#15151075
* we should never ask for USB permissions ourselves, instead we should rely on the external dialog printed by the system. If
* we do that the system will remember we have accesss
*/
val assumePermission = true
const val assumePermission = true
fun toInterfaceName(deviceName: String) = "s$deviceName"
@ -85,16 +85,6 @@ class SerialInterface(private val service: RadioInterfaceService, val address: S
}
}
private val debugLineBuf = kotlin.text.StringBuilder()
/** The index of the next byte we are hoping to receive */
private var ptr = 0
/** The two halves of our length */
private var msb = 0
private var lsb = 0
private var packetLen = 0
init {
val filter = IntentFilter()
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED)
@ -105,16 +95,15 @@ class SerialInterface(private val service: RadioInterfaceService, val address: S
}
override fun close() {
debug("Closing serial port for good")
service.unregisterReceiver(usbReceiver)
onDeviceDisconnect(true)
super.close()
}
/** Tell MeshService our device has gone away, but wait for it to come back
*
* @param waitForStopped if true we should wait for the manager to finish - must be false if called from inside the manager callbacks
* */
fun onDeviceDisconnect(waitForStopped: Boolean) {
override fun onDeviceDisconnect(waitForStopped: Boolean) {
ignoreException {
ioManager?.let {
debug("USB device disconnected, but it might come back")
@ -143,10 +132,10 @@ class SerialInterface(private val service: RadioInterfaceService, val address: S
}
}
service.onDisconnect(isPermanent = true) // if USB device disconnects it is definitely permantently gone, not sleeping)
super.onDeviceDisconnect(waitForStopped)
}
private fun connect() {
override fun connect() {
val manager = service.getSystemService(Context.USB_SERVICE) as UsbManager
val device = findSerial(service, address)
@ -175,101 +164,20 @@ class SerialInterface(private val service: RadioInterfaceService, val address: S
thread.name = "serial reader"
thread.start() // No need to keep reference to thread around, we quit by asking the ioManager to quit
// Before telling mesh service, send a few START1s to wake a sleeping device
val wakeBytes = byteArrayOf(START1, START1, START1, START1)
io.writeAsync(wakeBytes)
// Now tell clients they can (finally use the api)
service.onConnect()
super.connect()
}
} else {
errormsg("Can't find device")
}
}
override fun handleSendToRadio(p: ByteArray) {
// This method is called from a continuation and it might show up late, so check for uart being null
val header = ByteArray(4)
header[0] = START1
header[1] = START2
header[2] = (p.size shr 8).toByte()
header[3] = (p.size and 0xff).toByte()
override fun sendBytes(p: ByteArray) {
ioManager?.apply {
writeAsync(header)
writeAsync(p)
}
}
/** Print device serial debug output somewhere */
private fun debugOut(b: Byte) {
when (val c = b.toChar()) {
'\r' -> {
} // ignore
'\n' -> {
debug("DeviceLog: $debugLineBuf")
debugLineBuf.clear()
}
else ->
debugLineBuf.append(c)
}
}
private val rxPacket = ByteArray(MAX_TO_FROM_RADIO_SIZE)
private fun readChar(c: Byte) {
// Assume we will be advancing our pointer
var nextPtr = ptr + 1
fun lostSync() {
errormsg("Lost protocol sync")
nextPtr = 0
}
/// Deliver our current packet and restart our reader
fun deliverPacket() {
val buf = rxPacket.copyOf(packetLen)
service.handleFromRadio(buf)
nextPtr = 0 // Start parsing the next packet
}
when (ptr) {
0 -> // looking for START1
if (c != START1) {
debugOut(c)
nextPtr = 0 // Restart from scratch
}
1 -> // Looking for START2
if (c != START2)
lostSync() // Restart from scratch
2 -> // Looking for MSB of our 16 bit length
msb = c.toInt() and 0xff
3 -> { // Looking for LSB of our 16 bit length
lsb = c.toInt() and 0xff
// We've read our header, do one big read for the packet itself
packetLen = (msb shl 8) or lsb
if (packetLen > MAX_TO_FROM_RADIO_SIZE)
lostSync() // If packet len is too long, the bytes must have been corrupted, start looking for START1 again
else if (packetLen == 0)
deliverPacket() // zero length packets are valid and should be delivered immediately (because there won't be a next byte of payload)
}
else -> {
// We are looking at the packet bytes now
rxPacket[ptr - 4] = c
// Note: we have to check if ptr +1 is equal to packet length (for example, for a 1 byte packetlen, this code will be run with ptr of4
if (ptr - 4 + 1 == packetLen) {
deliverPacket()
}
}
}
ptr = nextPtr
}
/**
* Called when [SerialInputOutputManager.run] aborts due to an error.
*/

Wyświetl plik

@ -0,0 +1,132 @@
package com.geeksville.mesh.service
import com.geeksville.android.Logging
/**
* An interface that assumes we are talking to a meshtastic device over some sort of stream connection (serial or TCP probably)
*/
abstract class StreamInterface(protected val service: RadioInterfaceService, val address: String) :
Logging,
IRadioInterface {
companion object : Logging {
private const val START1 = 0x94.toByte()
private const val START2 = 0xc3.toByte()
private const val MAX_TO_FROM_RADIO_SIZE = 512
}
private val debugLineBuf = kotlin.text.StringBuilder()
/** The index of the next byte we are hoping to receive */
private var ptr = 0
/** The two halves of our length */
private var msb = 0
private var lsb = 0
private var packetLen = 0
override fun close() {
debug("Closing stream for good")
onDeviceDisconnect(true)
}
/** Tell MeshService our device has gone away, but wait for it to come back
*
* @param waitForStopped if true we should wait for the manager to finish - must be false if called from inside the manager callbacks
* */
protected open fun onDeviceDisconnect(waitForStopped: Boolean) {
service.onDisconnect(isPermanent = true) // if USB device disconnects it is definitely permantently gone, not sleeping)
}
protected open fun connect() {
// Before telling mesh service, send a few START1s to wake a sleeping device
val wakeBytes = byteArrayOf(START1, START1, START1, START1)
sendBytes(wakeBytes)
// Now tell clients they can (finally use the api)
service.onConnect()
}
abstract fun sendBytes(p: ByteArray)
override fun handleSendToRadio(p: ByteArray) {
// This method is called from a continuation and it might show up late, so check for uart being null
val header = ByteArray(4)
header[0] = START1
header[1] = START2
header[2] = (p.size shr 8).toByte()
header[3] = (p.size and 0xff).toByte()
sendBytes(header)
sendBytes(p)
}
/** Print device serial debug output somewhere */
private fun debugOut(b: Byte) {
when (val c = b.toChar()) {
'\r' -> {
} // ignore
'\n' -> {
debug("DeviceLog: $debugLineBuf")
debugLineBuf.clear()
}
else ->
debugLineBuf.append(c)
}
}
private val rxPacket = ByteArray(MAX_TO_FROM_RADIO_SIZE)
protected fun readChar(c: Byte) {
// Assume we will be advancing our pointer
var nextPtr = ptr + 1
fun lostSync() {
errormsg("Lost protocol sync")
nextPtr = 0
}
/// Deliver our current packet and restart our reader
fun deliverPacket() {
val buf = rxPacket.copyOf(packetLen)
service.handleFromRadio(buf)
nextPtr = 0 // Start parsing the next packet
}
when (ptr) {
0 -> // looking for START1
if (c != START1) {
debugOut(c)
nextPtr = 0 // Restart from scratch
}
1 -> // Looking for START2
if (c != START2)
lostSync() // Restart from scratch
2 -> // Looking for MSB of our 16 bit length
msb = c.toInt() and 0xff
3 -> { // Looking for LSB of our 16 bit length
lsb = c.toInt() and 0xff
// We've read our header, do one big read for the packet itself
packetLen = (msb shl 8) or lsb
if (packetLen > MAX_TO_FROM_RADIO_SIZE)
lostSync() // If packet len is too long, the bytes must have been corrupted, start looking for START1 again
else if (packetLen == 0)
deliverPacket() // zero length packets are valid and should be delivered immediately (because there won't be a next byte of payload)
}
else -> {
// We are looking at the packet bytes now
rxPacket[ptr - 4] = c
// Note: we have to check if ptr +1 is equal to packet length (for example, for a 1 byte packetlen, this code will be run with ptr of4
if (ptr - 4 + 1 == packetLen) {
deliverPacket()
}
}
}
ptr = nextPtr
}
}