Fix package names and imports
This commit is contained in:
parent
a093f1921e
commit
a510a8aedf
@ -36,7 +36,7 @@ Generally, a Device has Properties that can be read and written. Also, some Acti
|
|||||||
can optionally be applied on a device (may or may not affect properties).
|
can optionally be applied on a device (may or may not affect properties).
|
||||||
|
|
||||||
- `base` - contains baseline `Device` implementation
|
- `base` - contains baseline `Device` implementation
|
||||||
[`DeviceBase`](dataforge-device-core/src/commonMain/kotlin/hep/dataforge/control/base/DeviceBase.kt)
|
[`DeviceBase`](controls-core/src/commonMain/kotlin/ru/mipt/npm/controls/base/DeviceBase.kt)
|
||||||
and property implementation, including property asynchronous flows.
|
and property implementation, including property asynchronous flows.
|
||||||
|
|
||||||
- `controllers` - implements Message Controller that can be attached to the event bus, Message
|
- `controllers` - implements Message Controller that can be attached to the event bus, Message
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
package space.kscience.dataforge.control.api
|
package ru.mipt.npm.controls.api
|
||||||
|
|
||||||
import io.ktor.utils.io.core.Closeable
|
import io.ktor.utils.io.core.Closeable
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.cancel
|
import kotlinx.coroutines.cancel
|
||||||
import kotlinx.coroutines.flow.SharedFlow
|
import kotlinx.coroutines.flow.SharedFlow
|
||||||
|
import ru.mipt.npm.controls.api.Device.Companion.DEVICE_TARGET
|
||||||
import space.kscience.dataforge.context.ContextAware
|
import space.kscience.dataforge.context.ContextAware
|
||||||
import space.kscience.dataforge.control.api.Device.Companion.DEVICE_TARGET
|
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
import space.kscience.dataforge.meta.MetaItem
|
import space.kscience.dataforge.meta.MetaItem
|
||||||
import space.kscience.dataforge.misc.Type
|
import space.kscience.dataforge.misc.Type
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package space.kscience.dataforge.control.api
|
package ru.mipt.npm.controls.api
|
||||||
|
|
||||||
import space.kscience.dataforge.meta.MetaItem
|
import space.kscience.dataforge.meta.MetaItem
|
||||||
import space.kscience.dataforge.names.*
|
import space.kscience.dataforge.names.*
|
||||||
@ -38,9 +38,7 @@ public interface DeviceHub : Provider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public companion object {
|
public companion object
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public operator fun DeviceHub.get(nameToken: NameToken): Device =
|
public operator fun DeviceHub.get(nameToken: NameToken): Device =
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package space.kscience.dataforge.control.messages
|
package ru.mipt.npm.controls.api
|
||||||
|
|
||||||
import kotlinx.serialization.SerialName
|
import kotlinx.serialization.SerialName
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
@ -1,4 +1,4 @@
|
|||||||
package space.kscience.dataforge.control.api
|
package ru.mipt.npm.controls.api
|
||||||
|
|
||||||
import io.ktor.utils.io.core.Closeable
|
import io.ktor.utils.io.core.Closeable
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package space.kscience.dataforge.control.api
|
package ru.mipt.npm.controls.api
|
||||||
|
|
||||||
import space.kscience.dataforge.meta.Scheme
|
import space.kscience.dataforge.meta.Scheme
|
||||||
import space.kscience.dataforge.meta.string
|
import space.kscience.dataforge.meta.string
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package space.kscience.dataforge.control.base
|
package ru.mipt.npm.controls.base
|
||||||
|
|
||||||
import space.kscience.dataforge.control.api.ActionDescriptor
|
import ru.mipt.npm.controls.api.ActionDescriptor
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
import space.kscience.dataforge.meta.MetaItem
|
import space.kscience.dataforge.meta.MetaItem
|
||||||
import space.kscience.dataforge.meta.asMetaItem
|
import space.kscience.dataforge.meta.asMetaItem
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package space.kscience.dataforge.control.base
|
package ru.mipt.npm.controls.base
|
||||||
|
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
@ -7,10 +7,10 @@ import kotlinx.coroutines.flow.SharedFlow
|
|||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
|
import ru.mipt.npm.controls.api.ActionDescriptor
|
||||||
|
import ru.mipt.npm.controls.api.Device
|
||||||
|
import ru.mipt.npm.controls.api.PropertyDescriptor
|
||||||
import space.kscience.dataforge.context.Context
|
import space.kscience.dataforge.context.Context
|
||||||
import space.kscience.dataforge.control.api.ActionDescriptor
|
|
||||||
import space.kscience.dataforge.control.api.Device
|
|
||||||
import space.kscience.dataforge.control.api.PropertyDescriptor
|
|
||||||
import space.kscience.dataforge.meta.MetaItem
|
import space.kscience.dataforge.meta.MetaItem
|
||||||
import space.kscience.dataforge.misc.DFExperimental
|
import space.kscience.dataforge.misc.DFExperimental
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ public abstract class DeviceBase(override val context: Context) : Device {
|
|||||||
override val actionDescriptors: Collection<ActionDescriptor>
|
override val actionDescriptors: Collection<ActionDescriptor>
|
||||||
get() = _actions.values.map { it.descriptor }
|
get() = _actions.values.map { it.descriptor }
|
||||||
|
|
||||||
internal fun <P : ReadOnlyDeviceProperty> registerProperty(name: String, property: P) {
|
private fun <P : ReadOnlyDeviceProperty> registerProperty(name: String, property: P) {
|
||||||
if (_properties.contains(name)) error("Property with name $name already registered")
|
if (_properties.contains(name)) error("Property with name $name already registered")
|
||||||
_properties[name] = property
|
_properties[name] = property
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package space.kscience.dataforge.control.base
|
package ru.mipt.npm.controls.base
|
||||||
|
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import space.kscience.dataforge.control.api.PropertyDescriptor
|
import ru.mipt.npm.controls.api.PropertyDescriptor
|
||||||
import space.kscience.dataforge.meta.MetaItem
|
import space.kscience.dataforge.meta.MetaItem
|
||||||
import kotlin.time.Duration
|
import kotlin.time.Duration
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package space.kscience.dataforge.control.base
|
package ru.mipt.npm.controls.base
|
||||||
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package space.kscience.dataforge.control.base
|
package ru.mipt.npm.controls.base
|
||||||
|
|
||||||
import space.kscience.dataforge.control.api.ActionDescriptor
|
import ru.mipt.npm.controls.api.ActionDescriptor
|
||||||
import space.kscience.dataforge.meta.MetaBuilder
|
import space.kscience.dataforge.meta.MetaBuilder
|
||||||
import space.kscience.dataforge.meta.MetaItem
|
import space.kscience.dataforge.meta.MetaItem
|
||||||
import space.kscience.dataforge.meta.MetaItemNode
|
import space.kscience.dataforge.meta.MetaItemNode
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package space.kscience.dataforge.control.base
|
package ru.mipt.npm.controls.base
|
||||||
|
|
||||||
import space.kscience.dataforge.control.api.PropertyDescriptor
|
import ru.mipt.npm.controls.api.PropertyDescriptor
|
||||||
import space.kscience.dataforge.meta.*
|
import space.kscience.dataforge.meta.*
|
||||||
import space.kscience.dataforge.meta.transformations.MetaConverter
|
import space.kscience.dataforge.meta.transformations.MetaConverter
|
||||||
import space.kscience.dataforge.values.Null
|
import space.kscience.dataforge.values.Null
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package space.kscience.dataforge.control.base
|
package ru.mipt.npm.controls.base
|
||||||
|
|
||||||
import space.kscience.dataforge.meta.*
|
import space.kscience.dataforge.meta.*
|
||||||
import space.kscience.dataforge.meta.transformations.MetaConverter
|
import space.kscience.dataforge.meta.transformations.MetaConverter
|
||||||
|
@ -1,11 +1,8 @@
|
|||||||
package space.kscience.dataforge.control.controllers
|
package ru.mipt.npm.controls.controllers
|
||||||
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import space.kscience.dataforge.control.api.Device
|
import ru.mipt.npm.controls.api.*
|
||||||
import space.kscience.dataforge.control.api.DeviceHub
|
|
||||||
import space.kscience.dataforge.control.api.getOrNull
|
|
||||||
import space.kscience.dataforge.control.messages.*
|
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
import space.kscience.dataforge.meta.MetaItem
|
import space.kscience.dataforge.meta.MetaItem
|
||||||
import space.kscience.dataforge.misc.DFExperimental
|
import space.kscience.dataforge.misc.DFExperimental
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package space.kscience.dataforge.control.controllers
|
package ru.mipt.npm.controls.controllers
|
||||||
|
|
||||||
|
import ru.mipt.npm.controls.api.Device
|
||||||
|
import ru.mipt.npm.controls.api.DeviceHub
|
||||||
import space.kscience.dataforge.context.*
|
import space.kscience.dataforge.context.*
|
||||||
import space.kscience.dataforge.control.api.Device
|
|
||||||
import space.kscience.dataforge.control.api.DeviceHub
|
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
import space.kscience.dataforge.meta.MetaBuilder
|
import space.kscience.dataforge.meta.MetaBuilder
|
||||||
import space.kscience.dataforge.meta.get
|
import space.kscience.dataforge.meta.get
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
package space.kscience.dataforge.control.controllers
|
package ru.mipt.npm.controls.controllers
|
||||||
|
|
||||||
import kotlinx.coroutines.channels.Channel
|
import kotlinx.coroutines.channels.Channel
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.consumeAsFlow
|
import kotlinx.coroutines.flow.consumeAsFlow
|
||||||
import space.kscience.dataforge.control.api.DeviceHub
|
import ru.mipt.npm.controls.api.DeviceHub
|
||||||
import space.kscience.dataforge.control.api.getOrNull
|
import ru.mipt.npm.controls.api.DeviceMessage
|
||||||
import space.kscience.dataforge.control.messages.DeviceMessage
|
import ru.mipt.npm.controls.api.getOrNull
|
||||||
import space.kscience.dataforge.misc.DFExperimental
|
import space.kscience.dataforge.misc.DFExperimental
|
||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
import space.kscience.dataforge.names.toName
|
import space.kscience.dataforge.names.toName
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
package space.kscience.dataforge.control.ports
|
package ru.mipt.npm.controls.ports
|
||||||
|
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.channels.Channel
|
import kotlinx.coroutines.channels.Channel
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.receiveAsFlow
|
import kotlinx.coroutines.flow.receiveAsFlow
|
||||||
|
import ru.mipt.npm.controls.api.Socket
|
||||||
import space.kscience.dataforge.context.*
|
import space.kscience.dataforge.context.*
|
||||||
import space.kscience.dataforge.control.api.Socket
|
|
||||||
import kotlin.coroutines.CoroutineContext
|
import kotlin.coroutines.CoroutineContext
|
||||||
|
|
||||||
public interface Port : ContextAware, Socket<ByteArray>
|
public interface Port : ContextAware, Socket<ByteArray>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package space.kscience.dataforge.control.ports
|
package ru.mipt.npm.controls.ports
|
||||||
|
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package space.kscience.dataforge.control.ports
|
package ru.mipt.npm.controls.ports
|
||||||
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package space.kscience.dataforge.control.ports
|
package ru.mipt.npm.controls.ports
|
||||||
|
|
||||||
import io.ktor.utils.io.core.BytePacketBuilder
|
import io.ktor.utils.io.core.BytePacketBuilder
|
||||||
import io.ktor.utils.io.core.readBytes
|
import io.ktor.utils.io.core.readBytes
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package space.kscience.dataforge.control.controllers
|
package ru.mipt.npm.controls.controllers
|
||||||
|
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import space.kscience.dataforge.control.base.*
|
import ru.mipt.npm.controls.base.*
|
||||||
import space.kscience.dataforge.meta.MetaItem
|
import space.kscience.dataforge.meta.MetaItem
|
||||||
import space.kscience.dataforge.meta.transformations.MetaConverter
|
import space.kscience.dataforge.meta.transformations.MetaConverter
|
||||||
import kotlin.properties.ReadOnlyProperty
|
import kotlin.properties.ReadOnlyProperty
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package space.kscience.dataforge.control.ports
|
package ru.mipt.npm.controls.ports
|
||||||
|
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import space.kscience.dataforge.context.Context
|
import space.kscience.dataforge.context.Context
|
||||||
@ -41,7 +41,7 @@ public class TcpPort private constructor(
|
|||||||
*/
|
*/
|
||||||
public val startJob: Job get() = futureChannel
|
public val startJob: Job get() = futureChannel
|
||||||
|
|
||||||
private val listenerJob = this.scope.launch {
|
private val listenerJob = this.scope.launch(Dispatchers.IO) {
|
||||||
val channel = futureChannel.await()
|
val channel = futureChannel.await()
|
||||||
val buffer = ByteBuffer.allocate(1024)
|
val buffer = ByteBuffer.allocate(1024)
|
||||||
while (isActive) {
|
while (isActive) {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package space.kscience.dataforge.control.ports
|
package ru.mipt.npm.controls.ports
|
||||||
|
|
||||||
import kotlinx.coroutines.flow.flowOf
|
import kotlinx.coroutines.flow.flowOf
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
package space.kscience.dataforge.control.client
|
package ru.mipt.npm.controls.client
|
||||||
|
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.flow.catch
|
import kotlinx.coroutines.flow.catch
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import ru.mipt.npm.controls.api.DeviceMessage
|
||||||
|
import ru.mipt.npm.controls.controllers.DeviceManager
|
||||||
|
import ru.mipt.npm.controls.controllers.respondMessage
|
||||||
import ru.mipt.npm.magix.api.MagixEndpoint
|
import ru.mipt.npm.magix.api.MagixEndpoint
|
||||||
import ru.mipt.npm.magix.api.MagixMessage
|
import ru.mipt.npm.magix.api.MagixMessage
|
||||||
import space.kscience.dataforge.context.error
|
import space.kscience.dataforge.context.error
|
||||||
import space.kscience.dataforge.context.logger
|
import space.kscience.dataforge.context.logger
|
||||||
import space.kscience.dataforge.control.controllers.DeviceManager
|
|
||||||
import space.kscience.dataforge.control.controllers.respondMessage
|
|
||||||
import space.kscience.dataforge.control.messages.DeviceMessage
|
|
||||||
|
|
||||||
|
|
||||||
public const val DATAFORGE_MAGIX_FORMAT: String = "dataforge"
|
public const val DATAFORGE_MAGIX_FORMAT: String = "dataforge"
|
||||||
|
@ -5,13 +5,12 @@ import kotlinx.coroutines.flow.launchIn
|
|||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
|
import ru.mipt.npm.controls.api.get
|
||||||
|
import ru.mipt.npm.controls.controllers.DeviceManager
|
||||||
import ru.mipt.npm.magix.api.MagixEndpoint
|
import ru.mipt.npm.magix.api.MagixEndpoint
|
||||||
import ru.mipt.npm.magix.api.MagixMessage
|
import ru.mipt.npm.magix.api.MagixMessage
|
||||||
import space.kscience.dataforge.context.error
|
import space.kscience.dataforge.context.error
|
||||||
import space.kscience.dataforge.context.logger
|
import space.kscience.dataforge.context.logger
|
||||||
import space.kscience.dataforge.control.api.get
|
|
||||||
import space.kscience.dataforge.control.client.generateId
|
|
||||||
import space.kscience.dataforge.control.controllers.DeviceManager
|
|
||||||
import space.kscience.dataforge.meta.MetaItem
|
import space.kscience.dataforge.meta.MetaItem
|
||||||
|
|
||||||
public const val TANGO_MAGIX_FORMAT: String = "tango"
|
public const val TANGO_MAGIX_FORMAT: String = "tango"
|
||||||
@ -64,9 +63,8 @@ public data class TangoPayload(
|
|||||||
public fun DeviceManager.launchTangoMagix(
|
public fun DeviceManager.launchTangoMagix(
|
||||||
endpoint: MagixEndpoint<TangoPayload>,
|
endpoint: MagixEndpoint<TangoPayload>,
|
||||||
endpointID: String = TANGO_MAGIX_FORMAT,
|
endpointID: String = TANGO_MAGIX_FORMAT,
|
||||||
): Job = context.launch {
|
): Job {
|
||||||
|
suspend fun respond(request: MagixMessage<TangoPayload>, payloadBuilder: (TangoPayload) -> TangoPayload) {
|
||||||
suspend inline fun respond(request: MagixMessage<TangoPayload>, payloadBuilder: (TangoPayload) -> TangoPayload) {
|
|
||||||
endpoint.broadcast(
|
endpoint.broadcast(
|
||||||
request.copy(
|
request.copy(
|
||||||
id = generateId(request),
|
id = generateId(request),
|
||||||
@ -77,6 +75,8 @@ public fun DeviceManager.launchTangoMagix(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return context.launch {
|
||||||
endpoint.subscribe().onEach { request ->
|
endpoint.subscribe().onEach { request ->
|
||||||
try {
|
try {
|
||||||
val device = get(request.payload.device)
|
val device = get(request.payload.device)
|
||||||
@ -141,3 +141,4 @@ public fun DeviceManager.launchTangoMagix(
|
|||||||
// logger.error(error) { "Error while sending a message" }
|
// logger.error(error) { "Error while sending a message" }
|
||||||
// }.launchIn(this)
|
// }.launchIn(this)
|
||||||
}
|
}
|
||||||
|
}
|
@ -1,11 +1,11 @@
|
|||||||
package space.kscience.dataforge.control.serial
|
package ru.mipt.npm.controls.serial
|
||||||
|
|
||||||
import jssc.SerialPort.*
|
import jssc.SerialPort.*
|
||||||
import jssc.SerialPortEventListener
|
import jssc.SerialPortEventListener
|
||||||
|
import ru.mipt.npm.controls.ports.AbstractPort
|
||||||
|
import ru.mipt.npm.controls.ports.Port
|
||||||
|
import ru.mipt.npm.controls.ports.PortFactory
|
||||||
import space.kscience.dataforge.context.Context
|
import space.kscience.dataforge.context.Context
|
||||||
import space.kscience.dataforge.control.ports.AbstractPort
|
|
||||||
import space.kscience.dataforge.control.ports.Port
|
|
||||||
import space.kscience.dataforge.control.ports.PortFactory
|
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
import space.kscience.dataforge.meta.int
|
import space.kscience.dataforge.meta.int
|
||||||
import space.kscience.dataforge.meta.string
|
import space.kscience.dataforge.meta.string
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package space.kscience.dataforge.control.server
|
package ru.mipt.npm.controls.server
|
||||||
|
|
||||||
import io.ktor.application.ApplicationCall
|
import io.ktor.application.ApplicationCall
|
||||||
import io.ktor.http.ContentType
|
import io.ktor.http.ContentType
|
||||||
@ -7,8 +7,8 @@ import io.ktor.response.respondText
|
|||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import kotlinx.serialization.json.JsonObjectBuilder
|
import kotlinx.serialization.json.JsonObjectBuilder
|
||||||
import kotlinx.serialization.json.buildJsonObject
|
import kotlinx.serialization.json.buildJsonObject
|
||||||
import space.kscience.dataforge.control.messages.DeviceMessage
|
import ru.mipt.npm.controls.api.DeviceMessage
|
||||||
import space.kscience.dataforge.control.messages.toMeta
|
import ru.mipt.npm.controls.api.toMeta
|
||||||
import space.kscience.dataforge.io.*
|
import space.kscience.dataforge.io.*
|
||||||
import space.kscience.dataforge.meta.MetaSerializer
|
import space.kscience.dataforge.meta.MetaSerializer
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
@file:OptIn(ExperimentalCoroutinesApi::class, KtorExperimentalAPI::class, FlowPreview::class)
|
@file:OptIn(ExperimentalCoroutinesApi::class, KtorExperimentalAPI::class, FlowPreview::class)
|
||||||
|
|
||||||
package space.kscience.dataforge.control.server
|
package ru.mipt.npm.controls.server
|
||||||
|
|
||||||
|
|
||||||
import io.ktor.application.*
|
import io.ktor.application.*
|
||||||
@ -29,12 +29,12 @@ import kotlinx.serialization.json.Json
|
|||||||
import kotlinx.serialization.json.JsonObject
|
import kotlinx.serialization.json.JsonObject
|
||||||
import kotlinx.serialization.json.buildJsonArray
|
import kotlinx.serialization.json.buildJsonArray
|
||||||
import kotlinx.serialization.json.put
|
import kotlinx.serialization.json.put
|
||||||
import space.kscience.dataforge.control.api.getOrNull
|
import ru.mipt.npm.controls.api.DeviceMessage
|
||||||
import space.kscience.dataforge.control.controllers.DeviceManager
|
import ru.mipt.npm.controls.api.PropertyGetMessage
|
||||||
import space.kscience.dataforge.control.controllers.respondMessage
|
import ru.mipt.npm.controls.api.PropertySetMessage
|
||||||
import space.kscience.dataforge.control.messages.DeviceMessage
|
import ru.mipt.npm.controls.api.getOrNull
|
||||||
import space.kscience.dataforge.control.messages.PropertyGetMessage
|
import ru.mipt.npm.controls.controllers.DeviceManager
|
||||||
import space.kscience.dataforge.control.messages.PropertySetMessage
|
import ru.mipt.npm.controls.controllers.respondMessage
|
||||||
import space.kscience.dataforge.meta.toJson
|
import space.kscience.dataforge.meta.toJson
|
||||||
import space.kscience.dataforge.meta.toMeta
|
import space.kscience.dataforge.meta.toMeta
|
||||||
import space.kscience.dataforge.meta.toMetaItem
|
import space.kscience.dataforge.meta.toMetaItem
|
||||||
@ -42,7 +42,6 @@ import space.kscience.dataforge.meta.toMetaItem
|
|||||||
/**
|
/**
|
||||||
* Create and start a web server for several devices
|
* Create and start a web server for several devices
|
||||||
*/
|
*/
|
||||||
@OptIn(KtorExperimentalAPI::class)
|
|
||||||
public fun CoroutineScope.startDeviceServer(
|
public fun CoroutineScope.startDeviceServer(
|
||||||
manager: DeviceManager,
|
manager: DeviceManager,
|
||||||
port: Int = 8111,
|
port: Int = 8111,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package space.kscience.dataforge.control.ports
|
package ru.mipt.npm.controls.ports
|
||||||
|
|
||||||
import io.ktor.network.selector.ActorSelectorManager
|
import io.ktor.network.selector.ActorSelectorManager
|
||||||
import io.ktor.network.sockets.aSocket
|
import io.ktor.network.sockets.aSocket
|
||||||
|
@ -3,18 +3,18 @@ package ru.mipt.npm.controls.demo
|
|||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.asCoroutineDispatcher
|
import kotlinx.coroutines.asCoroutineDispatcher
|
||||||
|
import ru.mipt.npm.controls.base.*
|
||||||
|
import ru.mipt.npm.controls.controllers.double
|
||||||
import space.kscience.dataforge.context.Context
|
import space.kscience.dataforge.context.Context
|
||||||
import space.kscience.dataforge.context.Factory
|
import space.kscience.dataforge.context.Factory
|
||||||
import space.kscience.dataforge.control.base.*
|
|
||||||
import space.kscience.dataforge.control.controllers.double
|
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
import space.kscience.dataforge.values.asValue
|
import space.kscience.dataforge.values.asValue
|
||||||
import java.time.Instant
|
import java.time.Instant
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
import kotlin.math.cos
|
import kotlin.math.cos
|
||||||
import kotlin.math.sin
|
import kotlin.math.sin
|
||||||
|
import kotlin.time.Duration
|
||||||
import kotlin.time.ExperimentalTime
|
import kotlin.time.ExperimentalTime
|
||||||
import kotlin.time.seconds
|
|
||||||
|
|
||||||
@OptIn(ExperimentalTime::class)
|
@OptIn(ExperimentalTime::class)
|
||||||
class DemoDevice(context: Context) : DeviceBase(context) {
|
class DemoDevice(context: Context) : DeviceBase(context) {
|
||||||
@ -57,9 +57,9 @@ class DemoDevice(context: Context) : DeviceBase(context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
sin.readEvery(0.2.seconds)
|
sin.readEvery(Duration.seconds(0.2))
|
||||||
cos.readEvery(0.2.seconds)
|
cos.readEvery(Duration.seconds(0.2))
|
||||||
coordinates.readEvery(0.3.seconds)
|
coordinates.readEvery(Duration.seconds(0.3))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun close() {
|
override fun close() {
|
||||||
|
@ -5,10 +5,10 @@ import kotlinx.coroutines.flow.*
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.html.div
|
import kotlinx.html.div
|
||||||
import kotlinx.html.link
|
import kotlinx.html.link
|
||||||
|
import ru.mipt.npm.controls.controllers.devices
|
||||||
|
import ru.mipt.npm.controls.server.startDeviceServer
|
||||||
|
import ru.mipt.npm.controls.server.whenStarted
|
||||||
import space.kscience.dataforge.context.Context
|
import space.kscience.dataforge.context.Context
|
||||||
import space.kscience.dataforge.control.controllers.devices
|
|
||||||
import space.kscience.dataforge.control.server.startDeviceServer
|
|
||||||
import space.kscience.dataforge.control.server.whenStarted
|
|
||||||
import space.kscience.dataforge.meta.double
|
import space.kscience.dataforge.meta.double
|
||||||
import space.kscience.dataforge.names.NameToken
|
import space.kscience.dataforge.names.NameToken
|
||||||
import space.kscience.plotly.layout
|
import space.kscience.plotly.layout
|
||||||
|
@ -2,7 +2,7 @@ package ru.mipt.npm.controls.demo
|
|||||||
|
|
||||||
import com.github.ricky12awesome.jss.encodeToSchema
|
import com.github.ricky12awesome.jss.encodeToSchema
|
||||||
import com.github.ricky12awesome.jss.globalJson
|
import com.github.ricky12awesome.jss.globalJson
|
||||||
import space.kscience.dataforge.control.messages.DeviceMessage
|
import ru.mipt.npm.controls.api.DeviceMessage
|
||||||
|
|
||||||
fun main() {
|
fun main() {
|
||||||
val schema = globalJson.encodeToSchema(DeviceMessage.serializer(), generateDefinitions = false)
|
val schema = globalJson.encodeToSchema(DeviceMessage.serializer(), generateDefinitions = false)
|
||||||
|
@ -3,11 +3,8 @@ package ru.mipt.npm.magix.api
|
|||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.json.JsonElement
|
import kotlinx.serialization.json.JsonElement
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
/*
|
||||||
* Magix message according to [magix specification](https://github.com/piazza-controls/rfc/tree/master/1)
|
|
||||||
* with a [correction](https://github.com/piazza-controls/rfc/issues/12)
|
|
||||||
*
|
|
||||||
* {
|
* {
|
||||||
* "format": "string[required]",
|
* "format": "string[required]",
|
||||||
* "id":"string|number[optional, but desired]",
|
* "id":"string|number[optional, but desired]",
|
||||||
@ -19,6 +16,13 @@ import kotlinx.serialization.json.JsonElement
|
|||||||
* "payload":"object[optional]"
|
* "payload":"object[optional]"
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Magix message according to [magix specification](https://github.com/piazza-controls/rfc/tree/master/1)
|
||||||
|
* with a [correction](https://github.com/piazza-controls/rfc/issues/12)
|
||||||
|
*
|
||||||
|
*/
|
||||||
@Serializable
|
@Serializable
|
||||||
public data class MagixMessage<T>(
|
public data class MagixMessage<T>(
|
||||||
val format: String,
|
val format: String,
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
package space.kscience.dataforge.magix.api
|
package ru.mipt.npm.magix.api
|
||||||
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import ru.mipt.npm.magix.api.MagixEndpoint
|
|
||||||
import ru.mipt.npm.magix.api.MagixMessage
|
|
||||||
import ru.mipt.npm.magix.api.MagixMessageFilter
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Launch magix message converter service
|
* Launch magix message converter service
|
||||||
|
@ -6,8 +6,8 @@ import kotlinx.serialization.KSerializer
|
|||||||
import ru.mipt.npm.magix.api.MagixEndpoint
|
import ru.mipt.npm.magix.api.MagixEndpoint
|
||||||
import ru.mipt.npm.magix.api.MagixMessage
|
import ru.mipt.npm.magix.api.MagixMessage
|
||||||
import ru.mipt.npm.magix.api.MagixMessageFilter
|
import ru.mipt.npm.magix.api.MagixMessageFilter
|
||||||
import space.kscience.dataforge.magix.service.RSocketMagixEndpoint
|
import ru.mipt.npm.magix.service.RSocketMagixEndpoint
|
||||||
import space.kscience.dataforge.magix.service.withTcp
|
import ru.mipt.npm.magix.service.withTcp
|
||||||
import java.util.concurrent.Flow
|
import java.util.concurrent.Flow
|
||||||
|
|
||||||
public class ControlsMagixClient<T>(
|
public class ControlsMagixClient<T>(
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package space.kscience.dataforge.magix.server
|
package ru.mipt.npm.magix.server
|
||||||
|
|
||||||
import io.ktor.network.selector.ActorSelectorManager
|
import io.ktor.network.selector.ActorSelectorManager
|
||||||
import io.ktor.network.sockets.aSocket
|
import io.ktor.network.sockets.aSocket
|
||||||
@ -13,9 +13,6 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import ru.mipt.npm.magix.api.MagixEndpoint.Companion.DEFAULT_MAGIX_RAW_PORT
|
import ru.mipt.npm.magix.api.MagixEndpoint.Companion.DEFAULT_MAGIX_RAW_PORT
|
||||||
import ru.mipt.npm.magix.api.MagixEndpoint.Companion.DEFAULT_MAGIX_WS_PORT
|
import ru.mipt.npm.magix.api.MagixEndpoint.Companion.DEFAULT_MAGIX_WS_PORT
|
||||||
import ru.mipt.npm.magix.server.GenericMagixMessage
|
|
||||||
import ru.mipt.npm.magix.server.magixAcceptor
|
|
||||||
import ru.mipt.npm.magix.server.magixModule
|
|
||||||
|
|
||||||
@OptIn(KtorExperimentalAPI::class)
|
@OptIn(KtorExperimentalAPI::class)
|
||||||
public fun CoroutineScope.startMagixServer(
|
public fun CoroutineScope.startMagixServer(
|
||||||
|
@ -29,8 +29,6 @@ import ru.mipt.npm.magix.api.MagixEndpoint.Companion.magixJson
|
|||||||
import ru.mipt.npm.magix.api.MagixMessage
|
import ru.mipt.npm.magix.api.MagixMessage
|
||||||
import ru.mipt.npm.magix.api.MagixMessageFilter
|
import ru.mipt.npm.magix.api.MagixMessageFilter
|
||||||
import ru.mipt.npm.magix.api.filter
|
import ru.mipt.npm.magix.api.filter
|
||||||
import space.kscience.dataforge.magix.server.SseEvent
|
|
||||||
import space.kscience.dataforge.magix.server.respondSse
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
public typealias GenericMagixMessage = MagixMessage<JsonElement>
|
public typealias GenericMagixMessage = MagixMessage<JsonElement>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package space.kscience.dataforge.magix.server
|
package ru.mipt.npm.magix.server
|
||||||
|
|
||||||
import io.ktor.application.ApplicationCall
|
import io.ktor.application.ApplicationCall
|
||||||
import io.ktor.http.CacheControl
|
import io.ktor.http.CacheControl
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package space.kscience.dataforge.magix.service
|
package ru.mipt.npm.magix.service
|
||||||
|
|
||||||
import io.ktor.client.HttpClient
|
import io.ktor.client.HttpClient
|
||||||
import io.ktor.client.features.websocket.WebSockets
|
import io.ktor.client.features.websocket.WebSockets
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package space.kscience.dataforge.magix.service
|
package ru.mipt.npm.magix.service
|
||||||
|
|
||||||
import io.ktor.network.selector.ActorSelectorManager
|
import io.ktor.network.selector.ActorSelectorManager
|
||||||
import io.ktor.network.sockets.SocketOptions
|
import io.ktor.network.sockets.SocketOptions
|
||||||
|
@ -11,10 +11,10 @@ import javafx.scene.layout.VBox
|
|||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import ru.mipt.npm.controls.controllers.DeviceManager
|
||||||
|
import ru.mipt.npm.controls.controllers.installing
|
||||||
import space.kscience.dataforge.context.Global
|
import space.kscience.dataforge.context.Global
|
||||||
import space.kscience.dataforge.context.fetch
|
import space.kscience.dataforge.context.fetch
|
||||||
import space.kscience.dataforge.control.controllers.DeviceManager
|
|
||||||
import space.kscience.dataforge.control.controllers.installing
|
|
||||||
import tornadofx.*
|
import tornadofx.*
|
||||||
|
|
||||||
class PiMotionMasterApp : App(PiMotionMasterView::class)
|
class PiMotionMasterApp : App(PiMotionMasterView::class)
|
||||||
|
@ -3,21 +3,22 @@
|
|||||||
package ru.mipt.npm.devices.pimotionmaster
|
package ru.mipt.npm.devices.pimotionmaster
|
||||||
|
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.flow.transformWhile
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
|
import ru.mipt.npm.controls.api.DeviceHub
|
||||||
|
import ru.mipt.npm.controls.api.PropertyDescriptor
|
||||||
|
import ru.mipt.npm.controls.base.*
|
||||||
|
import ru.mipt.npm.controls.controllers.DeviceFactory
|
||||||
|
import ru.mipt.npm.controls.controllers.duration
|
||||||
|
import ru.mipt.npm.controls.ports.*
|
||||||
import space.kscience.dataforge.context.*
|
import space.kscience.dataforge.context.*
|
||||||
import space.kscience.dataforge.control.api.DeviceHub
|
|
||||||
import space.kscience.dataforge.control.api.PropertyDescriptor
|
|
||||||
import space.kscience.dataforge.control.base.*
|
|
||||||
import space.kscience.dataforge.control.controllers.*
|
|
||||||
import space.kscience.dataforge.control.ports.*
|
|
||||||
import space.kscience.dataforge.meta.*
|
import space.kscience.dataforge.meta.*
|
||||||
import space.kscience.dataforge.names.NameToken
|
import space.kscience.dataforge.names.NameToken
|
||||||
import space.kscience.dataforge.values.asValue
|
import space.kscience.dataforge.values.asValue
|
||||||
import tornadofx.*
|
import kotlin.collections.component1
|
||||||
import java.util.*
|
import kotlin.collections.component2
|
||||||
import kotlin.error
|
|
||||||
import kotlin.time.Duration
|
import kotlin.time.Duration
|
||||||
|
|
||||||
class PiMotionMasterDevice(
|
class PiMotionMasterDevice(
|
||||||
|
@ -5,10 +5,10 @@ import kotlinx.coroutines.channels.Channel
|
|||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
import kotlinx.coroutines.sync.Mutex
|
import kotlinx.coroutines.sync.Mutex
|
||||||
import kotlinx.coroutines.sync.withLock
|
import kotlinx.coroutines.sync.withLock
|
||||||
|
import ru.mipt.npm.controls.api.Socket
|
||||||
|
import ru.mipt.npm.controls.ports.AbstractPort
|
||||||
|
import ru.mipt.npm.controls.ports.withDelimiter
|
||||||
import space.kscience.dataforge.context.*
|
import space.kscience.dataforge.context.*
|
||||||
import space.kscience.dataforge.control.api.Socket
|
|
||||||
import space.kscience.dataforge.control.ports.AbstractPort
|
|
||||||
import space.kscience.dataforge.control.ports.withDelimiter
|
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.time.Duration
|
import kotlin.time.Duration
|
||||||
|
|
||||||
@ -167,7 +167,7 @@ class PiMotionMasterVirtualDevice(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun evaluateRequest(request: ByteArray) {
|
override suspend fun evaluateRequest(request: ByteArray) {
|
||||||
assert(request.last() == '\n'.toByte())
|
assert(request.last() == '\n'.code.toByte())
|
||||||
val string = request.decodeToString().substringBefore("\n")
|
val string = request.decodeToString().substringBefore("\n")
|
||||||
.dropWhile { it != '*' && it != '#' && it !in 'A'..'Z' } //filter junk symbols at the beginning of the line
|
.dropWhile { it != '*' && it != '#' && it !in 'A'..'Z' } //filter junk symbols at the beginning of the line
|
||||||
|
|
||||||
|
@ -6,11 +6,11 @@ import javafx.beans.property.ReadOnlyProperty
|
|||||||
import kotlinx.coroutines.flow.catch
|
import kotlinx.coroutines.flow.catch
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import ru.mipt.npm.controls.api.Device
|
||||||
|
import ru.mipt.npm.controls.base.TypedDeviceProperty
|
||||||
|
import ru.mipt.npm.controls.base.TypedReadOnlyDeviceProperty
|
||||||
import space.kscience.dataforge.context.info
|
import space.kscience.dataforge.context.info
|
||||||
import space.kscience.dataforge.context.logger
|
import space.kscience.dataforge.context.logger
|
||||||
import space.kscience.dataforge.control.api.Device
|
|
||||||
import space.kscience.dataforge.control.base.TypedDeviceProperty
|
|
||||||
import space.kscience.dataforge.control.base.TypedReadOnlyDeviceProperty
|
|
||||||
import tornadofx.*
|
import tornadofx.*
|
||||||
|
|
||||||
fun <T : Any> TypedReadOnlyDeviceProperty<T>.fxProperty(ownerDevice: Device?): ReadOnlyProperty<T> =
|
fun <T : Any> TypedReadOnlyDeviceProperty<T>.fxProperty(ownerDevice: Device?): ReadOnlyProperty<T> =
|
||||||
|
@ -5,7 +5,6 @@ import io.ktor.network.sockets.aSocket
|
|||||||
import io.ktor.network.sockets.openReadChannel
|
import io.ktor.network.sockets.openReadChannel
|
||||||
import io.ktor.network.sockets.openWriteChannel
|
import io.ktor.network.sockets.openWriteChannel
|
||||||
import io.ktor.util.InternalAPI
|
import io.ktor.util.InternalAPI
|
||||||
import io.ktor.util.KtorExperimentalAPI
|
|
||||||
import io.ktor.util.moveToByteArray
|
import io.ktor.util.moveToByteArray
|
||||||
import io.ktor.utils.io.writeAvailable
|
import io.ktor.utils.io.writeAvailable
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
@ -18,7 +17,7 @@ val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
|
|||||||
throwable.printStackTrace()
|
throwable.printStackTrace()
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(KtorExperimentalAPI::class, InternalAPI::class)
|
@OptIn(InternalAPI::class)
|
||||||
fun Context.launchPiDebugServer(port: Int, axes: List<String>): Job = launch(exceptionHandler) {
|
fun Context.launchPiDebugServer(port: Int, axes: List<String>): Job = launch(exceptionHandler) {
|
||||||
val virtualDevice = PiMotionMasterVirtualDevice(this@launchPiDebugServer, axes)
|
val virtualDevice = PiMotionMasterVirtualDevice(this@launchPiDebugServer, axes)
|
||||||
val server = aSocket(ActorSelectorManager(Dispatchers.IO)).tcp().bind(InetSocketAddress("localhost", port))
|
val server = aSocket(ActorSelectorManager(Dispatchers.IO)).tcp().bind(InetSocketAddress("localhost", port))
|
||||||
|
Loading…
Reference in New Issue
Block a user