forked from NPM/numass-framework
Fix viewer cache
This commit is contained in:
parent
d3689638e0
commit
15d17f2cc4
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -126,7 +126,7 @@ interface Value : Serializable, Comparable<Value> {
|
||||
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)
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
||||
|
||||
|
25
dataforge-gui/build.gradle.kts
Normal file
25
dataforge-gui/build.gradle.kts
Normal 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")
|
||||
}
|
||||
|
||||
|
@ -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'
|
||||
}
|
||||
|
@ -1,13 +0,0 @@
|
||||
//allprojects {
|
||||
// apply plugin: 'org.openjfx.javafxplugin'
|
||||
//
|
||||
// javafx {
|
||||
// modules = ['javafx.controls']
|
||||
// }
|
||||
//}
|
||||
|
||||
description = 'dataforge-plots'
|
||||
|
||||
dependencies {
|
||||
api project(':dataforge-core')
|
||||
}
|
14
dataforge-plots/build.gradle.kts
Normal file
14
dataforge-plots/build.gradle.kts
Normal 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"))
|
||||
}
|
@ -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<StorageElement>
|
||||
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<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) {
|
||||
// Read path as directory if type not found and path is directory
|
||||
if (Files.isDirectory(path)) {
|
||||
|
@ -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<KotlinCompile> {
|
||||
kotlinOptions.jvmTarget = "1.8"
|
||||
}
|
@ -28,7 +28,6 @@ import java.util.stream.Stream
|
||||
*/
|
||||
interface NumassPoint : Metoid, ParentBlock, Provider {
|
||||
|
||||
|
||||
override val blocks: List<NumassBlock>
|
||||
|
||||
/**
|
||||
|
@ -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')
|
||||
}
|
||||
|
42
numass-viewer/build.gradle.kts
Normal file
42
numass-viewer/build.gradle.kts
Normal 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",
|
||||
)
|
||||
}
|
@ -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<PointCache>()
|
||||
|
||||
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<String, CachedPoint> = FXCollections.observableHashMap()
|
||||
private val data: ObservableMap<String, NumassPoint> = FXCollections.observableHashMap()
|
||||
private val plots: ObservableMap<String, Goal<Plottable>> = 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<String, CachedPoint>) {
|
||||
fun addAll(data: Map<String, NumassPoint>) {
|
||||
this.data.putAll(data);
|
||||
}
|
||||
|
||||
private fun invalidate() {
|
||||
data.forEach { key, point ->
|
||||
data.forEach { (key, point) ->
|
||||
plots.getOrPut(key) {
|
||||
runGoal<Plottable>("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<String, CachedPoint>) {
|
||||
fun setAll(map: Map<String, NumassPoint>) {
|
||||
plots.clear();
|
||||
//Remove obsolete keys
|
||||
data.keys.filter { !map.containsKey(it) }.forEach {
|
||||
|
@ -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) } }
|
||||
}
|
@ -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<PointCache>()
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
@ -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)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<PointCache>()
|
||||
|
||||
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<String, CachedSet> = FXCollections.observableHashMap();
|
||||
private val data: ObservableMap<String, NumassSet> = 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<out String, out NumassSet> ->
|
||||
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<Values> ->
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -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<PointCache>()
|
||||
|
||||
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<Container>? by lazy {
|
||||
when (content) {
|
||||
is Storage -> runBlocking { content.children }.map { buildContainer(it, this) }.sortedWith(
|
||||
object : Comparator<Container> {
|
||||
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<Container>? = 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}");
|
||||
}
|
||||
}
|
||||
|
@ -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<String, CachedPoint> = FXCollections.observableHashMap()
|
||||
private val data: ObservableMap<String, NumassPoint> = FXCollections.observableHashMap()
|
||||
private val plots: ObservableMap<String, Goal<Plottable>> = 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<String, CachedPoint>) {
|
||||
fun addAll(data: Map<String, NumassPoint>) {
|
||||
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<String, CachedPoint>) {
|
||||
fun setAll(map: Map<String, NumassPoint>) {
|
||||
plots.clear();
|
||||
//Remove obsolete keys
|
||||
data.keys.filter { !map.containsKey(it) }.forEach {
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal val App.context get() = (this as Viewer).context
|
@ -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<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 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<String>) {
|
||||
Application.launch(ViewerComponentsTestApp::class.java, *args);
|
||||
Application.launch(ViewerComponentsTestApp::class.java, *args)
|
||||
}
|
Loading…
Reference in New Issue
Block a user