Fix storage closing

This commit is contained in:
Alexander Nozik 2021-11-14 19:46:00 +03:00
parent 15d17f2cc4
commit 35801d82fc
20 changed files with 161 additions and 173 deletions

View File

@ -403,7 +403,7 @@ open class Context(
get() = plugins[ExecutorPlugin::class] ?: parent?.executors ?: Global.executors get() = plugins[ExecutorPlugin::class] ?: parent?.executors ?: Global.executors
override val coroutineContext: CoroutineContext override val coroutineContext: CoroutineContext
get() = this.executors.coroutineContext get() = executors.coroutineContext
companion object { companion object {

View File

@ -41,7 +41,7 @@ interface ExecutorPlugin : Plugin, CoroutineScope {
@PluginDef(group = "hep.dataforge", name = "executor", support = true, info = "Executor plugin") @PluginDef(group = "hep.dataforge", name = "executor", support = true, info = "Executor plugin")
class DefaultExecutorPlugin(meta: Meta = Meta.empty()) : BasicPlugin(meta), ExecutorPlugin { class DefaultExecutorPlugin(meta: Meta = Meta.empty()) : BasicPlugin(meta), ExecutorPlugin {
private val executors = HashMap<Meta, ExecutorService>(); private val executors = HashMap<Meta, ExecutorService>()
/** /**
* Create a default executor that uses plugin meta * Create a default executor that uses plugin meta
@ -51,21 +51,16 @@ class DefaultExecutorPlugin(meta: Meta = Meta.empty()) : BasicPlugin(meta), Exec
getExecutor(meta) getExecutor(meta)
} }
override fun getExecutor(meta: Meta): ExecutorService { @Synchronized
synchronized(context) { override fun getExecutor(meta: Meta): ExecutorService = executors.getOrPut(meta) {
return executors.getOrPut(meta) { val workerName = meta.getString("workerName", "worker")
val workerName = meta.getString("workerName", "worker"); val threads = meta.getInt("threads", Runtime.getRuntime().availableProcessors())
val threads = meta.getInt("threads", Runtime.getRuntime().availableProcessors()) val factory = { pool: ForkJoinPool ->
val factory = { pool: ForkJoinPool -> ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool).apply {
ForkJoinPool.defaultForkJoinWorkerThreadFactory.newThread(pool).apply { name = "${context.name}_$workerName-$poolIndex"
name = "${context.name}_$workerName-$poolIndex"
}
}
ForkJoinPool(
threads,
factory, null, false)
} }
} }
ForkJoinPool(threads, factory, null, false)
} }
override val coroutineContext: CoroutineContext by lazy { defaultExecutor.asCoroutineDispatcher() } override val coroutineContext: CoroutineContext by lazy { defaultExecutor.asCoroutineDispatcher() }

View File

@ -45,9 +45,7 @@ interface EnvelopeReader {
/** /**
* Read the envelope from channel * Read the envelope from channel
*/ */
fun read(channel: ReadableByteChannel): Envelope { fun read(channel: ReadableByteChannel): Envelope = read(Channels.newInputStream(channel))
return read(Channels.newInputStream(channel))
}
/** /**
* Read the envelope from buffer (could produce lazy envelope) * Read the envelope from buffer (could produce lazy envelope)
@ -59,9 +57,7 @@ interface EnvelopeReader {
/** /**
* Read the envelope from NIO file (could produce lazy envelope) * Read the envelope from NIO file (could produce lazy envelope)
*/ */
fun read(file: Path): Envelope { fun read(file: Path): Envelope = Files.newByteChannel(file, READ).use { read(it) }
return Files.newByteChannel(file, READ).use { read(it) }
}
companion object { companion object {

View File

@ -160,21 +160,19 @@ interface Value : Serializable, Comparable<Value> {
* @param obj a [Object] object. * @param obj a [Object] object.
* @return a [Value] object. * @return a [Value] object.
*/ */
fun of(value: Any?): Value { fun of(value: Any?): Value = when (value) {
return when (value) { null -> NULL
null -> Value.NULL is Value -> value
is Value -> value is Number -> NumberValue(value)
is Number -> NumberValue(value) is Instant -> TimeValue(value)
is Instant -> TimeValue(value) is LocalDateTime -> TimeValue(value)
is LocalDateTime -> TimeValue(value) is Boolean -> BooleanValue.ofBoolean(value)
is Boolean -> BooleanValue.ofBoolean(value) is String -> StringValue(value)
is String -> StringValue(value) is Collection<Any?> -> of(value)
is Collection<Any?> -> Value.of(value) is Stream<*> -> of(value.toList())
is Stream<*> -> Value.of(value.toList()) is Array<*> -> of(value.map(::of))
is Array<*> -> Value.of(value.map(::of)) is Enum<*> -> StringValue(value.name)
is Enum<*> -> StringValue(value.name) else -> StringValue(value.toString())
else -> StringValue(value.toString())
}
} }
} }
} }

View File

@ -1,5 +1,6 @@
package hep.dataforge.fx package hep.dataforge.fx
import hep.dataforge.context.Context
import hep.dataforge.context.Global import hep.dataforge.context.Global
import hep.dataforge.goals.Coal import hep.dataforge.goals.Coal
import hep.dataforge.goals.Goal import hep.dataforge.goals.Goal
@ -14,13 +15,13 @@ import javafx.scene.image.ImageView
import javafx.scene.layout.Region import javafx.scene.layout.Region
import javafx.scene.paint.Color import javafx.scene.paint.Color
import javafx.stage.Stage import javafx.stage.Stage
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.plus
import kotlinx.coroutines.GlobalScope
import tornadofx.* import tornadofx.*
import java.util.* import java.util.*
import java.util.concurrent.Executor import java.util.concurrent.Executor
import java.util.function.BiConsumer import java.util.function.BiConsumer
import kotlin.collections.HashMap import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext
val dfIcon: Image = Image(Global::class.java.getResourceAsStream("/img/df.png")) val dfIcon: Image = Image(Global::class.java.getResourceAsStream("/img/df.png"))
val dfIconView = ImageView(dfIcon) val dfIconView = ImageView(dfIcon)
@ -73,9 +74,14 @@ private fun removeMonitor(component: UIComponent, id: String) {
} }
} }
fun <R> UIComponent.runGoal(id: String, scope: CoroutineScope = GlobalScope, block: suspend GoalMonitor.() -> R): Coal<R> { fun <R> UIComponent.runGoal(
context: Context,
id: String,
coroutineContext: CoroutineContext = EmptyCoroutineContext,
block: suspend GoalMonitor.() -> R,
): Coal<R> {
val monitor = getMonitor(id); val monitor = getMonitor(id);
return Coal(scope, Collections.emptyList(), id) { return Coal(context + coroutineContext, Collections.emptyList(), id) {
monitor.progress = -1.0 monitor.progress = -1.0
block(monitor).also { block(monitor).also {
monitor.progress = 1.0 monitor.progress = 1.0
@ -121,9 +127,9 @@ fun addWindowResizeListener(component: Region, action: Runnable) {
fun colorToString(color: Color): String { fun colorToString(color: Color): String {
return String.format("#%02X%02X%02X", return String.format("#%02X%02X%02X",
(color.red * 255).toInt(), (color.red * 255).toInt(),
(color.green * 255).toInt(), (color.green * 255).toInt(),
(color.blue * 255).toInt()) (color.blue * 255).toInt())
} }
/** /**
@ -144,9 +150,10 @@ fun runNow(r: Runnable) {
* A display window that could be toggled * A display window that could be toggled
*/ */
class ToggleUIComponent( class ToggleUIComponent(
val component: UIComponent, val component: UIComponent,
val owner: Node, val owner: Node,
val toggle: BooleanProperty) { val toggle: BooleanProperty,
) {
val stage: Stage by lazy { val stage: Stage by lazy {
val res = component.modalStage ?: component.openWindow(owner = owner.scene.window) val res = component.modalStage ?: component.openWindow(owner = owner.scene.window)
?: throw RuntimeException("Can'topen window for $component") ?: throw RuntimeException("Can'topen window for $component")

View File

@ -62,6 +62,10 @@ interface StorageElement : Named, Metoid, Provider, ContextAware, AutoConnectibl
parent?.fullName?.plus(name) ?: Name.ofSingle(name) parent?.fullName?.plus(name) ?: Name.ofSingle(name)
} }
override fun close() {
//DO nothing
}
companion object { companion object {
const val STORAGE_TARGET = "storage" const val STORAGE_TARGET = "storage"
} }
@ -75,14 +79,14 @@ interface Storage : StorageElement {
/** /**
* Top level children of this storage * Top level children of this storage
*/ */
val children: Collection<StorageElement> fun getChildren(): Collection<StorageElement>
/** /**
* Names of direct children for provider * Names of direct children for provider
*/ */
@get:ProvidesNames(STORAGE_TARGET) @get:ProvidesNames(STORAGE_TARGET)
val childrenNames: Collection<String> val childrenNames: Collection<String>
get() = runBlocking { children.map { it.name } } get() = runBlocking { getChildren().map { it.name } }
/** /**
* Get storage element (name notation for recursive calls). Null if not present * Get storage element (name notation for recursive calls). Null if not present
@ -99,7 +103,7 @@ interface Storage : StorageElement {
operator fun get(name: Name): StorageElement? { operator fun get(name: Name): StorageElement? {
return if (name.length == 1) { return if (name.length == 1) {
children.find { it.name == name.unescaped } getChildren().find { it.name == name.unescaped }
} else { } else {
(get(name.first) as Storage?)?.get(name.cutFirst()) (get(name.first) as Storage?)?.get(name.cutFirst())
} }
@ -107,14 +111,6 @@ interface Storage : StorageElement {
override fun getDefaultTarget(): String = STORAGE_TARGET override fun getDefaultTarget(): String = STORAGE_TARGET
/**
* By default closes all children on close. If overridden, children should be closed before parent.
*/
override fun close() {
children.forEach { it.close() }
}
} }
/** /**
@ -202,7 +198,5 @@ interface StorageElementType : Named {
fun create(context: Context, meta: Meta, parent: StorageElement? = null): StorageElement fun create(context: Context, meta: Meta, parent: StorageElement? = null): StorageElement
fun create(parent: StorageElement, meta: Meta): StorageElement { fun create(parent: StorageElement, meta: Meta): StorageElement = create(parent.context, meta, parent)
return create(parent.context, meta, parent)
}
} }

View File

@ -50,7 +50,6 @@ class StorageConnection(storageFactory: () -> MutableStorage) : Connection, Cont
isOpen = true isOpen = true
} }
@Throws(Exception::class)
override fun close() { override fun close() {
if (isOpen) { if (isOpen) {
storage.close() storage.close()

View File

@ -27,7 +27,6 @@ import hep.dataforge.meta.buildMeta
import hep.dataforge.nullable import hep.dataforge.nullable
import hep.dataforge.providers.Provides import hep.dataforge.providers.Provides
import hep.dataforge.providers.ProvidesNames import hep.dataforge.providers.ProvidesNames
import kotlin.streams.toList
@PluginDef(name = "storage", group = "hep.dataforge", info = "Dataforge root storage plugin") @PluginDef(name = "storage", group = "hep.dataforge", info = "Dataforge root storage plugin")
@ -37,7 +36,7 @@ class StorageManager : BasicPlugin(), MutableStorage {
private val _connectionHelper = ConnectionHelper(this) private val _connectionHelper = ConnectionHelper(this)
private val _children = HashMap<String, StorageElement>() private val _children = HashMap<String, StorageElement>()
override val children get() = _children.values override fun getChildren() = _children.values
override fun getConnectionHelper(): ConnectionHelper = _connectionHelper override fun getConnectionHelper(): ConnectionHelper = _connectionHelper

View File

@ -20,7 +20,6 @@ import hep.dataforge.Named
import hep.dataforge.asName import hep.dataforge.asName
import hep.dataforge.connections.ConnectionHelper import hep.dataforge.connections.ConnectionHelper
import hep.dataforge.context.Context import hep.dataforge.context.Context
import hep.dataforge.context.launch
import hep.dataforge.description.ValueDef import hep.dataforge.description.ValueDef
import hep.dataforge.description.ValueDefs import hep.dataforge.description.ValueDefs
import hep.dataforge.io.envelopes.Envelope import hep.dataforge.io.envelopes.Envelope
@ -33,8 +32,8 @@ import hep.dataforge.storage.MutableStorage
import hep.dataforge.storage.StorageElement import hep.dataforge.storage.StorageElement
import hep.dataforge.storage.StorageElementType import hep.dataforge.storage.StorageElementType
import hep.dataforge.storage.StorageManager import hep.dataforge.storage.StorageManager
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async
import kotlinx.coroutines.joinAll import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import java.nio.file.* import java.nio.file.*
import kotlin.streams.asSequence import kotlin.streams.asSequence
@ -74,17 +73,17 @@ class FileStorage(
override fun getConnectionHelper(): ConnectionHelper = _connectionHelper override fun getConnectionHelper(): ConnectionHelper = _connectionHelper
private var isInitialized = false override fun getChildren(): Collection<StorageElement> = runBlocking {
private val _children = HashMap<Path, StorageElement>() Files.list(path).toList().map { path ->
async{
type.read(context, path, this@FileStorage).also {
override val children: Collection<StorageElement> if(it == null){
get() = runBlocking(Dispatchers.IO) { logger.warn("Can't read $path")
if (!isInitialized) { }
refresh() }
} }
_children.values }.awaitAll().filterNotNull()
} }
override fun resolveType(meta: Meta): StorageElementType? { override fun resolveType(meta: Meta): StorageElementType? {
@ -105,33 +104,8 @@ class FileStorage(
//TODO actually watch for file change //TODO actually watch for file change
override fun create(meta: Meta): StorageElement { override fun create(meta: Meta): StorageElement =
val path = path.resolve(meta.getString("path", meta.getString("name"))) resolveType(meta)?.create(this, meta) ?: error("Can't resolve storage element type.")
return _children.getOrPut(path) {
resolveType(meta)
?.create(this, meta)
?: error("Can't resolve storage element type.")
}
}
/**
* Manually refresh storage state
*/
suspend fun refresh() {
//Remove non-existent entries
_children.keys.filter { !Files.exists(it) }.forEach { _children.remove(it) }
//update existing entries if needed
Files.list(path).map { path ->
launch {
if (!_children.contains(path)) {
type.read(context, path, this@FileStorage)?.let { _children[path] = it }
?: logger.debug("Could not resolve type for $path in $this")
}
}
}.toList().joinAll()
isInitialized = true
}
companion object { companion object {
@ -161,7 +135,7 @@ class FileStorage(
return file.fileName.toString().substringBeforeLast(".") return file.fileName.toString().substringBeforeLast(".")
} }
val directory = FileStorage.Directory() val directory = Directory()
} }
open class Directory : FileStorageElementType { open class Directory : FileStorageElementType {
@ -208,7 +182,7 @@ class FileStorage(
null null
} }
} else { } else {
//Otherwise delegate to the type //Otherwise, delegate to the type
type.read(context, path, parent) type.read(context, path, parent)
}.also { }.also {
if (it != null && parent == null) { if (it != null && parent == null) {

View File

@ -22,7 +22,7 @@ class NumassDataFactory : DataFactory<NumassSet>(NumassSet::class.java) {
*/ */
private fun Storage.sequence(prefix: Name = Name.empty()): Sequence<Pair<Name, StorageElement>> { private fun Storage.sequence(prefix: Name = Name.empty()): Sequence<Pair<Name, StorageElement>> {
return sequence { return sequence {
runBlocking { children }.forEach { runBlocking { getChildren() }.forEach {
val newName = prefix + it.name val newName = prefix + it.name
yield(Pair(newName, it)) yield(Pair(newName, it))
if (it is Storage) { if (it is Storage) {

View File

@ -20,7 +20,7 @@ private suspend fun createSummaryNode(storage: Storage): MetaBuilder {
.setValue("name", storage.name) .setValue("name", storage.name)
.setValue("path", storage.fullName) .setValue("path", storage.fullName)
storage.children.forEach { element -> storage.getChildren().forEach { element ->
if(element is Storage && element.name.startsWith("Fill")){ if(element is Storage && element.name.startsWith("Fill")){
builder.putNode(createSummaryNode(element)) builder.putNode(createSummaryNode(element))
} else if(element is NumassDataLoader){ } else if(element is NumassDataLoader){

View File

@ -43,7 +43,7 @@ object Threshold {
fun Storage.loaders(): Sequence<NumassDataLoader> { fun Storage.loaders(): Sequence<NumassDataLoader> {
return sequence<NumassDataLoader> { return sequence<NumassDataLoader> {
print("Reading ${this@loaders.fullName}") print("Reading ${this@loaders.fullName}")
runBlocking { this@loaders.children }.forEach { runBlocking { this@loaders.getChildren() }.forEach {
if (it is NumassDataLoader) { if (it is NumassDataLoader) {
yield(it) yield(it)
} else if (it is Storage) { } else if (it is Storage) {

View File

@ -25,6 +25,7 @@ import javafx.collections.ObservableMap
import javafx.scene.control.CheckBox import javafx.scene.control.CheckBox
import javafx.scene.control.ChoiceBox import javafx.scene.control.ChoiceBox
import javafx.scene.image.ImageView import javafx.scene.image.ImageView
import kotlinx.coroutines.Dispatchers
import tornadofx.* import tornadofx.*
class AmplitudeView : View(title = "Numass amplitude spectrum plot", icon = ImageView(dfIcon)) { class AmplitudeView : View(title = "Numass amplitude spectrum plot", icon = ImageView(dfIcon)) {
@ -127,7 +128,7 @@ class AmplitudeView : View(title = "Numass amplitude spectrum plot", icon = Imag
private fun invalidate() { private fun invalidate() {
data.forEach { (key, point) -> data.forEach { (key, point) ->
plots.getOrPut(key) { plots.getOrPut(key) {
runGoal<Plottable>("loadAmplitudeSpectrum_$key") { runGoal<Plottable>(app.context, "loadAmplitudeSpectrum_$key", Dispatchers.IO) {
val valueAxis = if (normalize) { val valueAxis = if (normalize) {
NumassAnalyzer.COUNT_RATE_KEY NumassAnalyzer.COUNT_RATE_KEY
} else { } else {

View File

@ -15,6 +15,7 @@ import javafx.collections.FXCollections
import javafx.collections.MapChangeListener import javafx.collections.MapChangeListener
import javafx.collections.ObservableMap import javafx.collections.ObservableMap
import javafx.scene.image.ImageView import javafx.scene.image.ImageView
import kotlinx.coroutines.Dispatchers
import tornadofx.* import tornadofx.*
@ -54,7 +55,7 @@ class HVView : View(title = "High voltage time plot", icon = ImageView(dfIcon))
} }
if (change.wasAdded()) { if (change.wasAdded()) {
runLater { container.progress = -1.0 } runLater { container.progress = -1.0 }
runGoal("hvData[${change.key}]") { runGoal(app.context,"hvData[${change.key}]", Dispatchers.IO) {
change.valueAdded.getHvData() change.valueAdded.getHvData()
} ui { table -> } ui { table ->
if (table != null) { if (table != null) {

View File

@ -17,7 +17,10 @@ import javafx.scene.layout.Priority
import javafx.scene.text.Font import javafx.scene.text.Font
import javafx.stage.DirectoryChooser import javafx.stage.DirectoryChooser
import javafx.stage.FileChooser import javafx.stage.FileChooser
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.javafx.JavaFx
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.controlsfx.control.StatusBar import org.controlsfx.control.StatusBar
import tornadofx.* import tornadofx.*
import java.io.File import java.io.File
@ -28,6 +31,8 @@ class MainView : View(title = "Numass viewer", icon = dfIconView) {
private val pointCache by inject<PointCache>() private val pointCache by inject<PointCache>()
val storageView by inject<StorageView>()
private val statusBar = StatusBar() private val statusBar = StatusBar()
// private val logFragment = LogFragment().apply { // private val logFragment = LogFragment().apply {
// addLogHandler(context.logger) // addLogHandler(context.logger)
@ -140,7 +145,7 @@ class MainView : View(title = "Numass viewer", icon = dfIconView) {
if (Files.isDirectory(path)) { if (Files.isDirectory(path)) {
if (Files.exists(path.resolve(NumassDataLoader.META_FRAGMENT_NAME))) { if (Files.exists(path.resolve(NumassDataLoader.META_FRAGMENT_NAME))) {
//build set view //build set view
runGoal("viewer.load.set[$path]") { runGoal(app.context, "viewer.load.set[$path]", Dispatchers.IO) {
title = "Load set ($path)" title = "Load set ($path)"
message = "Building numass set..." message = "Building numass set..."
NumassDataLoader(app.context, null, path.fileName.toString(), path) NumassDataLoader(app.context, null, path.fileName.toString(), path)
@ -158,12 +163,12 @@ class MainView : View(title = "Numass viewer", icon = dfIconView) {
} }
} else { } else {
//build storage //build storage
runGoal("viewer.load.storage[$path]") { app.context.launch {
title = "Load storage ($path)" val storageElement = NumassDirectory.INSTANCE.read(app.context, path) as Storage
message = "Building numass storage tree..." withContext(Dispatchers.JavaFx){
NumassDirectory.INSTANCE.read(app.context, path) contentView = storageView
} ui { storageView.storageProperty.set(storageElement)
contentView = StorageView(it as Storage) }
} }
} }
} else { } else {

View File

@ -62,37 +62,3 @@ class PointCache : Controller() {
cache.clear() cache.clear()
} }
} }
//class CachedSet(set: NumassSet, context: Context) {
// override val points: ObservableList<CachedPoint> by lazy {
// set.points.map { CachedPoint(it, context) }.toObservable()
// }
// init {
// var watcher: WatchService? = null
//
// if (set is NumassDataLoader) {
// context.launch(Dispatchers.IO) {
// watcher = set.path.fileSystem.newWatchService()
// try {
// val key: WatchKey = set.path.register(watcher!!, ENTRY_CREATE)
// while (true) {
// key.pollEvents().forEach { event ->
// if (event.kind() == ENTRY_CREATE) {
// val path: Path = event.context() as Path
// if (path.fileName.toString().startsWith(NumassDataLoader.POINT_FRAGMENT_NAME)) {
// val envelope: Envelope = NumassEnvelopeType.infer(path)?.reader?.read(path)
// ?: kotlin.error("Can't read point file")
// val point = NumassDataUtils.read(envelope)
// points.add(CachedPoint(point, context))
// }
// }
// }
// }
// } catch (x: IOException) {
// x.printStackTrace()
// }
// }
// }
// }
//}

View File

@ -16,6 +16,7 @@ import javafx.collections.FXCollections
import javafx.collections.MapChangeListener import javafx.collections.MapChangeListener
import javafx.collections.ObservableMap import javafx.collections.ObservableMap
import javafx.scene.image.ImageView import javafx.scene.image.ImageView
import kotlinx.coroutines.Dispatchers
import tornadofx.* import tornadofx.*
/** /**
@ -43,7 +44,7 @@ class SlowControlView : View(title = "Numass slow control view", icon = ImageVie
plot.remove(change.key) plot.remove(change.key)
} }
if (change.wasAdded()) { if (change.wasAdded()) {
runGoal("loadTable[${change.key}]") { runGoal(app.context,"loadTable[${change.key}]", Dispatchers.IO) {
val plotData = getData(change.valueAdded) val plotData = getData(change.valueAdded)
val names = plotData.format.namesAsArray().filter { it != "timestamp" } val names = plotData.format.namesAsArray().filter { it != "timestamp" }

View File

@ -3,13 +3,10 @@ package inr.numass.viewer
import hep.dataforge.configure import hep.dataforge.configure
import hep.dataforge.fx.dfIcon import hep.dataforge.fx.dfIcon
import hep.dataforge.fx.plots.PlotContainer import hep.dataforge.fx.plots.PlotContainer
import hep.dataforge.fx.runGoal
import hep.dataforge.fx.ui
import hep.dataforge.names.Name import hep.dataforge.names.Name
import hep.dataforge.plots.data.DataPlot import hep.dataforge.plots.data.DataPlot
import hep.dataforge.plots.jfreechart.JFreeChartFrame import hep.dataforge.plots.jfreechart.JFreeChartFrame
import hep.dataforge.tables.Adapters import hep.dataforge.tables.Adapters
import hep.dataforge.values.Values
import inr.numass.data.analyzers.countInWindow import inr.numass.data.analyzers.countInWindow
import inr.numass.data.api.NumassSet import inr.numass.data.api.NumassSet
import javafx.beans.property.SimpleIntegerProperty import javafx.beans.property.SimpleIntegerProperty
@ -20,6 +17,10 @@ import javafx.geometry.Insets
import javafx.geometry.Orientation import javafx.geometry.Orientation
import javafx.scene.image.ImageView import javafx.scene.image.ImageView
import javafx.util.converter.NumberStringConverter import javafx.util.converter.NumberStringConverter
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.javafx.JavaFx
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.controlsfx.control.RangeSlider import org.controlsfx.control.RangeSlider
import tornadofx.* import tornadofx.*
import java.util.concurrent.atomic.AtomicInteger import java.util.concurrent.atomic.AtomicInteger
@ -123,13 +124,13 @@ class SpectrumView : View(title = "Numass spectrum plot", icon = ImageView(dfIco
val plot: DataPlot = val plot: DataPlot =
frame.plots[Name.ofSingle(name)] as DataPlot? ?: DataPlot(name).apply { frame.add(this) } frame.plots[Name.ofSingle(name)] as DataPlot? ?: DataPlot(name).apply { frame.add(this) }
runGoal("spectrumData[$name]") { app.context.launch {
set.points.map { val points = set.points.map {
pointCache.getCachedPoint("$name/${it.voltage}[${it.index}]", it) pointCache.getCachedPoint("$name/${it.voltage}[${it.index}]", it)
}.map { cachedPoint -> }.map { cachedPoint ->
val count = cachedPoint.spectrum.await().countInWindow(loChannel.toShort(), upChannel.toShort()) val count = cachedPoint.spectrum.await().countInWindow(loChannel.toShort(), upChannel.toShort())
val seconds = cachedPoint.length.toMillis() / 1000.0 val seconds = cachedPoint.length.toMillis() / 1000.0
runLater { launch(Dispatchers.JavaFx) {
container.progress = progress.incrementAndGet().toDouble() / totalProgress container.progress = progress.incrementAndGet().toDouble() / totalProgress
} }
Adapters.buildXYDataPoint( Adapters.buildXYDataPoint(
@ -138,10 +139,10 @@ class SpectrumView : View(title = "Numass spectrum plot", icon = ImageView(dfIco
sqrt(count.toDouble()) / seconds sqrt(count.toDouble()) / seconds
) )
} }
} ui { points: List<Values> -> withContext(Dispatchers.JavaFx) {
plot.fillData(points) plot.fillData(points)
container.progress = 1.0 container.progress = 1.0
//spectrumExportButton.isDisable = false }
} }
} }
} }

View File

@ -6,19 +6,25 @@ import hep.dataforge.meta.Meta
import hep.dataforge.meta.Metoid import hep.dataforge.meta.Metoid
import hep.dataforge.names.AlphanumComparator import hep.dataforge.names.AlphanumComparator
import hep.dataforge.storage.Storage import hep.dataforge.storage.Storage
import hep.dataforge.storage.files.FileStorage
import hep.dataforge.storage.files.FileTableLoader import hep.dataforge.storage.files.FileTableLoader
import hep.dataforge.storage.tables.TableLoader import hep.dataforge.storage.tables.TableLoader
import inr.numass.data.api.NumassPoint import inr.numass.data.api.NumassPoint
import inr.numass.data.api.NumassSet import inr.numass.data.api.NumassSet
import inr.numass.data.storage.NumassDataLoader import inr.numass.data.storage.NumassDataLoader
import javafx.beans.property.SimpleBooleanProperty import javafx.beans.property.SimpleBooleanProperty
import javafx.beans.property.SimpleObjectProperty
import javafx.collections.ObservableList import javafx.collections.ObservableList
import javafx.scene.control.ContextMenu import javafx.scene.control.ContextMenu
import javafx.scene.control.TreeItem import javafx.scene.control.TreeItem
import tornadofx.* import tornadofx.*
import java.nio.file.WatchService
class StorageView(val storage: Storage) : View(title = "Numass storage", icon = dfIconView) { class StorageView : View(title = "Numass storage", icon = dfIconView) {
val storageProperty = SimpleObjectProperty<Storage>()
val storage by storageProperty
private val pointCache by inject<PointCache>() private val pointCache by inject<PointCache>()
@ -28,7 +34,11 @@ class StorageView(val storage: Storage) : View(title = "Numass storage", icon =
private val hvView: HVView by inject() private val hvView: HVView by inject()
private val scView: SlowControlView by inject() private val scView: SlowControlView by inject()
init { private var watcher: WatchService? = null
fun clear() {
watcher?.close()
ampView.clear() ampView.clear()
timeView.clear() timeView.clear()
spectrumView.clear() spectrumView.clear()
@ -81,7 +91,7 @@ class StorageView(val storage: Storage) : View(title = "Numass storage", icon =
} }
fun getChildren(): ObservableList<Container>? = when (content) { fun getChildren(): ObservableList<Container>? = when (content) {
is Storage -> content.children.map { is Storage -> content.getChildren().map {
buildContainer(it, this) buildContainer(it, this)
}.sortedWith(Comparator.comparing({ it.id }, AlphanumComparator)).asObservable() }.sortedWith(Comparator.comparing({ it.id }, AlphanumComparator)).asObservable()
is NumassSet -> content.points is NumassSet -> content.points
@ -90,6 +100,35 @@ class StorageView(val storage: Storage) : View(title = "Numass storage", icon =
.asObservable() .asObservable()
else -> null else -> null
} }
/*
is NumassDataLoader -> {
val res = content.points.sortedBy { it.index }.map { buildContainer(it, this) }.toObservable()
watchJob = app.context.launch(Dispatchers.IO) {
val key: WatchKey = content.path.register(watcher!!, ENTRY_CREATE)
coroutineContext[Job]?.invokeOnCompletion {
key.cancel()
}
while (watcher != null && isActive) {
try {
key.pollEvents().forEach { event ->
if (event.kind() == ENTRY_CREATE) {
val path: Path = event.context() as Path
if (path.fileName.toString().startsWith(NumassDataLoader.POINT_FRAGMENT_NAME)) {
val envelope: Envelope = NumassEnvelopeType.infer(path)?.reader?.read(path)
?: kotlin.error("Can't read point file")
val point = NumassDataUtils.read(envelope)
res.add(buildContainer(point, this@Container))
}
}
}
} catch (x: Throwable) {
app.context.logger.error("Error during dynamic point read", x)
}
}
}
res
}
*/
val hasChildren: Boolean = (content is Storage) || (content is NumassSet) val hasChildren: Boolean = (content is Storage) || (content is NumassSet)
@ -99,13 +138,24 @@ class StorageView(val storage: Storage) : View(title = "Numass storage", icon =
override val root = splitpane { override val root = splitpane {
treeview<Container> { treeview<Container> {
//isShowRoot = false //isShowRoot = false
root = TreeItem(Container(storage.name, storage)) storageProperty.onChange { storage ->
root.isExpanded = true clear()
lazyPopulate(leafCheck = { if (storage == null) return@onChange
!it.value.hasChildren root = TreeItem(Container(storage.name, storage))
}) { root.isExpanded = true
it.value.getChildren() lazyPopulate(leafCheck = {
!it.value.hasChildren
}) {
it.value.getChildren()
}
watcher?.close()
watcher = if (storage is FileStorage) {
storage.path.fileSystem.newWatchService()
} else {
null
}
} }
cellFormat { value: Container -> cellFormat { value: Container ->
when (value.content) { when (value.content) {
is Storage -> { is Storage -> {

View File

@ -21,6 +21,7 @@ import javafx.beans.binding.DoubleBinding
import javafx.collections.FXCollections import javafx.collections.FXCollections
import javafx.collections.ObservableMap import javafx.collections.ObservableMap
import javafx.scene.image.ImageView import javafx.scene.image.ImageView
import kotlinx.coroutines.Dispatchers
import tornadofx.* import tornadofx.*
class TimeView : View(title = "Numass time spectrum plot", icon = ImageView(dfIcon)) { class TimeView : View(title = "Numass time spectrum plot", icon = ImageView(dfIcon)) {
@ -103,7 +104,7 @@ class TimeView : View(title = "Numass time spectrum plot", icon = ImageView(dfIc
private fun invalidate() { private fun invalidate() {
data.forEach { key, point -> data.forEach { key, point ->
plots.getOrPut(key) { plots.getOrPut(key) {
runGoal<Plottable>("loadAmplitudeSpectrum_$key") { runGoal<Plottable>(app.context, "loadAmplitudeSpectrum_$key", Dispatchers.IO) {
val initialEstimate = analyzer.analyze(point) val initialEstimate = analyzer.analyze(point)
val cr = initialEstimate.getDouble("cr") val cr = initialEstimate.getDouble("cr")