Viewer update

This commit is contained in:
Alexander Nozik 2018-04-21 16:47:48 +03:00
parent 1a9309fd21
commit 6cd618e5a3
7 changed files with 348 additions and 226 deletions

View File

@ -15,6 +15,7 @@
*/
package inr.numass.data.storage
import hep.dataforge.context.Context
import hep.dataforge.exceptions.StorageException
import hep.dataforge.io.ColumnedDataReader
import hep.dataforge.io.envelopes.Envelope
@ -23,6 +24,7 @@ import hep.dataforge.meta.MetaBuilder
import hep.dataforge.providers.Provider
import hep.dataforge.storage.api.ObjectLoader
import hep.dataforge.storage.api.Storage
import hep.dataforge.storage.commons.DummyStorage
import hep.dataforge.storage.filestorage.FileStorage
import hep.dataforge.storage.loaders.AbstractLoader
import hep.dataforge.tables.Table
@ -30,7 +32,6 @@ import inr.numass.data.api.NumassPoint
import inr.numass.data.api.NumassSet
import inr.numass.data.legacy.NumassFileEnvelope
import org.slf4j.LoggerFactory
import java.io.IOException
import java.nio.file.Files
import java.nio.file.Path
@ -170,6 +171,10 @@ class NumassDataLoader(
return NumassDataLoader(storage, name, annotation, items)
}
fun fromDir(context: Context, directory: Path, name: String = FileStorage.entryName(directory)): NumassDataLoader {
return fromDir(DummyStorage(context), directory, name)
}
/**
* "start_time": "2016-04-20T04:08:50",
*

View File

@ -7,16 +7,13 @@ import hep.dataforge.fx.ui
import hep.dataforge.goals.Goal
import hep.dataforge.kodex.configure
import hep.dataforge.kodex.toList
import hep.dataforge.meta.Meta
import hep.dataforge.plots.PlotFrame
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 hep.dataforge.tables.Table
import inr.numass.data.analyzers.NumassAnalyzer
import inr.numass.data.analyzers.SimpleAnalyzer
import inr.numass.data.analyzers.withBinning
import inr.numass.data.api.MetaBlock
import inr.numass.data.api.NumassBlock
@ -32,12 +29,8 @@ import javafx.scene.control.CheckBox
import javafx.scene.control.ChoiceBox
import javafx.scene.image.ImageView
import tornadofx.*
import java.util.concurrent.ConcurrentHashMap
class AmplitudeView(
private val analyzer: NumassAnalyzer = SimpleAnalyzer(),
private val cache: MutableMap<NumassBlock, Table> = ConcurrentHashMap()
) : View(title = "Numass amplitude spectrum plot", icon = ImageView(dfIcon)) {
class AmplitudeView : View(title = "Numass amplitude spectrum plot", icon = ImageView(dfIcon)) {
private val frame: PlotFrame = JFreeChartFrame().configure {
"title" to "Detector response plot"
@ -116,13 +109,6 @@ class AmplitudeView(
center = container.root
}
/**
* Calculate or get spectrum from the immutable
*/
private fun getSpectrum(point: NumassBlock): Table {
return cache.computeIfAbsent(point) { analyzer.getAmplitudeSpectrum(point, Meta.empty()) }
}
/**
* Put or replace current plot with name `key`
*/
@ -138,7 +124,7 @@ class AmplitudeView(
* Distinct map of channel number to corresponding grouping block
*/
private fun NumassPoint.getChannels(): Map<Int, NumassBlock> {
return blocks.toList().groupBy { it.channel ?: 0 }.mapValues { entry ->
return blocks.toList().groupBy { it.channel }.mapValues { entry ->
if (entry.value.size == 1) {
entry.value.first()
} else {
@ -164,7 +150,7 @@ class AmplitudeView(
DataPlot.plot(
key,
adapter,
getSpectrum(point).withBinning(binning)
PointCache[point].withBinning(binning)
)
} else {
val group = PlotGroup.typed<DataPlot>(key)
@ -172,7 +158,7 @@ class AmplitudeView(
val plot = DataPlot.plot(
key.toString(),
adapter,
getSpectrum(block).withBinning(binning)
PointCache[point].withBinning(binning)
)
group.add(plot)
}

View File

@ -0,0 +1,200 @@
package inr.numass.viewer
import hep.dataforge.context.Context
import hep.dataforge.context.Global
import hep.dataforge.fx.*
import hep.dataforge.fx.fragments.LogFragment
import hep.dataforge.storage.commons.StorageManager
import inr.numass.NumassProperties
import inr.numass.data.api.NumassPoint
import inr.numass.data.legacy.NumassFileEnvelope
import inr.numass.data.storage.NumassDataLoader
import inr.numass.data.storage.NumassStorageFactory
import javafx.beans.property.SimpleObjectProperty
import javafx.geometry.Insets
import javafx.scene.control.Alert
import javafx.scene.layout.Priority
import javafx.scene.text.Font
import javafx.stage.DirectoryChooser
import javafx.stage.FileChooser
import kotlinx.coroutines.experimental.async
import org.controlsfx.control.StatusBar
import tornadofx.*
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) {
private val statusBar = StatusBar();
private val logFragment = LogFragment().apply {
addLogHandler(context.logger)
}
private val pathProperty = SimpleObjectProperty<Path>()
private var path: Path by pathProperty
val contentViewProperty = SimpleObjectProperty<UIComponent>()
var contentView: UIComponent? by contentViewProperty
override val root = borderpane {
prefHeight = 600.0
prefWidth = 800.0
top {
toolbar {
prefHeight = 40.0
button("Load directory") {
action {
val chooser = DirectoryChooser()
chooser.title = "Select directory to view"
val homeDir = NumassProperties.getNumassProperty("numass.viewer.lastPath")
try {
if (homeDir == null) {
chooser.initialDirectory = File(".").absoluteFile
} else {
chooser.initialDirectory = File(homeDir)
}
} catch (ex: Exception) {
NumassProperties.setNumassProperty("numass.viewer.lastPath", null)
}
val rootDir = chooser.showDialog(primaryStage.scene.window)
if (rootDir != null) {
NumassProperties.setNumassProperty("numass.viewer.lastPath", rootDir.absolutePath)
async {
runLater {
path = rootDir.toPath()
}
load(rootDir.toPath())
}
}
}
}
button("Load file") {
action {
val chooser = FileChooser()
chooser.title = "Select file to view"
val homeDir = NumassProperties.getNumassProperty("numass.viewer.lastPath")
try {
if (homeDir == null) {
chooser.initialDirectory = File(".").absoluteFile
} else {
chooser.initialDirectory = File(homeDir)
}
} catch (ex: Exception) {
NumassProperties.setNumassProperty("numass.viewer.lastPath", null)
}
val file = chooser.showOpenDialog(primaryStage.scene.window)
if (file != null) {
NumassProperties.setNumassProperty("numass.viewer.lastPath", file.parentFile.absolutePath)
async {
runLater {
path = file.toPath()
}
load(file.toPath())
}
}
}
}
label(pathProperty.asString()) {
padding = Insets(0.0, 0.0, 0.0, 10.0);
font = Font.font("System Bold", 13.0);
}
pane {
hgrow = Priority.ALWAYS
}
togglebutton("Console") {
isSelected = false
logFragment.bindWindow(this@togglebutton)
}
}
}
bottom = statusBar
}
init {
contentViewProperty.onChange {
root.center = it?.root
}
}
private suspend fun load(path: Path) {
runLater {
contentView = null
}
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.fromDir(context, path)
} ui {
contentView = SpectrumView().apply {
add(it.name, it)
}
} except {
alert(
type = Alert.AlertType.ERROR,
header = "Error during set loading",
content = it.toString()
).show()
}
} else {
//build storage
runGoal("viewer.load.storage[$path]") {
title = "Load storage ($path)"
message = "Building numass storage tree..."
StorageManager.buildStorage(
context,
NumassStorageFactory.buildStorageMeta(path.toUri(), true, false)
)
} ui {
contentView = StorageView(it)
} except {
alert(
type = Alert.AlertType.ERROR,
header = "Error during storage loading",
content = it.toString()
).show()
}
}
} else {
//Reading individual file
val envelope = try {
NumassFileEnvelope.open(path,true)
} catch (ex: Exception){
runLater {
alert(
type = Alert.AlertType.ERROR,
header = "Can't load DF envelope from file $path",
content = ex.toString()
).show()
}
null
}
envelope?.let {
if(it.meta.hasMeta("external_meta")){
//try to read as point
val point = NumassPoint.read(it)
runLater {
contentView = AmplitudeView().apply {
add(path.toString(), point)
}
}
} else {
alert(
type = Alert.AlertType.ERROR,
header = "Unknown envelope content: $path"
).show()
}
}
}
}
}

View File

@ -0,0 +1,35 @@
package inr.numass.viewer
import hep.dataforge.fx.meta.MetaViewer
import inr.numass.data.analyzers.NumassAnalyzer
import inr.numass.data.api.NumassPoint
import tornadofx.*
import tornadofx.controlsfx.borders
class PointInfoView(val point: NumassPoint) : MetaViewer(point.meta) {
private val count: Int by lazy {
PointCache[point].sumBy { it.getValue(NumassAnalyzer.COUNT_KEY).int }
}
override val root = super.root.apply {
top {
gridpane {
borders {
lineBorder().build()
}
row {
hbox {
label("Total number of events: ")
label("$count")
}
}
row {
hbox {
label("Total count rate: ")
label(String.format("%.2f", count.toDouble() / point.length.toMillis() * 1000))
}
}
}
}
}
}

View File

@ -5,16 +5,11 @@ import hep.dataforge.fx.plots.PlotContainer
import hep.dataforge.fx.runGoal
import hep.dataforge.fx.ui
import hep.dataforge.kodex.configure
import hep.dataforge.meta.Meta
import hep.dataforge.plots.PlotFrame
import hep.dataforge.plots.data.DataPlot
import hep.dataforge.plots.jfreechart.JFreeChartFrame
import hep.dataforge.tables.Adapters
import hep.dataforge.tables.Table
import inr.numass.data.analyzers.NumassAnalyzer
import inr.numass.data.analyzers.SimpleAnalyzer
import inr.numass.data.analyzers.countInWindow
import inr.numass.data.api.NumassPoint
import inr.numass.data.api.NumassSet
import javafx.beans.property.SimpleIntegerProperty
import javafx.collections.FXCollections
@ -26,7 +21,6 @@ import javafx.scene.image.ImageView
import javafx.util.converter.NumberStringConverter
import org.controlsfx.control.RangeSlider
import tornadofx.*
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicInteger
import java.util.stream.Collectors
@ -35,10 +29,7 @@ import java.util.stream.Collectors
* @param analyzer
* @param cache - optional global point immutable
*/
class SpectrumView(
val analyzer: NumassAnalyzer = SimpleAnalyzer(),
val cache: MutableMap<NumassPoint, Table> = ConcurrentHashMap()
) : View(title = "Numass spectrum plot", icon = ImageView(dfIcon)) {
class SpectrumView : View(title = "Numass spectrum plot", icon = ImageView(dfIcon)) {
private val frame: PlotFrame = JFreeChartFrame().configure {
"xAxis.title" to "U"
@ -119,11 +110,6 @@ class SpectrumView(
}
}
private fun getSpectrum(point: NumassPoint): Table {
return cache.computeIfAbsent(point) { analyzer.getAmplitudeSpectrum(point, Meta.empty()) }
}
private fun updateView() {
runLater { container.progress = 0.0 }
val progress = AtomicInteger(0)
@ -134,7 +120,7 @@ class SpectrumView(
runGoal("spectrumData[$name]") {
set.points.map { point ->
val count = getSpectrum(point).countInWindow(loChannel.toShort(), upChannel.toShort());
val count = PointCache[point].countInWindow(loChannel.toShort(), upChannel.toShort());
val seconds = point.length.toMillis() / 1000.0;
runLater {
container.progress = progress.incrementAndGet().toDouble() / totalProgress

View File

@ -1,55 +1,25 @@
package inr.numass.viewer
import hep.dataforge.context.Context
import hep.dataforge.context.Global
import hep.dataforge.fx.*
import hep.dataforge.fx.fragments.LogFragment
import hep.dataforge.fx.dfIconView
import hep.dataforge.fx.meta.MetaViewer
import hep.dataforge.fx.runGoal
import hep.dataforge.meta.Metoid
import hep.dataforge.storage.api.Loader
import hep.dataforge.storage.api.Storage
import hep.dataforge.storage.api.TableLoader
import hep.dataforge.storage.commons.StorageManager
import hep.dataforge.tables.Table
import inr.numass.NumassProperties
import inr.numass.data.api.NumassPoint
import inr.numass.data.api.NumassSet
import inr.numass.data.storage.NumassDataLoader
import inr.numass.data.storage.NumassStorageFactory
import javafx.beans.property.SimpleBooleanProperty
import javafx.beans.property.SimpleObjectProperty
import javafx.beans.property.SimpleStringProperty
import javafx.geometry.Insets
import javafx.scene.control.Alert
import javafx.scene.control.ContextMenu
import javafx.scene.control.TreeItem
import javafx.scene.image.ImageView
import javafx.scene.layout.Priority
import javafx.scene.text.Font
import javafx.stage.DirectoryChooser
import org.controlsfx.control.StatusBar
import tornadofx.*
import java.io.File
import java.net.URI
import java.util.concurrent.ConcurrentHashMap
import kotlin.streams.toList
class StorageView(private val context: Context = Global) : View(title = "Numass storage", icon = ImageView(dfIcon)) {
class StorageView(val storage: Storage) : View(title = "Numass storage", icon = dfIconView) {
val storageProperty = SimpleObjectProperty<Storage?>()
var storage by storageProperty
private val storageNameProperty = SimpleStringProperty("")
private var storageName by storageNameProperty
private val statusBar = StatusBar();
private val cache: MutableMap<NumassPoint, Table> = ConcurrentHashMap()
private val ampView: AmplitudeView by inject(params = mapOf("cache" to cache));
private val spectrumView: SpectrumView by inject(params = mapOf("cache" to cache));
private val ampView: AmplitudeView by inject();
private val spectrumView: SpectrumView by inject();
private val hvView: HVView by inject();
private val scView: SlowControlView by inject();
@ -57,6 +27,14 @@ class StorageView(private val context: Context = Global) : View(title = "Numass
val checkedProperty = SimpleBooleanProperty(false)
var checked by checkedProperty
val infoView: UIComponent? by lazy {
when (content) {
is NumassPoint -> PointInfoView(content)
is Metoid -> MetaViewer(content.meta, title = "Meta view: $id")
else -> null
}
}
init {
checkedProperty.onChange { selected ->
when (content) {
@ -90,7 +68,10 @@ class StorageView(private val context: Context = Global) : View(title = "Numass
val children: List<Container>? by lazy {
when (content) {
is Storage -> (content.shelves().sorted() + content.loaders().sorted()).map { buildContainer(it, this) }
is NumassSet -> content.points.map { buildContainer(it, this) }.toList().sortedBy { it.id }
is NumassSet -> content.points
.sorted(compareBy { it.index })
.map { buildContainer(it, this) }
.toList()
else -> null
}
}
@ -100,158 +81,87 @@ class StorageView(private val context: Context = Global) : View(title = "Numass
}
override val root = borderpane {
top {
toolbar {
prefHeight = 40.0
button("Load") {
action {
val chooser = DirectoryChooser()
chooser.title = "Select numass storage root"
val storageRoot = NumassProperties.getNumassProperty("numass.storage.root")
try {
if (storageRoot == null) {
chooser.initialDirectory = File(".").absoluteFile
} else {
chooser.initialDirectory = File(storageRoot)
}
} catch (ex: Exception) {
NumassProperties.setNumassProperty("numass.storage.root", null)
}
val rootDir = chooser.showDialog(primaryStage.scene.window)
if (rootDir != null) {
NumassProperties.setNumassProperty("numass.storage.root", rootDir.absolutePath)
loadDirectory(rootDir.toURI())
}
}
}
label(storageNameProperty) {
padding = Insets(0.0, 0.0, 0.0, 10.0);
font = Font.font("System Bold", 13.0);
}
pane {
hgrow = Priority.ALWAYS
}
togglebutton("Console") {
isSelected = false
LogFragment().apply {
addLogHandler(context.logger)
bindWindow(this@togglebutton)
}
}
override val root = splitpane {
treeview<Container> {
//isShowRoot = false
root = TreeItem(Container(storage.name, storage))
root.isExpanded = true
runGoal("viewer.storage.populateTree") {
populate { parent -> parent.value.children }
}
}
center {
splitpane {
treeview<Container> {
//isShowRoot = false
storageProperty.onChange {
if (it != null) {
root = TreeItem(Container(it.name, it))
root.isExpanded = true
runGoal("populateTree") {
runLater { statusBar.progress = -1.0 }
populate { parent ->
val value = parent.value.content
when (value) {
is Storage -> (value.shelves().sorted() + value.loaders().sorted()).map { buildContainer(it, parent.value) }
is NumassSet -> value.points.map { buildContainer(it, parent.value) }.toList().sortedBy { it.id }
else -> null
}
}
runLater { statusBar.progress = 0.0 }
}
/*
lazyPopulate( leafCheck = { it.value.hasChildren }) {
runLater { statusBar.progress = -1.0 }
it.value.children.also {
runLater { statusBar.progress = 0.0 }
}
}
*/
cellFormat { value ->
when (value.content) {
is Storage -> {
text = value.content.name
graphic = null
}
is NumassSet -> {
text = null
graphic = checkbox(value.content.name).apply {
selectedProperty().bindBidirectional(value.checkedProperty)
}
}
cellFormat { value ->
when (value.content) {
is Storage -> {
text = value.content.name
graphic = null
}
is NumassSet -> {
text = null
graphic = checkbox(value.content.name).apply {
selectedProperty().bindBidirectional(value.checkedProperty)
}
}
is NumassPoint -> {
text = null
graphic = checkbox("${value.content.voltage}[${value.content.index}]").apply {
selectedProperty().bindBidirectional(value.checkedProperty)
}
}
is TableLoader -> {
text = null
graphic = checkbox(value.content.name).apply {
selectedProperty().bindBidirectional(value.checkedProperty)
}
}
else -> {
text = value.id
graphic = null
}
is NumassPoint -> {
text = null
graphic = checkbox("${value.content.voltage}[${value.content.index}]").apply {
selectedProperty().bindBidirectional(value.checkedProperty)
}
contextMenu = ContextMenu()
contextMenu.item("Clear all") {
}
is TableLoader -> {
text = null
graphic = checkbox(value.content.name).apply {
selectedProperty().bindBidirectional(value.checkedProperty)
}
}
else -> {
text = value.id
graphic = null
}
}
contextMenu = ContextMenu().apply {
item("Clear all") {
action {
this@cellFormat.treeItem.uncheckAll()
}
}
value.infoView?.let {
item("Info") {
action {
this@cellFormat.treeItem.uncheckAll()
it.openModal(escapeClosesWindow = true)
}
}
if (value.content is Metoid) {
contextMenu.item("Meta") {
action {
openInternalWindow(MetaViewer(value.content.meta), escapeClosesWindow = true)
}
}
}
}
}
tabpane {
tab("Amplitude spectra") {
content = ampView.root
isClosable = false
//visibleWhen(ampView.isEmpty.not())
}
tab("HV") {
content = hvView.root
isClosable = false
//visibleWhen(hvView.isEmpty.not())
}
tab("Numass spectra") {
content = spectrumView.root
isClosable = false
//visibleWhen(spectrumView.isEmpty.not())
}
tab("Slow control") {
content = scView.root
isClosable = false
//visibleWhen(scView.isEmpty.not())
}
}
setDividerPosition(0, 0.3);
}
}
bottom = statusBar;
tabpane {
tab("Amplitude spectra") {
content = ampView.root
isClosable = false
//visibleWhen(ampView.isEmpty.not())
}
tab("HV") {
content = hvView.root
isClosable = false
//visibleWhen(hvView.isEmpty.not())
}
tab("Numass spectra") {
content = spectrumView.root
isClosable = false
//visibleWhen(spectrumView.isEmpty.not())
}
tab("Slow control") {
content = scView.root
isClosable = false
//visibleWhen(scView.isEmpty.not())
}
}
setDividerPosition(0, 0.3);
}
private fun TreeItem<Container>.uncheckAll() {
this.value.checked = false
this.children.forEach { it.uncheckAll() }
@ -280,21 +190,4 @@ class StorageView(private val context: Context = Global) : View(title = "Numass
else -> throw IllegalArgumentException("Unknown content type: ${content::class.java}");
}
private fun loadDirectory(path: URI) {
statusBar.text = "Loading storage: $path"
runGoal("loadDirectory[$path]") {
title = "Load storage ($path)"
message = "Building numass storage tree..."
StorageManager.buildStorage(context, NumassStorageFactory.buildStorageMeta(path, true, false))
} ui {
storage = it
storageName = "Storage: $path"
statusBar.text = "OK"
} except {
alert(type = Alert.AlertType.ERROR, header = "Error during storage loading", content = it.toString()).show()
it.printStackTrace()
}
}
}

View File

@ -4,6 +4,11 @@ import ch.qos.logback.classic.Level
import ch.qos.logback.classic.Logger
import hep.dataforge.context.Global
import hep.dataforge.fx.dfIcon
import hep.dataforge.meta.Meta
import hep.dataforge.tables.Table
import hep.dataforge.utils.Misc
import inr.numass.data.analyzers.SimpleAnalyzer
import inr.numass.data.api.NumassPoint
import javafx.stage.Stage
import org.slf4j.LoggerFactory
import tornadofx.*
@ -11,7 +16,7 @@ import tornadofx.*
/**
* Created by darksnake on 14-Apr-17.
*/
class Viewer : App(StorageView::class) {
class Viewer : App(MainView::class) {
init{
(LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME) as Logger).level = Level.INFO
}
@ -26,3 +31,15 @@ class Viewer : App(StorageView::class) {
Global.terminate();
}
}
/**
* Global point cache
*/
object PointCache{
private val analyzer = SimpleAnalyzer()
private val cache: MutableMap<NumassPoint, Table> = Misc.getLRUCache(1000)
operator fun get(point: NumassPoint): Table {
return cache.computeIfAbsent(point) { analyzer.getAmplitudeSpectrum(point, Meta.empty()) }
}
}