[WIP] first commit
This commit is contained in:
parent
2525c3d0ec
commit
ddbd9bf3cd
9
.gitignore
vendored
9
.gitignore
vendored
@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
.idea/
|
||||||
|
*.iws
|
||||||
|
out/
|
||||||
|
.gradle
|
||||||
|
build/
|
||||||
|
|
||||||
|
|
||||||
|
!gradle-wrapper.jar
|
14
build.gradle.kts
Normal file
14
build.gradle.kts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
plugins {
|
||||||
|
id("ru.mipt.npm.project")
|
||||||
|
}
|
||||||
|
|
||||||
|
group = "ru.inr.mass"
|
||||||
|
version = "0.1.0-SHAPSHOT"
|
||||||
|
|
||||||
|
val dataforgeVersion by extra("0.3.0-dev")
|
||||||
|
|
||||||
|
val spaceRepo by extra("https://maven.pkg.jetbrains.space/mipt-npm/p/numass/maven")
|
||||||
|
|
||||||
|
apiValidation{
|
||||||
|
validationDisabled = true
|
||||||
|
}
|
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
5
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
5
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8-bin.zip
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
185
gradlew
vendored
Normal file
185
gradlew
vendored
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright 2015 the original author or authors.
|
||||||
|
#
|
||||||
|
# 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
|
||||||
|
#
|
||||||
|
# https://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.
|
||||||
|
#
|
||||||
|
|
||||||
|
##############################################################################
|
||||||
|
##
|
||||||
|
## Gradle start up script for UN*X
|
||||||
|
##
|
||||||
|
##############################################################################
|
||||||
|
|
||||||
|
# Attempt to set APP_HOME
|
||||||
|
# Resolve links: $0 may be a link
|
||||||
|
PRG="$0"
|
||||||
|
# Need this for relative symlinks.
|
||||||
|
while [ -h "$PRG" ] ; do
|
||||||
|
ls=`ls -ld "$PRG"`
|
||||||
|
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||||
|
if expr "$link" : '/.*' > /dev/null; then
|
||||||
|
PRG="$link"
|
||||||
|
else
|
||||||
|
PRG=`dirname "$PRG"`"/$link"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
SAVED="`pwd`"
|
||||||
|
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||||
|
APP_HOME="`pwd -P`"
|
||||||
|
cd "$SAVED" >/dev/null
|
||||||
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
|
APP_BASE_NAME=`basename "$0"`
|
||||||
|
|
||||||
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
|
MAX_FD="maximum"
|
||||||
|
|
||||||
|
warn () {
|
||||||
|
echo "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
die () {
|
||||||
|
echo
|
||||||
|
echo "$*"
|
||||||
|
echo
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# OS specific support (must be 'true' or 'false').
|
||||||
|
cygwin=false
|
||||||
|
msys=false
|
||||||
|
darwin=false
|
||||||
|
nonstop=false
|
||||||
|
case "`uname`" in
|
||||||
|
CYGWIN* )
|
||||||
|
cygwin=true
|
||||||
|
;;
|
||||||
|
Darwin* )
|
||||||
|
darwin=true
|
||||||
|
;;
|
||||||
|
MINGW* )
|
||||||
|
msys=true
|
||||||
|
;;
|
||||||
|
NONSTOP* )
|
||||||
|
nonstop=true
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
# Determine the Java command to use to start the JVM.
|
||||||
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
|
# IBM's JDK on AIX uses strange locations for the executables
|
||||||
|
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||||
|
else
|
||||||
|
JAVACMD="$JAVA_HOME/bin/java"
|
||||||
|
fi
|
||||||
|
if [ ! -x "$JAVACMD" ] ; then
|
||||||
|
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
JAVACMD="java"
|
||||||
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
|
||||||
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
location of your Java installation."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Increase the maximum file descriptors if we can.
|
||||||
|
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||||
|
MAX_FD_LIMIT=`ulimit -H -n`
|
||||||
|
if [ $? -eq 0 ] ; then
|
||||||
|
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||||
|
MAX_FD="$MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
ulimit -n $MAX_FD
|
||||||
|
if [ $? -ne 0 ] ; then
|
||||||
|
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Darwin, add options to specify how the application appears in the dock
|
||||||
|
if $darwin; then
|
||||||
|
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
|
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||||
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
|
|
||||||
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
|
||||||
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
|
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||||
|
SEP=""
|
||||||
|
for dir in $ROOTDIRSRAW ; do
|
||||||
|
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||||
|
SEP="|"
|
||||||
|
done
|
||||||
|
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||||
|
# Add a user-defined pattern to the cygpath arguments
|
||||||
|
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||||
|
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||||
|
fi
|
||||||
|
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||||
|
i=0
|
||||||
|
for arg in "$@" ; do
|
||||||
|
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||||
|
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||||
|
|
||||||
|
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||||
|
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||||
|
else
|
||||||
|
eval `echo args$i`="\"$arg\""
|
||||||
|
fi
|
||||||
|
i=`expr $i + 1`
|
||||||
|
done
|
||||||
|
case $i in
|
||||||
|
0) set -- ;;
|
||||||
|
1) set -- "$args0" ;;
|
||||||
|
2) set -- "$args0" "$args1" ;;
|
||||||
|
3) set -- "$args0" "$args1" "$args2" ;;
|
||||||
|
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||||
|
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||||
|
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||||
|
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||||
|
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||||
|
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Escape application args
|
||||||
|
save () {
|
||||||
|
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||||
|
echo " "
|
||||||
|
}
|
||||||
|
APP_ARGS=`save "$@"`
|
||||||
|
|
||||||
|
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||||
|
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||||
|
|
||||||
|
exec "$JAVACMD" "$@"
|
89
gradlew.bat
vendored
Normal file
89
gradlew.bat
vendored
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
@rem
|
||||||
|
@rem Copyright 2015 the original author or authors.
|
||||||
|
@rem
|
||||||
|
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@rem you may not use this file except in compliance with the License.
|
||||||
|
@rem You may obtain a copy of the License at
|
||||||
|
@rem
|
||||||
|
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
@rem
|
||||||
|
@rem Unless required by applicable law or agreed to in writing, software
|
||||||
|
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
@rem See the License for the specific language governing permissions and
|
||||||
|
@rem limitations under the License.
|
||||||
|
@rem
|
||||||
|
|
||||||
|
@if "%DEBUG%" == "" @echo off
|
||||||
|
@rem ##########################################################################
|
||||||
|
@rem
|
||||||
|
@rem Gradle startup script for Windows
|
||||||
|
@rem
|
||||||
|
@rem ##########################################################################
|
||||||
|
|
||||||
|
@rem Set local scope for the variables with windows NT shell
|
||||||
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
|
set DIRNAME=%~dp0
|
||||||
|
if "%DIRNAME%" == "" set DIRNAME=.
|
||||||
|
set APP_BASE_NAME=%~n0
|
||||||
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||||
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
|
@rem Find java.exe
|
||||||
|
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||||
|
|
||||||
|
set JAVA_EXE=java.exe
|
||||||
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
|
if "%ERRORLEVEL%" == "0" goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:findJavaFromJavaHome
|
||||||
|
set JAVA_HOME=%JAVA_HOME:"=%
|
||||||
|
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||||
|
|
||||||
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
|
echo.
|
||||||
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
|
echo location of your Java installation.
|
||||||
|
|
||||||
|
goto fail
|
||||||
|
|
||||||
|
:execute
|
||||||
|
@rem Setup the command line
|
||||||
|
|
||||||
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
|
@rem Execute Gradle
|
||||||
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||||
|
|
||||||
|
:end
|
||||||
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||||
|
|
||||||
|
:fail
|
||||||
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
|
rem the _cmd.exe /c_ return code!
|
||||||
|
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||||
|
exit /b 1
|
||||||
|
|
||||||
|
:mainEnd
|
||||||
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
|
||||||
|
:omega
|
10
numass-data-model/build.gradle.kts
Normal file
10
numass-data-model/build.gradle.kts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
plugins {
|
||||||
|
kotlin("jvm")
|
||||||
|
id("ru.mipt.npm.kscience")
|
||||||
|
}
|
||||||
|
|
||||||
|
val dataforgeVersion: String by rootProject.extra
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
api("hep.dataforge:dataforge-context:$dataforgeVersion")
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
package ru.inr.mass.data.api
|
||||||
|
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.asFlow
|
||||||
|
import kotlinx.coroutines.flow.flatMap
|
||||||
|
import kotlinx.coroutines.flow.flatMapConcat
|
||||||
|
import java.time.Duration
|
||||||
|
import java.time.Instant
|
||||||
|
import java.util.stream.Stream
|
||||||
|
|
||||||
|
public interface ParentBlock : NumassBlock {
|
||||||
|
public val blocks: List<NumassBlock>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If true, the sub-blocks a considered to be isSequential, if not, the sub-blocks are parallel
|
||||||
|
*/
|
||||||
|
public val isSequential: Boolean get() = true
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A block constructed from a set of other blocks. Internal blocks are not necessary subsequent. Blocks are automatically sorted.
|
||||||
|
* Created by darksnake on 16.07.2017.
|
||||||
|
*/
|
||||||
|
public class MetaBlock(override val blocks: List<NumassBlock>) : ParentBlock {
|
||||||
|
|
||||||
|
override val startTime: Instant
|
||||||
|
get() = blocks.first().startTime
|
||||||
|
|
||||||
|
override val length: Duration
|
||||||
|
get() = Duration.ofNanos(blocks.stream().mapToLong { block -> block.length.toNanos() }.sum())
|
||||||
|
|
||||||
|
override val events: Flow<NumassEvent>
|
||||||
|
get() = blocks.sortedBy { it.startTime }.asFlow().flatMapConcat { it.events }
|
||||||
|
|
||||||
|
override val frames: Flow<NumassFrame>
|
||||||
|
get() = blocks.sortedBy { it.startTime }.asFlow().flatMapConcat { it.frames }
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,114 @@
|
|||||||
|
/*
|
||||||
|
* 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 ru.inr.mass.data.api
|
||||||
|
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.asFlow
|
||||||
|
import kotlinx.coroutines.flow.emptyFlow
|
||||||
|
import java.time.Duration
|
||||||
|
import java.time.Instant
|
||||||
|
|
||||||
|
public open class OrphanNumassEvent(public val amplitude: Short, public val timeOffset: Long) :
|
||||||
|
Comparable<OrphanNumassEvent> {
|
||||||
|
public operator fun component1(): Short = amplitude
|
||||||
|
public operator fun component2(): Long = timeOffset
|
||||||
|
|
||||||
|
override fun compareTo(other: OrphanNumassEvent): Int {
|
||||||
|
return this.timeOffset.compareTo(other.timeOffset)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A single numass event with given amplitude and time.
|
||||||
|
*
|
||||||
|
* @author Darksnake
|
||||||
|
* @property amp the amplitude of the event
|
||||||
|
* @property timeOffset time in nanoseconds relative to block start
|
||||||
|
* @property owner an owner block for this event
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class NumassEvent(amplitude: Short, timeOffset: Long, public val owner: NumassBlock) :
|
||||||
|
OrphanNumassEvent(amplitude, timeOffset) {
|
||||||
|
|
||||||
|
public val channel: Int get() = owner.channel
|
||||||
|
|
||||||
|
public val time: Instant get() = owner.startTime.plusNanos(timeOffset)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A single continuous measurement block. The block can contain both isolated events and signal frames
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Created by darksnake on 06-Jul-17.
|
||||||
|
*/
|
||||||
|
public interface NumassBlock {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The absolute start time of the block
|
||||||
|
*/
|
||||||
|
public val startTime: Instant
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The length of the block
|
||||||
|
*/
|
||||||
|
public val length: Duration
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stream of isolated events. Could be empty
|
||||||
|
*/
|
||||||
|
public val events: Flow<NumassEvent>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stream of frames. Could be empty
|
||||||
|
*/
|
||||||
|
public val frames: Flow<NumassFrame>
|
||||||
|
|
||||||
|
public val channel: Int get() = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun OrphanNumassEvent.adopt(parent: NumassBlock): NumassEvent {
|
||||||
|
return NumassEvent(this.amplitude, this.timeOffset, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple in-memory implementation of block of events. No frames are allowed
|
||||||
|
* Created by darksnake on 08.07.2017.
|
||||||
|
*/
|
||||||
|
public class SimpleBlock(
|
||||||
|
override val startTime: Instant,
|
||||||
|
override val length: Duration,
|
||||||
|
rawEvents: Iterable<OrphanNumassEvent>,
|
||||||
|
) : NumassBlock {
|
||||||
|
|
||||||
|
private val eventList by lazy { rawEvents.map { it.adopt(this) } }
|
||||||
|
|
||||||
|
override val frames: Flow<NumassFrame> get() = emptyFlow()
|
||||||
|
|
||||||
|
override val events: Flow<NumassEvent> get() = eventList.asFlow()
|
||||||
|
|
||||||
|
public companion object {
|
||||||
|
public suspend fun produce(
|
||||||
|
startTime: Instant,
|
||||||
|
length: Duration,
|
||||||
|
producer: suspend () -> Iterable<OrphanNumassEvent>,
|
||||||
|
): SimpleBlock {
|
||||||
|
return SimpleBlock(startTime, length, producer())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,27 @@
|
|||||||
|
package ru.inr.mass.data.api
|
||||||
|
|
||||||
|
import java.nio.ShortBuffer
|
||||||
|
import java.time.Duration
|
||||||
|
import java.time.Instant
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The continuous frame of digital detector data
|
||||||
|
* Created by darksnake on 06-Jul-17.
|
||||||
|
*/
|
||||||
|
public class NumassFrame(
|
||||||
|
/**
|
||||||
|
* The absolute start time of the frame
|
||||||
|
*/
|
||||||
|
public val time: Instant,
|
||||||
|
/**
|
||||||
|
* The time interval per tick
|
||||||
|
*/
|
||||||
|
public val tickSize: Duration,
|
||||||
|
/**
|
||||||
|
* The buffered signal shape in ticks
|
||||||
|
*/
|
||||||
|
public val signal: ShortBuffer) {
|
||||||
|
|
||||||
|
public val length: Duration
|
||||||
|
get() = tickSize.multipliedBy(signal.capacity().toLong())
|
||||||
|
}
|
@ -0,0 +1,130 @@
|
|||||||
|
/*
|
||||||
|
* 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 ru.inr.mass.data.api
|
||||||
|
|
||||||
|
import hep.dataforge.meta.*
|
||||||
|
import hep.dataforge.names.Name
|
||||||
|
import hep.dataforge.names.toName
|
||||||
|
import hep.dataforge.provider.Provider
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.asFlow
|
||||||
|
import kotlinx.coroutines.flow.flatMap
|
||||||
|
import kotlinx.coroutines.flow.flatMapConcat
|
||||||
|
import java.time.Duration
|
||||||
|
import java.time.Instant
|
||||||
|
import java.util.stream.Stream
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by darksnake on 06-Jul-17.
|
||||||
|
*/
|
||||||
|
public interface NumassPoint : ParentBlock, Provider {
|
||||||
|
|
||||||
|
public val meta: Meta
|
||||||
|
|
||||||
|
override val blocks: List<NumassBlock>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Distinct map of channel number to corresponding grouping block
|
||||||
|
*/
|
||||||
|
public val channels: Map<Int, NumassBlock>
|
||||||
|
get() = blocks.toList().groupBy { it.channel }.mapValues { entry ->
|
||||||
|
if (entry.value.size == 1) {
|
||||||
|
entry.value.first()
|
||||||
|
} else {
|
||||||
|
MetaBlock(entry.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun content(target: String): Map<Name, Any> = when (target) {
|
||||||
|
NUMASS_BLOCK_TARGET -> blocks.mapIndexed { index, numassBlock ->
|
||||||
|
"block[$index]".toName() to numassBlock
|
||||||
|
}.toMap()
|
||||||
|
NUMASS_CHANNEL_TARGET -> channels.mapKeys { "channel[${it.key}]".toName() }
|
||||||
|
else -> super.content(target)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the voltage setting for the point
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public val voltage: Double get() = meta[HV_KEY].double ?: 0.0
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the index for this point in the set
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public val index: Int get() = meta[INDEX_KEY].int ?: -1
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the starting time from meta or from first block
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
override val startTime: Instant
|
||||||
|
get() = meta[START_TIME_KEY]?.long?.let { Instant.ofEpochMilli(it) } ?: firstBlock.startTime
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the length key of meta or calculate length as a sum of block lengths. The latter could be a bit slow
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
override val length: Duration
|
||||||
|
get() = Duration.ofNanos(blocks.stream().filter { it.channel == 0 }.mapToLong { it -> it.length.toNanos() }
|
||||||
|
.sum())
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all events it all blocks as a single sequence
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Some performance analysis of different stream concatenation approaches is given here: https://www.techempower.com/blog/2016/10/19/efficient-multiple-stream-concatenation-in-java/
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
override val events: Flow<NumassEvent>
|
||||||
|
get() = blocks.asFlow().flatMapConcat { it.events }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all frames in all blocks as a single sequence
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
override val frames: Flow<NumassFrame>
|
||||||
|
get() = blocks.asFlow().flatMapConcat { it.frames }
|
||||||
|
|
||||||
|
|
||||||
|
override val isSequential: Boolean
|
||||||
|
get() = channels.size == 1
|
||||||
|
|
||||||
|
public companion object {
|
||||||
|
public const val NUMASS_BLOCK_TARGET: String = "block"
|
||||||
|
public const val NUMASS_CHANNEL_TARGET: String = "channel"
|
||||||
|
|
||||||
|
public const val START_TIME_KEY: String = "start"
|
||||||
|
public const val LENGTH_KEY: String = "length"
|
||||||
|
public const val HV_KEY: String = "voltage"
|
||||||
|
public const val INDEX_KEY: String = "index"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the first block if it exists. Throw runtime exception otherwise.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public val NumassPoint.firstBlock: NumassBlock
|
||||||
|
get() = blocks.firstOrNull() ?: throw RuntimeException("The point is empty")
|
@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* To change this license header, choose License Headers in Project Properties.
|
||||||
|
* To change this template file, choose Tools | Templates
|
||||||
|
* and open the template in the editor.
|
||||||
|
*/
|
||||||
|
package ru.inr.mass.data.api
|
||||||
|
|
||||||
|
import hep.dataforge.context.Named
|
||||||
|
import hep.dataforge.meta.Meta
|
||||||
|
import hep.dataforge.meta.get
|
||||||
|
import hep.dataforge.meta.long
|
||||||
|
import hep.dataforge.names.Name
|
||||||
|
import hep.dataforge.names.toName
|
||||||
|
import hep.dataforge.provider.Provider
|
||||||
|
import java.time.Instant
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A single set of numass points previously called file.
|
||||||
|
*
|
||||||
|
* @author [Alexander Nozik](mailto:altavir@gmail.com)
|
||||||
|
*/
|
||||||
|
public interface NumassSet : Named, Iterable<NumassPoint>, Provider {
|
||||||
|
|
||||||
|
public val meta: Meta
|
||||||
|
|
||||||
|
public val points: List<NumassPoint>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the starting time from meta or from first point
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public val startTime: Instant
|
||||||
|
get() = meta[NumassPoint.START_TIME_KEY].long?.let { Instant.ofEpochMilli(it) } ?: firstPoint.startTime
|
||||||
|
|
||||||
|
//suspend fun getHvData(): Table?
|
||||||
|
|
||||||
|
override fun iterator(): Iterator<NumassPoint> {
|
||||||
|
return points.iterator()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override val defaultTarget: String get() = NUMASS_POINT_PROVIDER_KEY
|
||||||
|
|
||||||
|
override fun content(target: String): Map<Name, Any> {
|
||||||
|
return if(target == NUMASS_POINT_PROVIDER_KEY){
|
||||||
|
points.associate { "point[${it.voltage}]".toName() to it }
|
||||||
|
}else {
|
||||||
|
super.content(target)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val DESCRIPTION_KEY = "info"
|
||||||
|
const val NUMASS_POINT_PROVIDER_KEY = "point"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List all points with given voltage
|
||||||
|
*
|
||||||
|
* @param voltage
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public fun NumassSet.getPoints(voltage: Double): List<NumassPoint> {
|
||||||
|
return points.filter { it -> it.voltage == voltage }.toList()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find first point with given voltage
|
||||||
|
*
|
||||||
|
* @param voltage
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public fun NumassSet.pointOrNull(voltage: Double): NumassPoint? {
|
||||||
|
return points.firstOrNull { it -> it.voltage == voltage }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the first point if it exists. Throw runtime exception otherwise.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public val NumassSet.firstPoint: NumassPoint
|
||||||
|
get() = points.firstOrNull() ?: throw RuntimeException("The set is empty")
|
@ -0,0 +1,11 @@
|
|||||||
|
package ru.inr.mass.data.api
|
||||||
|
|
||||||
|
import java.util.stream.Stream
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An ancestor to numass frame analyzers
|
||||||
|
* Created by darksnake on 07.07.2017.
|
||||||
|
*/
|
||||||
|
public interface SignalProcessor {
|
||||||
|
public fun analyze(frame: NumassFrame): Stream<NumassEvent>
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package ru.inr.mass.data.api
|
||||||
|
|
||||||
|
import hep.dataforge.meta.Meta
|
||||||
|
import hep.dataforge.meta.MetaBuilder
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple static implementation of NumassPoint
|
||||||
|
* Created by darksnake on 08.07.2017.
|
||||||
|
*/
|
||||||
|
public class SimpleNumassPoint(
|
||||||
|
override val blocks: List<NumassBlock>,
|
||||||
|
override val meta: Meta,
|
||||||
|
override val isSequential: Boolean = true,
|
||||||
|
) : NumassPoint {
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Input blocks must be sorted
|
||||||
|
// * @param voltage
|
||||||
|
// * @param blocks
|
||||||
|
// */
|
||||||
|
// constructor(blocks: Collection<NumassBlock>, voltage: Double) :
|
||||||
|
// this(blocks.sortedBy { it.startTime }, MetaBuilder("point").setValue(NumassPoint.HV_KEY, voltage))
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (blocks.isEmpty()) {
|
||||||
|
throw IllegalArgumentException("No blocks in collection")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
53
numass-data-proto/build.gradle.kts
Normal file
53
numass-data-proto/build.gradle.kts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import com.google.protobuf.gradle.*
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
java
|
||||||
|
kotlin("jvm")
|
||||||
|
id("ru.mipt.npm.kscience")
|
||||||
|
id("com.google.protobuf") version "0.8.14"
|
||||||
|
}
|
||||||
|
|
||||||
|
val dataforgeVersion: String by rootProject.extra
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
api(project(":numass-data-model"))
|
||||||
|
api("hep.dataforge:dataforge-workspace:$dataforgeVersion")
|
||||||
|
implementation("com.google.protobuf:protobuf-java:3.14.0")
|
||||||
|
implementation("javax.annotation:javax.annotation-api:1.3.1")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
|
||||||
|
dependsOn(":numass-data-proto:generateProto")
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
create("proto") {
|
||||||
|
proto {
|
||||||
|
srcDir("src/main/proto")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
create("gen"){
|
||||||
|
java{
|
||||||
|
srcDir("gen/main/java")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//kotlin{
|
||||||
|
// sourceSets{
|
||||||
|
// main{
|
||||||
|
// de
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
protobuf {
|
||||||
|
// Configure the protoc executable
|
||||||
|
protoc {
|
||||||
|
// Download from repositories
|
||||||
|
artifact = "com.google.protobuf:protoc:3.14.0"
|
||||||
|
}
|
||||||
|
|
||||||
|
generatedFilesBaseDir = "$projectDir/gen"
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,139 @@
|
|||||||
|
///*
|
||||||
|
// * 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 ru.inr.mass.data.proto
|
||||||
|
//
|
||||||
|
//import hep.dataforge.io.envelopes.*
|
||||||
|
//import hep.dataforge.values.Value
|
||||||
|
//import org.slf4j.LoggerFactory
|
||||||
|
//import java.io.IOException
|
||||||
|
//import java.nio.ByteBuffer
|
||||||
|
//import java.nio.channels.FileChannel
|
||||||
|
//import java.nio.file.Path
|
||||||
|
//import java.nio.file.StandardOpenOption
|
||||||
|
//import java.util.*
|
||||||
|
//
|
||||||
|
///**
|
||||||
|
// * An envelope type for legacy numass tags. Reads legacy tag and writes DF02 tags
|
||||||
|
// */
|
||||||
|
//class NumassEnvelopeType : EnvelopeType {
|
||||||
|
//
|
||||||
|
// override val code: Int = DefaultEnvelopeType.DEFAULT_ENVELOPE_CODE
|
||||||
|
//
|
||||||
|
// override val name: String = "numass"
|
||||||
|
//
|
||||||
|
// override fun description(): String = "Numass legacy envelope"
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * Read as legacy
|
||||||
|
// */
|
||||||
|
// override fun getReader(properties: Map<String, String>): EnvelopeReader {
|
||||||
|
// return NumassEnvelopeReader()
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * Write as default
|
||||||
|
// */
|
||||||
|
// override fun getWriter(properties: Map<String, String>): EnvelopeWriter {
|
||||||
|
// return DefaultEnvelopeWriter(this, MetaType.resolve(properties))
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// class LegacyTag : EnvelopeTag() {
|
||||||
|
// override val startSequence: ByteArray
|
||||||
|
// get() = LEGACY_START_SEQUENCE
|
||||||
|
//
|
||||||
|
// override val endSequence: ByteArray
|
||||||
|
// get() = LEGACY_END_SEQUENCE
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * Get the length of tag in bytes. -1 means undefined size in case tag was modified
|
||||||
|
// *
|
||||||
|
// * @return
|
||||||
|
// */
|
||||||
|
// override val length: Int
|
||||||
|
// get() = 30
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * Read leagscy version 1 tag without leading tag head
|
||||||
|
// *
|
||||||
|
// * @param buffer
|
||||||
|
// * @return
|
||||||
|
// * @throws IOException
|
||||||
|
// */
|
||||||
|
// override fun readHeader(buffer: ByteBuffer): Map<String, Value> {
|
||||||
|
// val res = HashMap<String, Value>()
|
||||||
|
//
|
||||||
|
// val type = buffer.getInt(2)
|
||||||
|
// res[Envelope.TYPE_PROPERTY] = Value.of(type)
|
||||||
|
//
|
||||||
|
// val metaTypeCode = buffer.getShort(10)
|
||||||
|
// val metaType = MetaType.resolve(metaTypeCode)
|
||||||
|
//
|
||||||
|
// if (metaType != null) {
|
||||||
|
// res[Envelope.META_TYPE_PROPERTY] = metaType.name.parseValue()
|
||||||
|
// } else {
|
||||||
|
// LoggerFactory.getLogger(EnvelopeTag::class.java).warn("Could not resolve meta type. Using default")
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// val metaLength = Integer.toUnsignedLong(buffer.getInt(14))
|
||||||
|
// res[Envelope.META_LENGTH_PROPERTY] = Value.of(metaLength)
|
||||||
|
// val dataLength = Integer.toUnsignedLong(buffer.getInt(22))
|
||||||
|
// res[Envelope.DATA_LENGTH_PROPERTY] = Value.of(dataLength)
|
||||||
|
// return res
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private class NumassEnvelopeReader : DefaultEnvelopeReader() {
|
||||||
|
// override fun newTag(): EnvelopeTag {
|
||||||
|
// return LegacyTag()
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// companion object {
|
||||||
|
// val INSTANCE = NumassEnvelopeType()
|
||||||
|
//
|
||||||
|
// val LEGACY_START_SEQUENCE = byteArrayOf('#'.toByte(), '!'.toByte())
|
||||||
|
// val LEGACY_END_SEQUENCE = byteArrayOf('!'.toByte(), '#'.toByte(), '\r'.toByte(), '\n'.toByte())
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * Replacement for standard type infer to include legacy type
|
||||||
|
// *
|
||||||
|
// * @param path
|
||||||
|
// * @return
|
||||||
|
// */
|
||||||
|
// fun infer(path: Path): EnvelopeType? {
|
||||||
|
// return try {
|
||||||
|
// FileChannel.open(path, StandardOpenOption.READ).use {
|
||||||
|
// val buffer = it.map(FileChannel.MapMode.READ_ONLY, 0, 6)
|
||||||
|
// when {
|
||||||
|
// //TODO use templates from appropriate types
|
||||||
|
// buffer.get(0) == '#'.toByte() && buffer.get(1) == '!'.toByte() -> INSTANCE
|
||||||
|
// buffer.get(0) == '#'.toByte() && buffer.get(1) == '!'.toByte() &&
|
||||||
|
// buffer.get(4) == 'T'.toByte() && buffer.get(5) == 'L'.toByte() -> TaglessEnvelopeType.INSTANCE
|
||||||
|
// buffer.get(0) == '#'.toByte() && buffer.get(1) == '~'.toByte() -> DefaultEnvelopeType.INSTANCE
|
||||||
|
// else -> null
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// } catch (ex: Exception) {
|
||||||
|
// LoggerFactory.getLogger(EnvelopeType::class.java).warn("Could not infer envelope type of file {} due to exception: {}", path, ex)
|
||||||
|
// null
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//}
|
@ -0,0 +1,52 @@
|
|||||||
|
///*
|
||||||
|
// * 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 ru.inr.mass.data.proto
|
||||||
|
//
|
||||||
|
//import hep.dataforge.meta.Meta
|
||||||
|
//import hep.dataforge.storage.files.MutableFileEnvelope
|
||||||
|
//import java.nio.ByteBuffer
|
||||||
|
//import java.nio.file.Files
|
||||||
|
//import java.nio.file.Path
|
||||||
|
//import java.nio.file.StandardOpenOption
|
||||||
|
//
|
||||||
|
//class NumassFileEnvelope(path: Path) : MutableFileEnvelope(path) {
|
||||||
|
//
|
||||||
|
// private val tag by lazy { Files.newByteChannel(path, StandardOpenOption.READ).use { NumassEnvelopeType.LegacyTag().read(it) } }
|
||||||
|
//
|
||||||
|
// override val dataOffset: Long by lazy { (tag.length + tag.metaSize).toLong() }
|
||||||
|
//
|
||||||
|
// override var dataLength: Int
|
||||||
|
// get() = tag.dataSize
|
||||||
|
// set(value) {
|
||||||
|
// if (value > Int.MAX_VALUE) {
|
||||||
|
// throw RuntimeException("Too large data block")
|
||||||
|
// }
|
||||||
|
// tag.dataSize = value
|
||||||
|
// if (channel.write(tag.toBytes(), 0L) < tag.length) {
|
||||||
|
// throw error("Tag is not overwritten.")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// override val meta: Meta by lazy {
|
||||||
|
// val buffer = ByteBuffer.allocate(tag.metaSize).also {
|
||||||
|
// channel.read(it, tag.length.toLong())
|
||||||
|
// }
|
||||||
|
// tag.metaType.reader.readBuffer(buffer)
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//
|
@ -0,0 +1,20 @@
|
|||||||
|
package ru.inr.mass.data.proto
|
||||||
|
|
||||||
|
import hep.dataforge.context.AbstractPlugin
|
||||||
|
import hep.dataforge.context.Context
|
||||||
|
import hep.dataforge.context.PluginFactory
|
||||||
|
import hep.dataforge.context.PluginTag
|
||||||
|
import hep.dataforge.io.IOPlugin
|
||||||
|
import hep.dataforge.meta.Meta
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
public class NumassProtoPlugin : AbstractPlugin() {
|
||||||
|
val io by require(IOPlugin)
|
||||||
|
override val tag: PluginTag get() = Companion.tag
|
||||||
|
|
||||||
|
public companion object : PluginFactory<NumassProtoPlugin> {
|
||||||
|
override fun invoke(meta: Meta, context: Context): NumassProtoPlugin = NumassProtoPlugin()
|
||||||
|
override val tag: PluginTag = PluginTag("numass-proto", group = "ru.inr.mass")
|
||||||
|
override val type: KClass<out NumassProtoPlugin> = NumassProtoPlugin::class
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,180 @@
|
|||||||
|
/*
|
||||||
|
* 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 ru.inr.mass.data.proto
|
||||||
|
|
||||||
|
import hep.dataforge.context.Context
|
||||||
|
import hep.dataforge.io.Envelope
|
||||||
|
import hep.dataforge.io.io
|
||||||
|
import hep.dataforge.io.readEnvelopeFile
|
||||||
|
import hep.dataforge.meta.*
|
||||||
|
import kotlinx.coroutines.flow.*
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import kotlinx.io.asInputStream
|
||||||
|
import kotlinx.io.readByteArray
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
import ru.inr.mass.data.api.*
|
||||||
|
import java.io.ByteArrayInputStream
|
||||||
|
import java.io.ByteArrayOutputStream
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.nio.file.Path
|
||||||
|
import java.time.Duration
|
||||||
|
import java.time.Instant
|
||||||
|
import java.util.zip.Inflater
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Protobuf based numass point
|
||||||
|
* Created by Alexander Nozik on 09.07.2017.
|
||||||
|
*/
|
||||||
|
public class ProtoNumassPoint(
|
||||||
|
override val meta: Meta,
|
||||||
|
private val protoBuilder: () -> NumassProto.Point,
|
||||||
|
) : NumassPoint {
|
||||||
|
|
||||||
|
private val proto: NumassProto.Point get() = protoBuilder()
|
||||||
|
|
||||||
|
override val blocks: List<NumassBlock>
|
||||||
|
get() = proto.channelsList
|
||||||
|
.flatMap { channel ->
|
||||||
|
channel.blocksList
|
||||||
|
.map { block -> ProtoBlock(channel.id.toInt(), block, this) }
|
||||||
|
.sortedBy { it.startTime }
|
||||||
|
}
|
||||||
|
|
||||||
|
override val channels: Map<Int, NumassBlock>
|
||||||
|
get() = proto.channelsList.groupBy { it.id.toInt() }.mapValues { entry ->
|
||||||
|
MetaBlock(entry.value.flatMap { it.blocksList }.map { ProtoBlock(entry.key, it, this) })
|
||||||
|
}
|
||||||
|
|
||||||
|
override val voltage: Double get() = meta["external_meta.HV1_value"].double ?: super.voltage
|
||||||
|
|
||||||
|
override val index: Int get() = meta["external_meta.point_index"].int ?: super.index
|
||||||
|
|
||||||
|
override val startTime: Instant
|
||||||
|
get() = meta["start_time"].long?.let {
|
||||||
|
Instant.ofEpochMilli(it)
|
||||||
|
} ?: super.startTime
|
||||||
|
|
||||||
|
override val length: Duration
|
||||||
|
get() = meta["acquisition_time"].double?.let {
|
||||||
|
Duration.ofMillis((it * 1000).toLong())
|
||||||
|
} ?: super.length
|
||||||
|
|
||||||
|
|
||||||
|
public companion object {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get valid data stream utilizing compression if it is present
|
||||||
|
*/
|
||||||
|
private fun <R> Envelope.useData(block: (InputStream) -> R): R? = when {
|
||||||
|
data == null -> null
|
||||||
|
meta["compression"].string == "zlib" -> {
|
||||||
|
//TODO move to new type of data
|
||||||
|
val inflater = Inflater()
|
||||||
|
|
||||||
|
val array: ByteArray = data?.read {
|
||||||
|
readByteArray()
|
||||||
|
} ?: ByteArray(0)
|
||||||
|
|
||||||
|
inflater.setInput(array)
|
||||||
|
val bos = ByteArrayOutputStream()
|
||||||
|
val buffer = ByteArray(8192)
|
||||||
|
while (!inflater.finished()) {
|
||||||
|
val size = inflater.inflate(buffer)
|
||||||
|
bos.write(buffer, 0, size)
|
||||||
|
}
|
||||||
|
val unzippeddata = bos.toByteArray()
|
||||||
|
inflater.end()
|
||||||
|
ByteArrayInputStream(unzippeddata).use(block)
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
data?.read {
|
||||||
|
block(asInputStream())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun fromEnvelope(envelope: Envelope): ProtoNumassPoint? {
|
||||||
|
val proto = envelope.useData {
|
||||||
|
NumassProto.Point.parseFrom(it)
|
||||||
|
}
|
||||||
|
return proto?.let { ProtoNumassPoint(envelope.meta) { it } }
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun fromFile(context: Context, path: Path): ProtoNumassPoint? {
|
||||||
|
val envelope = context.io.readEnvelopeFile(path) ?: error("Envelope could not be read from $path")
|
||||||
|
return fromEnvelope(envelope)
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun fromFile(context: Context, path: String): ProtoNumassPoint? {
|
||||||
|
return fromFile(context,Path.of(path))
|
||||||
|
}
|
||||||
|
|
||||||
|
public fun ofEpochNanos(nanos: Long): Instant {
|
||||||
|
val seconds = Math.floorDiv(nanos, 1e9.toInt().toLong())
|
||||||
|
val reminder = (nanos % 1e9).toInt()
|
||||||
|
return Instant.ofEpochSecond(seconds, reminder.toLong())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ProtoBlock(
|
||||||
|
override val channel: Int,
|
||||||
|
private val block: NumassProto.Point.Channel.Block,
|
||||||
|
public val parent: NumassPoint? = null,
|
||||||
|
) : NumassBlock {
|
||||||
|
|
||||||
|
override val startTime: Instant
|
||||||
|
get() = ProtoNumassPoint.ofEpochNanos(block.time)
|
||||||
|
|
||||||
|
override val length: Duration = when {
|
||||||
|
block.length > 0 -> Duration.ofNanos(block.length)
|
||||||
|
parent?.meta["acquisition_time"] != null ->
|
||||||
|
Duration.ofMillis((parent?.meta["acquisition_time"].double ?: 0.0 * 1000).toLong())
|
||||||
|
else -> {
|
||||||
|
LoggerFactory.getLogger(javaClass)
|
||||||
|
.error("No length information on block. Trying to infer from first and last events")
|
||||||
|
val times = runBlocking { events.map { it.timeOffset }.toList() }
|
||||||
|
val nanos = (times.maxOrNull()!! - times.minOrNull()!!)
|
||||||
|
Duration.ofNanos(nanos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override val events: Flow<NumassEvent>
|
||||||
|
get() = if (block.hasEvents()) {
|
||||||
|
val events = block.events
|
||||||
|
if (events.timesCount != events.amplitudesCount) {
|
||||||
|
LoggerFactory.getLogger(javaClass)
|
||||||
|
.error("The block is broken. Number of times is ${events.timesCount} and number of amplitudes is ${events.amplitudesCount}")
|
||||||
|
}
|
||||||
|
(0..events.timesCount).asFlow()
|
||||||
|
.map { i -> NumassEvent(events.getAmplitudes(i).toShort(), events.getTimes(i), this) }
|
||||||
|
} else {
|
||||||
|
emptyFlow<NumassEvent>()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
override val frames: Flow<NumassFrame>
|
||||||
|
get() {
|
||||||
|
val tickSize = Duration.ofNanos(block.binSize)
|
||||||
|
return block.framesList.asFlow().map { frame ->
|
||||||
|
val time = startTime.plusNanos(frame.time)
|
||||||
|
val frameData = frame.data.asReadOnlyByteBuffer()
|
||||||
|
NumassFrame(time, tickSize, frameData.asShortBuffer())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package ru.inr.mass.data.proto;
|
||||||
|
|
||||||
|
message Point {
|
||||||
|
// A single channel for multichannel detector readout
|
||||||
|
message Channel {
|
||||||
|
//A continuous measurement block
|
||||||
|
message Block {
|
||||||
|
// Raw data frame
|
||||||
|
message Frame {
|
||||||
|
uint64 time = 1; // Time in nanos from the beginning of the block
|
||||||
|
bytes data = 2; // Frame data as an array of int16 mesured in arbitrary channels
|
||||||
|
}
|
||||||
|
// Event block obtained directly from device of from frame analysis
|
||||||
|
// In order to save space, times and amplitudes are in separate arrays.
|
||||||
|
// Amplitude and time with the same index correspond to the same event
|
||||||
|
message Events {
|
||||||
|
repeated uint64 times = 1; // Array of time in nanos from the beginning of the block
|
||||||
|
repeated uint64 amplitudes = 2; // Array of amplitudes of events in channels
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64 time = 1; // Block start in epoch nanos
|
||||||
|
repeated Frame frames = 2; // Frames array
|
||||||
|
Events events = 3; // Events array
|
||||||
|
uint64 length = 4; // block size in nanos. If missing, take from meta.
|
||||||
|
uint64 bin_size = 5; // tick size in nanos. Obsolete, to be removed
|
||||||
|
}
|
||||||
|
uint64 id = 1; // The number of measuring channel
|
||||||
|
repeated Block blocks = 2; // Blocks
|
||||||
|
}
|
||||||
|
repeated Channel channels = 1; // Array of measuring channels
|
||||||
|
}
|
28
settings.gradle.kts
Normal file
28
settings.gradle.kts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
pluginManagement {
|
||||||
|
repositories {
|
||||||
|
mavenLocal()
|
||||||
|
jcenter()
|
||||||
|
gradlePluginPortal()
|
||||||
|
maven("https://dl.bintray.com/kotlin/kotlin-eap")
|
||||||
|
maven("https://dl.bintray.com/kotlin/kotlin-dev")
|
||||||
|
maven("https://dl.bintray.com/mipt-npm/dataforge")
|
||||||
|
maven("https://dl.bintray.com/mipt-npm/kscience")
|
||||||
|
maven("https://dl.bintray.com/mipt-npm/dev")
|
||||||
|
}
|
||||||
|
|
||||||
|
val toolsVersion = "0.7.1"
|
||||||
|
val kotlinVersion = "1.4.21"
|
||||||
|
|
||||||
|
plugins {
|
||||||
|
id("ru.mipt.npm.project") version toolsVersion
|
||||||
|
id("ru.mipt.npm.mpp") version toolsVersion
|
||||||
|
id("ru.mipt.npm.jvm") version toolsVersion
|
||||||
|
id("ru.mipt.npm.js") version toolsVersion
|
||||||
|
id("ru.mipt.npm.publish") version toolsVersion
|
||||||
|
kotlin("jvm") version kotlinVersion
|
||||||
|
kotlin("js") version kotlinVersion
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
include("numass-data-model")
|
||||||
|
include("numass-data-proto")
|
Loading…
Reference in New Issue
Block a user