Handle property clearing in updates.

This commit is contained in:
Alexander Nozik 2020-12-22 13:33:05 +03:00
parent 66ea23ad60
commit 216be4a6a1
7 changed files with 90 additions and 53 deletions

View File

@ -3,6 +3,7 @@ package ru.mipt.npm.sat
import hep.dataforge.names.toName import hep.dataforge.names.toName
import hep.dataforge.vision.solid.Solid import hep.dataforge.vision.solid.Solid
import hep.dataforge.vision.solid.clear
import hep.dataforge.vision.solid.color import hep.dataforge.vision.solid.color
import hep.dataforge.vision.solid.invoke import hep.dataforge.vision.solid.invoke
import hep.dataforge.vision.three.server.* import hep.dataforge.vision.three.server.*
@ -42,7 +43,7 @@ fun main() {
val targetVision = sat[target] as Solid val targetVision = sat[target] as Solid
targetVision.color("red") targetVision.color("red")
delay(300) delay(300)
targetVision.color("darkgreen") targetVision.color.clear()
delay(10) delay(10)
} }
} }

View File

@ -57,6 +57,9 @@ public interface Vision : Described {
*/ */
public fun setProperty(name: Name, item: MetaItem<*>?, notify: Boolean = true) public fun setProperty(name: Name, item: MetaItem<*>?, notify: Boolean = true)
/**
* Subscribe on property updates. The subscription is bound to the given [scope] and canceled when the scope is canceled
*/
public fun onPropertyChange(scope: CoroutineScope, callback: suspend (Name) -> Unit) public fun onPropertyChange(scope: CoroutineScope, callback: suspend (Name) -> Unit)
/** /**
@ -81,7 +84,7 @@ public interface Vision : Described {
public suspend fun notifyPropertyChanged(propertyName: Name): Unit public suspend fun notifyPropertyChanged(propertyName: Name): Unit
/** /**
* Update this vision using external meta. Children are not updated. * Update this vision using a dif represented by [VisionChange].
*/ */
public fun update(change: VisionChange) public fun update(change: VisionChange)

View File

@ -3,12 +3,14 @@ package hep.dataforge.vision
import hep.dataforge.meta.Config import hep.dataforge.meta.Config
import hep.dataforge.meta.MetaItem import hep.dataforge.meta.MetaItem
import hep.dataforge.meta.MutableMeta import hep.dataforge.meta.MutableMeta
import hep.dataforge.meta.asMetaItem
import hep.dataforge.meta.descriptors.NodeDescriptor import hep.dataforge.meta.descriptors.NodeDescriptor
import hep.dataforge.meta.descriptors.defaultItem import hep.dataforge.meta.descriptors.defaultItem
import hep.dataforge.meta.descriptors.get import hep.dataforge.meta.descriptors.get
import hep.dataforge.meta.update
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.asName import hep.dataforge.names.asName
import hep.dataforge.names.plus
import hep.dataforge.values.Null
import hep.dataforge.values.ValueType import hep.dataforge.values.ValueType
import hep.dataforge.vision.Vision.Companion.STYLE_KEY import hep.dataforge.vision.Vision.Companion.STYLE_KEY
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@ -27,6 +29,7 @@ internal data class PropertyListener(
val action: (name: Name) -> Unit, val action: (name: Name) -> Unit,
) )
@Serializable @Serializable
@SerialName("vision") @SerialName("vision")
public open class VisionBase : Vision { public open class VisionBase : Vision {
@ -100,6 +103,8 @@ public open class VisionBase : Vision {
} }
} }
//TODO check memory consumption for the flow
@Transient @Transient
private val propertyInvalidationFlow: MutableSharedFlow<Name> = MutableSharedFlow() private val propertyInvalidationFlow: MutableSharedFlow<Name> = MutableSharedFlow()
@ -116,13 +121,30 @@ public open class VisionBase : Vision {
propertyInvalidationFlow.emit(propertyName) propertyInvalidationFlow.emit(propertyName)
} }
public fun configure(block: MutableMeta<*>.() -> Unit) { public fun configure(block: suspend MutableMeta<*>.() -> Unit) {
scope.launch {
getOrCreateConfig().block() getOrCreateConfig().block()
} }
}
override fun update(change: VisionChange) { override fun update(change: VisionChange) {
fun updateProperties(at: Name, item: MetaItem<*>) {
when (item) {
is MetaItem.ValueItem -> {
if (item.value == Null) {
setProperty(at, null)
} else
setProperty(at, item)
}
is MetaItem.NodeItem -> item.node.items.forEach { (token, childItem) ->
updateProperties(at + token, childItem)
}
}
}
change.properties?.let { change.properties?.let {
getOrCreateConfig().update(it) updateProperties(Name.EMPTY, it.asMetaItem())
} }
} }

View File

