[WIP] directory watcher

This commit is contained in:
Alexander Nozik 2021-11-17 18:25:09 +03:00
parent d683170a73
commit f7d9aff838
6 changed files with 236 additions and 125 deletions

View File

@ -59,7 +59,28 @@ runtime {
) )
jpackage { jpackage {
jvmArgs = addJvmArgs jvmArgs = addJvmArgs
//imageOptions = listOf("--linux-deb-maintainer", "nozik.aa@mipt.ru", "--linux-menu-group", "Science") val currentOs = org.gradle.internal.os.OperatingSystem.current()
installerOptions = installerOptions + listOf("--vendor", "MIPT-NPM lab")
if (currentOs.isWindows) {
installerOptions = installerOptions + listOf(
"--win-menu",
"--win-menu-group", "Numass",
"--win-dir-chooser",
"--win-shortcut"
)
} else if (currentOs.isLinux) {
installerType = "deb"
installerOptions = installerOptions + listOf(
"--linux-package-name", "numass-viewer",
"--linux-shortcut"
)
imageOptions = listOf(
"--linux-deb-maintainer", "nozik.aa@mipt.ru",
"--linux-menu-group", "Science",
"--linux-shortcut"
)
}
} }
launcher { launcher {
jvmArgs = addJvmArgs jvmArgs = addJvmArgs

View File

@ -56,7 +56,7 @@ class AmplitudeView : View(title = "Numass amplitude spectrum plot", icon = Imag
var normalize by normalizeProperty var normalize by normalizeProperty
private val container = PlotContainer(frame).apply { private val plotContainer = PlotContainer(frame).apply {
val binningSelector: ChoiceBox<Int> = ChoiceBox(FXCollections.observableArrayList(1, 2, 8, 16, 32, 50)).apply { val binningSelector: ChoiceBox<Int> = ChoiceBox(FXCollections.observableArrayList(1, 2, 8, 16, 32, 50)).apply {
minWidth = 0.0 minWidth = 0.0
selectionModel.selectLast() selectionModel.selectLast()
@ -71,7 +71,6 @@ class AmplitudeView : View(title = "Numass amplitude spectrum plot", icon = Imag
private val plotJobs: ObservableMap<String, Job> = FXCollections.observableHashMap() private val plotJobs: ObservableMap<String, Job> = FXCollections.observableHashMap()
val isEmpty = booleanBinding(data) { isEmpty() }
private val progress = object : DoubleBinding() { private val progress = object : DoubleBinding() {
init { init {
@ -102,7 +101,7 @@ class AmplitudeView : View(title = "Numass amplitude spectrum plot", icon = Imag
replot() replot()
} }
container.progressProperty.bind(progress) plotContainer.progressProperty.bind(progress)
} }
private fun replotOne(key: String, point: DataController.CachedPoint) { private fun replotOne(key: String, point: DataController.CachedPoint) {
@ -159,6 +158,6 @@ class AmplitudeView : View(title = "Numass amplitude spectrum plot", icon = Imag
} }
override val root = borderpane { override val root = borderpane {
center = container.root center = plotContainer.root
} }
} }

View File

@ -30,6 +30,8 @@ class DataController : Controller() {
val voltage = point.voltage val voltage = point.voltage
val index = point.index
val meta = point.meta val meta = point.meta
val channelSpectra: Deferred<Map<Int, Table>> = context.async(Dispatchers.IO) { val channelSpectra: Deferred<Map<Int, Table>> = context.async(Dispatchers.IO) {

View File

@ -0,0 +1,107 @@
package inr.numass.viewer
import hep.dataforge.fx.dfIconView
import hep.dataforge.io.envelopes.Envelope
import inr.numass.data.NumassDataUtils
import inr.numass.data.NumassEnvelopeType
import inr.numass.data.storage.NumassDataLoader
import javafx.beans.property.SimpleObjectProperty
import javafx.collections.FXCollections
import javafx.collections.MapChangeListener
import javafx.scene.control.ContextMenu
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import tornadofx.*
import java.nio.file.Path
import java.nio.file.StandardWatchEventKinds.ENTRY_CREATE
import java.nio.file.WatchKey
class DirectoryWatchView : View(title = "Numass storage", icon = dfIconView) {
val pathProperty = SimpleObjectProperty<Path>()
private val dataController by inject<DataController>()
private val ampView: AmplitudeView by inject()
private val timeView: TimeView by inject()
private var watcherProperty = pathProperty.objectBinding {
it?.fileSystem?.newWatchService()
}
private val files = FXCollections.observableArrayList<DataController.CachedPoint>()
private var watchJob: Job? = null
init {
dataController.points.addListener(MapChangeListener { change ->
if (change.wasAdded()) {
files.add(change.valueAdded)
} else if (change.wasRemoved()) {
files.remove(change.valueRemoved)
}
})
watcherProperty.onChange { watchService ->
watchJob?.cancel()
if (watchService != null) {
watchJob = app.context.launch(Dispatchers.IO) {
val key: WatchKey = pathProperty.get().register(watchService, ENTRY_CREATE)
coroutineContext[Job]?.invokeOnCompletion {
key.cancel()
}
while (isActive) {
try {
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)
files.add(dataController.getCachedPoint(path.toString(), point))
}
}
}
} catch (x: Throwable) {
app.context.logger.error("Error during dynamic point read", x)
}
}
}
}
}
}
override val root = splitpane {
listview(files) {
multiSelect(true)
cellFormat { value: DataController.CachedPoint ->
text = "${value.voltage}[${value.index}]"
graphic = null
contextMenu = ContextMenu().apply {
item("Info") {
action {
PointInfoView(value).openModal(escapeClosesWindow = true)
}
}
}
}
}
tabpane {
tab("Amplitude spectra") {
content = ampView.root
isClosable = false
//visibleWhen(ampView.isEmpty.not())
}
tab("Time spectra") {
content = timeView.root
isClosable = false
//visibleWhen(ampView.isEmpty.not())
}
}
setDividerPosition(0, 0.3);
}
}

View File

@ -43,6 +43,17 @@ class MainView : View(title = "Numass viewer", icon = dfIconView) {
private val contentViewProperty = SimpleObjectProperty<UIComponent>() private val contentViewProperty = SimpleObjectProperty<UIComponent>()
private var contentView: UIComponent? by contentViewProperty private var contentView: UIComponent? by contentViewProperty
private val spectrumView by inject<SpectrumView>()
private val amplitudeView by inject<AmplitudeView>()
private val directoryWatchView by inject<DirectoryWatchView>()
init {
contentViewProperty.onChange {
root.center = it?.root
}
}
override val root = borderpane { override val root = borderpane {
prefHeight = 600.0 prefHeight = 600.0
@ -72,10 +83,38 @@ class MainView : View(title = "Numass viewer", icon = dfIconView) {
if (rootDir != null) { if (rootDir != null) {
NumassProperties.setNumassProperty("numass.viewer.lastPath", rootDir.absolutePath) NumassProperties.setNumassProperty("numass.viewer.lastPath", rootDir.absolutePath)
app.context.launch { app.context.launch {
dataController.clear()
runLater { runLater {
path = rootDir.toPath() path = rootDir.toPath()
contentView = null
}
if (Files.exists(path.resolve(NumassDataLoader.META_FRAGMENT_NAME))) {
//build set view
runGoal(app.context, "viewer.load.set[$path]", Dispatchers.IO) {
title = "Load set ($path)"
message = "Building numass set..."
NumassDataLoader(app.context, null, path.fileName.toString(), path)
} ui { loader: NumassDataLoader ->
contentView = spectrumView
dataController.addSet(loader.name, loader)
} except {
alert(
type = Alert.AlertType.ERROR,
header = "Error during set loading",
content = it.toString()
).show()
}
} else {
//build storage
app.context.launch {
val storageElement = NumassDirectory.INSTANCE.read(app.context, path) as Storage
withContext(Dispatchers.JavaFx) {
contentView = storageView
storageView.storageProperty.set(storageElement)
}
}
} }
load(rootDir.toPath())
} }
} }
} catch (ex: Exception) { } catch (ex: Exception) {
@ -102,10 +141,69 @@ class MainView : View(title = "Numass viewer", icon = dfIconView) {
NumassProperties.setNumassProperty("numass.viewer.lastPath", NumassProperties.setNumassProperty("numass.viewer.lastPath",
file.parentFile.absolutePath) file.parentFile.absolutePath)
app.context.launch { app.context.launch {
dataController.clear()
runLater { runLater {
path = file.toPath() path = file.toPath()
contentView = null
}
//Reading individual file
val envelope = try {
NumassFileEnvelope(path)
} 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 {
//try to read as point
val point = NumassDataUtils.read(it)
runLater {
contentView = amplitudeView
dataController.addPoint(path.fileName.toString(), point)
}
}
}
}
} catch (ex: Exception) {
NumassProperties.setNumassProperty("numass.viewer.lastPath", null)
error("Error", content = "Failed to laod file with message: ${ex.message}")
}
}
}
button("Watch 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 {
val file = File(homeDir)
if (file.isDirectory) {
chooser.initialDirectory = file
} else {
chooser.initialDirectory = file.parentFile
}
}
val dir = chooser.showDialog(primaryStage.scene.window)
if (dir != null) {
NumassProperties.setNumassProperty("numass.viewer.lastPath", dir.absolutePath)
app.context.launch {
dataController.clear()
runLater {
path = dir.toPath()
contentView = null
} }
load(file.toPath())
} }
} }
} catch (ex: Exception) { } catch (ex: Exception) {
@ -131,72 +229,4 @@ class MainView : View(title = "Numass viewer", icon = dfIconView) {
bottom = statusBar bottom = statusBar
} }
init {
contentViewProperty.onChange {
root.center = it?.root
}
}
private val spectrumView by inject<SpectrumView>()
private suspend fun load(path: Path) {
runLater {
contentView = null
}
dataController.clear()
if (Files.isDirectory(path)) {
if (Files.exists(path.resolve(NumassDataLoader.META_FRAGMENT_NAME))) {
//build set view
runGoal(app.context, "viewer.load.set[$path]", Dispatchers.IO) {
title = "Load set ($path)"
message = "Building numass set..."
NumassDataLoader(app.context, null, path.fileName.toString(), path)
} ui { loader: NumassDataLoader ->
contentView = spectrumView
dataController.addSet(loader.name, loader)
} except {
alert(
type = Alert.AlertType.ERROR,
header = "Error during set loading",
content = it.toString()
).show()
}
} else {
//build storage
app.context.launch {
val storageElement = NumassDirectory.INSTANCE.read(app.context, path) as Storage
withContext(Dispatchers.JavaFx) {
contentView = storageView
storageView.storageProperty.set(storageElement)
}
}
}
} else {
//Reading individual file
val envelope = try {
NumassFileEnvelope(path)
} 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 {
//try to read as point
val point = NumassDataUtils.read(it)
runLater {
contentView = AmplitudeView().apply {
dataController.addPoint(path.fileName.toString(), point)
}
}
}
}
}
} }

View File

@ -33,12 +33,6 @@ class StorageView : View(title = "Numass storage", icon = dfIconView) {
private val hvView: HVView by inject() private val hvView: HVView by inject()
private val scView: SlowControlView by inject() private val scView: SlowControlView by inject()
// private var watcher: WatchService? = null
fun clear() {
dataController.clear()
}
private inner class Container(val id: String, val content: Any) { private inner class Container(val id: String, val content: Any) {
val checkedProperty = SimpleBooleanProperty(false) val checkedProperty = SimpleBooleanProperty(false)
var checked by checkedProperty var checked by checkedProperty
@ -102,38 +96,6 @@ class StorageView : View(title = "Numass storage", icon = dfIconView) {
private var watchJob: Job? = null private var watchJob: Job? = null
// private fun toggleWatch(watch: Boolean) {
// if (watch) {
// if (watchJob != null && content is NumassDataLoader) {
// watchJob = app.context.launch(Dispatchers.IO) {
// val key: WatchKey = content.path.register(watcher!!, ENTRY_CREATE)
// coroutineContext[Job]?.invokeOnCompletion {
// key.cancel()
// }
// while (watcher != null && isActive) {
// try {
// 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)
// children!!.add(buildContainer(point, this@Container))
// }
// }
// }
// } catch (x: Throwable) {
// app.context.logger.error("Error during dynamic point read", x)
// }
// }
// }
// }
// } else {
// watchJob?.cancel()
// watchJob = null
// }
// }
} }
@ -141,7 +103,7 @@ class StorageView : View(title = "Numass storage", icon = dfIconView) {
treeview<Container> { treeview<Container> {
//isShowRoot = false //isShowRoot = false
storageProperty.onChange { storage -> storageProperty.onChange { storage ->
clear() dataController.clear()
if (storage == null) return@onChange if (storage == null) return@onChange
root = TreeItem(Container(storage.name, storage)) root = TreeItem(Container(storage.name, storage))
root.isExpanded = true root.isExpanded = true
@ -150,12 +112,7 @@ class StorageView : View(title = "Numass storage", icon = dfIconView) {
}) { }) {
it.value.children it.value.children
} }
// watcher?.close()
// watcher = if (storage is FileStorage) {
// storage.path.fileSystem.newWatchService()
// } else {
// null
// }
} }
cellFormat { value: Container -> cellFormat { value: Container ->
@ -198,11 +155,6 @@ class StorageView : View(title = "Numass storage", icon = dfIconView) {
value.infoView.openModal(escapeClosesWindow = true) value.infoView.openModal(escapeClosesWindow = true)
} }
} }
// if(value.content is NumassDataLoader) {
// checkmenuitem("Watch") {
// selectedProperty().bindBidirectional(value.watchedProperty)
// }
// }
} }
} }
} }