Switch to ZGC. Centralize data storage

This commit is contained in:
Alexander Nozik 2021-11-16 19:46:39 +03:00
parent 78ff8d4f6e
commit d683170a73
13 changed files with 259 additions and 425 deletions

View File

@ -82,7 +82,7 @@ object NumassDataUtils {
}
fun read(envelope: Envelope): NumassPoint =
if (envelope.meta.hasMeta("dpp_params") || envelope.meta.hasMeta("tqdc")) {
if (envelope.meta.hasMeta("dpp_params") || envelope.meta.hasMeta("channels") || envelope.meta.hasMeta("tqdc")) {
ProtoNumassPoint.fromEnvelope(envelope)
} else {
ClassicNumassPoint(envelope)

View File

@ -30,6 +30,7 @@ dependencies {
}
val addJvmArgs = listOf(
"-XX:+UseZGC",
"--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",

View File

@ -2,35 +2,30 @@ package inr.numass.viewer
import hep.dataforge.configure
import hep.dataforge.fx.dfIcon
import hep.dataforge.fx.except
import hep.dataforge.fx.plots.PlotContainer
import hep.dataforge.fx.runGoal
import hep.dataforge.fx.ui
import hep.dataforge.goals.Goal
import hep.dataforge.names.Name
import hep.dataforge.plots.PlotGroup
import hep.dataforge.plots.Plottable
import hep.dataforge.plots.data.DataPlot
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
import javafx.beans.property.SimpleObjectProperty
import javafx.collections.FXCollections
import javafx.collections.MapChangeListener
import javafx.collections.ObservableMap
import javafx.scene.control.CheckBox
import javafx.scene.control.ChoiceBox
import javafx.scene.image.ImageView
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.*
import kotlinx.coroutines.javafx.JavaFx
import tornadofx.*
class AmplitudeView : View(title = "Numass amplitude spectrum plot", icon = ImageView(dfIcon)) {
private val pointCache by inject<PointCache>()
private val dataController by inject<DataController>()
private val data get() = dataController.points
private val frame = JFreeChartFrame().configure {
"title" to "Detector response plot"
@ -74,129 +69,96 @@ class AmplitudeView : View(title = "Numass amplitude spectrum plot", icon = Imag
addToSideBar(0, binningSelector, normalizeSwitch)
}
private val data: ObservableMap<String, NumassPoint> = FXCollections.observableHashMap()
private val plots: ObservableMap<String, Goal<Plottable>> = FXCollections.observableHashMap()
private val plotJobs: ObservableMap<String, Job> = FXCollections.observableHashMap()
val isEmpty = booleanBinding(data) { isEmpty() }
private val progress = object : DoubleBinding() {
init {
bind(plots)
}
override fun computeValue(): Double {
return plots.values.count { it.isDone }.toDouble() / data.size;
bind(plotJobs)
}
override fun computeValue(): Double = plotJobs.values.count { it.isCompleted }.toDouble() / plotJobs.size
}
init {
data.addListener { _: Observable ->
invalidate()
}
data.addListener(MapChangeListener { change ->
val key = change.key
if (change.wasAdded()) {
replotOne(key, change.valueAdded)
} else if (change.wasRemoved()) {
plotJobs[key]?.cancel()
plotJobs.remove(key)
frame.plots.remove(Name.ofSingle(key))
progress.invalidate()
}
})
binningProperty.onChange {
frame.plots.clear()
plots.clear()
invalidate()
replot()
}
normalizeProperty.onChange {
frame.plots.clear()
plots.clear()
invalidate()
replot()
}
container.progressProperty.bind(progress)
}
override val root = borderpane {
center = container.root
}
private fun replotOne(key: String, point: DataController.CachedPoint) {
plotJobs[key]?.cancel()
plotJobs[key] = app.context.launch {
val valueAxis = if (normalize) {
NumassAnalyzer.COUNT_RATE_KEY
} else {
NumassAnalyzer.COUNT_KEY
}
val adapter = Adapters.buildXYAdapter(NumassAnalyzer.CHANNEL_KEY, valueAxis)
/**
* Put or replace current plot with name `key`
*/
operator fun set(key: String, point: NumassPoint) {
data[key] = point
}
val channels = point.channelSpectra.await()
fun addAll(data: Map<String, NumassPoint>) {
this.data.putAll(data);
}
private fun invalidate() {
data.forEach { (key, point) ->
plots.getOrPut(key) {
runGoal<Plottable>(app.context, "loadAmplitudeSpectrum_$key", Dispatchers.IO) {
val valueAxis = if (normalize) {
NumassAnalyzer.COUNT_RATE_KEY
} else {
NumassAnalyzer.COUNT_KEY
}
val adapter = Adapters.buildXYAdapter(NumassAnalyzer.CHANNEL_KEY, valueAxis)
val channels = pointCache.getChannelSpectra(key, point)
return@runGoal if (channels.size == 1) {
DataPlot.plot(
key,
channels.values.first().withBinning(binning),
adapter
)
} else {
val group = PlotGroup.typed<DataPlot>(key)
channels.forEach { key, spectrum ->
val plot = DataPlot.plot(
key.toString(),
spectrum.withBinning(binning),
adapter
)
group.add(plot)
}
group
}
} ui { plot ->
frame.add(plot)
progress.invalidate()
} except {
val plot = if (channels.size == 1) {
DataPlot.plot(
key,
channels.values.first().withBinning(binning),
adapter
)
} else {
val group = PlotGroup.typed<DataPlot>(key)
channels.forEach { (key, spectrum) ->
val plot = DataPlot.plot(
key.toString(),
spectrum.withBinning(binning),
adapter
)
group.add(plot)
}
group
}
ensureActive()
withContext(Dispatchers.JavaFx) {
frame.add(plot)
}
}.apply {
invokeOnCompletion {
runLater{
progress.invalidate()
}
}
plots.keys.filter { !data.containsKey(it) }.forEach { remove(it) }
}
}
fun clear() {
data.clear()
plots.values.forEach{
it.cancel()
private fun replot() {
frame.plots.clear()
plotJobs.forEach { (_, job) -> job.cancel() }
plotJobs.clear()
data.forEach { (key, point) ->
replotOne(key, point)
}
plots.clear()
invalidate()
}
/**
* Remove the plot and cancel loading task if it is in progress.
*/
fun remove(name: String) {
frame.plots.remove(Name.ofSingle(name))
plots[name]?.cancel()
plots.remove(name)
data.remove(name)
progress.invalidate()
override val root = borderpane {
center = container.root
}
/**
* Set frame content to the given map. All keys not in the map are removed.
*/
fun setAll(map: Map<String, NumassPoint>) {
plots.clear();
//Remove obsolete keys
data.keys.filter { !map.containsKey(it) }.forEach {
remove(it)
}
this.addAll(map);
}
}

View File

@ -0,0 +1,115 @@
package inr.numass.viewer
import hep.dataforge.meta.Meta
import hep.dataforge.storage.tables.TableLoader
import hep.dataforge.tables.Adapters
import hep.dataforge.tables.ListTable
import hep.dataforge.tables.Table
import hep.dataforge.tables.TableFormatBuilder
import hep.dataforge.utils.Misc
import hep.dataforge.values.ValueMap
import inr.numass.data.analyzers.NumassAnalyzer
import inr.numass.data.analyzers.TimeAnalyzer
import inr.numass.data.api.NumassPoint
import inr.numass.data.api.NumassSet
import javafx.collections.FXCollections
import javafx.collections.ObservableMap
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import tornadofx.*
import kotlin.math.floor
class DataController : Controller() {
private val context = app.context
val analyzer = TimeAnalyzer()
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)
}
val timeSpectrum: Deferred<Table> = context.async(Dispatchers.IO) {
val cr = spectrum.await().sumOf {
it.getValue(NumassAnalyzer.COUNT_KEY).int
}.toDouble() / point.length.toMillis() * 1000
val binNum = 200
//inputMeta.getInt("binNum", 1000);
val binSize = 1.0 / cr * 10 / binNum * 1e6
//inputMeta.getDouble("binSize", 1.0 / cr * 10 / binNum * 1e6)
val format = TableFormatBuilder()
.addNumber("x", Adapters.X_VALUE_KEY)
.addNumber(NumassAnalyzer.COUNT_KEY, Adapters.Y_VALUE_KEY)
.build()
ListTable.Builder(format).rows(
analyzer.getEventsWithDelay(point, Meta.empty())
.map { it.second.toDouble() / 1000.0 }
.groupBy { floor(it / binSize) }
.toSortedMap()
.map {
ValueMap.ofPairs("x" to it.key, "count" to it.value.count())
}
).build()
}
}
private val cache = Misc.getLRUCache<String, CachedPoint>(400)
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()
val sets: ObservableMap<String, NumassSet> = FXCollections.observableHashMap()
val points: ObservableMap<String, CachedPoint> = FXCollections.observableHashMap()
val sc: ObservableMap<String, TableLoader> = FXCollections.observableHashMap()
fun clear() {
cache.clear()
sets.clear()
points.clear()
sc.clear()
}
fun addPoint(id: String, point: NumassPoint) {
points[id] = getCachedPoint(id, point)
}
fun addSet(id: String, set: NumassSet) {
sets[id] = set
}
fun addSc(id: String, set: TableLoader) {
sc[id] = set
}
fun remove(id: String) {
points.remove(id)
sets.remove(id)
sc.remove(id)
}
fun addAllPoints(points: Map<String, NumassPoint>) {
TODO()
}
}

View File

@ -11,9 +11,7 @@ import hep.dataforge.plots.data.TimePlot
import hep.dataforge.plots.jfreechart.JFreeChartFrame
import hep.dataforge.tables.Adapters
import inr.numass.data.api.NumassSet
import javafx.collections.FXCollections
import javafx.collections.MapChangeListener
import javafx.collections.ObservableMap
import javafx.scene.image.ImageView
import kotlinx.coroutines.Dispatchers
import tornadofx.*
@ -24,6 +22,9 @@ import tornadofx.*
*/
class HVView : View(title = "High voltage time plot", icon = ImageView(dfIcon)) {
private val dataController by inject<DataController>()
private val data get() = dataController.sets
private val frame = JFreeChartFrame().configure {
"xAxis.title" to "time"
"xAxis.type" to "time"
@ -44,7 +45,6 @@ class HVView : View(title = "High voltage time plot", icon = ImageView(dfIcon))
center = PlotContainer(frame).root
}
private val data: ObservableMap<String, NumassSet> = FXCollections.observableHashMap()
val isEmpty = booleanBinding(data) { data.isEmpty() }
init {
@ -71,18 +71,4 @@ class HVView : View(title = "High voltage time plot", icon = ImageView(dfIcon))
}
}
operator fun set(id: String, set: NumassSet) {
data[id] = set
}
fun remove(id: String) {
data.remove(id);
}
fun clear() {
data.clear()
}
}

View File

@ -29,7 +29,7 @@ import java.nio.file.Path
class MainView : View(title = "Numass viewer", icon = dfIconView) {
private val pointCache by inject<PointCache>()
private val dataController by inject<DataController>()
val storageView by inject<StorageView>()
@ -42,7 +42,7 @@ class MainView : View(title = "Numass viewer", icon = dfIconView) {
private var path: Path by pathProperty
private val contentViewProperty = SimpleObjectProperty<UIComponent>()
var contentView: UIComponent? by contentViewProperty
private var contentView: UIComponent? by contentViewProperty
override val root = borderpane {
prefHeight = 600.0
@ -137,11 +137,13 @@ class MainView : View(title = "Numass viewer", icon = dfIconView) {
}
}
private val spectrumView by inject<SpectrumView>()
private suspend fun load(path: Path) {
runLater {
contentView = null
}
pointCache.clear()
dataController.clear()
if (Files.isDirectory(path)) {
if (Files.exists(path.resolve(NumassDataLoader.META_FRAGMENT_NAME))) {
//build set view
@ -150,10 +152,9 @@ class MainView : View(title = "Numass viewer", icon = dfIconView) {
message = "Building numass set..."
NumassDataLoader(app.context, null, path.fileName.toString(), path)
} ui { loader: NumassDataLoader ->
contentView = SpectrumView().apply {
clear()
set(loader.name, loader)
}
contentView = spectrumView
dataController.addSet(loader.name, loader)
} except {
alert(
type = Alert.AlertType.ERROR,
@ -191,7 +192,7 @@ class MainView : View(title = "Numass viewer", icon = dfIconView) {
val point = NumassDataUtils.read(it)
runLater {
contentView = AmplitudeView().apply {
set(path.fileName.toString(), point)
dataController.addPoint(path.fileName.toString(), point)
}
}
}

View File

@ -1,64 +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.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>(400)
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()
}
}

View File

@ -9,7 +9,7 @@ import tornadofx.*
import tornadofx.controlsfx.borders
import tornadofx.controlsfx.toGlyph
class PointInfoView(val cachedPoint: PointCache.CachedPoint) : MetaViewer(cachedPoint.meta) {
class PointInfoView(val cachedPoint: DataController.CachedPoint) : MetaViewer(cachedPoint.meta) {
val countProperty = SimpleIntegerProperty(0)
var count by countProperty

View File

@ -11,10 +11,7 @@ import hep.dataforge.plots.jfreechart.JFreeChartFrame
import hep.dataforge.storage.tables.TableLoader
import hep.dataforge.storage.tables.asTable
import hep.dataforge.tables.Adapters
import hep.dataforge.tables.Table
import javafx.collections.FXCollections
import javafx.collections.MapChangeListener
import javafx.collections.ObservableMap
import javafx.scene.image.ImageView
import kotlinx.coroutines.Dispatchers
import tornadofx.*
@ -24,6 +21,9 @@ import tornadofx.*
*/
class SlowControlView : View(title = "Numass slow control view", icon = ImageView(dfIcon)) {
private val dataController by inject<DataController>()
private val data get() = dataController.sc
private val plot = JFreeChartFrame().configure {
"xAxis.type" to "time"
"yAxis.type" to "log"
@ -33,7 +33,6 @@ class SlowControlView : View(title = "Numass slow control view", icon = ImageVie
center = PlotContainer(plot).root
}
val data: ObservableMap<String, TableLoader> = FXCollections.observableHashMap();
val isEmpty = booleanBinding(data) {
data.isEmpty()
}
@ -45,7 +44,7 @@ class SlowControlView : View(title = "Numass slow control view", icon = ImageVie
}
if (change.wasAdded()) {
runGoal(app.context,"loadTable[${change.key}]", Dispatchers.IO) {
val plotData = getData(change.valueAdded)
val plotData = change.valueAdded.asTable().await()
val names = plotData.format.namesAsArray().filter { it != "timestamp" }
val group = PlotGroup(change.key)
@ -68,21 +67,4 @@ class SlowControlView : View(title = "Numass slow control view", icon = ImageVie
}
}
private suspend fun getData(loader: TableLoader): Table {
//TODO add query
return loader.asTable().await()
}
operator fun set(id: String, loader: TableLoader) {
this.data[id] = loader
}
fun remove(id: String) {
this.data.remove(id)
}
fun clear(){
data.clear()
}
}

View File

@ -10,9 +10,7 @@ import hep.dataforge.tables.Adapters
import inr.numass.data.analyzers.countInWindow
import inr.numass.data.api.NumassSet
import javafx.beans.property.SimpleIntegerProperty
import javafx.collections.FXCollections
import javafx.collections.MapChangeListener
import javafx.collections.ObservableMap
import javafx.geometry.Insets
import javafx.geometry.Orientation
import javafx.scene.image.ImageView
@ -33,7 +31,8 @@ import kotlin.math.sqrt
*/
class SpectrumView : View(title = "Numass spectrum plot", icon = ImageView(dfIcon)) {
private val pointCache by inject<PointCache>()
private val dataController by inject<DataController>()
private val data get() = dataController.sets
private val frame = JFreeChartFrame().configure {
"xAxis.title" to "U"
@ -44,7 +43,6 @@ class SpectrumView : View(title = "Numass spectrum plot", icon = ImageView(dfIco
}
private val container = PlotContainer(frame)
private val loChannelProperty = SimpleIntegerProperty(500).apply {
addListener { _ -> updateView() }
}
@ -55,9 +53,7 @@ class SpectrumView : View(title = "Numass spectrum plot", icon = ImageView(dfIco
}
private var upChannel by upChannelProperty
private val data: ObservableMap<String, NumassSet> = FXCollections.observableHashMap()
val isEmpty = booleanBinding(data) { data.isEmpty() }
private val isEmpty = booleanBinding(data) { data.isEmpty() }
override val root = borderpane {
top {
@ -126,7 +122,7 @@ class SpectrumView : View(title = "Numass spectrum plot", icon = ImageView(dfIco
app.context.launch {
val points = set.points.map {
pointCache.getCachedPoint("$name/${it.voltage}[${it.index}]", it)
dataController.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
@ -146,16 +142,4 @@ class SpectrumView : View(title = "Numass spectrum plot", icon = ImageView(dfIco
}
}
}
operator fun set(key: String, value: NumassSet) {
data[key] = value
}
fun remove(key: String) {
data.remove(key)
}
fun clear() {
data.clear()
}
}

View File

@ -25,7 +25,7 @@ class StorageView : View(title = "Numass storage", icon = dfIconView) {
val storageProperty = SimpleObjectProperty<Storage>()
val storage by storageProperty
private val pointCache by inject<PointCache>()
private val dataController by inject<DataController>()
private val ampView: AmplitudeView by inject()
private val timeView: TimeView by inject()
@ -35,14 +35,8 @@ class StorageView : View(title = "Numass storage", icon = dfIconView) {
// private var watcher: WatchService? = null
fun clear() {
//watcher?.close()
ampView.clear()
timeView.clear()
spectrumView.clear()
hvView.clear()
scView.clear()
dataController.clear()
}
private inner class Container(val id: String, val content: Any) {
@ -51,7 +45,7 @@ class StorageView : View(title = "Numass storage", icon = dfIconView) {
val infoView: UIComponent by lazy {
when (content) {
is NumassPoint -> PointInfoView(pointCache.getCachedPoint(id, content))
is NumassPoint -> PointInfoView(dataController.getCachedPoint(id, content))
is Metoid -> MetaViewer(content.meta, title = "Meta view: $id")
else -> MetaViewer(Meta.empty(), title = "Meta view: $id")
}
@ -64,27 +58,23 @@ class StorageView : View(title = "Numass storage", icon = dfIconView) {
when (content) {
is NumassPoint -> {
if (selected) {
ampView[id] = content
timeView[id] = content
dataController.addPoint(id, content)
} else {
ampView.remove(id)
timeView.remove(id)
dataController.remove(id)
}
}
is NumassSet -> {
if (selected) {
spectrumView[id] = content
hvView[id] = content
dataController.addSet(id, content)
} else {
spectrumView.remove(id)
hvView.remove(id)
dataController.remove(id)
}
}
is TableLoader -> {
if (selected) {
scView[id] = content
dataController.addSc(id, content)
} else {
scView.remove(id)
dataController.remove(id)
}
}
}

View File

@ -2,30 +2,28 @@ package inr.numass.viewer
import hep.dataforge.configure
import hep.dataforge.fx.dfIcon
import hep.dataforge.fx.except
import hep.dataforge.fx.plots.PlotContainer
import hep.dataforge.fx.runGoal
import hep.dataforge.fx.ui
import hep.dataforge.goals.Goal
import hep.dataforge.meta.Meta
import hep.dataforge.names.Name
import hep.dataforge.plots.Plottable
import hep.dataforge.plots.data.DataPlot
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 hep.dataforge.tables.Table
import javafx.beans.binding.DoubleBinding
import javafx.collections.FXCollections
import javafx.collections.MapChangeListener
import javafx.collections.ObservableMap
import javafx.scene.image.ImageView
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.javafx.JavaFx
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import tornadofx.*
class TimeView : View(title = "Numass time spectrum plot", icon = ImageView(dfIcon)) {
private val dataController by inject<DataController>()
private val frame = JFreeChartFrame().configure {
"title" to "Time plot"
node("xAxis") {
@ -47,128 +45,75 @@ class TimeView : View(title = "Numass time spectrum plot", icon = ImageView(dfIc
}.setType<DataPlot>()
}
// val stepProperty = SimpleDoubleProperty()
// var step by stepProperty
//
// private val container = PlotContainer(frame).apply {
// val binningSelector: ChoiceBox<Int> = ChoiceBox(FXCollections.observableArrayList(1, 5, 10, 20, 50)).apply {
// minWidth = 0.0
// selectionModel.selectLast()
// stepProperty.bind(this.selectionModel.selectedItemProperty())
// }
// addToSideBar(0, binningSelector)
// }
private val container = PlotContainer(frame)
private val data: ObservableMap<String, NumassPoint> = FXCollections.observableHashMap()
private val plots: ObservableMap<String, Goal<Plottable>> = FXCollections.observableHashMap()
//private val data: ObservableMap<String, NumassPoint> = FXCollections.observableHashMap()
private val data get() = dataController.points
private val plotJobs: ObservableMap<String, Job> = FXCollections.observableHashMap()
val isEmpty = booleanBinding(data) { isEmpty() }
private val progress = object : DoubleBinding() {
init {
bind(plots)
bind(plotJobs)
}
override fun computeValue(): Double {
return plots.values.count { it.isDone }.toDouble() / data.size;
}
override fun computeValue(): Double = plotJobs.values.count { it.isCompleted }.toDouble() / data.size
}
init {
data.addListener { _: Observable ->
invalidate()
}
data.addListener(MapChangeListener { change ->
val key = change.key
if (change.wasAdded()) {
replotOne(key, change.valueAdded)
} else if(change.wasRemoved()){
plotJobs[key]?.cancel()
plotJobs.remove(key)
frame.plots.remove(Name.ofSingle(key))
progress.invalidate()
}
})
}
override val root = borderpane {
center = container.root
}
/**
* Put or replace current plot with name `key`
*/
operator fun set(key: String, point: NumassPoint) {
data[key] = point
}
private fun replotOne(key: String, point: DataController.CachedPoint) {
plotJobs[key]?.cancel()
plotJobs[key] = app.context.launch {
try {
val histogram: Table = point.timeSpectrum.await()
fun addAll(data: Map<String, NumassPoint>) {
this.data.putAll(data);
}
private val analyzer = TimeAnalyzer();
private fun invalidate() {
data.forEach { key, point ->
plots.getOrPut(key) {
runGoal<Plottable>(app.context, "loadAmplitudeSpectrum_$key", Dispatchers.IO) {
val initialEstimate = analyzer.analyze(point)
val cr = initialEstimate.getDouble("cr")
val binNum = 200//inputMeta.getInt("binNum", 1000);
val binSize = 1.0 / cr * 10 / binNum * 1e6//inputMeta.getDouble("binSize", 1.0 / cr * 10 / binNum * 1e6)
val histogram = analyzer.getEventsWithDelay(point, Meta.empty())
.map { it.second.toDouble() / 1000.0 }
.groupBy { Math.floor(it / binSize) }
.toSortedMap()
.map {
ValueMap.ofPairs("x" to it.key, "count" to it.value.count())
}
DataPlot(key, adapter = Adapters.buildXYAdapter("x", "count"))
.configure {
"showLine" to true
"showSymbol" to false
"showErrors" to false
"connectionType" to "step"
}.fillData(histogram)
} ui { plot ->
val plot = DataPlot(key, adapter = Adapters.buildXYAdapter("x", "count"))
.configure {
"showLine" to true
"showSymbol" to false
"showErrors" to false
"connectionType" to "step"
}.fillData(histogram)
withContext(Dispatchers.JavaFx) {
frame.add(plot)
progress.invalidate()
} except {
}
} finally {
withContext(Dispatchers.JavaFx) {
progress.invalidate()
}
}
plots.keys.filter { !data.containsKey(it) }.forEach { remove(it) }
}
}
fun clear() {
data.clear()
plots.values.forEach {
it.cancel()
}
plots.clear()
invalidate()
}
/**
* Remove the plot and cancel loading task if it is in progress.
*/
fun remove(name: String) {
frame.plots.remove(Name.ofSingle(name))
plots[name]?.cancel()
plots.remove(name)
data.remove(name)
progress.invalidate()
}
private fun replot() {
frame.plots.clear()
plotJobs.forEach { (_, job) -> job.cancel() }
plotJobs.clear()
/**
* Set frame content to the given map. All keys not in the map are removed.
*/
fun setAll(map: Map<String, NumassPoint>) {
plots.clear();
//Remove obsolete keys
data.keys.filter { !map.containsKey(it) }.forEach {
remove(it)
data.forEach { (key, point) ->
replotOne(key, point)
}
this.addAll(map);
}
}

View File

@ -1,68 +0,0 @@
package inr.numass.viewer.test
import hep.dataforge.context.Global
import hep.dataforge.fx.dfIcon
import hep.dataforge.nullable
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.AmplitudeView
import inr.numass.viewer.HVView
import inr.numass.viewer.SpectrumView
import javafx.application.Application
import javafx.scene.image.ImageView
import kotlinx.coroutines.launch
import tornadofx.*
import java.io.File
import java.util.concurrent.ConcurrentHashMap
class ViewerComponentsTestApp : App(ViewerComponentsTest::class)
class ViewerComponentsTest : View(title = "Numass viewer test", icon = ImageView(dfIcon)) {
//val rootDir = File("D:\\Work\\Numass\\data\\2017_05\\Fill_2")
//val set: NumassSet = NumassStorageFactory.buildLocal(rootDir).provide("loader::set_8", NumassSet::class.java).orElseThrow { RuntimeException("err") }
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))
val hv: HVView by inject()
override val root = borderpane {
top {
button("Click me!") {
action {
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)
}
}
}
}
center {
tabpane {
tab("amplitude", amp.root)
tab("spectrum", sp.root)
tab("hv", hv.root)
}
}
}
fun update(set: NumassSet) {
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)
}