[WIP] first commit

This commit is contained in:
Alexander Nozik 2021-01-19 13:24:24 +03:00
parent 2525c3d0ec
commit ddbd9bf3cd
22 changed files with 6249 additions and 0 deletions

9
.gitignore vendored
View File

@ -0,0 +1,9 @@
.idea/
*.iws
out/
.gradle
build/
!gradle-wrapper.jar

14
build.gradle.kts Normal file
View 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

Binary file not shown.

View 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
View 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
View 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

View 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")
}

View File

@ -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 }
}

View File

@ -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())
}
}
}

View File

@ -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())
}

View File

@ -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")

View File

@ -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")

View File

@ -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>
}

View File

@ -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")
}
}
}

View 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

View File

@ -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
// }
//
// }
//
// }
//
//}

View File

@ -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)
// }
//}
//

View File

@ -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
}
}

View File

@ -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())
}
}
}

View File

@ -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
View 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")