@ -3,6 +3,7 @@ package hep.dataforge.vision
import hep.dataforge.meta.* import hep.dataforge.meta.*
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.plus import hep.dataforge.names.plus
import hep.dataforge.values.Null
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
@ -30,7 +31,8 @@ public class VisionChangeBuilder : VisionContainerBuilder<Vision> {
public fun propertyChanged(visionName: Name, propertyName: Name, item: MetaItem<*>?) { public fun propertyChanged(visionName: Name, propertyName: Name, item: MetaItem<*>?) {
if (visionName == Name.EMPTY) { if (visionName == Name.EMPTY) {
propertyChange[propertyName] = item //Write property removal as [Null]
propertyChange[propertyName] = (item ?: Null.asMetaItem())
} else { } else {
getOrPutChild(visionName).propertyChanged(Name.EMPTY, propertyName, item) getOrPutChild(visionName).propertyChanged(Name.EMPTY, propertyName, item)
} }
@ -112,6 +114,10 @@ public fun Vision.flowChanges(
coroutineScope { coroutineScope {
collectChange(Name.EMPTY, this@flowChanges) { collector } collectChange(Name.EMPTY, this@flowChanges) { collector }
//Send initial vision state
val initialChange = VisionChange(vision = isolate(manager))
emit(initialChange)
while (currentCoroutineContext().isActive) { while (currentCoroutineContext().isActive) {
//Wait for changes to accumulate //Wait for changes to accumulate
delay(collectionDuration) delay(collectionDuration)

View File

@ -134,14 +134,6 @@ public class VisionServer internal constructor(
try { try {
withContext(visionManager.context.coroutineContext) { withContext(visionManager.context.coroutineContext) {
val initialVision = VisionChange(vision = vision)
val initialJson = visionManager.jsonFormat.encodeToString(
VisionChange.serializer(),
initialVision
)
outgoing.send(Frame.Text(initialJson))
vision.flowChanges(visionManager, updateInterval.milliseconds).collect { update -> vision.flowChanges(visionManager, updateInterval.milliseconds).collect { update ->
val json = visionManager.jsonFormat.encodeToString( val json = visionManager.jsonFormat.encodeToString(
VisionChange.serializer(), VisionChange.serializer(),

View File

@ -0,0 +1,51 @@
package hep.dataforge.vision.solid
import hep.dataforge.meta.MutableItemProvider
import hep.dataforge.meta.set
import hep.dataforge.meta.value
import hep.dataforge.names.Name
import hep.dataforge.values.Value
import hep.dataforge.values.asValue
import hep.dataforge.values.string
import hep.dataforge.vision.Colors
import hep.dataforge.vision.VisionBuilder
@VisionBuilder
public class ColorAccessor(private val parent: MutableItemProvider, private val colorKey: Name) {
public var value: Value?
get() = parent.getItem(colorKey).value
set(value) {
parent[colorKey] = value
}
}
public var ColorAccessor?.string: String?
get() = this?.value?.string
set(value) {
this?.value = value?.asValue()
}
/**
* Set [webcolor](https://en.wikipedia.org/wiki/Web_colors) as string
*/
public operator fun ColorAccessor?.invoke(webColor: String) {
this?.value = webColor.asValue()
}
/**
* Set color as RGB integer
*/
public operator fun ColorAccessor?.invoke(rgb: Int) {
this?.value = Colors.rgbToString(rgb).asValue()
}
/**
* Set color as RGB
*/
public operator fun ColorAccessor?.invoke(r: UByte, g: UByte, b: UByte) {
this?.value = Colors.rgbToString(r, g, b).asValue()
}
public fun ColorAccessor?.clear(){
this?.value = null
}

View File

@ -6,51 +6,13 @@ import hep.dataforge.meta.descriptors.attributes
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.names.asName import hep.dataforge.names.asName
import hep.dataforge.names.plus import hep.dataforge.names.plus
import hep.dataforge.values.Value
import hep.dataforge.values.ValueType import hep.dataforge.values.ValueType
import hep.dataforge.values.asValue import hep.dataforge.values.asValue
import hep.dataforge.values.string
import hep.dataforge.vision.* import hep.dataforge.vision.*
import hep.dataforge.vision.solid.SolidMaterial.Companion.MATERIAL_COLOR_KEY import hep.dataforge.vision.solid.SolidMaterial.Companion.MATERIAL_COLOR_KEY
import hep.dataforge.vision.solid.SolidMaterial.Companion.MATERIAL_KEY import hep.dataforge.vision.solid.SolidMaterial.Companion.MATERIAL_KEY
import hep.dataforge.vision.solid.SolidMaterial.Companion.MATERIAL_OPACITY_KEY import hep.dataforge.vision.solid.SolidMaterial.Companion.MATERIAL_OPACITY_KEY
@VisionBuilder
public class ColorAccessor(private val parent: MutableItemProvider, private val colorKey: Name) {
public var value: Value?
get() = parent.getItem(colorKey).value
set(value) {
parent[colorKey] = value
}
}
public var ColorAccessor?.string: String?
get() = this?.value?.string
set(value) {
this?.value = value?.asValue()
}
/**
* Set [webcolor](https://en.wikipedia.org/wiki/Web_colors) as string
*/
public operator fun ColorAccessor?.invoke(webColor: String) {
this?.value = webColor.asValue()
}
/**
* Set color as RGB integer
*/
public operator fun ColorAccessor?.invoke(rgb: Int) {
this?.value = Colors.rgbToString(rgb).asValue()
}
/**
* Set color as RGB
*/
public operator fun ColorAccessor?.invoke(r: UByte, g: UByte, b: UByte) {
this?.value = Colors.rgbToString(r, g, b).asValue()
}
@VisionBuilder @VisionBuilder
public class SolidMaterial : Scheme() { public class SolidMaterial : Scheme() {