From 15d17f2cc48bbe8c9083cc006a5db39076318485 Mon Sep 17 00:00:00 2001 From: Alexander Nozik Date: Sun, 14 Nov 2021 16:46:32 +0300 Subject: [PATCH] Fix viewer cache --- build.gradle.kts | 4 +- .../dataforge/names/AlphanumComparator.java | 126 ------------------ .../main/java/hep/dataforge/utils/Misc.java | 3 +- .../kotlin/hep/dataforge/context/Context.kt | 4 +- .../kotlin/hep/dataforge/context/Global.kt | 16 +-- .../hep/dataforge/names/AlphanumComparator.kt | 106 +++++++++++++++ .../main/kotlin/hep/dataforge/values/Value.kt | 2 +- .../hep/dataforge/workspace/WorkspaceTest.kt | 1 + dataforge-gui/build.gradle | 23 ---- dataforge-gui/build.gradle.kts | 25 ++++ dataforge-gui/dataforge-html/build.gradle | 3 +- dataforge-plots/build.gradle | 13 -- dataforge-plots/build.gradle.kts | 14 ++ .../dataforge/storage/files/FileStorage.kt | 50 ++++--- numass-core/numass-data-api/build.gradle.kts | 8 -- .../kotlin/inr/numass/data/api/NumassPoint.kt | 1 - numass-viewer/build.gradle | 30 ----- numass-viewer/build.gradle.kts | 42 ++++++ .../kotlin/inr/numass/viewer/AmplitudeView.kt | 15 ++- .../main/kotlin/inr/numass/viewer/Cache.kt | 48 ------- .../main/kotlin/inr/numass/viewer/MainView.kt | 49 +++---- .../kotlin/inr/numass/viewer/PointCache.kt | 98 ++++++++++++++ .../kotlin/inr/numass/viewer/PointInfoView.kt | 15 ++- .../kotlin/inr/numass/viewer/SpectrumView.kt | 36 ++--- .../kotlin/inr/numass/viewer/StorageView.kt | 76 +++++------ .../main/kotlin/inr/numass/viewer/TimeView.kt | 9 +- .../main/kotlin/inr/numass/viewer/Viewer.kt | 12 +- .../inr/numass/viewer/test/ComponentTest.kt | 20 +-- 28 files changed, 456 insertions(+), 393 deletions(-) delete mode 100644 dataforge-core/src/main/java/hep/dataforge/names/AlphanumComparator.java create mode 100644 dataforge-core/src/main/kotlin/hep/dataforge/names/AlphanumComparator.kt delete mode 100644 dataforge-gui/build.gradle create mode 100644 dataforge-gui/build.gradle.kts delete mode 100644 dataforge-plots/build.gradle create mode 100644 dataforge-plots/build.gradle.kts delete mode 100644 numass-viewer/build.gradle create mode 100644 numass-viewer/build.gradle.kts delete mode 100644 numass-viewer/src/main/kotlin/inr/numass/viewer/Cache.kt create mode 100644 numass-viewer/src/main/kotlin/inr/numass/viewer/PointCache.kt diff --git a/build.gradle.kts b/build.gradle.kts index e23c6fa9..1d6d6a30 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,6 +1,6 @@ plugins { kotlin("jvm") version "1.5.31" - id("org.openjfx.javafxplugin") version "0.0.9" apply false + id("org.openjfx.javafxplugin") version "0.0.10" apply false id("com.github.johnrengelman.shadow") version "7.1.0" apply false } @@ -12,7 +12,7 @@ allprojects { repositories { mavenCentral() - jcenter() + maven("https://oss.sonatype.org/content/repositories/snapshots") } dependencies { diff --git a/dataforge-core/src/main/java/hep/dataforge/names/AlphanumComparator.java b/dataforge-core/src/main/java/hep/dataforge/names/AlphanumComparator.java deleted file mode 100644 index d8b20fdb..00000000 --- a/dataforge-core/src/main/java/hep/dataforge/names/AlphanumComparator.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * The Alphanum Algorithm is an improved sorting algorithm for strings - * containing numbers. Instead of sorting numbers in ASCII order like - * a standard sort, this algorithm sorts numbers in numeric order. - * - * The Alphanum Algorithm is discussed at http://www.DaveKoelle.com - * - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -package hep.dataforge.names; - -import java.util.Comparator; - -/** - * This is an updated version with enhancements made by Daniel Migowski, - * Andre Bogus, and David Koelle - * To use this class: - * Use the static "sort" method from the java.util.Collections class: - * Collections.sort(your list, new AlphanumComparator()); - */ -public class AlphanumComparator implements Comparator { - public static final AlphanumComparator INSTANCE = new AlphanumComparator(); - - private Comparator comparator = new NaturalComparator(); - - public AlphanumComparator(Comparator comparator) { - this.comparator = comparator; - } - - public AlphanumComparator() { - - } - - private final boolean isDigit(char ch) { - return ch >= 48 && ch <= 57; - } - - /** - * Length of string is passed in for improved efficiency (only need to calculate it once) - **/ - private final String getChunk(String s, int slength, int marker) { - StringBuilder chunk = new StringBuilder(); - char c = s.charAt(marker); - chunk.append(c); - marker++; - if (isDigit(c)) { - while (marker < slength) { - c = s.charAt(marker); - if (!isDigit(c)) - break; - chunk.append(c); - marker++; - } - } else { - while (marker < slength) { - c = s.charAt(marker); - if (isDigit(c)) - break; - chunk.append(c); - marker++; - } - } - return chunk.toString(); - } - - public int compare(String s1, String s2) { - - int thisMarker = 0; - int thatMarker = 0; - int s1Length = s1.length(); - int s2Length = s2.length(); - - while (thisMarker < s1Length && thatMarker < s2Length) { - String thisChunk = getChunk(s1, s1Length, thisMarker); - thisMarker += thisChunk.length(); - - String thatChunk = getChunk(s2, s2Length, thatMarker); - thatMarker += thatChunk.length(); - - // If both chunks contain numeric characters, sort them numerically - int result = 0; - if (isDigit(thisChunk.charAt(0)) && isDigit(thatChunk.charAt(0))) { - // Simple chunk comparison by length. - int thisChunkLength = thisChunk.length(); - result = thisChunkLength - thatChunk.length(); - // If equal, the first different number counts - if (result == 0) { - for (int i = 0; i < thisChunkLength; i++) { - result = thisChunk.charAt(i) - thatChunk.charAt(i); - if (result != 0) { - return result; - } - } - } - } else { - result = comparator.compare(thisChunk, thatChunk); - } - - if (result != 0) - return result; - } - - return s1Length - s2Length; - } - - private static class NaturalComparator implements Comparator { - public int compare(String o1, String o2) { - return o1.compareTo(o2); - } - } -} \ No newline at end of file diff --git a/dataforge-core/src/main/java/hep/dataforge/utils/Misc.java b/dataforge-core/src/main/java/hep/dataforge/utils/Misc.java index df1166c1..614b9dc1 100644 --- a/dataforge-core/src/main/java/hep/dataforge/utils/Misc.java +++ b/dataforge-core/src/main/java/hep/dataforge/utils/Misc.java @@ -6,6 +6,7 @@ package hep.dataforge.utils; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; @@ -15,7 +16,7 @@ import java.util.concurrent.CancellationException; * @author Alexander Nozik */ public class Misc { - public static final Charset UTF = Charset.forName("UTF-8"); + public static final Charset UTF = StandardCharsets.UTF_8; /** * A synchronized lru cache diff --git a/dataforge-core/src/main/kotlin/hep/dataforge/context/Context.kt b/dataforge-core/src/main/kotlin/hep/dataforge/context/Context.kt index b9eadc42..766d7b4e 100644 --- a/dataforge-core/src/main/kotlin/hep/dataforge/context/Context.kt +++ b/dataforge-core/src/main/kotlin/hep/dataforge/context/Context.kt @@ -36,6 +36,7 @@ import hep.dataforge.values.Value import hep.dataforge.values.ValueProvider import hep.dataforge.values.ValueProvider.Companion.VALUE_TARGET import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.cancel import org.slf4j.Logger import org.slf4j.LoggerFactory import java.io.File @@ -47,7 +48,6 @@ import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ExecutorService import java.util.concurrent.Executors import java.util.stream.Stream -import kotlin.collections.HashMap import kotlin.collections.set import kotlin.coroutines.CoroutineContext import kotlin.streams.asSequence @@ -308,10 +308,12 @@ open class Context( */ @Throws(Exception::class) override fun close() { + logger.info("Closing context: $name") //detach all plugins plugins.close() if (started) { + coroutineContext.cancel() dispatcher.shutdown() } } diff --git a/dataforge-core/src/main/kotlin/hep/dataforge/context/Global.kt b/dataforge-core/src/main/kotlin/hep/dataforge/context/Global.kt index 2212fe33..a5afbf2a 100644 --- a/dataforge-core/src/main/kotlin/hep/dataforge/context/Global.kt +++ b/dataforge-core/src/main/kotlin/hep/dataforge/context/Global.kt @@ -133,15 +133,13 @@ object Global : Context("GLOBAL", null, Thread.currentThread().contextClassLoade * @return */ @Synchronized - fun getContext(name: String): Context { - return contextRegistry - .findFirst { ctx -> ctx.name == name } - .orElseGet { - val ctx = Context(name) - contextRegistry.add(ctx) - ctx - } - } + fun getContext(name: String): Context = contextRegistry + .findFirst { ctx -> ctx.name == name } + .orElseGet { + val ctx = Context(name) + contextRegistry.add(ctx) + ctx + } /** * Close all contexts and terminate framework diff --git a/dataforge-core/src/main/kotlin/hep/dataforge/names/AlphanumComparator.kt b/dataforge-core/src/main/kotlin/hep/dataforge/names/AlphanumComparator.kt new file mode 100644 index 00000000..bc8d36cc --- /dev/null +++ b/dataforge-core/src/main/kotlin/hep/dataforge/names/AlphanumComparator.kt @@ -0,0 +1,106 @@ +/* + * The Alphanum Algorithm is an improved sorting algorithm for strings + * containing numbers. Instead of sorting numbers in ASCII order like + * a standard sort, this algorithm sorts numbers in numeric order. + * + * The Alphanum Algorithm is discussed at http://www.DaveKoelle.com + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ +package hep.dataforge.names + +/** + * This is an updated version with enhancements made by Daniel Migowski, + * Andre Bogus, and David Koelle + * To use this class: + * Use the static "sort" method from the java.util.Collections class: + * Collections.sort(your list, new AlphanumComparator()); + */ +object AlphanumComparator : Comparator { + private val comparator: Comparator = NaturalComparator() + + private fun isDigit(ch: Char): Boolean { + return ch.code in 48..57 + } + + /** + * Length of string is passed in for improved efficiency (only need to calculate it once) + */ + private fun getChunk(s: String, slength: Int, marker: Int): String { + var modMarker = marker + val chunk = StringBuilder() + var c = s[modMarker] + chunk.append(c) + modMarker++ + if (isDigit(c)) { + while (modMarker < slength) { + c = s[modMarker] + if (!isDigit(c)) break + chunk.append(c) + modMarker++ + } + } else { + while (modMarker < slength) { + c = s[modMarker] + if (isDigit(c)) break + chunk.append(c) + modMarker++ + } + } + return chunk.toString() + } + + override fun compare(s1: String, s2: String): Int { + var thisMarker = 0 + var thatMarker = 0 + val s1Length = s1.length + val s2Length = s2.length + while (thisMarker < s1Length && thatMarker < s2Length) { + val thisChunk = getChunk(s1, s1Length, thisMarker) + thisMarker += thisChunk.length + val thatChunk = getChunk(s2, s2Length, thatMarker) + thatMarker += thatChunk.length + + // If both chunks contain numeric characters, sort them numerically + var result = 0 + if (isDigit(thisChunk[0]) && isDigit(thatChunk[0])) { + // Simple chunk comparison by length. + val thisChunkLength = thisChunk.length + result = thisChunkLength - thatChunk.length + // If equal, the first different number counts + if (result == 0) { + for (i in 0 until thisChunkLength) { + result = thisChunk[i] - thatChunk[i] + if (result != 0) { + return result + } + } + } + } else { + result = comparator.compare(thisChunk, thatChunk) + } + if (result != 0) return result + } + return s1Length - s2Length + } + + private class NaturalComparator : Comparator { + override fun compare(o1: String, o2: String): Int { + return o1.compareTo(o2) + } + } +} \ No newline at end of file diff --git a/dataforge-core/src/main/kotlin/hep/dataforge/values/Value.kt b/dataforge-core/src/main/kotlin/hep/dataforge/values/Value.kt index a54fb5ec..d725e958 100644 --- a/dataforge-core/src/main/kotlin/hep/dataforge/values/Value.kt +++ b/dataforge-core/src/main/kotlin/hep/dataforge/values/Value.kt @@ -126,7 +126,7 @@ interface Value : Serializable, Comparable { return when (type) { ValueType.NUMBER -> ValueUtils.NUMBER_COMPARATOR.compare(number, other.number) ValueType.BOOLEAN -> boolean.compareTo(other.boolean) - ValueType.STRING -> AlphanumComparator.INSTANCE.compare(this.string, other.string) + ValueType.STRING -> AlphanumComparator.compare(this.string, other.string) ValueType.TIME -> time.compareTo(other.time) ValueType.NULL -> if (other.type == ValueType.NULL) 0 else -1 ValueType.BINARY -> binary.compareTo(other.binary) diff --git a/dataforge-core/src/test/kotlin/hep/dataforge/workspace/WorkspaceTest.kt b/dataforge-core/src/test/kotlin/hep/dataforge/workspace/WorkspaceTest.kt index 210d9538..8b57c67b 100644 --- a/dataforge-core/src/test/kotlin/hep/dataforge/workspace/WorkspaceTest.kt +++ b/dataforge-core/src/test/kotlin/hep/dataforge/workspace/WorkspaceTest.kt @@ -70,6 +70,7 @@ class WorkspaceTest { @BeforeClass @JvmStatic fun setup() { + counter.set(0) val context = Global.getContext("TEST").apply { load(CachePlugin::class.java, MetaBuilder().setValue("fileCache.enabled", false)) } diff --git a/dataforge-gui/build.gradle b/dataforge-gui/build.gradle deleted file mode 100644 index c38840ef..00000000 --- a/dataforge-gui/build.gradle +++ /dev/null @@ -1,23 +0,0 @@ -//apply plugin: 'org.openjfx.javafxplugin' -// -//javafx { -// modules = [ 'javafx.controls', 'javafx.web' ] -//} - -description = "A tornadofx based kotlin library" - - -dependencies { - api project(':dataforge-plots') - api project(':dataforge-gui:dataforge-html') - api 'org.controlsfx:controlsfx:8.40.14' - api "no.tornado:tornadofx:1.7.19" - api 'no.tornado:tornadofx-controlsfx:0.1.1' - api group: 'org.fxmisc.richtext', name: 'richtextfx', version: '0.10.2' - api 'org.jetbrains.kotlinx:kotlinx-coroutines-javafx:1.5.0' - - // optional dependency for JFreeChart - //compileOnly project(":dataforge-plots:plots-jfc") -} - - diff --git a/dataforge-gui/build.gradle.kts b/dataforge-gui/build.gradle.kts new file mode 100644 index 00000000..bd628c9c --- /dev/null +++ b/dataforge-gui/build.gradle.kts @@ -0,0 +1,25 @@ +plugins { + id("org.openjfx.javafxplugin") +} + +javafx { + modules = listOf("javafx.controls", "javafx.web") + version = "11" +} + +description = "A tornadofx based kotlin library" + +dependencies { + api(project(":dataforge-plots")) + api(project(":dataforge-gui:dataforge-html")) + api("no.tornado:tornadofx:1.7.20") + api("org.controlsfx:controlsfx:11.1.0") + api("no.tornado:tornadofx-controlsfx:0.1.1") + api("org.fxmisc.richtext:richtextfx:0.10.7") + api("org.jetbrains.kotlinx:kotlinx-coroutines-javafx:1.5.2") + + // optional dependency for JFreeChart + //compileOnly project(":dataforge-plots:plots-jfc") +} + + diff --git a/dataforge-gui/dataforge-html/build.gradle b/dataforge-gui/dataforge-html/build.gradle index 8d337534..dc959b96 100644 --- a/dataforge-gui/dataforge-html/build.gradle +++ b/dataforge-gui/dataforge-html/build.gradle @@ -2,5 +2,6 @@ description = "An html rendering core and HTML output" dependencies { api project(':dataforge-core') - api 'org.jetbrains.kotlinx:kotlinx-html-jvm:0.6.11' + // https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-html-jvm + api 'org.jetbrains.kotlinx:kotlinx-html-jvm:0.7.3' } diff --git a/dataforge-plots/build.gradle b/dataforge-plots/build.gradle deleted file mode 100644 index e31d8b2a..00000000 --- a/dataforge-plots/build.gradle +++ /dev/null @@ -1,13 +0,0 @@ -//allprojects { -// apply plugin: 'org.openjfx.javafxplugin' -// -// javafx { -// modules = ['javafx.controls'] -// } -//} - -description = 'dataforge-plots' - -dependencies { - api project(':dataforge-core') -} diff --git a/dataforge-plots/build.gradle.kts b/dataforge-plots/build.gradle.kts new file mode 100644 index 00000000..595216c2 --- /dev/null +++ b/dataforge-plots/build.gradle.kts @@ -0,0 +1,14 @@ +plugins { + id("org.openjfx.javafxplugin") +} + +javafx { + modules = listOf("javafx.controls") + version = "11" +} + +description = "dataforge-plots" + +dependencies { + api(project(":dataforge-core")) +} diff --git a/dataforge-storage/src/main/kotlin/hep/dataforge/storage/files/FileStorage.kt b/dataforge-storage/src/main/kotlin/hep/dataforge/storage/files/FileStorage.kt index e4e0b76c..a38e0e8d 100644 --- a/dataforge-storage/src/main/kotlin/hep/dataforge/storage/files/FileStorage.kt +++ b/dataforge-storage/src/main/kotlin/hep/dataforge/storage/files/FileStorage.kt @@ -33,11 +33,11 @@ import hep.dataforge.storage.MutableStorage import hep.dataforge.storage.StorageElement import hep.dataforge.storage.StorageElementType import hep.dataforge.storage.StorageManager +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.joinAll import kotlinx.coroutines.runBlocking import java.nio.file.* import kotlin.streams.asSequence -import kotlin.streams.toList /** * An element of file storage with fixed path @@ -62,12 +62,12 @@ interface FileStorageElementType : StorageElementType, Named { } class FileStorage( - override val context: Context, - override val name: String, - override val meta: Meta, - override val path: Path, - override val parent: StorageElement? = null, - val type: FileStorageElementType + override val context: Context, + override val name: String, + override val meta: Meta, + override val path: Path, + override val parent: StorageElement? = null, + val type: FileStorageElementType, ) : MutableStorage, FileStorageElement { private val _connectionHelper by lazy { ConnectionHelper(this) } @@ -79,15 +79,14 @@ class FileStorage( override val children: Collection - get() = synchronized(this) { - runBlocking { - if (!isInitialized) { - refresh() - } + get() = runBlocking(Dispatchers.IO) { + if (!isInitialized) { + refresh() } _children.values } + override fun resolveType(meta: Meta): StorageElementType? { val type = meta.optString(StorageManager.STORAGE_META_TYPE_KEY).nullable return if (type == null) { @@ -104,14 +103,14 @@ class FileStorage( (parent as? FileStorage)?.watchService ?: path.fileSystem.newWatchService() } - //TODO actually watch for file change +//TODO actually watch for file change override fun create(meta: Meta): StorageElement { val path = path.resolve(meta.getString("path", meta.getString("name"))) return _children.getOrPut(path) { resolveType(meta) - ?.create(this, meta) - ?: error("Can't resolve storage element type.") + ?.create(this, meta) + ?: error("Can't resolve storage element type.") } } @@ -127,7 +126,7 @@ class FileStorage( 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") + ?: logger.debug("Could not resolve type for $path in $this") } } }.toList().joinAll() @@ -141,11 +140,14 @@ class FileStorage( /** * Resolve meta for given path if it is available. If directory search for file called meta or meta.df inside */ - fun resolveMeta(path: Path, metaReader: (Path) -> Meta? = { EnvelopeType.infer(it)?.reader?.read(it)?.meta }): Meta? { + fun resolveMeta( + path: Path, + metaReader: (Path) -> Meta? = { EnvelopeType.infer(it)?.reader?.read(it)?.meta }, + ): Meta? { return if (Files.isDirectory(path)) { Files.list(path).asSequence() - .find { it.fileName.toString() == "meta.df" || it.fileName.toString() == "meta" } - ?.let(metaReader) + .find { it.fileName.toString() == "meta.df" || it.fileName.toString() == "meta" } + ?.let(metaReader) } else { metaReader(path) } @@ -166,8 +168,10 @@ class FileStorage( override val name: String = "hep.dataforge.storage.directory" @ValueDefs( - ValueDef(key = "path", info = "The relative path to the shelf inside parent storage or absolute path"), - ValueDef(key = "name", required = true, info = "The name of the new storage. By default use last segment shelf name") + ValueDef(key = "path", info = "The relative path to the shelf inside parent storage or absolute path"), + ValueDef(key = "name", + required = true, + info = "The name of the new storage. By default use last segment shelf name") ) override fun create(context: Context, meta: Meta, parent: StorageElement?): FileStorageElement { val shelfName = meta.getString("name") @@ -192,7 +196,9 @@ class FileStorage( override suspend fun read(context: Context, path: Path, parent: StorageElement?): FileStorageElement? { val meta = resolveMeta(path) val name = meta?.optString("name").nullable ?: path.fileName.toString() - val type = meta?.optString("type").nullable?.let { context.load().getType(it) } as? FileStorageElementType + val type = meta?.optString("type").nullable?.let { + context.load().getType(it) + } as? FileStorageElementType return if (type == null || type is Directory) { // Read path as directory if type not found and path is directory if (Files.isDirectory(path)) { diff --git a/numass-core/numass-data-api/build.gradle.kts b/numass-core/numass-data-api/build.gradle.kts index 2963d76f..6465d674 100644 --- a/numass-core/numass-data-api/build.gradle.kts +++ b/numass-core/numass-data-api/build.gradle.kts @@ -1,11 +1,7 @@ -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile - plugins { - idea kotlin("jvm") } - repositories { mavenCentral() } @@ -13,7 +9,3 @@ repositories { dependencies { api(project(":dataforge-core")) } - -tasks.withType { - kotlinOptions.jvmTarget = "1.8" -} \ No newline at end of file diff --git a/numass-core/numass-data-api/src/main/kotlin/inr/numass/data/api/NumassPoint.kt b/numass-core/numass-data-api/src/main/kotlin/inr/numass/data/api/NumassPoint.kt index a9021ace..e53fef5e 100644 --- a/numass-core/numass-data-api/src/main/kotlin/inr/numass/data/api/NumassPoint.kt +++ b/numass-core/numass-data-api/src/main/kotlin/inr/numass/data/api/NumassPoint.kt @@ -28,7 +28,6 @@ import java.util.stream.Stream */ interface NumassPoint : Metoid, ParentBlock, Provider { - override val blocks: List /** diff --git a/numass-viewer/build.gradle b/numass-viewer/build.gradle deleted file mode 100644 index ea0b51a2..00000000 --- a/numass-viewer/build.gradle +++ /dev/null @@ -1,30 +0,0 @@ -apply plugin: 'application' -apply plugin: 'kotlin' - -repositories { - mavenCentral() -} - -//apply plugin: 'org.openjfx.javafxplugin' -// -//javafx { -// modules = [ 'javafx.controls' ] -//} - -if (!hasProperty('mainClass')) { - ext.mainClass = 'inr.numass.viewer.Viewer'//"inr.numass.viewer.test.TestApp" -} - -mainClassName = mainClass - -version = "0.5.6" - -description = "The viewer for numass data" - - -dependencies { - api project(':numass-core') - api project(':dataforge-plots:plots-jfc') - api project(':dataforge-gui') -} - diff --git a/numass-viewer/build.gradle.kts b/numass-viewer/build.gradle.kts new file mode 100644 index 00000000..15cf2ff6 --- /dev/null +++ b/numass-viewer/build.gradle.kts @@ -0,0 +1,42 @@ +plugins { + kotlin("jvm") + id("org.openjfx.javafxplugin") + application +} + +javafx { + modules = listOf("javafx.controls", "javafx.web") + version = "11" +} + +repositories { + mavenCentral() +} + +application { + mainClass.set("inr.numass.viewer.Viewer") +} + +version = "0.6.0" + +description = "The viewer for numass data" + +dependencies { + api(project(":numass-core")) + api(project(":dataforge-plots:plots-jfc")) + api(project(":dataforge-gui")) +} + +application { + applicationDefaultJvmArgs = listOf( + "--add-exports=javafx.graphics/com.sun.glass.ui=ALL-UNNAMED", + "--add-opens=javafx.graphics/com.sun.javafx.css=ALL-UNNAMED", + "--add-opens=javafx.graphics/com.sun.javafx.scene=ALL-UNNAMED", + "--add-opens=javafx.graphics/com.sun.javafx.scene.traversal=ALL-UNNAMED", + "--add-opens=javafx.graphics/javafx.scene=ALL-UNNAMED", + "--add-opens=javafx.controls/com.sun.javafx.scene.control=ALL-UNNAMED", + "--add-opens=javafx.controls/com.sun.javafx.scene.control.behavior=ALL-UNNAMED", + "--add-opens=javafx.controls/javafx.scene.control.skin=ALL-UNNAMED", + "--add-exports=javafx.controls/com.sun.javafx.scene.control.inputmap=ALL-UNNAMED", + ) +} \ No newline at end of file diff --git a/numass-viewer/src/main/kotlin/inr/numass/viewer/AmplitudeView.kt b/numass-viewer/src/main/kotlin/inr/numass/viewer/AmplitudeView.kt index 7f1df961..440dfa6b 100644 --- a/numass-viewer/src/main/kotlin/inr/numass/viewer/AmplitudeView.kt +++ b/numass-viewer/src/main/kotlin/inr/numass/viewer/AmplitudeView.kt @@ -15,6 +15,7 @@ import hep.dataforge.plots.jfreechart.JFreeChartFrame import hep.dataforge.tables.Adapters import inr.numass.data.analyzers.NumassAnalyzer import inr.numass.data.analyzers.withBinning +import inr.numass.data.api.NumassPoint import javafx.beans.Observable import javafx.beans.binding.DoubleBinding import javafx.beans.property.SimpleBooleanProperty @@ -28,6 +29,8 @@ import tornadofx.* class AmplitudeView : View(title = "Numass amplitude spectrum plot", icon = ImageView(dfIcon)) { + private val pointCache by inject() + private val frame = JFreeChartFrame().configure { "title" to "Detector response plot" node("xAxis") { @@ -70,7 +73,7 @@ class AmplitudeView : View(title = "Numass amplitude spectrum plot", icon = Imag addToSideBar(0, binningSelector, normalizeSwitch) } - private val data: ObservableMap = FXCollections.observableHashMap() + private val data: ObservableMap = FXCollections.observableHashMap() private val plots: ObservableMap> = FXCollections.observableHashMap() val isEmpty = booleanBinding(data) { isEmpty() } @@ -113,16 +116,16 @@ class AmplitudeView : View(title = "Numass amplitude spectrum plot", icon = Imag /** * Put or replace current plot with name `key` */ - operator fun set(key: String, point: CachedPoint) { + operator fun set(key: String, point: NumassPoint) { data[key] = point } - fun addAll(data: Map) { + fun addAll(data: Map) { this.data.putAll(data); } private fun invalidate() { - data.forEach { key, point -> + data.forEach { (key, point) -> plots.getOrPut(key) { runGoal("loadAmplitudeSpectrum_$key") { val valueAxis = if (normalize) { @@ -132,7 +135,7 @@ class AmplitudeView : View(title = "Numass amplitude spectrum plot", icon = Imag } val adapter = Adapters.buildXYAdapter(NumassAnalyzer.CHANNEL_KEY, valueAxis) - val channels = point.channelSpectra.await() + val channels = pointCache.getChannelSpectra(key, point) return@runGoal if (channels.size == 1) { DataPlot.plot( @@ -186,7 +189,7 @@ class AmplitudeView : View(title = "Numass amplitude spectrum plot", icon = Imag /** * Set frame content to the given map. All keys not in the map are removed. */ - fun setAll(map: Map) { + fun setAll(map: Map) { plots.clear(); //Remove obsolete keys data.keys.filter { !map.containsKey(it) }.forEach { diff --git a/numass-viewer/src/main/kotlin/inr/numass/viewer/Cache.kt b/numass-viewer/src/main/kotlin/inr/numass/viewer/Cache.kt deleted file mode 100644 index dafe9629..00000000 --- a/numass-viewer/src/main/kotlin/inr/numass/viewer/Cache.kt +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2018 Alexander Nozik. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package inr.numass.viewer - -import hep.dataforge.meta.Meta -import hep.dataforge.tables.Table -import inr.numass.data.analyzers.SimpleAnalyzer -import inr.numass.data.api.NumassBlock -import inr.numass.data.api.NumassPoint -import inr.numass.data.api.NumassSet -import kotlinx.coroutines.CoroutineStart -import kotlinx.coroutines.Deferred -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.async - -private val analyzer = SimpleAnalyzer() - - -class CachedPoint(val point: NumassPoint) : NumassPoint by point { - - override val blocks: List by lazy { point.blocks } - - override val meta: Meta = point.meta - - val channelSpectra: Deferred> = GlobalScope.async(start = CoroutineStart.LAZY) { - point.channels.mapValues { (_, value) -> analyzer.getAmplitudeSpectrum(value) } - } - - val spectrum: Deferred = GlobalScope.async(start = CoroutineStart.LAZY) { analyzer.getAmplitudeSpectrum(point) } -} - -class CachedSet(set: NumassSet) : NumassSet by set { - override val points: List by lazy { set.points.map { CachedPoint(it) } } -} diff --git a/numass-viewer/src/main/kotlin/inr/numass/viewer/MainView.kt b/numass-viewer/src/main/kotlin/inr/numass/viewer/MainView.kt index c08788d1..10f55fb4 100644 --- a/numass-viewer/src/main/kotlin/inr/numass/viewer/MainView.kt +++ b/numass-viewer/src/main/kotlin/inr/numass/viewer/MainView.kt @@ -1,7 +1,5 @@ package inr.numass.viewer -import hep.dataforge.context.Context -import hep.dataforge.context.Global import hep.dataforge.fx.dfIconView import hep.dataforge.fx.except import hep.dataforge.fx.runGoal @@ -19,7 +17,6 @@ import javafx.scene.layout.Priority import javafx.scene.text.Font import javafx.stage.DirectoryChooser import javafx.stage.FileChooser -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import org.controlsfx.control.StatusBar import tornadofx.* @@ -27,9 +24,11 @@ import java.io.File import java.nio.file.Files import java.nio.file.Path -class MainView(val context: Context = Global.getContext("viewer")) : View(title = "Numass viewer", icon = dfIconView) { +class MainView : View(title = "Numass viewer", icon = dfIconView) { - private val statusBar = StatusBar(); + private val pointCache by inject() + + private val statusBar = StatusBar() // private val logFragment = LogFragment().apply { // addLogHandler(context.logger) // } @@ -67,7 +66,7 @@ class MainView(val context: Context = Global.getContext("viewer")) : View(title if (rootDir != null) { NumassProperties.setNumassProperty("numass.viewer.lastPath", rootDir.absolutePath) - GlobalScope.launch { + app.context.launch { runLater { path = rootDir.toPath() } @@ -95,8 +94,9 @@ class MainView(val context: Context = Global.getContext("viewer")) : View(title val file = chooser.showOpenDialog(primaryStage.scene.window) if (file != null) { - NumassProperties.setNumassProperty("numass.viewer.lastPath", file.parentFile.absolutePath) - GlobalScope.launch { + NumassProperties.setNumassProperty("numass.viewer.lastPath", + file.parentFile.absolutePath) + app.context.launch { runLater { path = file.toPath() } @@ -110,9 +110,9 @@ class MainView(val context: Context = Global.getContext("viewer")) : View(title } } - label(pathProperty.stringBinding{it?.toString() ?: "NOT LOADED"}) { - padding = Insets(0.0, 0.0, 0.0, 10.0); - font = Font.font("System Bold", 13.0); + label(pathProperty.stringBinding { it?.toString() ?: "NOT LOADED" }) { + padding = Insets(0.0, 0.0, 0.0, 10.0) + font = Font.font("System Bold", 13.0) } pane { hgrow = Priority.ALWAYS @@ -136,23 +136,24 @@ class MainView(val context: Context = Global.getContext("viewer")) : View(title runLater { contentView = null } + pointCache.clear() if (Files.isDirectory(path)) { if (Files.exists(path.resolve(NumassDataLoader.META_FRAGMENT_NAME))) { //build set view runGoal("viewer.load.set[$path]") { title = "Load set ($path)" message = "Building numass set..." - NumassDataLoader(context, null, path.fileName.toString(), path) - } ui { + NumassDataLoader(app.context, null, path.fileName.toString(), path) + } ui { loader: NumassDataLoader -> contentView = SpectrumView().apply { clear() - set(it.name, CachedSet(it)) + set(loader.name, loader) } } except { alert( - type = Alert.AlertType.ERROR, - header = "Error during set loading", - content = it.toString() + type = Alert.AlertType.ERROR, + header = "Error during set loading", + content = it.toString() ).show() } } else { @@ -160,7 +161,7 @@ class MainView(val context: Context = Global.getContext("viewer")) : View(title runGoal("viewer.load.storage[$path]") { title = "Load storage ($path)" message = "Building numass storage tree..." - NumassDirectory.INSTANCE.read(context, path) + NumassDirectory.INSTANCE.read(app.context, path) } ui { contentView = StorageView(it as Storage) } @@ -172,9 +173,9 @@ class MainView(val context: Context = Global.getContext("viewer")) : View(title } catch (ex: Exception) { runLater { alert( - type = Alert.AlertType.ERROR, - header = "Can't load DF envelope from file $path", - content = ex.toString() + type = Alert.AlertType.ERROR, + header = "Can't load DF envelope from file $path", + content = ex.toString() ).show() } null @@ -186,13 +187,13 @@ class MainView(val context: Context = Global.getContext("viewer")) : View(title val point = NumassDataUtils.read(it) runLater { contentView = AmplitudeView().apply { - set(path.fileName.toString(), CachedPoint(point)) + set(path.fileName.toString(), point) } } } else { alert( - type = Alert.AlertType.ERROR, - header = "Unknown envelope content: $path" + type = Alert.AlertType.ERROR, + header = "Unknown envelope content: $path" ).show() } } diff --git a/numass-viewer/src/main/kotlin/inr/numass/viewer/PointCache.kt b/numass-viewer/src/main/kotlin/inr/numass/viewer/PointCache.kt new file mode 100644 index 00000000..22549d3b --- /dev/null +++ b/numass-viewer/src/main/kotlin/inr/numass/viewer/PointCache.kt @@ -0,0 +1,98 @@ +/* + * Copyright 2018 Alexander Nozik. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package inr.numass.viewer + +import hep.dataforge.tables.Table +import hep.dataforge.utils.Misc +import inr.numass.data.analyzers.SimpleAnalyzer +import inr.numass.data.api.NumassPoint +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.async +import tornadofx.* + + +private val analyzer = SimpleAnalyzer() + + +class PointCache : Controller() { + private val context = app.context + + inner class CachedPoint(point: NumassPoint) { + val length = point.length + + val voltage = point.voltage + + val meta = point.meta + + val channelSpectra: Deferred> = context.async(Dispatchers.IO) { + point.channels.mapValues { (_, value) -> analyzer.getAmplitudeSpectrum(value) } + } + + val spectrum: Deferred
= context.async(Dispatchers.IO) { + analyzer.getAmplitudeSpectrum(point) + } + } + + private val cache = Misc.getLRUCache(1000) + + fun getCachedPoint(id: String,point: NumassPoint): CachedPoint = cache.getOrPut(id) { CachedPoint(point) } + + fun getSpectrumAsync(id: String, point: NumassPoint): Deferred
= + getCachedPoint(id, point).spectrum + + suspend fun getChannelSpectra(id: String, point: NumassPoint): Map = + getCachedPoint(id, point).channelSpectra.await() + + fun clear(){ + cache.clear() + } +} + + +//class CachedSet(set: NumassSet, context: Context) { +// override val points: ObservableList 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() +// } +// } +// } +// } +//} diff --git a/numass-viewer/src/main/kotlin/inr/numass/viewer/PointInfoView.kt b/numass-viewer/src/main/kotlin/inr/numass/viewer/PointInfoView.kt index c410b73d..3ace6180 100644 --- a/numass-viewer/src/main/kotlin/inr/numass/viewer/PointInfoView.kt +++ b/numass-viewer/src/main/kotlin/inr/numass/viewer/PointInfoView.kt @@ -3,15 +3,13 @@ package inr.numass.viewer import hep.dataforge.fx.meta.MetaViewer import inr.numass.data.analyzers.NumassAnalyzer import javafx.beans.property.SimpleIntegerProperty -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import org.controlsfx.glyphfont.FontAwesome import tornadofx.* import tornadofx.controlsfx.borders import tornadofx.controlsfx.toGlyph -class PointInfoView(val point: CachedPoint) : MetaViewer(point.meta) { - +class PointInfoView(val cachedPoint: PointCache.CachedPoint) : MetaViewer(cachedPoint.meta) { val countProperty = SimpleIntegerProperty(0) var count by countProperty @@ -25,8 +23,10 @@ class PointInfoView(val point: CachedPoint) : MetaViewer(point.meta) { row { button(graphic = FontAwesome.Glyph.REFRESH.toGlyph()) { action { - GlobalScope.launch { - val res = point.spectrum.await().sumOf { it.getValue(NumassAnalyzer.COUNT_KEY).int } + app.context.launch { + val res = cachedPoint.spectrum.await().sumOf { + it.getValue(NumassAnalyzer.COUNT_KEY).int + } runLater { count = res } } } @@ -44,7 +44,10 @@ class PointInfoView(val point: CachedPoint) : MetaViewer(point.meta) { hbox { label("Total count rate: ") label { - textProperty().bind(countProperty.stringBinding { String.format("%.2f", it!!.toDouble() / point.length.toMillis() * 1000) }) + textProperty().bind(countProperty.stringBinding { + String.format("%.2f", + it!!.toDouble() / cachedPoint.length.toMillis() * 1000) + }) } } } diff --git a/numass-viewer/src/main/kotlin/inr/numass/viewer/SpectrumView.kt b/numass-viewer/src/main/kotlin/inr/numass/viewer/SpectrumView.kt index d37cbf3a..95f18992 100644 --- a/numass-viewer/src/main/kotlin/inr/numass/viewer/SpectrumView.kt +++ b/numass-viewer/src/main/kotlin/inr/numass/viewer/SpectrumView.kt @@ -9,6 +9,7 @@ import hep.dataforge.names.Name import hep.dataforge.plots.data.DataPlot import hep.dataforge.plots.jfreechart.JFreeChartFrame import hep.dataforge.tables.Adapters +import hep.dataforge.values.Values import inr.numass.data.analyzers.countInWindow import inr.numass.data.api.NumassSet import javafx.beans.property.SimpleIntegerProperty @@ -22,6 +23,7 @@ import javafx.util.converter.NumberStringConverter import org.controlsfx.control.RangeSlider import tornadofx.* import java.util.concurrent.atomic.AtomicInteger +import kotlin.math.sqrt /** * View for energy spectrum @@ -30,6 +32,8 @@ import java.util.concurrent.atomic.AtomicInteger */ class SpectrumView : View(title = "Numass spectrum plot", icon = ImageView(dfIcon)) { + private val pointCache by inject() + private val frame = JFreeChartFrame().configure { "xAxis.title" to "U" "xAxis.units" to "V" @@ -37,7 +41,7 @@ class SpectrumView : View(title = "Numass spectrum plot", icon = ImageView(dfIco "yAxis.units" to "Hz" //"legend.show" to false } - private val container = PlotContainer(frame); + private val container = PlotContainer(frame) private val loChannelProperty = SimpleIntegerProperty(500).apply { @@ -51,7 +55,7 @@ class SpectrumView : View(title = "Numass spectrum plot", icon = ImageView(dfIco private var upChannel by upChannelProperty - private val data: ObservableMap = FXCollections.observableHashMap(); + private val data: ObservableMap = FXCollections.observableHashMap() val isEmpty = booleanBinding(data) { data.isEmpty() } override val root = borderpane { @@ -86,7 +90,7 @@ class SpectrumView : View(title = "Numass spectrum plot", icon = ImageView(dfIco vbox { label("Up channel") textfield { - isEditable = true; + isEditable = true prefWidth = 60.0 textProperty().bindBidirectional(upChannelProperty, NumberStringConverter()) } @@ -100,7 +104,7 @@ class SpectrumView : View(title = "Numass spectrum plot", icon = ImageView(dfIco init { data.addListener { change: MapChangeListener.Change -> if (change.wasRemoved()) { - frame.plots.remove(Name.ofSingle(change.key)); + frame.plots.remove(Name.ofSingle(change.key)) } if (change.wasAdded()) { @@ -115,24 +119,26 @@ class SpectrumView : View(title = "Numass spectrum plot", icon = ImageView(dfIco val progress = AtomicInteger(0) val totalProgress = data.values.stream().mapToInt { it.points.size }.sum() - data.forEach { name, set -> - val plot: DataPlot = frame.plots[Name.ofSingle(name)] as DataPlot? ?: DataPlot(name).apply { frame.add(this) } + data.forEach { (name, set) -> + val plot: DataPlot = + frame.plots[Name.ofSingle(name)] as DataPlot? ?: DataPlot(name).apply { frame.add(this) } runGoal("spectrumData[$name]") { - set.points.forEach { it.spectrum.start() } - set.points.map { point -> - val count = point.spectrum.await().countInWindow(loChannel.toShort(), upChannel.toShort()); - val seconds = point.length.toMillis() / 1000.0; + set.points.map { + pointCache.getCachedPoint("$name/${it.voltage}[${it.index}]", it) + }.map { cachedPoint -> + val count = cachedPoint.spectrum.await().countInWindow(loChannel.toShort(), upChannel.toShort()) + val seconds = cachedPoint.length.toMillis() / 1000.0 runLater { container.progress = progress.incrementAndGet().toDouble() / totalProgress } Adapters.buildXYDataPoint( - point.voltage, - (count / seconds), - Math.sqrt(count.toDouble()) / seconds + cachedPoint.voltage, + (count / seconds), + sqrt(count.toDouble()) / seconds ) } - } ui { points -> + } ui { points: List -> plot.fillData(points) container.progress = 1.0 //spectrumExportButton.isDisable = false @@ -140,7 +146,7 @@ class SpectrumView : View(title = "Numass spectrum plot", icon = ImageView(dfIco } } - operator fun set(key: String, value: CachedSet) { + operator fun set(key: String, value: NumassSet) { data[key] = value } diff --git a/numass-viewer/src/main/kotlin/inr/numass/viewer/StorageView.kt b/numass-viewer/src/main/kotlin/inr/numass/viewer/StorageView.kt index bbc3817e..95c67608 100644 --- a/numass-viewer/src/main/kotlin/inr/numass/viewer/StorageView.kt +++ b/numass-viewer/src/main/kotlin/inr/numass/viewer/StorageView.kt @@ -12,13 +12,16 @@ import inr.numass.data.api.NumassPoint import inr.numass.data.api.NumassSet import inr.numass.data.storage.NumassDataLoader import javafx.beans.property.SimpleBooleanProperty +import javafx.collections.ObservableList import javafx.scene.control.ContextMenu import javafx.scene.control.TreeItem -import kotlinx.coroutines.runBlocking import tornadofx.* + class StorageView(val storage: Storage) : View(title = "Numass storage", icon = dfIconView) { + private val pointCache by inject() + private val ampView: AmplitudeView by inject() private val timeView: TimeView by inject() private val spectrumView: SpectrumView by inject() @@ -39,7 +42,7 @@ class StorageView(val storage: Storage) : View(title = "Numass storage", icon = val infoView: UIComponent by lazy { when (content) { - is CachedPoint -> PointInfoView(content) + is NumassPoint -> PointInfoView(pointCache.getCachedPoint(id, content)) is Metoid -> MetaViewer(content.meta, title = "Meta view: $id") else -> MetaViewer(Meta.empty(), title = "Meta view: $id") } @@ -48,7 +51,7 @@ class StorageView(val storage: Storage) : View(title = "Numass storage", icon = init { checkedProperty.onChange { selected -> when (content) { - is CachedPoint -> { + is NumassPoint -> { if (selected) { ampView[id] = content timeView[id] = content @@ -57,7 +60,7 @@ class StorageView(val storage: Storage) : View(title = "Numass storage", icon = timeView.remove(id) } } - is CachedSet -> { + is NumassSet -> { if (selected) { spectrumView[id] = content hvView[id] = content @@ -77,22 +80,18 @@ class StorageView(val storage: Storage) : View(title = "Numass storage", icon = } } - val children: List? by lazy { - when (content) { - is Storage -> runBlocking { content.children }.map { buildContainer(it, this) }.sortedWith( - object : Comparator { - private val alphanumComparator = AlphanumComparator() - override fun compare(o1: Container, o2: Container): Int = alphanumComparator.compare(o1.id, o2.id) - } - ) - is NumassSet -> content.points - .sortedBy { it.index } - .map { buildContainer(it, this) } - .toList() - else -> null - } + fun getChildren(): ObservableList? = when (content) { + is Storage -> content.children.map { + buildContainer(it, this) + }.sortedWith(Comparator.comparing({ it.id }, AlphanumComparator)).asObservable() + is NumassSet -> content.points + .sortedBy { it.index } + .map { buildContainer(it, this) } + .asObservable() + else -> null } + val hasChildren: Boolean = (content is Storage) || (content is NumassSet) } @@ -102,10 +101,12 @@ class StorageView(val storage: Storage) : View(title = "Numass storage", icon = //isShowRoot = false root = TreeItem(Container(storage.name, storage)) root.isExpanded = true - lazyPopulate(leafCheck = { !it.value.hasChildren }) { - it.value.children + lazyPopulate(leafCheck = { + !it.value.hasChildren + }) { + it.value.getChildren() } - cellFormat { value -> + cellFormat { value: Container -> when (value.content) { is Storage -> { text = value.content.name @@ -187,25 +188,20 @@ class StorageView(val storage: Storage) : View(title = "Numass storage", icon = private fun buildContainer(content: Any, parent: Container): Container = - when (content) { - is Storage -> { - Container(content.fullName.toString(), content) + when (content) { + is Storage -> Container(content.fullName.toString(), content) + is NumassSet -> { + val id: String = if (content is NumassDataLoader) { + content.fullName.unescaped + } else { + content.name } - is NumassSet -> { - val id: String = if (content is NumassDataLoader) { - content.fullName.unescaped - } else { - content.name - } - Container(id, content as? CachedSet ?: CachedSet(content)) - } - is NumassPoint -> { - Container("${parent.id}/${content.voltage}[${content.index}]", content as? CachedPoint - ?: CachedPoint(content)) - } - is FileTableLoader -> { - Container(content.path.toString(), content); - } - else -> throw IllegalArgumentException("Unknown content type: ${content::class.java}"); + Container(id, content) } + is NumassPoint -> { + Container("${parent.id}/${content.voltage}[${content.index}]", content) + } + is FileTableLoader -> Container(content.path.toString(), content) + else -> throw IllegalArgumentException("Unknown content type: ${content::class.java}"); + } } diff --git a/numass-viewer/src/main/kotlin/inr/numass/viewer/TimeView.kt b/numass-viewer/src/main/kotlin/inr/numass/viewer/TimeView.kt index 10882b68..81d7c4c4 100644 --- a/numass-viewer/src/main/kotlin/inr/numass/viewer/TimeView.kt +++ b/numass-viewer/src/main/kotlin/inr/numass/viewer/TimeView.kt @@ -15,6 +15,7 @@ import hep.dataforge.plots.jfreechart.JFreeChartFrame import hep.dataforge.tables.Adapters import hep.dataforge.values.ValueMap import inr.numass.data.analyzers.TimeAnalyzer +import inr.numass.data.api.NumassPoint import javafx.beans.Observable import javafx.beans.binding.DoubleBinding import javafx.collections.FXCollections @@ -59,7 +60,7 @@ class TimeView : View(title = "Numass time spectrum plot", icon = ImageView(dfIc private val container = PlotContainer(frame) - private val data: ObservableMap = FXCollections.observableHashMap() + private val data: ObservableMap = FXCollections.observableHashMap() private val plots: ObservableMap> = FXCollections.observableHashMap() val isEmpty = booleanBinding(data) { isEmpty() } @@ -88,11 +89,11 @@ class TimeView : View(title = "Numass time spectrum plot", icon = ImageView(dfIc /** * Put or replace current plot with name `key` */ - operator fun set(key: String, point: CachedPoint) { + operator fun set(key: String, point: NumassPoint) { data[key] = point } - fun addAll(data: Map) { + fun addAll(data: Map) { this.data.putAll(data); } @@ -160,7 +161,7 @@ class TimeView : View(title = "Numass time spectrum plot", icon = ImageView(dfIc /** * Set frame content to the given map. All keys not in the map are removed. */ - fun setAll(map: Map) { + fun setAll(map: Map) { plots.clear(); //Remove obsolete keys data.keys.filter { !map.containsKey(it) }.forEach { diff --git a/numass-viewer/src/main/kotlin/inr/numass/viewer/Viewer.kt b/numass-viewer/src/main/kotlin/inr/numass/viewer/Viewer.kt index a2922ddf..ca1ad6bd 100644 --- a/numass-viewer/src/main/kotlin/inr/numass/viewer/Viewer.kt +++ b/numass-viewer/src/main/kotlin/inr/numass/viewer/Viewer.kt @@ -2,6 +2,7 @@ package inr.numass.viewer import ch.qos.logback.classic.Level import ch.qos.logback.classic.Logger +import hep.dataforge.context.Context import hep.dataforge.context.Global import hep.dataforge.fx.dfIcon import javafx.stage.Stage @@ -16,13 +17,18 @@ class Viewer : App(MainView::class) { (LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME) as Logger).level = Level.INFO } + val context: Context = Global.getContext("numass-viewer") + override fun start(stage: Stage) { - stage.icons += dfIcon super.start(stage) + stage.icons += dfIcon } override fun stop() { - super.stop() + context.close() Global.terminate(); + super.stop() } -} \ No newline at end of file +} + +internal val App.context get() = (this as Viewer).context \ No newline at end of file diff --git a/numass-viewer/src/main/kotlin/inr/numass/viewer/test/ComponentTest.kt b/numass-viewer/src/main/kotlin/inr/numass/viewer/test/ComponentTest.kt index 65a5028d..4b5397bb 100644 --- a/numass-viewer/src/main/kotlin/inr/numass/viewer/test/ComponentTest.kt +++ b/numass-viewer/src/main/kotlin/inr/numass/viewer/test/ComponentTest.kt @@ -7,10 +7,11 @@ import hep.dataforge.tables.Table import inr.numass.data.api.NumassPoint import inr.numass.data.api.NumassSet import inr.numass.data.storage.NumassDirectory -import inr.numass.viewer.* +import inr.numass.viewer.AmplitudeView +import inr.numass.viewer.HVView +import inr.numass.viewer.SpectrumView import javafx.application.Application import javafx.scene.image.ImageView -import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch import tornadofx.* import java.io.File @@ -25,7 +26,8 @@ class ViewerComponentsTest : View(title = "Numass viewer test", icon = ImageView //val set: NumassSet = NumassStorageFactory.buildLocal(rootDir).provide("loader::set_8", NumassSet::class.java).orElseThrow { RuntimeException("err") } - private val cache: MutableMap = ConcurrentHashMap(); + private val cache: MutableMap = ConcurrentHashMap() + val context = Global val amp: AmplitudeView by inject(params = mapOf("cache" to cache))//= AmplitudeView(immutable = immutable) val sp: SpectrumView by inject(params = mapOf("cache" to cache)) @@ -35,11 +37,11 @@ class ViewerComponentsTest : View(title = "Numass viewer test", icon = ImageView top { button("Click me!") { action { - GlobalScope.launch { + context.launch { val set: NumassSet = NumassDirectory.INSTANCE.read(Global, File("D:\\Work\\Numass\\data\\2017_05\\Fill_2").toPath()) ?.provide("loader::set_2", NumassSet::class.java).nullable ?: kotlin.error("Error") - update(set); + update(set) } } } @@ -54,13 +56,13 @@ class ViewerComponentsTest : View(title = "Numass viewer test", icon = ImageView } fun update(set: NumassSet) { - amp.setAll(set.points.filter { it.voltage != 16000.0 }.associateBy({ "point_${it.voltage}" }) { CachedPoint(it) }); - sp.set("test", CachedSet(set)); - hv.set(set.name, set) + amp.setAll(set.points.filter { it.voltage != 16000.0 }.associateBy { "point_${it.voltage}" }) + sp["test"] = set + hv[set.name] = set } } fun main(args: Array) { - Application.launch(ViewerComponentsTestApp::class.java, *args); + Application.launch(ViewerComponentsTestApp::class.java, *args) } \ No newline at end of file