Unisolate properties

This commit is contained in:
Alexander Nozik 2020-09-08 10:13:14 +03:00
parent 10bb85ac83
commit 4b5bc40a4f
21 changed files with 524 additions and 493 deletions

View File

@ -1,3 +1,8 @@
plugins{
kotlin("jvm") version "1.4.0" apply false
kotlin("js") version "1.4.0" apply false
}
val dataforgeVersion by extra("0.1.9-dev-2") val dataforgeVersion by extra("0.1.9-dev-2")
allprojects { allprojects {
@ -6,6 +11,7 @@ allprojects {
maven("https://dl.bintray.com/pdvrieze/maven") maven("https://dl.bintray.com/pdvrieze/maven")
maven("http://maven.jzy3d.org/releases") maven("http://maven.jzy3d.org/releases")
maven("https://kotlin.bintray.com/js-externals") maven("https://kotlin.bintray.com/js-externals")
maven("https://maven.pkg.github.com/altavir/kotlin-logging/")
} }
group = "hep.dataforge" group = "hep.dataforge"

View File

@ -1,19 +1,11 @@
plugins { plugins {
id("kscience.mpp") id("ru.mipt.npm.mpp")
id("kscience.publish") id("ru.mipt.npm.publish")
} }
val ktorVersion: String by extra("1.4.0") val ktorVersion: String by extra("1.4.0")
kotlin { kotlin {
// js {
// browser {
// dceTask {
// keep("ktor-ktor-io.\$\$importsForInline\$\$.ktor-ktor-io.io.ktor.utils.io")
// }
// }
// }
sourceSets { sourceSets {
commonMain { commonMain {
dependencies { dependencies {

View File

@ -1,6 +1,6 @@
plugins { plugins {
id("kscience.mpp") id("ru.mipt.npm.mpp")
id("kscience.publish") id("ru.mipt.npm.publish")
} }
val dataforgeVersion: String by rootProject.extra val dataforgeVersion: String by rootProject.extra
@ -15,7 +15,6 @@ kotlin {
commonMain{ commonMain{
dependencies { dependencies {
api("hep.dataforge:dataforge-io:$dataforgeVersion") api("hep.dataforge:dataforge-io:$dataforgeVersion")
//implementation("org.jetbrains.kotlinx:atomicfu-common:0.14.3")
} }
} }
jvmMain{ jvmMain{

View File

@ -3,7 +3,6 @@ package hep.dataforge.control.api
import hep.dataforge.control.api.Device.Companion.DEVICE_TARGET import hep.dataforge.control.api.Device.Companion.DEVICE_TARGET
import hep.dataforge.io.Envelope import hep.dataforge.io.Envelope
import hep.dataforge.io.EnvelopeBuilder import hep.dataforge.io.EnvelopeBuilder
import hep.dataforge.io.Responder
import hep.dataforge.meta.Meta import hep.dataforge.meta.Meta
import hep.dataforge.meta.MetaItem import hep.dataforge.meta.MetaItem
import hep.dataforge.provider.Type import hep.dataforge.provider.Type
@ -15,7 +14,7 @@ import kotlinx.io.Closeable
* General interface describing a managed Device * General interface describing a managed Device
*/ */
@Type(DEVICE_TARGET) @Type(DEVICE_TARGET)
public interface Device : Responder, Closeable { public interface Device : Closeable {
/** /**
* List of supported property descriptors * List of supported property descriptors
*/ */
@ -73,7 +72,7 @@ public interface Device : Responder, Closeable {
* [setProperty], [getProperty] or [execute] and not defined for a generic device. * [setProperty], [getProperty] or [execute] and not defined for a generic device.
* *
*/ */
override suspend fun respond(request: Envelope): EnvelopeBuilder = error("No binary response defined") public suspend fun respondWithData(request: Envelope): EnvelopeBuilder = error("No binary response defined")
override fun close() { override fun close() {
scope.cancel("The device is closed") scope.cancel("The device is closed")

View File

@ -6,9 +6,9 @@ import hep.dataforge.meta.MetaItem
* PropertyChangeListener Interface * PropertyChangeListener Interface
* [value] is a new value that property has after a change; null is for invalid state. * [value] is a new value that property has after a change; null is for invalid state.
*/ */
interface DeviceListener { public interface DeviceListener {
fun propertyChanged(propertyName: String, value: MetaItem<*>?) public fun propertyChanged(propertyName: String, value: MetaItem<*>?)
fun actionExecuted(action: String, argument: MetaItem<*>?, result: MetaItem<*>?) {} public fun actionExecuted(action: String, argument: MetaItem<*>?, result: MetaItem<*>?) {}
//TODO add general message listener method //TODO add general message listener method
} }

View File

@ -6,17 +6,17 @@ import hep.dataforge.meta.string
/** /**
* A descriptor for property * A descriptor for property
*/ */
class PropertyDescriptor(name: String) : Scheme() { public class PropertyDescriptor(name: String) : Scheme() {
val name by string(name) public val name: String by string(name)
var info by string() public var info: String? by string()
} }
/** /**
* A descriptor for property * A descriptor for property
*/ */
class ActionDescriptor(name: String) : Scheme() { public class ActionDescriptor(name: String) : Scheme() {
val name by string(name) public val name: String by string(name)
var info by string() public var info: String? by string()
//var descriptor by spec(ItemDescriptor) //var descriptor by spec(ItemDescriptor)
} }

View File

@ -1,74 +1,10 @@
package hep.dataforge.control.base package hep.dataforge.control.base
import hep.dataforge.control.api.ActionDescriptor import hep.dataforge.control.api.ActionDescriptor
import hep.dataforge.meta.MetaBuilder
import hep.dataforge.meta.MetaItem import hep.dataforge.meta.MetaItem
import hep.dataforge.values.Value
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
interface Action { public interface Action {
val name: String public val name: String
val descriptor: ActionDescriptor public val descriptor: ActionDescriptor
suspend operator fun invoke(arg: MetaItem<*>? = null): MetaItem<*>? public suspend operator fun invoke(arg: MetaItem<*>? = null): MetaItem<*>?
}
private fun DeviceBase.actionExecuted(action: String, argument: MetaItem<*>?, result: MetaItem<*>?){
notifyListeners { actionExecuted(action, argument, result) }
}
/**
* A stand-alone action
*/
class IsolatedAction(
override val name: String,
override val descriptor: ActionDescriptor,
val callback: (action: String, argument: MetaItem<*>?, result: MetaItem<*>?) -> Unit,
val block: suspend (MetaItem<*>?) -> MetaItem<*>?
) : Action {
override suspend fun invoke(arg: MetaItem<*>?): MetaItem<*>? = block(arg).also {
callback(name, arg, it)
}
}
class ActionDelegate<D : DeviceBase>(
val owner: D,
val descriptorBuilder: ActionDescriptor.() -> Unit = {},
val block: suspend (MetaItem<*>?) -> MetaItem<*>?
) : ReadOnlyProperty<D, Action> {
override fun getValue(thisRef: D, property: KProperty<*>): Action {
val name = property.name
return owner.registerAction(name) {
IsolatedAction(name, ActionDescriptor(name).apply(descriptorBuilder), owner::actionExecuted, block)
}
}
}
fun <D : DeviceBase> D.request(
descriptorBuilder: ActionDescriptor.() -> Unit = {},
block: suspend (MetaItem<*>?) -> MetaItem<*>?
): ActionDelegate<D> = ActionDelegate(this, descriptorBuilder, block)
fun <D : DeviceBase> D.requestValue(
descriptorBuilder: ActionDescriptor.() -> Unit = {},
block: suspend (MetaItem<*>?) -> Any?
): ActionDelegate<D> = ActionDelegate(this, descriptorBuilder) {
val res = block(it)
MetaItem.ValueItem(Value.of(res))
}
fun <D : DeviceBase> D.requestMeta(
descriptorBuilder: ActionDescriptor.() -> Unit = {},
block: suspend MetaBuilder.(MetaItem<*>?) -> Unit
): ActionDelegate<D> = ActionDelegate(this, descriptorBuilder) {
val res = MetaBuilder().apply { block(it) }
MetaItem.NodeItem(res)
}
fun <D : DeviceBase> D.action(
descriptorBuilder: ActionDescriptor.() -> Unit = {},
block: suspend (MetaItem<*>?) -> Unit
): ActionDelegate<D> = ActionDelegate(this, descriptorBuilder) {
block(it)
null
} }

View File

@ -5,14 +5,23 @@ import hep.dataforge.control.api.Device
import hep.dataforge.control.api.DeviceListener import hep.dataforge.control.api.DeviceListener
import hep.dataforge.control.api.PropertyDescriptor import hep.dataforge.control.api.PropertyDescriptor
import hep.dataforge.meta.MetaItem import hep.dataforge.meta.MetaItem
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
/** /**
* Baseline implementation of [Device] interface * Baseline implementation of [Device] interface
*/ */
abstract class DeviceBase : Device { public abstract class DeviceBase : Device {
private val properties = HashMap<String, ReadOnlyDeviceProperty>() private val _properties = HashMap<String, ReadOnlyDeviceProperty>()
private val actions = HashMap<String, Action>() public val properties: Map<String, ReadOnlyDeviceProperty> get() = _properties
private val _actions = HashMap<String, Action>()
public val actions: Map<String, Action> get() = _actions
private val listeners = ArrayList<Pair<Any?, DeviceListener>>(4) private val listeners = ArrayList<Pair<Any?, DeviceListener>>(4)
@ -24,11 +33,11 @@ abstract class DeviceBase : Device {
listeners.removeAll { it.first == owner } listeners.removeAll { it.first == owner }
} }
fun notifyListeners(block: DeviceListener.() -> Unit) { internal fun notifyListeners(block: DeviceListener.() -> Unit) {
listeners.forEach { it.second.block() } listeners.forEach { it.second.block() }
} }
fun notifyPropertyChanged(propertyName: String) { public fun notifyPropertyChanged(propertyName: String) {
scope.launch { scope.launch {
val value = getProperty(propertyName) val value = getProperty(propertyName)
notifyListeners { propertyChanged(propertyName, value) } notifyListeners { propertyChanged(propertyName, value) }
@ -36,41 +45,188 @@ abstract class DeviceBase : Device {
} }
override val propertyDescriptors: Collection<PropertyDescriptor> override val propertyDescriptors: Collection<PropertyDescriptor>
get() = properties.values.map { it.descriptor } get() = _properties.values.map { it.descriptor }
override val actionDescriptors: Collection<ActionDescriptor> override val actionDescriptors: Collection<ActionDescriptor>
get() = actions.values.map { it.descriptor } get() = _actions.values.map { it.descriptor }
internal fun registerProperty(name: String, builder: () -> ReadOnlyDeviceProperty): ReadOnlyDeviceProperty { internal fun <P : ReadOnlyDeviceProperty> registerProperty(name: String, property: P) {
return properties.getOrPut(name, builder) if (_properties.contains(name)) error("Property with name $name already registered")
_properties[name] = property
} }
internal fun registerMutableProperty(name: String, builder: () -> DeviceProperty): DeviceProperty { internal fun registerAction(name: String, action: Action) {
return properties.getOrPut(name, builder) as DeviceProperty if (_actions.contains(name)) error("Action with name $name already registered")
} _actions[name] = action
internal fun registerAction(name: String, builder: () -> Action): Action {
return actions.getOrPut(name, builder)
} }
override suspend fun getProperty(propertyName: String): MetaItem<*> = override suspend fun getProperty(propertyName: String): MetaItem<*> =
(properties[propertyName] ?: error("Property with name $propertyName not defined")).read() (_properties[propertyName] ?: error("Property with name $propertyName not defined")).read()
override suspend fun invalidateProperty(propertyName: String) { override suspend fun invalidateProperty(propertyName: String) {
(properties[propertyName] ?: error("Property with name $propertyName not defined")).invalidate() (_properties[propertyName] ?: error("Property with name $propertyName not defined")).invalidate()
} }
override suspend fun setProperty(propertyName: String, value: MetaItem<*>) { override suspend fun setProperty(propertyName: String, value: MetaItem<*>) {
(properties[propertyName] as? DeviceProperty ?: error("Property with name $propertyName not defined")).write( (_properties[propertyName] as? DeviceProperty ?: error("Property with name $propertyName not defined")).write(
value value
) )
} }
override suspend fun execute(command: String, argument: MetaItem<*>?): MetaItem<*>? = override suspend fun execute(command: String, argument: MetaItem<*>?): MetaItem<*>? =
(actions[command] ?: error("Request with name $command not defined")).invoke(argument) (_actions[command] ?: error("Request with name $command not defined")).invoke(argument)
@OptIn(ExperimentalCoroutinesApi::class)
private open inner class BasicReadOnlyDeviceProperty(
override val name: String,
default: MetaItem<*>?,
override val descriptor: PropertyDescriptor,
private val getter: suspend (before: MetaItem<*>?) -> MetaItem<*>,
) : ReadOnlyDeviceProperty {
companion object { override val scope: CoroutineScope get() = this@DeviceBase.scope
private val state: MutableStateFlow<MetaItem<*>?> = MutableStateFlow(default)
override val value: MetaItem<*>? get() = state.value
override suspend fun invalidate() {
state.value = null
}
override fun updateLogical(item: MetaItem<*>) {
state.value = item
notifyListeners {
propertyChanged(name, item)
}
}
override suspend fun read(force: Boolean): MetaItem<*> {
//backup current value
val currentValue = value
return if (force || currentValue == null) {
val res = withContext(scope.coroutineContext) {
//all device operations should be run on device context
//TODO add error catching
getter(currentValue)
}
updateLogical(res)
res
} else {
currentValue
}
}
override fun flow(): StateFlow<MetaItem<*>?> = state
}
/**
* Create a bound read-only property with given [getter]
*/
public fun newReadOnlyProperty(
name: String,
default: MetaItem<*>?,
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
getter: suspend (MetaItem<*>?) -> MetaItem<*>,
): ReadOnlyDeviceProperty {
val property = BasicReadOnlyDeviceProperty(
name,
default,
PropertyDescriptor(name).apply(descriptorBuilder),
getter
)
registerProperty(name, property)
return property
}
@OptIn(ExperimentalCoroutinesApi::class)
private inner class BasicDeviceProperty(
name: String,
default: MetaItem<*>?,
descriptor: PropertyDescriptor,
getter: suspend (MetaItem<*>?) -> MetaItem<*>,
private val setter: suspend (oldValue: MetaItem<*>?, newValue: MetaItem<*>) -> MetaItem<*>?,
) : BasicReadOnlyDeviceProperty(name, default, descriptor, getter), DeviceProperty {
override var value: MetaItem<*>?
get() = super.value
set(value) {
scope.launch {
if (value == null) {
invalidate()
} else {
write(value)
}
}
}
private val writeLock = Mutex()
override suspend fun write(item: MetaItem<*>) {
writeLock.withLock {
//fast return if value is not changed
if (item == value) return@withLock
val oldValue = value
//all device operations should be run on device context
withContext(scope.coroutineContext) {
//TODO add error catching
setter(oldValue, item)?.let {
updateLogical(it)
}
}
}
}
}
/**
* Create a bound mutable property with given [getter] and [setter]
*/
public fun newMutableProperty(
name: String,
default: MetaItem<*>?,
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
getter: suspend (MetaItem<*>?) -> MetaItem<*>,
setter: suspend (oldValue: MetaItem<*>?, newValue: MetaItem<*>) -> MetaItem<*>?,
): DeviceProperty {
val property = BasicDeviceProperty(
name,
default,
PropertyDescriptor(name).apply(descriptorBuilder),
getter,
setter
)
registerProperty(name, property)
return property
}
/**
* A stand-alone action
*/
private inner class BasicAction(
override val name: String,
override val descriptor: ActionDescriptor,
private val block: suspend (MetaItem<*>?) -> MetaItem<*>?,
) : Action {
override suspend fun invoke(arg: MetaItem<*>?): MetaItem<*>? = block(arg).also {
notifyListeners {
actionExecuted(name, arg, it)
}
}
}
/**
* Create a new bound action
*/
public fun newAction(
name: String,
descriptorBuilder: ActionDescriptor.() -> Unit = {},
block: suspend (MetaItem<*>?) -> MetaItem<*>?,
): Action {
val action = BasicAction(name, ActionDescriptor(name).apply(descriptorBuilder), block)
registerAction(name, action)
return action
}
public companion object {
} }
} }

View File

@ -1,329 +0,0 @@
package hep.dataforge.control.base
import hep.dataforge.control.api.PropertyDescriptor
import hep.dataforge.meta.*
import hep.dataforge.values.Null
import hep.dataforge.values.Value
import hep.dataforge.values.asValue
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
private fun DeviceBase.propertyChanged(name: String, item: MetaItem<*>?){
notifyListeners { propertyChanged(name, item) }
}
/**
* A stand-alone [ReadOnlyDeviceProperty] implementation not directly attached to a device
*/
@OptIn(ExperimentalCoroutinesApi::class)
public open class IsolatedReadOnlyDeviceProperty(
override val name: String,
default: MetaItem<*>?,
override val descriptor: PropertyDescriptor,
override val scope: CoroutineScope,
private val callback: (name: String, item: MetaItem<*>) -> Unit,
private val getter: suspend (before: MetaItem<*>?) -> MetaItem<*>
) : ReadOnlyDeviceProperty {
private val state: MutableStateFlow<MetaItem<*>?> = MutableStateFlow(default)
override val value: MetaItem<*>? get() = state.value
override suspend fun invalidate() {
state.value = null
}
override fun updateLogical(item: MetaItem<*>) {
state.value = item
callback(name, item)
}
override suspend fun read(force: Boolean): MetaItem<*> {
//backup current value
val currentValue = value
return if (force || currentValue == null) {
val res = withContext(scope.coroutineContext) {
//all device operations should be run on device context
//TODO add error catching
getter(currentValue)
}
updateLogical(res)
res
} else {
currentValue
}
}
override fun flow(): StateFlow<MetaItem<*>?> = state
}
public fun DeviceBase.readOnlyProperty(
name: String,
default: MetaItem<*>?,
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
getter: suspend (MetaItem<*>?) -> MetaItem<*>
): ReadOnlyDeviceProperty = registerProperty(name) {
IsolatedReadOnlyDeviceProperty(
name,
default,
PropertyDescriptor(name).apply(descriptorBuilder),
scope,
::propertyChanged,
getter
)
}
private class ReadOnlyDevicePropertyDelegate<D : DeviceBase>(
val owner: D,
val default: MetaItem<*>?,
val descriptorBuilder: PropertyDescriptor.() -> Unit = {},
private val getter: suspend (MetaItem<*>?) -> MetaItem<*>
) : ReadOnlyProperty<D, ReadOnlyDeviceProperty> {
override fun getValue(thisRef: D, property: KProperty<*>): ReadOnlyDeviceProperty {
val name = property.name
return owner.registerProperty(name) {
@OptIn(ExperimentalCoroutinesApi::class)
IsolatedReadOnlyDeviceProperty(
name,
default,
PropertyDescriptor(name).apply(descriptorBuilder),
owner.scope,
owner::propertyChanged,
getter
)
}
}
}
public fun <D : DeviceBase> D.reading(
default: MetaItem<*>? = null,
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
getter: suspend (MetaItem<*>?) -> MetaItem<*>
): ReadOnlyProperty<D, ReadOnlyDeviceProperty> = ReadOnlyDevicePropertyDelegate(
this,
default,
descriptorBuilder,
getter
)
public fun <D : DeviceBase> D.readingValue(
default: Value? = null,
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
getter: suspend () -> Any?
): ReadOnlyProperty<D, ReadOnlyDeviceProperty> = ReadOnlyDevicePropertyDelegate(
this,
default?.let { MetaItem.ValueItem(it) },
descriptorBuilder,
getter = { MetaItem.ValueItem(Value.of(getter())) }
)
public fun <D : DeviceBase> D.readingNumber(
default: Number? = null,
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
getter: suspend () -> Number
): ReadOnlyProperty<D, ReadOnlyDeviceProperty> = ReadOnlyDevicePropertyDelegate(
this,
default?.let { MetaItem.ValueItem(it.asValue()) },
descriptorBuilder,
getter = {
val number = getter()
MetaItem.ValueItem(number.asValue())
}
)
public fun <D : DeviceBase> D.readingString(
default: Number? = null,
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
getter: suspend () -> String
): ReadOnlyProperty<D, ReadOnlyDeviceProperty> = ReadOnlyDevicePropertyDelegate(
this,
default?.let { MetaItem.ValueItem(it.asValue()) },
descriptorBuilder,
getter = {
val number = getter()
MetaItem.ValueItem(number.asValue())
}
)
public fun <D : DeviceBase> D.readingMeta(
default: Meta? = null,
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
getter: suspend MetaBuilder.() -> Unit
): ReadOnlyProperty<D, ReadOnlyDeviceProperty> = ReadOnlyDevicePropertyDelegate(
this,
default?.let { MetaItem.NodeItem(it) },
descriptorBuilder,
getter = {
MetaItem.NodeItem(MetaBuilder().apply { getter() })
}
)
@OptIn(ExperimentalCoroutinesApi::class)
public class IsolatedDeviceProperty(
name: String,
default: MetaItem<*>?,
descriptor: PropertyDescriptor,
scope: CoroutineScope,
updateCallback: (name: String, item: MetaItem<*>?) -> Unit,
getter: suspend (MetaItem<*>?) -> MetaItem<*>,
private val setter: suspend (oldValue: MetaItem<*>?, newValue: MetaItem<*>) -> MetaItem<*>?
) : IsolatedReadOnlyDeviceProperty(name, default, descriptor, scope, updateCallback, getter), DeviceProperty {
override var value: MetaItem<*>?
get() = super.value
set(value) {
scope.launch {
if (value == null) {
invalidate()
} else {
write(value)
}
}
}
private val writeLock = Mutex()
override suspend fun write(item: MetaItem<*>) {
writeLock.withLock {
//fast return if value is not changed
if (item == value) return@withLock
val oldValue = value
//all device operations should be run on device context
withContext(scope.coroutineContext) {
//TODO add error catching
setter(oldValue, item)?.let {
updateLogical(it)
}
}
}
}
}
public fun DeviceBase.mutableProperty(
name: String,
default: MetaItem<*>?,
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
getter: suspend (MetaItem<*>?) -> MetaItem<*>,
setter: suspend (oldValue: MetaItem<*>?, newValue: MetaItem<*>) -> MetaItem<*>?
): DeviceProperty = registerMutableProperty(name) {
IsolatedDeviceProperty(
name,
default,
PropertyDescriptor(name).apply(descriptorBuilder),
scope,
::propertyChanged,
getter,
setter
)
}
private class DevicePropertyDelegate<D : DeviceBase>(
val owner: D,
val default: MetaItem<*>?,
val descriptorBuilder: PropertyDescriptor.() -> Unit = {},
private val getter: suspend (MetaItem<*>?) -> MetaItem<*>,
private val setter: suspend (oldValue: MetaItem<*>?, newValue: MetaItem<*>) -> MetaItem<*>?
) : ReadOnlyProperty<D, DeviceProperty> {
override fun getValue(thisRef: D, property: KProperty<*>): IsolatedDeviceProperty {
val name = property.name
return owner.registerMutableProperty(name) {
@OptIn(ExperimentalCoroutinesApi::class)
IsolatedDeviceProperty(
name,
default,
PropertyDescriptor(name).apply(descriptorBuilder),
owner.scope,
owner::propertyChanged,
getter,
setter
)
} as IsolatedDeviceProperty
}
}
public fun <D : DeviceBase> D.writing(
default: MetaItem<*>? = null,
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
getter: suspend (MetaItem<*>?) -> MetaItem<*>,
setter: suspend (oldValue: MetaItem<*>?, newValue: MetaItem<*>) -> MetaItem<*>?
): ReadOnlyProperty<D, DeviceProperty> = DevicePropertyDelegate(
this,
default,
descriptorBuilder,
getter,
setter
)
public fun <D : DeviceBase> D.writingVirtual(
default: MetaItem<*>,
descriptorBuilder: PropertyDescriptor.() -> Unit = {}
): ReadOnlyProperty<D, DeviceProperty> = writing(
default,
descriptorBuilder,
getter = { it ?: default },
setter = { _, newItem -> newItem }
)
public fun <D : DeviceBase> D.writingVirtual(
default: Value,
descriptorBuilder: PropertyDescriptor.() -> Unit = {}
): ReadOnlyProperty<D, DeviceProperty> = writing(
MetaItem.ValueItem(default),
descriptorBuilder,
getter = { it ?: MetaItem.ValueItem(default) },
setter = { _, newItem -> newItem }
)
public fun <D : DeviceBase> D.writingDouble(
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
getter: suspend (Double) -> Double,
setter: suspend (oldValue: Double?, newValue: Double) -> Double?
): ReadOnlyProperty<D, DeviceProperty> {
val innerGetter: suspend (MetaItem<*>?) -> MetaItem<*> = {
MetaItem.ValueItem(getter(it.double ?: Double.NaN).asValue())
}
val innerSetter: suspend (oldValue: MetaItem<*>?, newValue: MetaItem<*>) -> MetaItem<*>? = { oldValue, newValue ->
setter(oldValue.double, newValue.double ?: Double.NaN)?.asMetaItem()
}
return DevicePropertyDelegate(
this,
MetaItem.ValueItem(Double.NaN.asValue()),
descriptorBuilder,
innerGetter,
innerSetter
)
}
public fun <D : DeviceBase> D.writingBoolean(
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
getter: suspend (Boolean?) -> Boolean,
setter: suspend (oldValue: Boolean?, newValue: Boolean) -> Boolean?
): ReadOnlyProperty<D, DeviceProperty> {
val innerGetter: suspend (MetaItem<*>?) -> MetaItem<*> = {
MetaItem.ValueItem(getter(it.boolean).asValue())
}
val innerSetter: suspend (oldValue: MetaItem<*>?, newValue: MetaItem<*>) -> MetaItem<*>? = { oldValue, newValue ->
setter(oldValue.boolean, newValue.boolean?: error("Can't convert $newValue to boolean"))?.asValue()?.asMetaItem()
}
return DevicePropertyDelegate(
this,
MetaItem.ValueItem(Null),
descriptorBuilder,
innerGetter,
innerSetter
)
}

View File

@ -0,0 +1,59 @@
package hep.dataforge.control.base
import hep.dataforge.control.api.ActionDescriptor
import hep.dataforge.meta.MetaBuilder
import hep.dataforge.meta.MetaItem
import hep.dataforge.values.Value
import kotlin.properties.PropertyDelegateProvider
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
private fun <D : DeviceBase> D.provideAction(): ReadOnlyProperty<D, Action> =
ReadOnlyProperty { _: D, property: KProperty<*> ->
val name = property.name
return@ReadOnlyProperty actions[name]!!
}
public typealias ActionDelegate = ReadOnlyProperty<DeviceBase, Action>
private class ActionProvider<D : DeviceBase>(
val owner: D,
val descriptorBuilder: ActionDescriptor.() -> Unit = {},
val block: suspend (MetaItem<*>?) -> MetaItem<*>?,
) : PropertyDelegateProvider<D, ActionDelegate> {
override operator fun provideDelegate(thisRef: D, property: KProperty<*>): ActionDelegate {
val name = property.name
owner.newAction(name, descriptorBuilder, block)
return owner.provideAction()
}
}
public fun DeviceBase.requesting(
descriptorBuilder: ActionDescriptor.() -> Unit = {},
block: suspend (MetaItem<*>?) -> MetaItem<*>?,
): PropertyDelegateProvider<DeviceBase, ActionDelegate> = ActionProvider(this, descriptorBuilder, block)
public fun <D : DeviceBase> D.requestingValue(
descriptorBuilder: ActionDescriptor.() -> Unit = {},
block: suspend (MetaItem<*>?) -> Any?,
): PropertyDelegateProvider<D, ActionDelegate> = ActionProvider(this, descriptorBuilder) {
val res = block(it)
MetaItem.ValueItem(Value.of(res))
}
public fun <D : DeviceBase> D.requestingMeta(
descriptorBuilder: ActionDescriptor.() -> Unit = {},
block: suspend MetaBuilder.(MetaItem<*>?) -> Unit,
): PropertyDelegateProvider<D, ActionDelegate> = ActionProvider(this, descriptorBuilder) {
val res = MetaBuilder().apply { block(it) }
MetaItem.NodeItem(res)
}
public fun DeviceBase.acting(
descriptorBuilder: ActionDescriptor.() -> Unit = {},
block: suspend (MetaItem<*>?) -> Unit,
): PropertyDelegateProvider<DeviceBase, ActionDelegate> = ActionProvider(this, descriptorBuilder) {
block(it)
null
}

View File

@ -0,0 +1,196 @@
package hep.dataforge.control.base
import hep.dataforge.control.api.PropertyDescriptor
import hep.dataforge.meta.*
import hep.dataforge.values.Null
import hep.dataforge.values.Value
import hep.dataforge.values.asValue
import kotlin.properties.PropertyDelegateProvider
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
private fun <D : DeviceBase> D.provideProperty(): ReadOnlyProperty<D, ReadOnlyDeviceProperty> =
ReadOnlyProperty { _: D, property: KProperty<*> ->
val name = property.name
return@ReadOnlyProperty properties[name]!!
}
public typealias ReadOnlyPropertyDelegate = ReadOnlyProperty<DeviceBase, ReadOnlyDeviceProperty>
private class ReadOnlyDevicePropertyProvider<D : DeviceBase>(
val owner: D,
val default: MetaItem<*>?,
val descriptorBuilder: PropertyDescriptor.() -> Unit = {},
private val getter: suspend (MetaItem<*>?) -> MetaItem<*>,
) : PropertyDelegateProvider<D, ReadOnlyPropertyDelegate> {
override operator fun provideDelegate(thisRef: D, property: KProperty<*>): ReadOnlyPropertyDelegate {
val name = property.name
owner.newReadOnlyProperty(name, default, descriptorBuilder, getter)
return owner.provideProperty()
}
}
public fun DeviceBase.reading(
default: MetaItem<*>? = null,
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
getter: suspend (MetaItem<*>?) -> MetaItem<*>,
): PropertyDelegateProvider<DeviceBase, ReadOnlyPropertyDelegate> = ReadOnlyDevicePropertyProvider(
this,
default,
descriptorBuilder,
getter
)
public fun DeviceBase.readingValue(
default: Value? = null,
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
getter: suspend () -> Any?,
): PropertyDelegateProvider<DeviceBase, ReadOnlyPropertyDelegate> = ReadOnlyDevicePropertyProvider(
this,
default?.let { MetaItem.ValueItem(it) },
descriptorBuilder,
getter = { MetaItem.ValueItem(Value.of(getter())) }
)
public fun DeviceBase.readingNumber(
default: Number? = null,
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
getter: suspend () -> Number,
): PropertyDelegateProvider<DeviceBase, ReadOnlyPropertyDelegate> = ReadOnlyDevicePropertyProvider(
this,
default?.let { MetaItem.ValueItem(it.asValue()) },
descriptorBuilder,
getter = {
val number = getter()
MetaItem.ValueItem(number.asValue())
}
)
public fun DeviceBase.readingString(
default: Number? = null,
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
getter: suspend () -> String,
): PropertyDelegateProvider<DeviceBase, ReadOnlyPropertyDelegate> = ReadOnlyDevicePropertyProvider(
this,
default?.let { MetaItem.ValueItem(it.asValue()) },
descriptorBuilder,
getter = {
val number = getter()
MetaItem.ValueItem(number.asValue())
}
)
public fun DeviceBase.readingMeta(
default: Meta? = null,
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
getter: suspend MetaBuilder.() -> Unit,
): PropertyDelegateProvider<DeviceBase, ReadOnlyPropertyDelegate> = ReadOnlyDevicePropertyProvider(
this,
default?.let { MetaItem.NodeItem(it) },
descriptorBuilder,
getter = {
MetaItem.NodeItem(MetaBuilder().apply { getter() })
}
)
private fun DeviceBase.provideMutableProperty(): ReadOnlyProperty<DeviceBase, DeviceProperty> =
ReadOnlyProperty { _: DeviceBase, property: KProperty<*> ->
val name = property.name
return@ReadOnlyProperty properties[name] as DeviceProperty
}
public typealias PropertyDelegate = ReadOnlyProperty<DeviceBase, DeviceProperty>
private class DevicePropertyProvider<D : DeviceBase>(
val owner: D,
val default: MetaItem<*>?,
val descriptorBuilder: PropertyDescriptor.() -> Unit = {},
private val getter: suspend (MetaItem<*>?) -> MetaItem<*>,
private val setter: suspend (oldValue: MetaItem<*>?, newValue: MetaItem<*>) -> MetaItem<*>?,
) : PropertyDelegateProvider<D, PropertyDelegate> {
override operator fun provideDelegate(thisRef: D, property: KProperty<*>): PropertyDelegate {
val name = property.name
owner.newMutableProperty(name, default, descriptorBuilder, getter, setter)
return owner.provideMutableProperty()
}
}
public fun DeviceBase.writing(
default: MetaItem<*>? = null,
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
getter: suspend (MetaItem<*>?) -> MetaItem<*>,
setter: suspend (oldValue: MetaItem<*>?, newValue: MetaItem<*>) -> MetaItem<*>?,
): PropertyDelegateProvider<DeviceBase, PropertyDelegate> = DevicePropertyProvider(
this,
default,
descriptorBuilder,
getter,
setter
)
public fun DeviceBase.writingVirtual(
default: MetaItem<*>,
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
): PropertyDelegateProvider<DeviceBase, PropertyDelegate> = writing(
default,
descriptorBuilder,
getter = { it ?: default },
setter = { _, newItem -> newItem }
)
public fun DeviceBase.writingVirtual(
default: Value,
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
): PropertyDelegateProvider<DeviceBase, PropertyDelegate> = writing(
MetaItem.ValueItem(default),
descriptorBuilder,
getter = { it ?: MetaItem.ValueItem(default) },
setter = { _, newItem -> newItem }
)
public fun <D : DeviceBase> D.writingDouble(
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
getter: suspend (Double) -> Double,
setter: suspend (oldValue: Double?, newValue: Double) -> Double?,
): PropertyDelegateProvider<D, PropertyDelegate> {
val innerGetter: suspend (MetaItem<*>?) -> MetaItem<*> = {
MetaItem.ValueItem(getter(it.double ?: Double.NaN).asValue())
}
val innerSetter: suspend (oldValue: MetaItem<*>?, newValue: MetaItem<*>) -> MetaItem<*>? = { oldValue, newValue ->
setter(oldValue.double, newValue.double ?: Double.NaN)?.asMetaItem()
}
return DevicePropertyProvider(
this,
MetaItem.ValueItem(Double.NaN.asValue()),
descriptorBuilder,
innerGetter,
innerSetter
)
}
public fun <D : DeviceBase> D.writingBoolean(
descriptorBuilder: PropertyDescriptor.() -> Unit = {},
getter: suspend (Boolean?) -> Boolean,
setter: suspend (oldValue: Boolean?, newValue: Boolean) -> Boolean?,
): PropertyDelegateProvider<D, PropertyDelegate> {
val innerGetter: suspend (MetaItem<*>?) -> MetaItem<*> = {
MetaItem.ValueItem(getter(it.boolean).asValue())
}
val innerSetter: suspend (oldValue: MetaItem<*>?, newValue: MetaItem<*>) -> MetaItem<*>? = { oldValue, newValue ->
setter(oldValue.boolean, newValue.boolean ?: error("Can't convert $newValue to boolean"))?.asValue()
?.asMetaItem()
}
return DevicePropertyProvider(
this,
MetaItem.ValueItem(Null),
descriptorBuilder,
innerGetter,
innerSetter
)
}

View File

@ -78,7 +78,7 @@ class DeviceController(
} else if (target != null && target != deviceTarget) { } else if (target != null && target != deviceTarget) {
error("Wrong target name $deviceTarget expected but $target found") error("Wrong target name $deviceTarget expected but $target found")
} else { } else {
val response = device.respond(request).apply { val response = device.respondWithData(request).apply {
meta { meta {
"target" put request.meta["source"].string "target" put request.meta["source"].string
"source" put deviceTarget "source" put deviceTarget

View File

@ -8,6 +8,7 @@ import hep.dataforge.values.Null
import kotlin.properties.ReadOnlyProperty import kotlin.properties.ReadOnlyProperty
import kotlin.properties.ReadWriteProperty import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
import kotlin.time.Duration
operator fun ReadOnlyDeviceProperty.getValue(thisRef: Any?, property: KProperty<*>): MetaItem<*> = operator fun ReadOnlyDeviceProperty.getValue(thisRef: Any?, property: KProperty<*>): MetaItem<*> =
value ?: MetaItem.ValueItem(Null) value ?: MetaItem.ValueItem(Null)
@ -17,10 +18,8 @@ operator fun DeviceProperty.setValue(thisRef: Any?, property: KProperty<*>, valu
} }
fun <T : Any> ReadOnlyDeviceProperty.convert(metaConverter: MetaConverter<T>): ReadOnlyProperty<Any?, T> { fun <T : Any> ReadOnlyDeviceProperty.convert(metaConverter: MetaConverter<T>): ReadOnlyProperty<Any?, T> {
return object : ReadOnlyProperty<Any?, T> { return ReadOnlyProperty { thisRef, property ->
override fun getValue(thisRef: Any?, property: KProperty<*>): T { getValue(thisRef, property).let { metaConverter.itemToObject(it) }
return this@convert.getValue(thisRef, property).let { metaConverter.itemToObject(it) }
}
} }
} }
@ -44,3 +43,6 @@ fun DeviceProperty.int() = convert(MetaConverter.int)
fun ReadOnlyDeviceProperty.string() = convert(MetaConverter.string) fun ReadOnlyDeviceProperty.string() = convert(MetaConverter.string)
fun DeviceProperty.string() = convert(MetaConverter.string) fun DeviceProperty.string() = convert(MetaConverter.string)
fun ReadOnlyDeviceProperty.duration(): ReadOnlyProperty<Any?, Duration> = TODO()
fun DeviceProperty.duration(): ReadWriteProperty<Any?, Duration> = TODO()

View File

@ -1,6 +1,6 @@
plugins { plugins {
id("kscience.jvm") id("ru.mipt.npm.jvm")
id("kscience.publish") id("ru.mipt.npm.publish")
} }
dependencies{ dependencies{

View File

@ -1,6 +1,6 @@
plugins { plugins {
id("kscience.jvm") id("ru.mipt.npm.jvm")
id("kscience.publish") id("ru.mipt.npm.publish")
} }
kscience { kscience {
@ -8,7 +8,7 @@ kscience {
} }
val dataforgeVersion: String by rootProject.extra val dataforgeVersion: String by rootProject.extra
val ktorVersion: String by extra("1.3.2") val ktorVersion: String by extra("1.4.0")
dependencies{ dependencies{
implementation(project(":dataforge-device-core")) implementation(project(":dataforge-device-core"))

View File

@ -6,6 +6,7 @@ plugins {
repositories{ repositories{
mavenLocal()
jcenter() jcenter()
maven("https://kotlin.bintray.com/kotlinx") maven("https://kotlin.bintray.com/kotlinx")
maven("https://dl.bintray.com/kotlin/kotlin-eap") maven("https://dl.bintray.com/kotlin/kotlin-eap")
@ -21,7 +22,7 @@ dependencies{
implementation(project(":dataforge-device-client")) implementation(project(":dataforge-device-client"))
implementation("no.tornado:tornadofx:1.7.20") implementation("no.tornado:tornadofx:1.7.20")
implementation(kotlin("stdlib-jdk8")) implementation(kotlin("stdlib-jdk8"))
implementation("kscience.plotlykt:plotlykt-server:0.2.0") implementation("kscience.plotlykt:plotlykt-server:0.3.0-dev-2")
} }
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach { tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {

View File

@ -50,7 +50,7 @@ class DemoDevice(parentScope: CoroutineScope) : DeviceBase() {
} }
val resetScale: Action by action { val resetScale: Action by acting {
timeScaleValue = 5000.0 timeScaleValue = 5000.0
sinScaleValue = 1.0 sinScaleValue = 1.0
cosScaleValue = 1.0 cosScaleValue = 1.0

Binary file not shown.

View File

@ -1,6 +1,6 @@
plugins { plugins {
id("kscience.jvm") id("ru.mipt.npm.jvm")
id("kscience.publish") id("ru.mipt.npm.publish")
} }
//TODO to be moved to a separate project //TODO to be moved to a separate project

View File

@ -1,11 +1,14 @@
package ru.mipt.npm.devices.pimotionmaster package ru.mipt.npm.devices.pimotionmaster
import hep.dataforge.control.api.DeviceHub
import hep.dataforge.control.base.* import hep.dataforge.control.base.*
import hep.dataforge.control.controllers.duration
import hep.dataforge.control.ports.Port import hep.dataforge.control.ports.Port
import hep.dataforge.control.ports.PortProxy import hep.dataforge.control.ports.PortProxy
import hep.dataforge.control.ports.send import hep.dataforge.control.ports.send
import hep.dataforge.control.ports.withDelimiter import hep.dataforge.control.ports.withDelimiter
import hep.dataforge.meta.MetaItem import hep.dataforge.meta.MetaItem
import hep.dataforge.names.NameToken
import hep.dataforge.values.Null import hep.dataforge.values.Null
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
@ -14,11 +17,15 @@ import kotlinx.coroutines.flow.takeWhile
import kotlinx.coroutines.flow.toList import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withTimeout
import kotlin.time.Duration
public class PiMotionMasterDevice( public class PiMotionMasterDevice(
parentScope: CoroutineScope, parentScope: CoroutineScope,
axes: List<String>,
private val portFactory: suspend (MetaItem<*>?) -> Port, private val portFactory: suspend (MetaItem<*>?) -> Port,
) : DeviceBase() { ) : DeviceBase(), DeviceHub {
override val scope: CoroutineScope = CoroutineScope( override val scope: CoroutineScope = CoroutineScope(
parentScope.coroutineContext + Job(parentScope.coroutineContext[Job]) parentScope.coroutineContext + Job(parentScope.coroutineContext[Job])
@ -28,11 +35,17 @@ public class PiMotionMasterDevice(
info = "The port for TCP connector" info = "The port for TCP connector"
} }
public val timeout: DeviceProperty by writingVirtual(Null) {
info = "Timeout"
}
public var timeoutValue: Duration by timeout.duration()
private val connector = PortProxy { portFactory(port.value) } private val connector = PortProxy { portFactory(port.value) }
private val mutex = Mutex() private val mutex = Mutex()
private suspend fun sendCommand(command: String, vararg arguments: String) { private suspend fun sendCommandInternal(command: String, vararg arguments: String) {
val joinedArguments = if (arguments.isEmpty()) { val joinedArguments = if (arguments.isEmpty()) {
"" ""
} else { } else {
@ -46,9 +59,11 @@ public class PiMotionMasterDevice(
* Send a synchronous request and receive a list of lines as a response * Send a synchronous request and receive a list of lines as a response
*/ */
private suspend fun request(command: String, vararg arguments: String): List<String> = mutex.withLock { private suspend fun request(command: String, vararg arguments: String): List<String> = mutex.withLock {
sendCommand(command, *arguments) withTimeout(timeoutValue) {
sendCommandInternal(command, *arguments)
val phrases = connector.receiving().withDelimiter("\n") val phrases = connector.receiving().withDelimiter("\n")
return@withLock phrases.takeWhile { it.endsWith(" \n") }.toList() + phrases.first() phrases.takeWhile { it.endsWith(" \n") }.toList() + phrases.first()
}
} }
private suspend fun requestAndParse(command: String, vararg arguments: String): Map<String, String> = buildMap { private suspend fun requestAndParse(command: String, vararg arguments: String): Map<String, String> = buildMap {
@ -63,11 +78,13 @@ public class PiMotionMasterDevice(
*/ */
private suspend fun send(command: String, vararg arguments: String) { private suspend fun send(command: String, vararg arguments: String) {
mutex.withLock { mutex.withLock {
sendCommand(command, *arguments) withTimeout(timeoutValue) {
sendCommandInternal(command, *arguments)
}
} }
} }
public val initialize: Action by action { public val initialize: Action by acting {
send("INI") send("INI")
} }
@ -79,32 +96,37 @@ public class PiMotionMasterDevice(
override val scope: CoroutineScope get() = this@PiMotionMasterDevice.scope override val scope: CoroutineScope get() = this@PiMotionMasterDevice.scope
public val enabled: DeviceProperty by writingBoolean<Axis>( public val enabled: DeviceProperty by writingBoolean<Axis>(
getter = { getter = {
val result = requestAndParse("EAX?", axisId)[axisId]?.toIntOrNull() val eax = requestAndParse("EAX?", axisId)[axisId]?.toIntOrNull()
?: error("Malformed response. Should include integer value for $axisId") ?: error("Malformed EAX response. Should include integer value for $axisId")
result != 0 eax != 0
}, },
setter = { oldValue, newValue -> setter = { _, newValue ->
val value = if(newValue){ val value = if (newValue) {
"1" "1"
} else { } else {
"0" "0"
} }
send("EAX", axisId, value) send("EAX", axisId, value)
oldValue newValue
} }
) )
public val halt: Action by action { public val halt: Action by acting {
send("HLT", axisId) send("HLT", axisId)
} }
public val targetPosition: DeviceProperty by writingDouble<Axis>(
getter = {
requestAndParse("MOV?", axisId)[axisId]?.toDoubleOrNull()
?: error("Malformed MOV response. Should include float value for $axisId")
},
setter = { _, newValue ->
send("MOV", axisId, newValue.toString())
newValue
}
)
} }
init { override val devices: Map<NameToken, Axis> = axes.associate { NameToken(it) to Axis(it) }
//list everything here to ensure it is initialized
initialize
firmwareVersion
}
} }

View File

@ -1,6 +1,6 @@
pluginManagement { pluginManagement {
val kotlinVersion = "1.4.0" val kotlinVersion = "1.4.0"
val toolsVersion = "0.6.0-dev-1" val toolsVersion = "0.6.0-dev-4"
repositories { repositories {
mavenLocal() mavenLocal()
@ -9,25 +9,17 @@ pluginManagement {
maven("https://kotlin.bintray.com/kotlinx") maven("https://kotlin.bintray.com/kotlinx")
maven("https://dl.bintray.com/kotlin/kotlin-eap") maven("https://dl.bintray.com/kotlin/kotlin-eap")
maven("https://dl.bintray.com/mipt-npm/dataforge") maven("https://dl.bintray.com/mipt-npm/dataforge")
maven("https://dl.bintray.com/mipt-npm/scientifik") maven("https://dl.bintray.com/mipt-npm/kscience")
maven("https://dl.bintray.com/mipt-npm/dev") maven("https://dl.bintray.com/mipt-npm/dev")
} }
plugins { plugins {
id("ru.mipt.npm.mpp") version toolsVersion
id("ru.mipt.npm.jvm") version toolsVersion
id("ru.mipt.npm.js") version toolsVersion
id("ru.mipt.npm.publish") version toolsVersion
kotlin("jvm") version kotlinVersion kotlin("jvm") version kotlinVersion
id("scientifik.mpp") version toolsVersion kotlin("js") version kotlinVersion
id("scientifik.jvm") version toolsVersion
id("scientifik.js") version toolsVersion
id("scientifik.publish") version toolsVersion
}
resolutionStrategy {
eachPlugin {
when (requested.id.id) {
"kscience.publish", "kscience.mpp", "kscience.jvm", "kscience.js" -> useModule("ru.mipt.npm:gradle-tools:${toolsVersion}")
"kotlinx-atomicfu" -> useModule("org.jetbrains.kotlinx:atomicfu-gradle-plugin:${requested.version}")
}
}
} }
} }