Add test for remote hub
This commit is contained in:
parent
207064cd45
commit
4a10c3c443
@ -26,6 +26,11 @@ public interface DeviceHub : Provider {
|
|||||||
public companion object
|
public companion object
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public fun DeviceHub(deviceMap: Map<Name, Device>): DeviceHub = object : DeviceHub {
|
||||||
|
override val devices: Map<Name, Device>
|
||||||
|
get() = deviceMap
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List all devices, including sub-devices
|
* List all devices, including sub-devices
|
||||||
*/
|
*/
|
||||||
|
@ -163,23 +163,64 @@ private class MapBasedDeviceHub(val deviceMap: Map<Name, Device>, val prefix: Na
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public fun MagixEndpoint.remoteDeviceHub(
|
/**
|
||||||
|
* Create a dynamic [DeviceHub] from incoming messages
|
||||||
|
*/
|
||||||
|
public suspend fun MagixEndpoint.remoteDeviceHub(
|
||||||
context: Context,
|
context: Context,
|
||||||
thisEndpoint: String,
|
thisEndpoint: String,
|
||||||
deviceEndpoint: String,
|
deviceEndpoint: String,
|
||||||
): DeviceHub {
|
): DeviceHub {
|
||||||
val devices = mutableMapOf<Name, DeviceClient>()
|
val devices = mutableMapOf<Name, DeviceClient>()
|
||||||
val subscription = subscribe(DeviceManager.magixFormat, originFilter = listOf(deviceEndpoint)).map { it.second }
|
val subscription = subscribe(DeviceManager.magixFormat, originFilter = listOf(deviceEndpoint)).map { it.second }
|
||||||
subscription.filterIsInstance<DescriptionMessage>().onEach {
|
subscription.filterIsInstance<DescriptionMessage>().onEach { descriptionMessage ->
|
||||||
|
devices.getOrPut(descriptionMessage.sourceDevice) {
|
||||||
|
DeviceClient(
|
||||||
|
context = context,
|
||||||
|
deviceName = descriptionMessage.sourceDevice,
|
||||||
|
propertyDescriptors = descriptionMessage.properties,
|
||||||
|
actionDescriptors = descriptionMessage.actions,
|
||||||
|
incomingFlow = subscription
|
||||||
|
) {
|
||||||
|
send(
|
||||||
|
format = DeviceManager.magixFormat,
|
||||||
|
payload = it,
|
||||||
|
source = thisEndpoint,
|
||||||
|
target = deviceEndpoint,
|
||||||
|
id = stringUID()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}.run {
|
||||||
|
propertyDescriptors = descriptionMessage.properties
|
||||||
|
}
|
||||||
}.launchIn(context)
|
}.launchIn(context)
|
||||||
|
|
||||||
|
|
||||||
return object : DeviceHub {
|
send(
|
||||||
override val devices: Map<Name, Device>
|
format = DeviceManager.magixFormat,
|
||||||
get() = TODO("Not yet implemented")
|
payload = GetDescriptionMessage(targetDevice = null),
|
||||||
|
source = thisEndpoint,
|
||||||
|
target = deviceEndpoint,
|
||||||
|
id = stringUID()
|
||||||
|
)
|
||||||
|
|
||||||
}
|
return DeviceHub(devices)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request a description update for all devices on an endpoint
|
||||||
|
*/
|
||||||
|
public suspend fun MagixEndpoint.requestDeviceUpdate(
|
||||||
|
thisEndpoint: String,
|
||||||
|
deviceEndpoint: String,
|
||||||
|
) {
|
||||||
|
send(
|
||||||
|
format = DeviceManager.magixFormat,
|
||||||
|
payload = GetDescriptionMessage(),
|
||||||
|
source = thisEndpoint,
|
||||||
|
target = deviceEndpoint,
|
||||||
|
id = stringUID()
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,15 +1,21 @@
|
|||||||
package space.kscience.controls.client
|
package space.kscience.controls.client
|
||||||
|
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.merge
|
import kotlinx.coroutines.flow.merge
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
|
import space.kscience.controls.api.DeviceHub
|
||||||
import space.kscience.controls.api.DeviceMessage
|
import space.kscience.controls.api.DeviceMessage
|
||||||
import space.kscience.controls.manager.DeviceManager
|
import space.kscience.controls.manager.DeviceManager
|
||||||
|
import space.kscience.controls.manager.hubMessageFlow
|
||||||
import space.kscience.controls.manager.install
|
import space.kscience.controls.manager.install
|
||||||
import space.kscience.controls.manager.respondMessage
|
import space.kscience.controls.manager.respondHubMessage
|
||||||
import space.kscience.controls.spec.*
|
import space.kscience.controls.spec.*
|
||||||
import space.kscience.dataforge.context.Context
|
import space.kscience.dataforge.context.Context
|
||||||
import space.kscience.dataforge.context.Factory
|
import space.kscience.dataforge.context.Factory
|
||||||
@ -17,15 +23,43 @@ import space.kscience.dataforge.context.request
|
|||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
import space.kscience.dataforge.meta.get
|
import space.kscience.dataforge.meta.get
|
||||||
import space.kscience.dataforge.meta.int
|
import space.kscience.dataforge.meta.int
|
||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.asName
|
||||||
import space.kscience.magix.api.MagixEndpoint
|
import space.kscience.magix.api.MagixEndpoint
|
||||||
import space.kscience.magix.api.MagixMessage
|
import space.kscience.magix.api.MagixMessage
|
||||||
import space.kscience.magix.api.MagixMessageFilter
|
import space.kscience.magix.api.MagixMessageFilter
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertContains
|
import kotlin.test.assertContains
|
||||||
|
import kotlin.test.assertEquals
|
||||||
import kotlin.time.Duration.Companion.milliseconds
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
|
|
||||||
|
class VirtualMagixEndpoint(val hub: DeviceHub) : MagixEndpoint {
|
||||||
|
|
||||||
|
private val additionalMessages = MutableSharedFlow<DeviceMessage>(1)
|
||||||
|
|
||||||
|
override fun subscribe(
|
||||||
|
filter: MagixMessageFilter,
|
||||||
|
): Flow<MagixMessage> = merge(hub.hubMessageFlow(), additionalMessages).map {
|
||||||
|
MagixMessage(
|
||||||
|
format = DeviceManager.magixFormat.defaultFormat,
|
||||||
|
payload = MagixEndpoint.magixJson.encodeToJsonElement(DeviceManager.magixFormat.serializer, it),
|
||||||
|
sourceEndpoint = "device",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
override suspend fun broadcast(message: MagixMessage) {
|
||||||
|
hub.respondHubMessage(
|
||||||
|
Json.decodeFromJsonElement(DeviceManager.magixFormat.serializer, message.payload)
|
||||||
|
).forEach {
|
||||||
|
additionalMessages.emit(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
internal class RemoteDeviceConnect {
|
internal class RemoteDeviceConnect {
|
||||||
|
|
||||||
@ -53,40 +87,44 @@ internal class RemoteDeviceConnect {
|
|||||||
val context = Context {
|
val context = Context {
|
||||||
plugin(DeviceManager)
|
plugin(DeviceManager)
|
||||||
}
|
}
|
||||||
|
val deviceManager = context.request(DeviceManager)
|
||||||
|
|
||||||
val device = context.request(DeviceManager).install("test", TestDevice)
|
deviceManager.install("test", TestDevice)
|
||||||
|
|
||||||
val virtualMagixEndpoint = object : MagixEndpoint {
|
val virtualMagixEndpoint = VirtualMagixEndpoint(deviceManager)
|
||||||
|
|
||||||
|
val remoteDevice = virtualMagixEndpoint.remoteDevice(context, "client", "device", "test".asName())
|
||||||
private val additionalMessages = MutableSharedFlow<DeviceMessage>(1)
|
|
||||||
|
|
||||||
override fun subscribe(
|
|
||||||
filter: MagixMessageFilter,
|
|
||||||
): Flow<MagixMessage> = merge(device.messageFlow, additionalMessages).map {
|
|
||||||
MagixMessage(
|
|
||||||
format = DeviceManager.magixFormat.defaultFormat,
|
|
||||||
payload = MagixEndpoint.magixJson.encodeToJsonElement(DeviceManager.magixFormat.serializer, it),
|
|
||||||
sourceEndpoint = "device",
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun broadcast(message: MagixMessage) {
|
|
||||||
device.respondMessage(
|
|
||||||
Name.EMPTY,
|
|
||||||
Json.decodeFromJsonElement(DeviceManager.magixFormat.serializer, message.payload)
|
|
||||||
)?.let {
|
|
||||||
additionalMessages.emit(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun close() {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val remoteDevice = virtualMagixEndpoint.remoteDevice(context, "client", "device", Name.EMPTY)
|
|
||||||
|
|
||||||
assertContains(0.0..1.0, remoteDevice.read(TestDevice.value))
|
assertContains(0.0..1.0, remoteDevice.read(TestDevice.value))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun deviceHub() = runTest {
|
||||||
|
val context = Context {
|
||||||
|
plugin(DeviceManager)
|
||||||
|
}
|
||||||
|
val deviceManager = context.request(DeviceManager)
|
||||||
|
|
||||||
|
launch {
|
||||||
|
delay(50)
|
||||||
|
repeat(10) {
|
||||||
|
deviceManager.install("test[$it]", TestDevice)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val virtualMagixEndpoint = VirtualMagixEndpoint(deviceManager)
|
||||||
|
|
||||||
|
val remoteHub = virtualMagixEndpoint.remoteDeviceHub(context, "client", "device")
|
||||||
|
|
||||||
|
assertEquals(0, remoteHub.devices.size)
|
||||||
|
|
||||||
|
delay(60)
|
||||||
|
//switch context to use actual delay
|
||||||
|
withContext(Dispatchers.Default) {
|
||||||
|
virtualMagixEndpoint.requestDeviceUpdate("client", "device")
|
||||||
|
delay(30)
|
||||||
|
assertEquals(10, remoteHub.devices.size)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user