WIP add concept of interface factories

pull/276/head^2
Kevin Hester 2021-03-29 20:20:38 +08:00
rodzic 2ec15bf7b1
commit 2c75d0dee7
7 zmienionych plików z 90 dodań i 36 usunięć

Wyświetl plik

@ -78,7 +78,15 @@ A variable keepAllPackets, if set to true will suppress this behavior and instea
class BluetoothInterface(val service: RadioInterfaceService, val address: String) : IRadioInterface, class BluetoothInterface(val service: RadioInterfaceService, val address: String) : IRadioInterface,
Logging { Logging {
companion object : Logging { companion object : Logging, InterfaceFactory('x') {
override fun createInterface(
service: RadioInterfaceService,
rest: String
): IRadioInterface = BluetoothInterface(service, rest)
init {
registerFactory()
}
/// this service UUID is publically visible for scanning /// this service UUID is publically visible for scanning
val BTM_SERVICE_UUID = UUID.fromString("6ba1b218-15a8-461f-9fa8-5dcae273eafd") val BTM_SERVICE_UUID = UUID.fromString("6ba1b218-15a8-461f-9fa8-5dcae273eafd")
@ -100,12 +108,12 @@ class BluetoothInterface(val service: RadioInterfaceService, val address: String
fun toInterfaceName(deviceName: String) = "x$deviceName" fun toInterfaceName(deviceName: String) = "x$deviceName"
/** Return true if this address is still acceptable. For BLE that means, still bonded */ /** Return true if this address is still acceptable. For BLE that means, still bonded */
fun addressValid(context: Context, address: String): Boolean { override fun addressValid(context: Context, rest: String): Boolean {
val allPaired = val allPaired =
getBluetoothAdapter(context)?.bondedDevices.orEmpty().map { it.address }.toSet() getBluetoothAdapter(context)?.bondedDevices.orEmpty().map { it.address }.toSet()
return if (!allPaired.contains(address)) { return if (!allPaired.contains(rest)) {
warn("Ignoring stale bond to ${address.anonymize}") warn("Ignoring stale bond to ${rest.anonymize}")
false false
} else } else
true true

Wyświetl plik

@ -0,0 +1,23 @@
package com.geeksville.mesh.service
import android.content.Context
/**
* A base class for the singleton factories that make interfaces. One instance per interface type
*/
abstract class InterfaceFactory(val prefix: Char) {
companion object {
private val factories = mutableMapOf<Char, InterfaceFactory>()
fun getFactory(l: Char) = factories.get(l)
}
protected fun registerFactory() {
factories[prefix] = this
}
abstract fun createInterface(service: RadioInterfaceService, rest: String): IRadioInterface
/** Return true if this address is still acceptable. For BLE that means, still bonded */
open fun addressValid(context: Context, rest: String): Boolean = true
}

Wyświetl plik

@ -8,9 +8,15 @@ import okhttp3.internal.toHexString
/** A simulated interface that is used for testing in the simulator */ /** A simulated interface that is used for testing in the simulator */
class MockInterface(private val service: RadioInterfaceService) : Logging, IRadioInterface { class MockInterface(private val service: RadioInterfaceService) : Logging, IRadioInterface {
companion object : Logging { companion object : Logging, InterfaceFactory('m') {
override fun createInterface(
service: RadioInterfaceService,
rest: String
): IRadioInterface = MockInterface(service)
const val interfaceName = "m" init {
registerFactory()
}
} }
private var messageCount = 50 private var messageCount = 50

Wyświetl plik

@ -1,6 +1,19 @@
package com.geeksville.mesh.service package com.geeksville.mesh.service
import com.geeksville.android.Logging
class NopInterface : IRadioInterface { class NopInterface : IRadioInterface {
companion object : Logging, InterfaceFactory('n') {
override fun createInterface(
service: RadioInterfaceService,
rest: String
): IRadioInterface = NopInterface()
init {
registerFactory()
}
}
override fun handleSendToRadio(p: ByteArray) { override fun handleSendToRadio(p: ByteArray) {
} }

Wyświetl plik

@ -78,7 +78,7 @@ class RadioInterfaceService : Service(), Logging {
// If we are running on the emulator we default to the mock interface, so we can have some data to show to the user // If we are running on the emulator we default to the mock interface, so we can have some data to show to the user
if(address == null && isMockInterfaceAvailable(context)) if(address == null && isMockInterfaceAvailable(context))
address = MockInterface.interfaceName address = MockInterface.prefix.toString()
return address return address
} }
@ -104,13 +104,7 @@ class RadioInterfaceService : Service(), Logging {
if (address != null) { if (address != null) {
val c = address[0] val c = address[0]
val rest = address.substring(1) val rest = address.substring(1)
val isValid = when (c) { val isValid = InterfaceFactory.getFactory(c)?.addressValid(context, rest) ?: false
'x' -> BluetoothInterface.addressValid(context, rest)
's' -> SerialInterface.addressValid(context, rest)
'n' -> true
'm' -> true
else -> TODO("Unexpected interface type $c")
}
if (!isValid) if (!isValid)
return null return null
} }
@ -131,8 +125,7 @@ class RadioInterfaceService : Service(), Logging {
*/ */
var serviceScope = CoroutineScope(Dispatchers.IO + Job()) var serviceScope = CoroutineScope(Dispatchers.IO + Job())
private val nopIf = NopInterface() private var radioIf: IRadioInterface = NopInterface()
private var radioIf: IRadioInterface = nopIf
/** true if we have started our interface /** true if we have started our interface
* *
@ -217,7 +210,7 @@ class RadioInterfaceService : Service(), Logging {
/** Start our configured interface (if it isn't already running) */ /** Start our configured interface (if it isn't already running) */
private fun startInterface() { private fun startInterface() {
if (radioIf != nopIf) if (radioIf !is NopInterface)
warn("Can't start interface - $radioIf is already running") warn("Can't start interface - $radioIf is already running")
else { else {
val address = getBondedDeviceAddress(this) val address = getBondedDeviceAddress(this)
@ -234,26 +227,17 @@ class RadioInterfaceService : Service(), Logging {
val c = address[0] val c = address[0]
val rest = address.substring(1) val rest = address.substring(1)
radioIf = when (c) { radioIf = InterfaceFactory.getFactory(c)?.createInterface(this, rest) ?:
'x' -> BluetoothInterface(this, rest) NopInterface()
's' -> SerialInterface(this, rest)
'm' -> MockInterface(this)
'n' -> nopIf
else -> {
errormsg("Unexpected radio interface type")
nopIf
} }
} }
} }
}
}
private fun stopInterface() { private fun stopInterface() {
val r = radioIf val r = radioIf
info("stopping interface $r") info("stopping interface $r")
isStarted = false isStarted = false
radioIf = nopIf radioIf = NopInterface()
r.close() r.close()
// cancel any old jobs and get ready for the new ones // cancel any old jobs and get ready for the new ones
@ -266,7 +250,7 @@ class RadioInterfaceService : Service(), Logging {
receivedPacketsLog.close() receivedPacketsLog.close()
// Don't broadcast disconnects if we were just using the nop device // Don't broadcast disconnects if we were just using the nop device
if (r != nopIf) if (r !is NopInterface)
onDisconnect(isPermanent = true) // Tell any clients we are now offline onDisconnect(isPermanent = true) // Tell any clients we are now offline
} }

Wyświetl plik

@ -21,7 +21,15 @@ import com.hoho.android.usbserial.util.SerialInputOutputManager
*/ */
class SerialInterface(service: RadioInterfaceService, private val address: String) : class SerialInterface(service: RadioInterfaceService, private val address: String) :
StreamInterface(service), Logging, SerialInputOutputManager.Listener { StreamInterface(service), Logging, SerialInputOutputManager.Listener {
companion object : Logging { companion object : Logging, InterfaceFactory('s') {
override fun createInterface(
service: RadioInterfaceService,
rest: String
): IRadioInterface = SerialInterface(service, rest)
init {
registerFactory()
}
/** /**
* according to https://stackoverflow.com/questions/12388914/usb-device-access-pop-up-suppression/15151075#15151075 * according to https://stackoverflow.com/questions/12388914/usb-device-access-pop-up-suppression/15151075#15151075
@ -41,14 +49,14 @@ class SerialInterface(service: RadioInterfaceService, private val address: Strin
return drivers return drivers
} }
fun addressValid(context: Context, rest: String): Boolean { override fun addressValid(context: Context, rest: String): Boolean {
findSerial(context, rest)?.let { d -> findSerial(context, rest)?.let { d ->
return assumePermission || context.usbManager.hasPermission(d.device) return assumePermission || context.usbManager.hasPermission(d.device)
} }
return false return false
} }
fun findSerial(context: Context, rest: String): UsbSerialDriver? { private fun findSerial(context: Context, rest: String): UsbSerialDriver? {
val drivers = findDrivers(context) val drivers = findDrivers(context)
return if (drivers.isEmpty()) return if (drivers.isEmpty())
@ -61,7 +69,7 @@ class SerialInterface(service: RadioInterfaceService, private val address: Strin
private var uart: UsbSerialDriver? = null private var uart: UsbSerialDriver? = null
private var ioManager: SerialInputOutputManager? = null private var ioManager: SerialInputOutputManager? = null
var usbReceiver = object : BroadcastReceiver() { private var usbReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) = exceptionReporter { override fun onReceive(context: Context, intent: Intent) = exceptionReporter {
if (UsbManager.ACTION_USB_DEVICE_DETACHED == intent.action) { if (UsbManager.ACTION_USB_DEVICE_DETACHED == intent.action) {

Wyświetl plik

@ -1,5 +1,6 @@
package com.geeksville.mesh.service package com.geeksville.mesh.service
import com.geeksville.android.Logging
import java.io.* import java.io.*
import java.net.InetAddress import java.net.InetAddress
import java.net.Socket import java.net.Socket
@ -9,6 +10,17 @@ import kotlin.concurrent.thread
class TCPInterface(service: RadioInterfaceService, private val address: String) : class TCPInterface(service: RadioInterfaceService, private val address: String) :
StreamInterface(service) { StreamInterface(service) {
companion object : Logging, InterfaceFactory('t') {
override fun createInterface(
service: RadioInterfaceService,
rest: String
): IRadioInterface = TCPInterface(service, rest)
init {
registerFactory()
}
}
var socket: Socket? = null var socket: Socket? = null
lateinit var outStream: OutputStream lateinit var outStream: OutputStream
lateinit var inStream: InputStream lateinit var inStream: InputStream