Fix viewer cache

This commit is contained in:
Alexander Nozik 2021-11-14 16:46:32 +03:00
parent d3689638e0
commit 15d17f2cc4
28 changed files with 456 additions and 393 deletions

View File

@ -1,6 +1,6 @@
plugins { plugins {
kotlin("jvm") version "1.5.31" 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 id("com.github.johnrengelman.shadow") version "7.1.0" apply false
} }
@ -12,7 +12,7 @@ allprojects {
repositories { repositories {
mavenCentral() mavenCentral()
jcenter() maven("https://oss.sonatype.org/content/repositories/snapshots")
} }
dependencies { dependencies {

View File

@ -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<String> {
public static final AlphanumComparator INSTANCE = new AlphanumComparator();
private Comparator<String> comparator = new NaturalComparator();
public AlphanumComparator(Comparator<String> 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<String> {
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
}
}

View File

@ -6,6 +6,7 @@
package hep.dataforge.utils; package hep.dataforge.utils;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
@ -15,7 +16,7 @@ import java.util.concurrent.CancellationException;
* @author Alexander Nozik * @author Alexander Nozik
*/ */
public class Misc { public class Misc {
public static final Charset UTF = Charset.forName("UTF-8"); public static final Charset UTF = StandardCharsets.UTF_8;
/** /**
* A synchronized lru cache * A synchronized lru cache

View File

@ -36,6 +36,7 @@ import hep.dataforge.values.Value
import hep.dataforge.values.ValueProvider import hep.dataforge.values.ValueProvider
import hep.dataforge.values.ValueProvider.Companion.VALUE_TARGET import hep.dataforge.values.ValueProvider.Companion.VALUE_TARGET
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.cancel
import org.slf4j.Logger import org.slf4j.Logger
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import java.io.File import java.io.File
@ -47,7 +48,6 @@ import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ExecutorService import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors import java.util.concurrent.Executors
import java.util.stream.Stream import java.util.stream.Stream
import kotlin.collections.HashMap
import kotlin.collections.set import kotlin.collections.set
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlin.streams.asSequence import kotlin.streams.asSequence
@ -308,10 +308,12 @@ open class Context(
*/ */
@Throws(Exception::class) @Throws(Exception::class)
override fun close() { override fun close() {
logger.info("Closing context: $name")
//detach all plugins //detach all plugins
plugins.close() plugins.close()
if (started) { if (started) {
coroutineContext.cancel()
dispatcher.shutdown() dispatcher.shutdown()
} }
} }

View File

@ -133,15 +133,13 @@ object Global : Context("GLOBAL", null, Thread.currentThread().contextClassLoade
* @return * @return
*/ */
@Synchronized @Synchronized
fun getContext(name: String): Context { fun getContext(name: String): Context = contextRegistry
return contextRegistry .findFirst { ctx -> ctx.name == name }
.findFirst { ctx -> ctx.name == name } .orElseGet {
.orElseGet { val ctx = Context(name)
val ctx = Context(name) contextRegistry.add(ctx)
contextRegistry.add(ctx) ctx
ctx }
}
}
/** /**
* Close all contexts and terminate framework * Close all contexts and terminate framework

View File

@ -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<String> {
private val comparator: Comparator<String> = 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<String> {
override fun compare(o1: String, o2: String): Int {
return o1.compareTo(o2)
}
}
}

View File

@ -126,7 +126,7 @@ interface Value : Serializable, Comparable<Value> {
return when (type) { return when (type) {
ValueType.NUMBER -> ValueUtils.NUMBER_COMPARATOR.compare(number, other.number) ValueType.NUMBER -> ValueUtils.NUMBER_COMPARATOR.compare(number, other.number)
ValueType.BOOLEAN -> boolean.compareTo(other.boolean) 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.TIME -> time.compareTo(other.time)
ValueType.NULL -> if (other.type == ValueType.NULL) 0 else -1 ValueType.NULL -> if (other.type == ValueType.NULL) 0 else -1
ValueType.BINARY -> binary.compareTo(other.binary) ValueType.BINARY -> binary.compareTo(other.binary)

View File

@ -70,6 +70,7 @@ class WorkspaceTest {
@BeforeClass @BeforeClass
@JvmStatic @JvmStatic
fun setup() { fun setup() {
counter.set(0)
val context = Global.getContext("TEST").apply { val context = Global.getContext("TEST").apply {
load(CachePlugin::class.java, MetaBuilder().setValue("fileCache.enabled", false)) load(CachePlugin::class.java, MetaBuilder().setValue("fileCache.enabled", false))
} }

View File

@ -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")
}

View File

@ -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")
}

View File

@ -2,5 +2,6 @@ description = "An html rendering core and HTML output"
dependencies { dependencies {
api project(':dataforge-core') 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'
} }

View File

@ -1,13 +0,0 @@
//allprojects {
// apply plugin: 'org.openjfx.javafxplugin'
//
// javafx {
// modules = ['javafx.controls']
// }
//}
description = 'dataforge-plots'
dependencies {
api project(':dataforge-core')
}

View File

@ -0,0 +1,14 @@
plugins {
id("org.openjfx.javafxplugin")
}
javafx {
modules = listOf("javafx.controls")
version = "11"
}
description = "dataforge-plots"
dependencies {
api(project(":dataforge-core"))
}

View File

@ -33,11 +33,11 @@ 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.joinAll import kotlinx.coroutines.joinAll
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import java.nio.file.* import java.nio.file.*
import kotlin.streams.asSequence import kotlin.streams.asSequence
import kotlin.streams.toList
/** /**
* An element of file storage with fixed path * An element of file storage with fixed path
@ -62,12 +62,12 @@ interface FileStorageElementType : StorageElementType, Named {
} }
class FileStorage( class FileStorage(
override val context: Context, override val context: Context,
override val name: String, override val name: String,
override val meta: Meta, override val meta: Meta,
override val path: Path, override val path: Path,
override val parent: StorageElement? = null, override val parent: StorageElement? = null,
val type: FileStorageElementType val type: FileStorageElementType,
) : MutableStorage, FileStorageElement { ) : MutableStorage, FileStorageElement {
private val _connectionHelper by lazy { ConnectionHelper(this) } private val _connectionHelper by lazy { ConnectionHelper(this) }
@ -79,15 +79,14 @@ class FileStorage(
override val children: Collection<StorageElement> override val children: Collection<StorageElement>
get() = synchronized(this) { get() = runBlocking(Dispatchers.IO) {
runBlocking { if (!isInitialized) {
if (!isInitialized) { refresh()
refresh()
}
} }
_children.values _children.values
} }
override fun resolveType(meta: Meta): StorageElementType? { override fun resolveType(meta: Meta): StorageElementType? {
val type = meta.optString(StorageManager.STORAGE_META_TYPE_KEY).nullable val type = meta.optString(StorageManager.STORAGE_META_TYPE_KEY).nullable
return if (type == null) { return if (type == null) {
@ -104,14 +103,14 @@ class FileStorage(
(parent as? FileStorage)?.watchService ?: path.fileSystem.newWatchService() (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 { override fun create(meta: Meta): StorageElement {
val path = path.resolve(meta.getString("path", meta.getString("name"))) val path = path.resolve(meta.getString("path", meta.getString("name")))
return _children.getOrPut(path) { return _children.getOrPut(path) {
resolveType(meta) resolveType(meta)
?.create(this, meta) ?.create(this, meta)
?: error("Can't resolve storage element type.") ?: error("Can't resolve storage element type.")
} }
} }
@ -127,7 +126,7 @@ class FileStorage(
launch { launch {
if (!_children.contains(path)) { if (!_children.contains(path)) {
type.read(context, path, this@FileStorage)?.let { _children[path] = it } 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() }.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 * 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)) { return if (Files.isDirectory(path)) {
Files.list(path).asSequence() Files.list(path).asSequence()
.find { it.fileName.toString() == "meta.df" || it.fileName.toString() == "meta" } .find { it.fileName.toString() == "meta.df" || it.fileName.toString() == "meta" }
?.let(metaReader) ?.let(metaReader)
} else { } else {
metaReader(path) metaReader(path)
} }
@ -166,8 +168,10 @@ class FileStorage(
override val name: String = "hep.dataforge.storage.directory" override val name: String = "hep.dataforge.storage.directory"
@ValueDefs( @ValueDefs(
ValueDef(key = "path", info = "The relative path to the shelf inside parent storage or absolute path"), 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 = "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 { override fun create(context: Context, meta: Meta, parent: StorageElement?): FileStorageElement {
val shelfName = meta.getString("name") val shelfName = meta.getString("name")
@ -192,7 +196,9 @@ class FileStorage(
override suspend fun read(context: Context, path: Path, parent: StorageElement?): FileStorageElement? { override suspend fun read(context: Context, path: Path, parent: StorageElement?): FileStorageElement? {
val meta = resolveMeta(path) val meta = resolveMeta(path)
val name = meta?.optString("name").nullable ?: path.fileName.toString() val name = meta?.optString("name").nullable ?: path.fileName.toString()
val type = meta?.optString("type").nullable?.let { context.load<StorageManager>().getType(it) } as? FileStorageElementType val type = meta?.optString("type").nullable?.let {
context.load<StorageManager>().getType(it)
} as? FileStorageElementType
return if (type == null || type is Directory) { return if (type == null || type is Directory) {
// Read path as directory if type not found and path is directory // Read path as directory if type not found and path is directory
if (Files.isDirectory(path)) { if (Files.isDirectory(path)) {

View File

@ -1,11 +1,7 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins { plugins {
idea
kotlin("jvm") kotlin("jvm")
} }
repositories { repositories {
mavenCentral() mavenCentral()
} }
@ -13,7 +9,3 @@ repositories {
dependencies { dependencies {
api(project(":dataforge-core")) api(project(":dataforge-core"))
} }
tasks.withType<KotlinCompile> {
kotlinOptions.jvmTarget = "1.8"
}

View File

@ -28,7 +28,6 @@ import java.util.stream.Stream
*/ */
interface NumassPoint : Metoid, ParentBlock, Provider { interface NumassPoint : Metoid, ParentBlock, Provider {
override val blocks: List<NumassBlock> override val blocks: List<NumassBlock>
/** /**

View File

@ -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')
}

View File

@ -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",
)
}

View File

@ -15,6 +15,7 @@ import hep.dataforge.plots.jfreechart.JFreeChartFrame
import hep.dataforge.tables.Adapters import hep.dataforge.tables.Adapters
import inr.numass.data.analyzers.NumassAnalyzer import inr.numass.data.analyzers.NumassAnalyzer
import inr.numass.data.analyzers.withBinning import inr.numass.data.analyzers.withBinning
import inr.numass.data.api.NumassPoint
import javafx.beans.Observable import javafx.beans.Observable
import javafx.beans.binding.DoubleBinding import javafx.beans.binding.DoubleBinding
import javafx.beans.property.SimpleBooleanProperty import javafx.beans.property.SimpleBooleanProperty
@ -28,6 +29,8 @@ import tornadofx.*
class AmplitudeView : View(title = "Numass amplitude spectrum plot", icon = ImageView(dfIcon)) { class AmplitudeView : View(title = "Numass amplitude spectrum plot", icon = ImageView(dfIcon)) {
private val pointCache by inject<PointCache>()
private val frame = JFreeChartFrame().configure { private val frame = JFreeChartFrame().configure {
"title" to "Detector response plot" "title" to "Detector response plot"
node("xAxis") { node("xAxis") {
@ -70,7 +73,7 @@ class AmplitudeView : View(title = "Numass amplitude spectrum plot", icon = Imag
addToSideBar(0, binningSelector, normalizeSwitch) addToSideBar(0, binningSelector, normalizeSwitch)
} }
private val data: ObservableMap<String, CachedPoint> = FXCollections.observableHashMap() private val data: ObservableMap<String, NumassPoint> = FXCollections.observableHashMap()
private val plots: ObservableMap<String, Goal<Plottable>> = FXCollections.observableHashMap() private val plots: ObservableMap<String, Goal<Plottable>> = FXCollections.observableHashMap()
val isEmpty = booleanBinding(data) { isEmpty() } 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` * 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 data[key] = point
} }
fun addAll(data: Map<String, CachedPoint>) { fun addAll(data: Map<String, NumassPoint>) {
this.data.putAll(data); this.data.putAll(data);
} }
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>("loadAmplitudeSpectrum_$key") {
val valueAxis = if (normalize) { 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 adapter = Adapters.buildXYAdapter(NumassAnalyzer.CHANNEL_KEY, valueAxis)
val channels = point.channelSpectra.await() val channels = pointCache.getChannelSpectra(key, point)
return@runGoal if (channels.size == 1) { return@runGoal if (channels.size == 1) {
DataPlot.plot( 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. * Set frame content to the given map. All keys not in the map are removed.
*/ */
fun setAll(map: Map<String, CachedPoint>) { fun setAll(map: Map<String, NumassPoint>) {
plots.clear(); plots.clear();
//Remove obsolete keys //Remove obsolete keys
data.keys.filter { !map.containsKey(it) }.forEach { data.keys.filter { !map.containsKey(it) }.forEach {

View File

@ -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<NumassBlock> by lazy { point.blocks }
override val meta: Meta = point.meta
val channelSpectra: Deferred<Map<Int, Table>> = GlobalScope.async(start = CoroutineStart.LAZY) {
point.channels.mapValues { (_, value) -> analyzer.getAmplitudeSpectrum(value) }
}
val spectrum: Deferred<Table> = GlobalScope.async(start = CoroutineStart.LAZY) { analyzer.getAmplitudeSpectrum(point) }
}
class CachedSet(set: NumassSet) : NumassSet by set {
override val points: List<CachedPoint> by lazy { set.points.map { CachedPoint(it) } }
}

View File

@ -1,7 +1,5 @@
package inr.numass.viewer package inr.numass.viewer
import hep.dataforge.context.Context
import hep.dataforge.context.Global
import hep.dataforge.fx.dfIconView import hep.dataforge.fx.dfIconView
import hep.dataforge.fx.except import hep.dataforge.fx.except
import hep.dataforge.fx.runGoal import hep.dataforge.fx.runGoal
@ -19,7 +17,6 @@ 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.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.controlsfx.control.StatusBar import org.controlsfx.control.StatusBar
import tornadofx.* import tornadofx.*
@ -27,9 +24,11 @@ import java.io.File
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path 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<PointCache>()
private val statusBar = StatusBar()
// private val logFragment = LogFragment().apply { // private val logFragment = LogFragment().apply {
// addLogHandler(context.logger) // addLogHandler(context.logger)
// } // }
@ -67,7 +66,7 @@ class MainView(val context: Context = Global.getContext("viewer")) : View(title
if (rootDir != null) { if (rootDir != null) {
NumassProperties.setNumassProperty("numass.viewer.lastPath", rootDir.absolutePath) NumassProperties.setNumassProperty("numass.viewer.lastPath", rootDir.absolutePath)
GlobalScope.launch { app.context.launch {
runLater { runLater {
path = rootDir.toPath() path = rootDir.toPath()
} }
@ -95,8 +94,9 @@ class MainView(val context: Context = Global.getContext("viewer")) : View(title
val file = chooser.showOpenDialog(primaryStage.scene.window) val file = chooser.showOpenDialog(primaryStage.scene.window)
if (file != null) { if (file != null) {
NumassProperties.setNumassProperty("numass.viewer.lastPath", file.parentFile.absolutePath) NumassProperties.setNumassProperty("numass.viewer.lastPath",
GlobalScope.launch { file.parentFile.absolutePath)
app.context.launch {
runLater { runLater {
path = file.toPath() path = file.toPath()
} }
@ -110,9 +110,9 @@ class MainView(val context: Context = Global.getContext("viewer")) : View(title
} }
} }
label(pathProperty.stringBinding{it?.toString() ?: "NOT LOADED"}) { label(pathProperty.stringBinding { it?.toString() ?: "NOT LOADED" }) {
padding = Insets(0.0, 0.0, 0.0, 10.0); padding = Insets(0.0, 0.0, 0.0, 10.0)
font = Font.font("System Bold", 13.0); font = Font.font("System Bold", 13.0)
} }
pane { pane {
hgrow = Priority.ALWAYS hgrow = Priority.ALWAYS
@ -136,23 +136,24 @@ class MainView(val context: Context = Global.getContext("viewer")) : View(title
runLater { runLater {
contentView = null contentView = null
} }
pointCache.clear()
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("viewer.load.set[$path]") {
title = "Load set ($path)" title = "Load set ($path)"
message = "Building numass set..." message = "Building numass set..."
NumassDataLoader(context, null, path.fileName.toString(), path) NumassDataLoader(app.context, null, path.fileName.toString(), path)
} ui { } ui { loader: NumassDataLoader ->
contentView = SpectrumView().apply { contentView = SpectrumView().apply {
clear() clear()
set(it.name, CachedSet(it)) set(loader.name, loader)
} }
} except { } except {
alert( alert(
type = Alert.AlertType.ERROR, type = Alert.AlertType.ERROR,
header = "Error during set loading", header = "Error during set loading",
content = it.toString() content = it.toString()
).show() ).show()
} }
} else { } else {
@ -160,7 +161,7 @@ class MainView(val context: Context = Global.getContext("viewer")) : View(title
runGoal("viewer.load.storage[$path]") { runGoal("viewer.load.storage[$path]") {
title = "Load storage ($path)" title = "Load storage ($path)"
message = "Building numass storage tree..." message = "Building numass storage tree..."
NumassDirectory.INSTANCE.read(context, path) NumassDirectory.INSTANCE.read(app.context, path)
} ui { } ui {
contentView = StorageView(it as Storage) contentView = StorageView(it as Storage)
} }
@ -172,9 +173,9 @@ class MainView(val context: Context = Global.getContext("viewer")) : View(title
} catch (ex: Exception) { } catch (ex: Exception) {
runLater { runLater {
alert( alert(
type = Alert.AlertType.ERROR, type = Alert.AlertType.ERROR,
header = "Can't load DF envelope from file $path", header = "Can't load DF envelope from file $path",
content = ex.toString() content = ex.toString()
).show() ).show()
} }
null null
@ -186,13 +187,13 @@ class MainView(val context: Context = Global.getContext("viewer")) : View(title
val point = NumassDataUtils.read(it) val point = NumassDataUtils.read(it)
runLater { runLater {
contentView = AmplitudeView().apply { contentView = AmplitudeView().apply {
set(path.fileName.toString(), CachedPoint(point)) set(path.fileName.toString(), point)
} }
} }
} else { } else {
alert( alert(
type = Alert.AlertType.ERROR, type = Alert.AlertType.ERROR,
header = "Unknown envelope content: $path" header = "Unknown envelope content: $path"
).show() ).show()
} }
} }

View File

@ -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<Map<Int, Table>> = context.async(Dispatchers.IO) {
point.channels.mapValues { (_, value) -> analyzer.getAmplitudeSpectrum(value) }
}
val spectrum: Deferred<Table> = context.async(Dispatchers.IO) {
analyzer.getAmplitudeSpectrum(point)
}
}
private val cache = Misc.getLRUCache<String, CachedPoint>(1000)
fun getCachedPoint(id: String,point: NumassPoint): CachedPoint = cache.getOrPut(id) { CachedPoint(point) }
fun getSpectrumAsync(id: String, point: NumassPoint): Deferred<Table> =
getCachedPoint(id, point).spectrum
suspend fun getChannelSpectra(id: String, point: NumassPoint): Map<Int, Table> =
getCachedPoint(id, point).channelSpectra.await()
fun 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

@ -3,15 +3,13 @@ package inr.numass.viewer
import hep.dataforge.fx.meta.MetaViewer import hep.dataforge.fx.meta.MetaViewer
import inr.numass.data.analyzers.NumassAnalyzer import inr.numass.data.analyzers.NumassAnalyzer
import javafx.beans.property.SimpleIntegerProperty import javafx.beans.property.SimpleIntegerProperty
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.controlsfx.glyphfont.FontAwesome import org.controlsfx.glyphfont.FontAwesome
import tornadofx.* import tornadofx.*
import tornadofx.controlsfx.borders import tornadofx.controlsfx.borders
import tornadofx.controlsfx.toGlyph 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) val countProperty = SimpleIntegerProperty(0)
var count by countProperty var count by countProperty
@ -25,8 +23,10 @@ class PointInfoView(val point: CachedPoint) : MetaViewer(point.meta) {
row { row {
button(graphic = FontAwesome.Glyph.REFRESH.toGlyph()) { button(graphic = FontAwesome.Glyph.REFRESH.toGlyph()) {
action { action {
GlobalScope.launch { app.context.launch {
val res = point.spectrum.await().sumOf { it.getValue(NumassAnalyzer.COUNT_KEY).int } val res = cachedPoint.spectrum.await().sumOf {
it.getValue(NumassAnalyzer.COUNT_KEY).int
}
runLater { count = res } runLater { count = res }
} }
} }
@ -44,7 +44,10 @@ class PointInfoView(val point: CachedPoint) : MetaViewer(point.meta) {
hbox { hbox {
label("Total count rate: ") label("Total count rate: ")
label { 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)
})
} }
} }
} }

View File

@ -9,6 +9,7 @@ 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
@ -22,6 +23,7 @@ import javafx.util.converter.NumberStringConverter
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
import kotlin.math.sqrt
/** /**
* View for energy spectrum * View for energy spectrum
@ -30,6 +32,8 @@ import java.util.concurrent.atomic.AtomicInteger
*/ */
class SpectrumView : View(title = "Numass spectrum plot", icon = ImageView(dfIcon)) { class SpectrumView : View(title = "Numass spectrum plot", icon = ImageView(dfIcon)) {
private val pointCache by inject<PointCache>()
private val frame = JFreeChartFrame().configure { private val frame = JFreeChartFrame().configure {
"xAxis.title" to "U" "xAxis.title" to "U"
"xAxis.units" to "V" "xAxis.units" to "V"
@ -37,7 +41,7 @@ class SpectrumView : View(title = "Numass spectrum plot", icon = ImageView(dfIco
"yAxis.units" to "Hz" "yAxis.units" to "Hz"
//"legend.show" to false //"legend.show" to false
} }
private val container = PlotContainer(frame); private val container = PlotContainer(frame)
private val loChannelProperty = SimpleIntegerProperty(500).apply { 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 var upChannel by upChannelProperty
private val data: ObservableMap<String, CachedSet> = FXCollections.observableHashMap(); private val data: ObservableMap<String, NumassSet> = FXCollections.observableHashMap()
val isEmpty = booleanBinding(data) { data.isEmpty() } val isEmpty = booleanBinding(data) { data.isEmpty() }
override val root = borderpane { override val root = borderpane {
@ -86,7 +90,7 @@ class SpectrumView : View(title = "Numass spectrum plot", icon = ImageView(dfIco
vbox { vbox {
label("Up channel") label("Up channel")
textfield { textfield {
isEditable = true; isEditable = true
prefWidth = 60.0 prefWidth = 60.0
textProperty().bindBidirectional(upChannelProperty, NumberStringConverter()) textProperty().bindBidirectional(upChannelProperty, NumberStringConverter())
} }
@ -100,7 +104,7 @@ class SpectrumView : View(title = "Numass spectrum plot", icon = ImageView(dfIco
init { init {
data.addListener { change: MapChangeListener.Change<out String, out NumassSet> -> data.addListener { change: MapChangeListener.Change<out String, out NumassSet> ->
if (change.wasRemoved()) { if (change.wasRemoved()) {
frame.plots.remove(Name.ofSingle(change.key)); frame.plots.remove(Name.ofSingle(change.key))
} }
if (change.wasAdded()) { if (change.wasAdded()) {
@ -115,24 +119,26 @@ class SpectrumView : View(title = "Numass spectrum plot", icon = ImageView(dfIco
val progress = AtomicInteger(0) val progress = AtomicInteger(0)
val totalProgress = data.values.stream().mapToInt { it.points.size }.sum() val totalProgress = data.values.stream().mapToInt { it.points.size }.sum()
data.forEach { name, set -> data.forEach { (name, set) ->
val plot: DataPlot = frame.plots[Name.ofSingle(name)] as DataPlot? ?: DataPlot(name).apply { frame.add(this) } val plot: DataPlot =
frame.plots[Name.ofSingle(name)] as DataPlot? ?: DataPlot(name).apply { frame.add(this) }
runGoal("spectrumData[$name]") { runGoal("spectrumData[$name]") {
set.points.forEach { it.spectrum.start() } set.points.map {
set.points.map { point -> pointCache.getCachedPoint("$name/${it.voltage}[${it.index}]", it)
val count = point.spectrum.await().countInWindow(loChannel.toShort(), upChannel.toShort()); }.map { cachedPoint ->
val seconds = point.length.toMillis() / 1000.0; val count = cachedPoint.spectrum.await().countInWindow(loChannel.toShort(), upChannel.toShort())
val seconds = cachedPoint.length.toMillis() / 1000.0
runLater { runLater {
container.progress = progress.incrementAndGet().toDouble() / totalProgress container.progress = progress.incrementAndGet().toDouble() / totalProgress
} }
Adapters.buildXYDataPoint( Adapters.buildXYDataPoint(
point.voltage, cachedPoint.voltage,
(count / seconds), (count / seconds),
Math.sqrt(count.toDouble()) / seconds sqrt(count.toDouble()) / seconds
) )
} }
} ui { points -> } ui { points: List<Values> ->
plot.fillData(points) plot.fillData(points)
container.progress = 1.0 container.progress = 1.0
//spectrumExportButton.isDisable = false //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 data[key] = value
} }

View File

@ -12,13 +12,16 @@ 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.collections.ObservableList
import javafx.scene.control.ContextMenu import javafx.scene.control.ContextMenu
import javafx.scene.control.TreeItem import javafx.scene.control.TreeItem
import kotlinx.coroutines.runBlocking
import tornadofx.* import tornadofx.*
class StorageView(val storage: Storage) : View(title = "Numass storage", icon = dfIconView) { class StorageView(val storage: Storage) : View(title = "Numass storage", icon = dfIconView) {
private val pointCache by inject<PointCache>()
private val ampView: AmplitudeView by inject() private val ampView: AmplitudeView by inject()
private val timeView: TimeView by inject() private val timeView: TimeView by inject()
private val spectrumView: SpectrumView 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 { val infoView: UIComponent by lazy {
when (content) { when (content) {
is CachedPoint -> PointInfoView(content) is NumassPoint -> PointInfoView(pointCache.getCachedPoint(id, content))
is Metoid -> MetaViewer(content.meta, title = "Meta view: $id") is Metoid -> MetaViewer(content.meta, title = "Meta view: $id")
else -> MetaViewer(Meta.empty(), 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 { init {
checkedProperty.onChange { selected -> checkedProperty.onChange { selected ->
when (content) { when (content) {
is CachedPoint -> { is NumassPoint -> {
if (selected) { if (selected) {
ampView[id] = content ampView[id] = content
timeView[id] = content timeView[id] = content
@ -57,7 +60,7 @@ class StorageView(val storage: Storage) : View(title = "Numass storage", icon =
timeView.remove(id) timeView.remove(id)
} }
} }
is CachedSet -> { is NumassSet -> {
if (selected) { if (selected) {
spectrumView[id] = content spectrumView[id] = content
hvView[id] = content hvView[id] = content
@ -77,22 +80,18 @@ class StorageView(val storage: Storage) : View(title = "Numass storage", icon =
} }
} }
val children: List<Container>? by lazy { fun getChildren(): ObservableList<Container>? = when (content) {
when (content) { is Storage -> content.children.map {
is Storage -> runBlocking { content.children }.map { buildContainer(it, this) }.sortedWith( buildContainer(it, this)
object : Comparator<Container> { }.sortedWith(Comparator.comparing({ it.id }, AlphanumComparator)).asObservable()
private val alphanumComparator = AlphanumComparator() is NumassSet -> content.points
override fun compare(o1: Container, o2: Container): Int = alphanumComparator.compare(o1.id, o2.id) .sortedBy { it.index }
} .map { buildContainer(it, this) }
) .asObservable()
is NumassSet -> content.points else -> null
.sortedBy { it.index }
.map { buildContainer(it, this) }
.toList()
else -> null
}
} }
val hasChildren: Boolean = (content is Storage) || (content is NumassSet) 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 //isShowRoot = false
root = TreeItem(Container(storage.name, storage)) root = TreeItem(Container(storage.name, storage))
root.isExpanded = true root.isExpanded = true
lazyPopulate(leafCheck = { !it.value.hasChildren }) { lazyPopulate(leafCheck = {
it.value.children !it.value.hasChildren
}) {
it.value.getChildren()
} }
cellFormat { value -> cellFormat { value: Container ->
when (value.content) { when (value.content) {
is Storage -> { is Storage -> {
text = value.content.name 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 = private fun buildContainer(content: Any, parent: Container): Container =
when (content) { when (content) {
is Storage -> { is Storage -> Container(content.fullName.toString(), content)
Container(content.fullName.toString(), content) is NumassSet -> {
val id: String = if (content is NumassDataLoader) {
content.fullName.unescaped
} else {
content.name
} }
is NumassSet -> { Container(id, content)
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}");
} }
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}");
}
} }

View File

@ -15,6 +15,7 @@ import hep.dataforge.plots.jfreechart.JFreeChartFrame
import hep.dataforge.tables.Adapters import hep.dataforge.tables.Adapters
import hep.dataforge.values.ValueMap import hep.dataforge.values.ValueMap
import inr.numass.data.analyzers.TimeAnalyzer import inr.numass.data.analyzers.TimeAnalyzer
import inr.numass.data.api.NumassPoint
import javafx.beans.Observable import javafx.beans.Observable
import javafx.beans.binding.DoubleBinding import javafx.beans.binding.DoubleBinding
import javafx.collections.FXCollections 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 container = PlotContainer(frame)
private val data: ObservableMap<String, CachedPoint> = FXCollections.observableHashMap() private val data: ObservableMap<String, NumassPoint> = FXCollections.observableHashMap()
private val plots: ObservableMap<String, Goal<Plottable>> = FXCollections.observableHashMap() private val plots: ObservableMap<String, Goal<Plottable>> = FXCollections.observableHashMap()
val isEmpty = booleanBinding(data) { isEmpty() } 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` * 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 data[key] = point
} }
fun addAll(data: Map<String, CachedPoint>) { fun addAll(data: Map<String, NumassPoint>) {
this.data.putAll(data); 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. * Set frame content to the given map. All keys not in the map are removed.
*/ */
fun setAll(map: Map<String, CachedPoint>) { fun setAll(map: Map<String, NumassPoint>) {
plots.clear(); plots.clear();
//Remove obsolete keys //Remove obsolete keys
data.keys.filter { !map.containsKey(it) }.forEach { data.keys.filter { !map.containsKey(it) }.forEach {

View File

@ -2,6 +2,7 @@ package inr.numass.viewer
import ch.qos.logback.classic.Level import ch.qos.logback.classic.Level
import ch.qos.logback.classic.Logger import ch.qos.logback.classic.Logger
import hep.dataforge.context.Context
import hep.dataforge.context.Global import hep.dataforge.context.Global
import hep.dataforge.fx.dfIcon import hep.dataforge.fx.dfIcon
import javafx.stage.Stage import javafx.stage.Stage
@ -16,13 +17,18 @@ class Viewer : App(MainView::class) {
(LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME) as Logger).level = Level.INFO (LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME) as Logger).level = Level.INFO
} }
val context: Context = Global.getContext("numass-viewer")
override fun start(stage: Stage) { override fun start(stage: Stage) {
stage.icons += dfIcon
super.start(stage) super.start(stage)
stage.icons += dfIcon
} }
override fun stop() { override fun stop() {
super.stop() context.close()
Global.terminate(); Global.terminate();
super.stop()
} }
} }
internal val App.context get() = (this as Viewer).context

View File

@ -7,10 +7,11 @@ import hep.dataforge.tables.Table
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.NumassDirectory 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.application.Application
import javafx.scene.image.ImageView import javafx.scene.image.ImageView
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import tornadofx.* import tornadofx.*
import java.io.File 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") } //val set: NumassSet = NumassStorageFactory.buildLocal(rootDir).provide("loader::set_8", NumassSet::class.java).orElseThrow { RuntimeException("err") }
private val cache: MutableMap<NumassPoint, Table> = ConcurrentHashMap(); private val cache: MutableMap<NumassPoint, Table> = ConcurrentHashMap()
val context = Global
val amp: AmplitudeView by inject(params = mapOf("cache" to cache))//= AmplitudeView(immutable = immutable) val amp: AmplitudeView by inject(params = mapOf("cache" to cache))//= AmplitudeView(immutable = immutable)
val sp: SpectrumView by inject(params = mapOf("cache" to cache)) val sp: SpectrumView by inject(params = mapOf("cache" to cache))
@ -35,11 +37,11 @@ class ViewerComponentsTest : View(title = "Numass viewer test", icon = ImageView
top { top {
button("Click me!") { button("Click me!") {
action { action {
GlobalScope.launch { context.launch {
val set: NumassSet = NumassDirectory.INSTANCE.read(Global, File("D:\\Work\\Numass\\data\\2017_05\\Fill_2").toPath()) 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 ?.provide("loader::set_2", NumassSet::class.java).nullable
?: kotlin.error("Error") ?: kotlin.error("Error")
update(set); update(set)
} }
} }
} }
@ -54,13 +56,13 @@ class ViewerComponentsTest : View(title = "Numass viewer test", icon = ImageView
} }
fun update(set: NumassSet) { fun update(set: NumassSet) {
amp.setAll(set.points.filter { it.voltage != 16000.0 }.associateBy({ "point_${it.voltage}" }) { CachedPoint(it) }); amp.setAll(set.points.filter { it.voltage != 16000.0 }.associateBy { "point_${it.voltage}" })
sp.set("test", CachedSet(set)); sp["test"] = set
hv.set(set.name, set) hv[set.name] = set
} }
} }
fun main(args: Array<String>) { fun main(args: Array<String>) {
Application.launch(ViewerComponentsTestApp::class.java, *args); Application.launch(ViewerComponentsTestApp::class.java, *args)
} }