Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d1e8913dd4 | |||
| c5d845e7ff | |||
| 5f836e7856 | |||
| 8c13c1c85d | |||
| e1ff42d464 | |||
| b014dd6afd | |||
| e412ae7092 | |||
| e63d2e3052 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -5,3 +5,4 @@
|
||||
.settings
|
||||
.project
|
||||
.classpath
|
||||
/schema/
|
||||
|
||||
62
Readme.md
62
Readme.md
@@ -35,7 +35,7 @@ usage: viewer
|
||||
|
||||
# Development
|
||||
When checking out the project from the repository there is the `target/generated-sources/xjc` folder missing.
|
||||
After checking out the project execute `mvn compile` to create the folder and the required classes.
|
||||
After checking out the project execute `mvn compile` to create the folder and the required classes.
|
||||
|
||||
To build project use `mvn clean install`.
|
||||
|
||||
@@ -104,4 +104,62 @@ GET fda/{trackingId}/done
|
||||
|
||||
# Notes
|
||||
## Upgrade FDA 1.x to 2.x
|
||||
In existing fda.properties file the following property need to be added: `ch.psi.fda.aq.data.dir=../data`
|
||||
In existing fda.properties file the following property need to be added: `ch.psi.fda.aq.data.dir=../data`
|
||||
|
||||
## Upgrade FDA 2.6.1
|
||||
There are 2 new properties for crlogic:
|
||||
|
||||
```
|
||||
ch.psi.fda.aq.crlogic.ioc=X05LA-VME-ES2
|
||||
ch.psi.fda.aq.crlogic.prefix=X05LA-ES2-CRL
|
||||
```
|
||||
|
||||
|
||||
# FDAQ
|
||||
## Overview
|
||||
This software is used to readout the FDAQ box developed at X10SA.
|
||||
On The FDAQ box there are 2 socket servers, one for retrieving the data and one for resetting the socket
|
||||
server for data retrieval.
|
||||
|
||||
The fdaq code provides exactly 2 functionalities. It can query on the data socket for data (need to specifying how many data point to read),
|
||||
and it can send a reset request on the reset channel.
|
||||
|
||||
## Usage
|
||||
|
||||
To acquire data into a file use:
|
||||
|
||||
```
|
||||
java -jar ch.psi.fda.fdaq-<version>.jar <file>
|
||||
```
|
||||
|
||||
You can terminate the acquisition with *CTRL+C*. If you submit 2 consequtive *CTRL+C* the program will terminate immediately (and data might be lost);
|
||||
|
||||
|
||||
|
||||
This will take the default connections settings:
|
||||
|
||||
* Hostname: mchip015.psi.ch
|
||||
* Port: 2233
|
||||
* Kill Port: 2234
|
||||
* Number Of Elements: Integer.MAX_VALUE/2
|
||||
|
||||
If you need to specify different settings create a property files with following content:
|
||||
|
||||
```
|
||||
ch.psi.fda.fdaq.hostname=myhost
|
||||
ch.psi.fda.fdaq.port=1234
|
||||
ch.psi.fda.fdaq.killPort=4321
|
||||
```
|
||||
|
||||
Use `-Dch.psi.fda.fdaq.config.file=<file>` to use this property file as base configuration.
|
||||
|
||||
|
||||
|
||||
## Development
|
||||
A standalone jar can be build via `mvn clean compile assembly:single`.
|
||||
|
||||
To upload the latest version to the artifact repository use ` mvn clean compile deploy`.
|
||||
|
||||
|
||||
## Notes
|
||||
Trigger port is `Trigger In 1`. For testing apply a 1kHz signal.
|
||||
|
||||
43
build.gradle
43
build.gradle
@@ -3,7 +3,20 @@ apply plugin: 'eclipse'
|
||||
|
||||
sourceCompatibility = 1.7
|
||||
|
||||
version = '2.6.8'
|
||||
version = '3.0.0'
|
||||
|
||||
buildscript {
|
||||
repositories {
|
||||
maven {
|
||||
url "https://plugins.gradle.org/m2/"
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
classpath "com.github.jacobono:gradle-jaxb-plugin:1.3.5"
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: "com.github.jacobono.jaxb"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
@@ -12,16 +25,12 @@ repositories {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile 'ch.psi:ch.psi.fda.core:2.3.4'
|
||||
compile 'ch.psi.fda:ch.psi.fda.xscan:2.6.8'
|
||||
compile 'ch.psi:ch.psi.fda.cdump:2.3.4'
|
||||
compile 'ch.psi:ch.psi.fda.fdaq:2.3.4'
|
||||
compile 'ch.psi:jcae:2.4.1'
|
||||
compile 'com.google.inject:guice:3.0'
|
||||
compile 'org.glassfish.jersey.containers:jersey-container-grizzly2-http:2.5.1'
|
||||
compile 'org.glassfish.jersey.media:jersey-media-sse:2.5.1'
|
||||
compile 'org.glassfish.jersey.media:jersey-media-json-jackson:2.5.1'
|
||||
compile 'org.zeromq:jeromq:0.3.5'
|
||||
compile 'org.zeromq:jeromq:0.3.4'
|
||||
compile 'org.apache.commons:cli:1.2'
|
||||
// For reading/writing Matlab files
|
||||
compile 'com.jmatio:jmatio:0.2u2psi1'
|
||||
@@ -29,6 +38,18 @@ dependencies {
|
||||
compile 'org.freehep:freehep-xdr:2.0.4'
|
||||
compile 'ch.psi:plot:2.1-SNAPSHOT'
|
||||
|
||||
compile 'com.google.guava:guava:>15.0'
|
||||
|
||||
compile 'com.sun.mail:javax.mail:1.5.0'
|
||||
compile 'javax.inject:javax.inject:1'
|
||||
compile 'org.python:jython:2.5.3'
|
||||
compile 'com.fasterxml.jackson.core:jackson-databind:2.5.2'
|
||||
|
||||
|
||||
jaxb 'com.sun.xml.bind:jaxb-xjc:2.2.7-b41'
|
||||
jaxb 'com.sun.xml.bind:jaxb-impl:2.2.7-b41'
|
||||
jaxb 'javax.xml.bind:jaxb-api:2.2.7'
|
||||
|
||||
testCompile 'junit:junit:4.+'
|
||||
}
|
||||
|
||||
@@ -37,16 +58,24 @@ task sourcesJar(type: Jar, dependsOn: classes) {
|
||||
from sourceSets.main.allSource
|
||||
}
|
||||
|
||||
/*
|
||||
task javadocJar(type: Jar, dependsOn: javadoc) {
|
||||
classifier = 'javadoc'
|
||||
from javadoc.destinationDir
|
||||
}
|
||||
*/
|
||||
|
||||
artifacts {
|
||||
archives sourcesJar
|
||||
archives javadocJar
|
||||
//archives javadocJar
|
||||
}
|
||||
|
||||
jaxb{
|
||||
xsdDir = "src/main/resources"
|
||||
xjc {
|
||||
generatePackage = "ch.psi.fda.model.v1"
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'maven'
|
||||
|
||||
|
||||
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
BIN
gradle/wrapper/gradle-wrapper.jar
vendored
Normal file
Binary file not shown.
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
6
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
#Thu Mar 17 08:20:35 CET 2016
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-bin.zip
|
||||
164
gradlew
vendored
Executable file
164
gradlew
vendored
Executable file
@@ -0,0 +1,164 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# 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
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
esac
|
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched.
|
||||
if $cygwin ; then
|
||||
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||
fi
|
||||
|
||||
# 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\"`/" >&-
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >&-
|
||||
|
||||
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" ] ; 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, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
# 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=$((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
|
||||
|
||||
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
||||
function splitJvmOpts() {
|
||||
JVM_OPTS=("$@")
|
||||
}
|
||||
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
||||
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
||||
|
||||
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
||||
90
gradlew.bat
vendored
Normal file
90
gradlew.bat
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
@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
|
||||
|
||||
@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=
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@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 init
|
||||
|
||||
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 init
|
||||
|
||||
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
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windowz variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
if "%@eval[2+2]" == "4" goto 4NT_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
goto execute
|
||||
|
||||
:4NT_args
|
||||
@rem Get arguments from the 4NT Shell from JP Software
|
||||
set CMD_LINE_ARGS=%$
|
||||
|
||||
: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 %CMD_LINE_ARGS%
|
||||
|
||||
: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
|
||||
17
src/main/java/ch/psi/fda/DescriptorProvider.java
Normal file
17
src/main/java/ch/psi/fda/DescriptorProvider.java
Normal file
@@ -0,0 +1,17 @@
|
||||
package ch.psi.fda;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import ch.psi.fda.edescriptor.EDescriptor;
|
||||
import ch.psi.fda.vdescriptor.VDescriptor;
|
||||
|
||||
public interface DescriptorProvider {
|
||||
|
||||
public void load(File ... files );
|
||||
|
||||
public EDescriptor getEDescriptor();
|
||||
public VDescriptor getVDescriptor();
|
||||
|
||||
public Class<?> getEDescriptorClass();
|
||||
|
||||
}
|
||||
28
src/main/java/ch/psi/fda/EContainer.java
Normal file
28
src/main/java/ch/psi/fda/EContainer.java
Normal file
@@ -0,0 +1,28 @@
|
||||
package ch.psi.fda;
|
||||
|
||||
public interface EContainer {
|
||||
|
||||
/**
|
||||
* Initialize execution container like required resources, etc.
|
||||
*/
|
||||
public void initialize();
|
||||
|
||||
/**
|
||||
* Executes the logic implemented by the ExecutionContainer
|
||||
* Execute is a blocking function and must not return before the actual logic is executed
|
||||
*/
|
||||
public void execute();
|
||||
|
||||
/**
|
||||
* Try to abort the execution of the logic
|
||||
*/
|
||||
public void abort();
|
||||
|
||||
public boolean isActive();
|
||||
|
||||
/**
|
||||
* Destroy execution container and free all allocated resources
|
||||
*/
|
||||
public void destroy();
|
||||
|
||||
}
|
||||
22
src/main/java/ch/psi/fda/EContainerFactory.java
Normal file
22
src/main/java/ch/psi/fda/EContainerFactory.java
Normal file
@@ -0,0 +1,22 @@
|
||||
package ch.psi.fda;
|
||||
|
||||
import ch.psi.fda.edescriptor.EDescriptor;
|
||||
|
||||
import com.google.common.eventbus.EventBus;
|
||||
|
||||
public interface EContainerFactory {
|
||||
|
||||
/**
|
||||
* Check whether the factory supports
|
||||
* @param descriptor
|
||||
* @return
|
||||
*/
|
||||
public boolean supportsEDescriptor(EDescriptor descriptor);
|
||||
|
||||
/**
|
||||
* Create the execution container based on the passed descriptor
|
||||
* @param descriptor
|
||||
* @return
|
||||
*/
|
||||
public EContainer getEContainer(EDescriptor descriptor, EventBus bus);
|
||||
}
|
||||
1085
src/main/java/ch/psi/fda/aq/Acquisition.java
Normal file
1085
src/main/java/ch/psi/fda/aq/Acquisition.java
Normal file
File diff suppressed because it is too large
Load Diff
187
src/main/java/ch/psi/fda/aq/AcquisitionConfiguration.java
Normal file
187
src/main/java/ch/psi/fda/aq/AcquisitionConfiguration.java
Normal file
@@ -0,0 +1,187 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.aq;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Properties;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class AcquisitionConfiguration {
|
||||
|
||||
|
||||
private static final Logger logger = Logger.getLogger(AcquisitionConfiguration.class.getName());
|
||||
|
||||
public final static String FDA_CONFIG_FILE = "ch.psi.fda.xscan.config.file";
|
||||
|
||||
private String crlogicPrefix;
|
||||
private String crlogicIoc;
|
||||
|
||||
/**
|
||||
* Base directory for data. The directory may contain date macros. The string may contain any @see java.text.SimpleDateFormat
|
||||
* patterns within ${ } brackets. The macros are resolved with the actual time while the get method
|
||||
* of this property is called.
|
||||
*/
|
||||
private String dataBaseDirectory = System.getProperty("user.home");
|
||||
/**
|
||||
* Prefix of the data file. The prefix may contain date macros. The string may contain any @see java.text.SimpleDateFormat
|
||||
* patterns within ${ } brackets. The macros are resolved with the actual time while the get method
|
||||
* of this property is called.
|
||||
*/
|
||||
private String dataFilePrefix = "";
|
||||
|
||||
/**
|
||||
* Maximum time for a actor move
|
||||
*/
|
||||
private Long actorMoveTimeout = 600000l; // 10 Minutes maximum move time
|
||||
|
||||
private String smptServer;
|
||||
|
||||
|
||||
/**
|
||||
* Default Constructor
|
||||
* The constructor will read the configuration from the /fda.properties file (resource) located in the classpath.
|
||||
*/
|
||||
public AcquisitionConfiguration(){
|
||||
loadConfiguration(System.getProperty(FDA_CONFIG_FILE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Load configuration from properties file
|
||||
*/
|
||||
public void loadConfiguration(String file) {
|
||||
Properties properties = new Properties();
|
||||
|
||||
File cfile = null;
|
||||
// Only read in the property file if a file is specified
|
||||
if(file != null){
|
||||
cfile = new File(file);
|
||||
try {
|
||||
properties.load(new FileReader(cfile));
|
||||
} catch (FileNotFoundException e) {
|
||||
throw new RuntimeException("Configuration file "+file+" not found", e);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Cannot read configuration file "+file, e);
|
||||
}
|
||||
}
|
||||
else{
|
||||
logger.warning("No configfile specified !");
|
||||
}
|
||||
|
||||
// The defaults are set here
|
||||
crlogicPrefix = properties.getProperty(AcquisitionConfiguration.class.getPackage().getName()+".crlogic.prefix", "");
|
||||
crlogicIoc= properties.getProperty(AcquisitionConfiguration.class.getPackage().getName()+".crlogic.ioc", "");
|
||||
|
||||
dataBaseDirectory = properties.getProperty(AcquisitionConfiguration.class.getPackage().getName()+".data.dir",".");
|
||||
if(cfile!=null && dataBaseDirectory.matches("^\\.\\.?/.*")){ // if basedir starts with . or .. we assume the data directory to be relative to the directory of the configuration file
|
||||
dataBaseDirectory = cfile.getParentFile().getAbsolutePath()+"/"+dataBaseDirectory;
|
||||
}
|
||||
dataFilePrefix = properties.getProperty(AcquisitionConfiguration.class.getPackage().getName()+".data.filePrefix","");
|
||||
|
||||
|
||||
actorMoveTimeout = new Long(properties.getProperty(AcquisitionConfiguration.class.getPackage().getName()+".actorMoveTimeout","600000"));
|
||||
|
||||
smptServer= properties.getProperty(AcquisitionConfiguration.class.getPackage().getName()+".notification.host","mail.psi.ch");
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace ${name} and ${date} macro given string
|
||||
* @param string
|
||||
* @param date
|
||||
* @param name
|
||||
* @return
|
||||
*/
|
||||
public String replaceMacros(String string, Date date, String name){
|
||||
String newString = string;
|
||||
|
||||
// Replace scan name macros
|
||||
newString = newString.replaceAll("\\$\\{name\\}", name);
|
||||
|
||||
|
||||
// Replace date macros
|
||||
Pattern pattern = Pattern.compile("\\$\\{[a-z,A-Z,-,_,:]*\\}");
|
||||
Matcher matcher = pattern.matcher(newString);
|
||||
while(matcher.find()){
|
||||
String datePattern = matcher.group();
|
||||
datePattern = datePattern.replaceAll("\\$\\{", "");
|
||||
datePattern = datePattern.replaceAll("\\}", "");
|
||||
SimpleDateFormat datef = new SimpleDateFormat(datePattern);
|
||||
newString = matcher.replaceFirst(datef.format(date));
|
||||
matcher = pattern.matcher(newString);
|
||||
}
|
||||
return newString;
|
||||
}
|
||||
|
||||
|
||||
public String getCrlogicPrefix() {
|
||||
return crlogicPrefix;
|
||||
}
|
||||
|
||||
public void setCrlogicPrefix(String crlogicPrefix) {
|
||||
this.crlogicPrefix = crlogicPrefix;
|
||||
}
|
||||
|
||||
public void setCrlogicIoc(String crlogicIoc) {
|
||||
this.crlogicIoc = crlogicIoc;
|
||||
}
|
||||
public String getCrlogicIoc() {
|
||||
return crlogicIoc;
|
||||
}
|
||||
|
||||
|
||||
public String getDataBaseDirectory() {
|
||||
return dataBaseDirectory;
|
||||
}
|
||||
|
||||
public void setDataBaseDirectory(String dataBaseDirectory) {
|
||||
this.dataBaseDirectory = dataBaseDirectory;
|
||||
}
|
||||
|
||||
public String getDataFilePrefix() {
|
||||
return dataFilePrefix;
|
||||
}
|
||||
|
||||
public void setDataFilePrefix(String dataFilePrefix) {
|
||||
this.dataFilePrefix = dataFilePrefix;
|
||||
}
|
||||
|
||||
public Long getActorMoveTimeout() {
|
||||
return actorMoveTimeout;
|
||||
}
|
||||
|
||||
public void setActorMoveTimeout(Long actorMoveTimeout) {
|
||||
this.actorMoveTimeout = actorMoveTimeout;
|
||||
}
|
||||
|
||||
public String getSmptServer() {
|
||||
return smptServer;
|
||||
}
|
||||
|
||||
public void setSmptServer(String smptServer) {
|
||||
this.smptServer = smptServer;
|
||||
}
|
||||
}
|
||||
102
src/main/java/ch/psi/fda/aq/Collector.java
Normal file
102
src/main/java/ch/psi/fda/aq/Collector.java
Normal file
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.aq;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.eventbus.EventBus;
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
|
||||
import ch.psi.fda.messages.DataMessage;
|
||||
import ch.psi.fda.messages.EndOfStreamMessage;
|
||||
import ch.psi.fda.messages.Message;
|
||||
import ch.psi.fda.messages.Metadata;
|
||||
import ch.psi.fda.messages.StreamDelimiterMessage;
|
||||
|
||||
/**
|
||||
* Collector class that is collecting and merging data from different Queues.
|
||||
*/
|
||||
public class Collector {
|
||||
|
||||
private EventBus bus;
|
||||
private List<MessageListener> listeners = new ArrayList<>();
|
||||
|
||||
private List<Metadata> metadata;
|
||||
private boolean first = true;
|
||||
|
||||
public Collector(EventBus b){
|
||||
this.bus = b;
|
||||
}
|
||||
|
||||
public void addEventBus(EventBus b){
|
||||
MessageListener l = new MessageListener();
|
||||
listeners.add(l);
|
||||
b.register(l);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private class MessageListener{
|
||||
|
||||
private DataMessage message;
|
||||
|
||||
@Subscribe
|
||||
public void onMessage(Message message){
|
||||
int level = listeners.indexOf(this);
|
||||
if(message instanceof DataMessage){
|
||||
this.message = (DataMessage) message;
|
||||
|
||||
if(level==0){
|
||||
if(first){
|
||||
metadata = new ArrayList<>();
|
||||
for(int i=listeners.size()-1;i>=0;i--){
|
||||
// Correct/Add dimension information
|
||||
for(Metadata m: listeners.get(i).getMessage().getMetadata()){
|
||||
m.setDimension(i);
|
||||
}
|
||||
metadata.addAll(listeners.get(i).getMessage().getMetadata());
|
||||
}
|
||||
|
||||
}
|
||||
DataMessage m = new DataMessage(metadata);
|
||||
for(int i=listeners.size()-1;i>=0;i--){
|
||||
m.getData().addAll(listeners.get(i).getMessage().getData());
|
||||
}
|
||||
bus.post(m);
|
||||
}
|
||||
}
|
||||
if(message instanceof EndOfStreamMessage){
|
||||
|
||||
StreamDelimiterMessage ddm = new StreamDelimiterMessage(level, ((EndOfStreamMessage)message).isIflag());
|
||||
bus.post(ddm);
|
||||
if(level==(listeners.size()-1)){ // if highest dimension then send end of stream
|
||||
bus.post(new EndOfStreamMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public DataMessage getMessage(){
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
79
src/main/java/ch/psi/fda/aq/Manipulator.java
Normal file
79
src/main/java/ch/psi/fda/aq/Manipulator.java
Normal file
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.aq;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.eventbus.EventBus;
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
|
||||
import ch.psi.fda.core.Manipulation;
|
||||
import ch.psi.fda.messages.DataMessage;
|
||||
import ch.psi.fda.messages.Message;
|
||||
import ch.psi.fda.messages.Metadata;
|
||||
|
||||
/**
|
||||
* Applies manipulations to the data stream
|
||||
*/
|
||||
public class Manipulator {
|
||||
|
||||
private EventBus bus;
|
||||
|
||||
private final List<Manipulation> manipulations;
|
||||
private boolean first = true;
|
||||
private List<Metadata> metadata = new ArrayList<>();
|
||||
|
||||
public Manipulator(EventBus b, List<Manipulation> manipulations){
|
||||
this.bus = b;
|
||||
this.manipulations = manipulations;
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onMessage(Message message){
|
||||
if(message instanceof DataMessage){
|
||||
if(first){
|
||||
first=false;
|
||||
|
||||
metadata.addAll(((DataMessage) message).getMetadata());
|
||||
|
||||
for(Manipulation manipulation: this.manipulations){
|
||||
manipulation.initialize(this.metadata);
|
||||
|
||||
// Add manipulation id to metadata
|
||||
this.metadata.add(new Metadata(manipulation.getId(),0)); // Calculated component always belongs to lowes dimension
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DataMessage dm = (DataMessage) message;
|
||||
// message = new DataMessage(metadata);
|
||||
for(Manipulation manipulation: manipulations){
|
||||
// ((DataMessage)message).getData().add(manipulation.execute(dm));
|
||||
dm.getData().add(manipulation.execute(dm));
|
||||
|
||||
// Need to update the metadata of the message
|
||||
dm.setMetadata(this.metadata);
|
||||
}
|
||||
}
|
||||
bus.post(message);
|
||||
//System.out.println(Thread.currentThread());
|
||||
}
|
||||
}
|
||||
109
src/main/java/ch/psi/fda/aq/NotificationAgent.java
Normal file
109
src/main/java/ch/psi/fda/aq/NotificationAgent.java
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (C) 2011 Paul Scherrer Institute
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package ch.psi.fda.aq;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.mail.Message;
|
||||
import javax.mail.MessagingException;
|
||||
import javax.mail.Session;
|
||||
import javax.mail.Transport;
|
||||
import javax.mail.internet.InternetAddress;
|
||||
import javax.mail.internet.MimeMessage;
|
||||
|
||||
import ch.psi.fda.model.v1.Recipient;
|
||||
|
||||
/**
|
||||
* Agent to send out notifications to specified recipients.
|
||||
*/
|
||||
public class NotificationAgent {
|
||||
|
||||
private final static String smsPostfix = "@sms.switch.ch";
|
||||
|
||||
private Properties properties;
|
||||
private String fromAddress;
|
||||
private final List<Recipient> recipients = new ArrayList<Recipient>();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param host SMTP server to send notifications to
|
||||
* @param recipients List of recipients
|
||||
* @param from Address string that will show up in the from field. For example: fda@psi.ch. This argument must not contain white spaces
|
||||
*/
|
||||
public NotificationAgent(String host, String from){
|
||||
|
||||
fromAddress = from;
|
||||
|
||||
properties = new Properties();
|
||||
properties.put("mail.smtp.host", host);
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void sendNotification(String aSubject, String aBody, boolean error, boolean success) {
|
||||
|
||||
for(Recipient recipient: recipients){
|
||||
|
||||
if((error && recipient.isError()) || (success && recipient.isSuccess())){
|
||||
String receiver;
|
||||
|
||||
// Verify mail recipients
|
||||
if(recipient.getValue().matches("[0-9,\\\\.,-,a-z,A-Z]*@[0-9,\\\\.,a-z,A-Z]*")){
|
||||
receiver = recipient.getValue();
|
||||
}
|
||||
else if(recipient.getValue().matches("[0-9]+")){
|
||||
// Assume that it is a SMS number
|
||||
receiver = recipient.getValue() + smsPostfix;
|
||||
}
|
||||
else{
|
||||
Logger.getLogger(NotificationAgent.class.getName()).log(Level.WARNING, "Invalid email address");
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
Logger.getLogger(NotificationAgent.class.getName()).log(Level.INFO, "Send notification to " + receiver);
|
||||
|
||||
//Here, no Authenticator argument is used (it is null).
|
||||
//Authenticators are used to prompt the user for user
|
||||
//name and password.
|
||||
Session session = Session.getDefaultInstance(properties, null);
|
||||
MimeMessage message = new MimeMessage(session);
|
||||
try {
|
||||
//The "from" address may be set in code, or set in the
|
||||
//config file under "mail.from" ; here, the latter style is used
|
||||
message.setFrom( new InternetAddress(fromAddress) );
|
||||
|
||||
message.addRecipient(Message.RecipientType.TO, new InternetAddress(receiver));
|
||||
message.setSubject(aSubject);
|
||||
message.setText(aBody);
|
||||
Transport.send(message);
|
||||
} catch (MessagingException ex) {
|
||||
Logger.getLogger(NotificationAgent.class.getName()).log(Level.WARNING, "Failed to send notification to " + receiver, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<Recipient> getRecipients() {
|
||||
return recipients;
|
||||
}
|
||||
|
||||
}
|
||||
50
src/main/java/ch/psi/fda/aq/XScanContainer.java
Normal file
50
src/main/java/ch/psi/fda/aq/XScanContainer.java
Normal file
@@ -0,0 +1,50 @@
|
||||
package ch.psi.fda.aq;
|
||||
|
||||
import com.google.common.eventbus.EventBus;
|
||||
|
||||
import ch.psi.fda.EContainer;
|
||||
import ch.psi.fda.model.v1.Configuration;
|
||||
import ch.psi.jcae.ChannelService;
|
||||
|
||||
public class XScanContainer implements EContainer {
|
||||
|
||||
private final Acquisition acquisition;
|
||||
|
||||
private EventBus bus;
|
||||
private Configuration xscanConfiguration;
|
||||
|
||||
public XScanContainer(ChannelService cservice, AcquisitionConfiguration config, EventBus bus, Configuration xscanConfiguration){
|
||||
acquisition = new Acquisition(cservice, config);
|
||||
this.bus = bus;
|
||||
this.xscanConfiguration = xscanConfiguration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
acquisition.initalize(bus, xscanConfiguration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
try {
|
||||
acquisition.execute();
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void abort() {
|
||||
acquisition.abort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
acquisition.destroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return acquisition.isActive();
|
||||
}
|
||||
}
|
||||
29
src/main/java/ch/psi/fda/aq/XScanDescriptor.java
Normal file
29
src/main/java/ch/psi/fda/aq/XScanDescriptor.java
Normal file
@@ -0,0 +1,29 @@
|
||||
package ch.psi.fda.aq;
|
||||
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import javax.xml.bind.annotation.XmlType;
|
||||
|
||||
import ch.psi.fda.edescriptor.EDescriptor;
|
||||
import ch.psi.fda.model.v1.Configuration;
|
||||
|
||||
@XmlRootElement(name="edescriptor")
|
||||
@XmlType(name="edescriptor")
|
||||
public class XScanDescriptor implements EDescriptor {
|
||||
|
||||
private Configuration configuration;
|
||||
|
||||
public XScanDescriptor(){
|
||||
}
|
||||
|
||||
public XScanDescriptor(Configuration configuration){
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
|
||||
public Configuration getConfiguration() {
|
||||
return configuration;
|
||||
}
|
||||
public void setConfiguration(Configuration configuration) {
|
||||
this.configuration = configuration;
|
||||
}
|
||||
}
|
||||
339
src/main/java/ch/psi/fda/aq/XScanDescriptorProvider.java
Normal file
339
src/main/java/ch/psi/fda/aq/XScanDescriptorProvider.java
Normal file
@@ -0,0 +1,339 @@
|
||||
package ch.psi.fda.aq;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import ch.psi.fda.DescriptorProvider;
|
||||
import ch.psi.fda.edescriptor.EDescriptor;
|
||||
import ch.psi.fda.model.ModelManager;
|
||||
import ch.psi.fda.model.v1.ArrayDetector;
|
||||
import ch.psi.fda.model.v1.Configuration;
|
||||
import ch.psi.fda.model.v1.ContinuousPositioner;
|
||||
import ch.psi.fda.model.v1.Data;
|
||||
import ch.psi.fda.model.v1.Detector;
|
||||
import ch.psi.fda.model.v1.LinearPositioner;
|
||||
import ch.psi.fda.model.v1.Positioner;
|
||||
import ch.psi.fda.model.v1.PseudoPositioner;
|
||||
import ch.psi.fda.model.v1.Visualization;
|
||||
import ch.psi.fda.vdescriptor.LinePlot;
|
||||
import ch.psi.fda.vdescriptor.VDescriptor;
|
||||
import ch.psi.fda.vdescriptor.XYSeries;
|
||||
import ch.psi.fda.vdescriptor.XYZSeries;
|
||||
import ch.psi.fda.vdescriptor.YSeries;
|
||||
import ch.psi.fda.vdescriptor.YZSeries;
|
||||
|
||||
public class XScanDescriptorProvider implements DescriptorProvider {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(XScanDescriptorProvider.class.getName());
|
||||
|
||||
private EDescriptor edescriptor;
|
||||
private VDescriptor vdescriptor;
|
||||
|
||||
@Override
|
||||
public void load(File... files) {
|
||||
|
||||
if(files.length<1 || files[0]==null){
|
||||
throw new IllegalArgumentException("There need to be at lease one file specified");
|
||||
}
|
||||
File file = files[0];
|
||||
|
||||
if(!file.exists()){
|
||||
throw new IllegalArgumentException("File "+file.getAbsolutePath()+" does not exist");
|
||||
}
|
||||
|
||||
Configuration c;
|
||||
try {
|
||||
c = ModelManager.unmarshall(file);
|
||||
} catch (Exception e) {
|
||||
throw new UnsupportedOperationException("Unable to deserialize configuration: "+e.getMessage(), e);
|
||||
}
|
||||
|
||||
// Set data file name
|
||||
// Determine name used for the data file
|
||||
String name = file.getName();
|
||||
name = name.replaceAll("\\.xml$", "");
|
||||
|
||||
if(c.getData()!=null){
|
||||
Data data = c.getData();
|
||||
// Only update filename if no name is specified in xml file
|
||||
if(data.getFileName()==null){
|
||||
data.setFileName(name);
|
||||
}
|
||||
}
|
||||
else{
|
||||
Data data = new Data();
|
||||
data.setFileName(name);
|
||||
c.setData(data);
|
||||
}
|
||||
|
||||
|
||||
// // Override number of executions
|
||||
// if(iterations != null){
|
||||
// c.setNumberOfExecution(iterations);
|
||||
// }
|
||||
// Fix configuration if iterations is specified with 0 and no iterations option is specified
|
||||
if(c.getNumberOfExecution()==0){
|
||||
c.setNumberOfExecution(1);
|
||||
}
|
||||
|
||||
this.edescriptor = new XScanDescriptor(c);
|
||||
this.vdescriptor = mapVisualizations(c.getVisualization());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a vdescriptor out of the scan description
|
||||
* @param vl
|
||||
* @return
|
||||
*/
|
||||
public static VDescriptor mapVisualizations(List<Visualization> vl){
|
||||
VDescriptor vd = new VDescriptor();
|
||||
|
||||
|
||||
for(Visualization v: vl){
|
||||
if(v instanceof ch.psi.fda.model.v1.LinePlot){
|
||||
ch.psi.fda.model.v1.LinePlot lp = (ch.psi.fda.model.v1.LinePlot) v;
|
||||
|
||||
String x = getId(lp.getX());
|
||||
|
||||
LinePlot lineplot = new LinePlot(lp.getTitle());
|
||||
List<Object> l = lp.getY();
|
||||
for(Object o: l){
|
||||
String y = getId(o);
|
||||
lineplot.getData().add(new XYSeries(x, y));
|
||||
}
|
||||
|
||||
vd.getPlots().add(lineplot);
|
||||
}
|
||||
else if(v instanceof ch.psi.fda.model.v1.LinePlotArray){
|
||||
// Array visualization
|
||||
ch.psi.fda.model.v1.LinePlotArray lp = (ch.psi.fda.model.v1.LinePlotArray) v;
|
||||
|
||||
LinePlot lineplot = new LinePlot(lp.getTitle());
|
||||
// Create data filter for visualization
|
||||
List<Object> l = lp.getY();
|
||||
for(Object o: l){
|
||||
String idY = getId(o);
|
||||
|
||||
// TODO Need to actually check if minX of
|
||||
lineplot.setMinX(new Double(lp.getOffset()));
|
||||
lineplot.setMaxX(new Double(lp.getOffset()+lp.getSize()));
|
||||
lineplot.setMaxSeries(lp.getMaxSeries());
|
||||
lineplot.getData().add(new YSeries(idY));
|
||||
}
|
||||
vd.getPlots().add(lineplot);
|
||||
}
|
||||
else if(v instanceof ch.psi.fda.model.v1.MatrixPlot){
|
||||
|
||||
// MatrixPlot does currently not support RegionPositioners because of the
|
||||
// plotting problems this would cause. If regions of the positioner have different
|
||||
// step sizes it is not easily possible (without (specialized) rasterization) to plot the data.
|
||||
|
||||
ch.psi.fda.model.v1.MatrixPlot mp = (ch.psi.fda.model.v1.MatrixPlot) v;
|
||||
|
||||
|
||||
double minX, maxX;
|
||||
int nX;
|
||||
double minY, maxY;
|
||||
int nY;
|
||||
|
||||
String idX, idY, idZ;
|
||||
|
||||
// X Axis
|
||||
if(mp.getX() instanceof LinearPositioner){
|
||||
LinearPositioner linp = ((LinearPositioner)mp.getX());
|
||||
idX = linp.getId();
|
||||
|
||||
minX = (Math.min(linp.getStart(), linp.getEnd()));
|
||||
maxX = (Math.max(linp.getStart(), linp.getEnd()));
|
||||
nX = ((int) Math.floor((Math.abs(maxX-minX))/linp.getStepSize()) + 1);
|
||||
}
|
||||
else if(mp.getX() instanceof PseudoPositioner){
|
||||
PseudoPositioner pp = ((PseudoPositioner)mp.getX());
|
||||
idX = pp.getId();
|
||||
minX = (1); // Count starts at 1
|
||||
maxX = (pp.getCounts());
|
||||
nX = (pp.getCounts());
|
||||
}
|
||||
else if(mp.getX() instanceof ContinuousPositioner){
|
||||
ContinuousPositioner conp = ((ContinuousPositioner)mp.getX());
|
||||
idX = conp.getId();
|
||||
|
||||
minX = (Math.min(conp.getStart(), conp.getEnd()));
|
||||
maxX = (Math.max(conp.getStart(), conp.getEnd()));
|
||||
nX = ((int) Math.floor((Math.abs(maxX-minX))/conp.getStepSize()) + 1);
|
||||
}
|
||||
else{
|
||||
// Fail as we cannot determine the min, max and number of steps
|
||||
throw new RuntimeException(mp.getX().getClass().getName()+" is not supported as x-axis of a MatrixPlot");
|
||||
}
|
||||
|
||||
// Y Axis
|
||||
if(mp.getY() instanceof LinearPositioner){
|
||||
LinearPositioner linp = ((LinearPositioner)mp.getY());
|
||||
idY = linp.getId();
|
||||
minY = (Math.min(linp.getStart(), linp.getEnd()));
|
||||
maxY = (Math.max(linp.getStart(), linp.getEnd()));
|
||||
nY = ((int) Math.floor((Math.abs(maxY-minY))/linp.getStepSize()) + 1);
|
||||
}
|
||||
else if(mp.getY() instanceof PseudoPositioner){
|
||||
PseudoPositioner pp = ((PseudoPositioner)mp.getY());
|
||||
idY = pp.getId();
|
||||
minY = (1); // Count starts at 1
|
||||
maxY = (pp.getCounts());
|
||||
nY = (pp.getCounts());
|
||||
}
|
||||
else{
|
||||
// Fail as we cannot determine the min, max and number of steps
|
||||
throw new RuntimeException(mp.getY().getClass().getName()+" is not supported as y-axis of a MatrixPlot");
|
||||
}
|
||||
|
||||
// Z Dimension
|
||||
idZ = getId(mp.getZ());
|
||||
|
||||
|
||||
ch.psi.fda.vdescriptor.MatrixPlot matrixplot = new ch.psi.fda.vdescriptor.MatrixPlot(mp.getTitle());
|
||||
matrixplot.setMinX(minX);
|
||||
matrixplot.setMaxX(maxX);
|
||||
matrixplot.setnX(nX);
|
||||
matrixplot.setMinY(minY);
|
||||
matrixplot.setMaxY(maxY);
|
||||
matrixplot.setnY(nY);
|
||||
matrixplot.setType(mp.getType());
|
||||
|
||||
matrixplot.getData().add(new XYZSeries(idX, idY, idZ));
|
||||
vd.getPlots().add(matrixplot);
|
||||
}
|
||||
else if(v instanceof ch.psi.fda.model.v1.MatrixPlotArray){
|
||||
// Support for 2D waveform plots
|
||||
ch.psi.fda.model.v1.MatrixPlotArray mp = (ch.psi.fda.model.v1.MatrixPlotArray) v;
|
||||
|
||||
// Get size of the array detector
|
||||
int arraySize = 0;
|
||||
Object o = mp.getZ();
|
||||
if(o instanceof ArrayDetector){
|
||||
ArrayDetector ad = (ArrayDetector) o;
|
||||
arraySize = ad.getArraySize();
|
||||
}
|
||||
else{
|
||||
// Workaround
|
||||
arraySize = mp.getSize(); // of array is from a manipulation the size is not known. Then the size will indicate the size of the array to display
|
||||
}
|
||||
|
||||
int offset = mp.getOffset();
|
||||
// Determine size for array
|
||||
int size = mp.getSize();
|
||||
if(size>0 && offset+size<arraySize){
|
||||
size = mp.getSize();
|
||||
}
|
||||
else{
|
||||
size=arraySize-offset;
|
||||
}
|
||||
|
||||
|
||||
double minY, maxY;
|
||||
int nY;
|
||||
|
||||
double minX = offset;
|
||||
double maxX = offset+size-1;
|
||||
int nX = size;
|
||||
|
||||
String idY, idZ;
|
||||
|
||||
// Y Axis
|
||||
if(mp.getY() instanceof LinearPositioner){
|
||||
LinearPositioner linp = ((LinearPositioner)mp.getY());
|
||||
idY = linp.getId();
|
||||
|
||||
minY = (Math.min(linp.getStart(), linp.getEnd()));
|
||||
maxY = (Math.max(linp.getStart(), linp.getEnd()));
|
||||
nY = ((int) Math.floor((Math.abs(maxY-minY))/linp.getStepSize()) + 1);
|
||||
}
|
||||
else if(mp.getY() instanceof PseudoPositioner){
|
||||
PseudoPositioner pp = ((PseudoPositioner)mp.getY());
|
||||
idY = pp.getId();
|
||||
minY = (1); // Count starts at 1
|
||||
maxY = (pp.getCounts());
|
||||
nY = (pp.getCounts());
|
||||
}
|
||||
else if(mp.getY() instanceof ContinuousPositioner){
|
||||
ContinuousPositioner conp = ((ContinuousPositioner)mp.getY());
|
||||
idY = conp.getId();
|
||||
|
||||
minY = (Math.min(conp.getStart(), conp.getEnd()));
|
||||
maxY = (Math.max(conp.getStart(), conp.getEnd()));
|
||||
nY = ((int) Math.floor((Math.abs(maxY-minY))/conp.getStepSize()) + 1);
|
||||
}
|
||||
else{
|
||||
// Fail as we cannot determine the min, max and number of steps
|
||||
throw new RuntimeException(mp.getY().getClass().getName()+" is not supported as x-axis of a MatrixPlot");
|
||||
}
|
||||
|
||||
|
||||
// Z Dimension
|
||||
idZ = getId(mp.getZ());
|
||||
|
||||
ch.psi.fda.vdescriptor.MatrixPlot matrixplot = new ch.psi.fda.vdescriptor.MatrixPlot(mp.getTitle());
|
||||
matrixplot.setMinX(minX);
|
||||
matrixplot.setMaxX(maxX);
|
||||
matrixplot.setnX(nX);
|
||||
matrixplot.setMinY(minY);
|
||||
matrixplot.setMaxY(maxY);
|
||||
matrixplot.setnY(nY);
|
||||
matrixplot.setType(mp.getType());
|
||||
|
||||
matrixplot.getData().add(new YZSeries(idY, idZ));
|
||||
vd.getPlots().add(matrixplot);
|
||||
|
||||
}
|
||||
else{
|
||||
logger.warning(v.getClass().getName()+" is not supported as visualization type");
|
||||
}
|
||||
}
|
||||
return vd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve id string of the passed object
|
||||
* @param object
|
||||
* @return Id string of object
|
||||
*/
|
||||
private static String getId(Object object){
|
||||
String id;
|
||||
if(object instanceof Positioner){
|
||||
id = ((Positioner)object).getId();
|
||||
}
|
||||
else if (object instanceof Detector){
|
||||
id = ((Detector)object).getId();
|
||||
}
|
||||
else if (object instanceof ch.psi.fda.model.v1.Manipulation){
|
||||
id = ((ch.psi.fda.model.v1.Manipulation)object).getId();
|
||||
}
|
||||
// For testing purposes
|
||||
else if(object instanceof String){
|
||||
id = (String) object;
|
||||
}
|
||||
else{
|
||||
throw new RuntimeException("Unable to identify id of object reference "+object);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public EDescriptor getEDescriptor() {
|
||||
return edescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VDescriptor getVDescriptor() {
|
||||
return vdescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getEDescriptorClass() {
|
||||
return XScanDescriptor.class;
|
||||
}
|
||||
|
||||
}
|
||||
36
src/main/java/ch/psi/fda/aq/XScanFactory.java
Normal file
36
src/main/java/ch/psi/fda/aq/XScanFactory.java
Normal file
@@ -0,0 +1,36 @@
|
||||
package ch.psi.fda.aq;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.google.common.eventbus.EventBus;
|
||||
|
||||
import ch.psi.fda.EContainer;
|
||||
import ch.psi.fda.EContainerFactory;
|
||||
import ch.psi.fda.edescriptor.EDescriptor;
|
||||
import ch.psi.jcae.ChannelService;
|
||||
|
||||
public class XScanFactory implements EContainerFactory {
|
||||
|
||||
@Inject
|
||||
private ChannelService cservice;
|
||||
|
||||
private AcquisitionConfiguration config = new AcquisitionConfiguration();
|
||||
|
||||
@Override
|
||||
public boolean supportsEDescriptor(EDescriptor descriptor) {
|
||||
return (descriptor instanceof XScanDescriptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EContainer getEContainer(EDescriptor descriptor, EventBus bus) {
|
||||
|
||||
if(! (descriptor instanceof XScanDescriptor)){
|
||||
throw new IllegalArgumentException("Descriptor of type "+descriptor.getClass().getName()+" is not supported - descriptor need to be of type "+XScanDescriptor.class);
|
||||
}
|
||||
|
||||
XScanDescriptor xdescriptor = (XScanDescriptor) descriptor;
|
||||
|
||||
return new XScanContainer(cservice, config, bus, xdescriptor.getConfiguration());
|
||||
}
|
||||
|
||||
}
|
||||
151
src/main/java/ch/psi/fda/cdump/Cdump.java
Normal file
151
src/main/java/ch/psi/fda/cdump/Cdump.java
Normal file
@@ -0,0 +1,151 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but without any
|
||||
* warranty; without even the implied warranty of merchantability or fitness for
|
||||
* a particular purpose. See the GNU Lesser General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.cdump;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.google.common.eventbus.EventBus;
|
||||
|
||||
import ch.psi.jcae.Channel;
|
||||
import ch.psi.jcae.ChannelDescriptor;
|
||||
import ch.psi.jcae.ChannelException;
|
||||
import ch.psi.jcae.ChannelService;
|
||||
|
||||
/**
|
||||
* Cdump readout logic - i.e. put monitor on a data waveform channel of the fast
|
||||
* ADC and send data to the eventbus
|
||||
*/
|
||||
public class Cdump {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(Cdump.class.getName());
|
||||
|
||||
public final static String[] SAMPLING_RATES = new String[] {"1Hz","2Hz", "5Hz", "10Hz", "20Hz", "50Hz", "100Hz", "200Hz", "500Hz",
|
||||
"1kHz", "2kHz", "5kHz", "10kHz", "20kHz", "50kHz", "100kHz"};
|
||||
|
||||
private Channel<int[]> adcData;
|
||||
|
||||
private enum AdcCmd {
|
||||
READY, GO
|
||||
};
|
||||
|
||||
private Channel<Integer> adcCmd;
|
||||
|
||||
private CdumpListener listener;
|
||||
private ChannelService cservice;
|
||||
private CdumpConfiguration configuration;
|
||||
|
||||
public Cdump(ChannelService cservice, EventBus ebus, CdumpConfiguration configuration) {
|
||||
this.cservice = cservice;
|
||||
this.configuration = configuration;
|
||||
this.listener = new CdumpListener(ebus, configuration.getNelements());
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquire data with the given sampling rate
|
||||
* @param samplingRate
|
||||
*/
|
||||
public void acquire(String samplingRate) {
|
||||
|
||||
logger.info("Start acquisition with sampling rate "+ samplingRate);
|
||||
|
||||
try {
|
||||
// Set ADC sampling rate
|
||||
Channel<Integer> smplRate = cservice.createChannel(new ChannelDescriptor<>(Integer.class, configuration.getSamplingRateChannel(), false));
|
||||
smplRate.setValue(getIntSamplingRate(samplingRate));
|
||||
smplRate.destroy();
|
||||
|
||||
adcData = cservice.createChannel(new ChannelDescriptor<>(int[].class, configuration.getDataChannel(), true));
|
||||
adcCmd = cservice.createChannel(new ChannelDescriptor<>(Integer.class, configuration.getControlChannel(), false));
|
||||
|
||||
adcCmd.setValue(AdcCmd.GO.ordinal());
|
||||
adcData.addPropertyChangeListener(listener);
|
||||
} catch (ChannelException | TimeoutException | InterruptedException | ExecutionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait until acquire is done
|
||||
*/
|
||||
public void waitAcquireDone(){
|
||||
try {
|
||||
adcCmd.waitForValue(AdcCmd.READY.ordinal());
|
||||
} catch (InterruptedException | ExecutionException | ChannelException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
|
||||
logger.info("Stop acquisition");
|
||||
|
||||
try {
|
||||
// Detach listener from channel - i.e. stop data acquisition
|
||||
adcData.removePropertyChangeListener(listener);
|
||||
adcCmd.setValue(AdcCmd.READY.ordinal());
|
||||
|
||||
listener.terminate();
|
||||
|
||||
adcCmd.destroy();
|
||||
adcData.destroy();
|
||||
} catch (ChannelException | InterruptedException | ExecutionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get sampling rate int value based on the passed rate string.
|
||||
* If the string does not match any of the strings specified in the rates variable
|
||||
* this function will set the rate to 1Hz
|
||||
*
|
||||
* 0 = 1Hz
|
||||
* 1 = 2Hz
|
||||
* 2 = 5Hz
|
||||
* 3 = 10Hz
|
||||
* 4 = 20Hz
|
||||
* 5 = 50Hz
|
||||
* 6 = 100Hz
|
||||
* 7 = 200Hz
|
||||
* 8 = 500Hz
|
||||
* 9 = 1000Hz
|
||||
* 10 = 2000Hz
|
||||
* 11 = 5000Hz
|
||||
* 12 = 10000Hz
|
||||
* 13 = 20000Hz
|
||||
* 14 = 50000Hz
|
||||
* 15 = 100000Hz
|
||||
*
|
||||
* @param rate
|
||||
*/
|
||||
private int getIntSamplingRate(String rate){
|
||||
|
||||
for(int i=0;i<SAMPLING_RATES.length; i++){
|
||||
if(rate.equals(SAMPLING_RATES[i])){
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
// Default sampling rate 10kHz
|
||||
logger.info("Using default sampling rate 12");
|
||||
return 12;
|
||||
}
|
||||
}
|
||||
87
src/main/java/ch/psi/fda/cdump/CdumpConfiguration.java
Normal file
87
src/main/java/ch/psi/fda/cdump/CdumpConfiguration.java
Normal file
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.cdump;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.util.Properties;
|
||||
|
||||
|
||||
public class CdumpConfiguration {
|
||||
|
||||
public final static String CDUMP_CONFIG = "ch.psi.fda.cdump.config.file";
|
||||
|
||||
private String dataChannel;
|
||||
private int nelements = 65536;
|
||||
private String controlChannel;
|
||||
private String samplingRateChannel;
|
||||
|
||||
public CdumpConfiguration(){
|
||||
String config = System.getProperty(CDUMP_CONFIG);
|
||||
|
||||
if(config != null){
|
||||
loadFile(new File(config));
|
||||
}
|
||||
else{
|
||||
throw new RuntimeException("No configuration file specified via -D"+CDUMP_CONFIG+"=...");
|
||||
}
|
||||
}
|
||||
|
||||
public void loadFile(File file) {
|
||||
Properties properties = new Properties();
|
||||
|
||||
if(file!=null){
|
||||
try {
|
||||
properties.load(new FileReader(file));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Cannot read file "+file, e);
|
||||
}
|
||||
}
|
||||
|
||||
dataChannel = properties.getProperty(CdumpConfiguration.class.getPackage().getName()+".dataChannel", "");
|
||||
controlChannel = properties.getProperty(CdumpConfiguration.class.getPackage().getName()+".controlChannel", "");
|
||||
samplingRateChannel = properties.getProperty(CdumpConfiguration.class.getPackage().getName()+".samplingRateChannel", "");
|
||||
nelements = Integer.parseInt(properties.getProperty(CdumpConfiguration.class.getPackage().getName()+".nelements", "65536"));
|
||||
|
||||
}
|
||||
|
||||
public String getDataChannel() {
|
||||
return dataChannel;
|
||||
}
|
||||
public void setDataChannel(String dataChannel) {
|
||||
this.dataChannel = dataChannel;
|
||||
}
|
||||
public String getControlChannel() {
|
||||
return controlChannel;
|
||||
}
|
||||
public void setControlChannel(String controlChannel) {
|
||||
this.controlChannel = controlChannel;
|
||||
}
|
||||
public String getSamplingRateChannel() {
|
||||
return samplingRateChannel;
|
||||
}
|
||||
public void setSamplingRateChannel(String samplingRateChannel) {
|
||||
this.samplingRateChannel = samplingRateChannel;
|
||||
}
|
||||
public int getNelements() {
|
||||
return nelements;
|
||||
}
|
||||
}
|
||||
73
src/main/java/ch/psi/fda/cdump/CdumpEContainer.java
Normal file
73
src/main/java/ch/psi/fda/cdump/CdumpEContainer.java
Normal file
@@ -0,0 +1,73 @@
|
||||
package ch.psi.fda.cdump;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import com.google.common.eventbus.EventBus;
|
||||
|
||||
import ch.psi.fda.EContainer;
|
||||
import ch.psi.fda.messages.EndOfStreamMessage;
|
||||
import ch.psi.fda.serializer.SerializerTXT;
|
||||
import ch.psi.jcae.ChannelService;
|
||||
|
||||
public class CdumpEContainer implements EContainer {
|
||||
|
||||
private final ChannelService cservice;
|
||||
private final CdumpEDescriptor edescriptor;
|
||||
private final EventBus eventbus;
|
||||
|
||||
private Cdump cdump;
|
||||
private SerializerTXT serializer;
|
||||
|
||||
private volatile boolean running = false;
|
||||
|
||||
public CdumpEContainer(ChannelService cservice, EventBus eventbus, CdumpEDescriptor edescriptor) {
|
||||
this.cservice = cservice;
|
||||
this.eventbus = eventbus;
|
||||
this.edescriptor = edescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
cdump = new Cdump(cservice, eventbus, new CdumpConfiguration());
|
||||
|
||||
File file = new File(edescriptor.getFileName());
|
||||
file.getParentFile().mkdirs(); // Create data base directory
|
||||
|
||||
serializer = new SerializerTXT(file);
|
||||
serializer.setShowDimensionHeader(false);
|
||||
|
||||
eventbus.register(serializer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
running = true;
|
||||
try{
|
||||
cdump.acquire(edescriptor.getSamplingRate());
|
||||
cdump.waitAcquireDone();
|
||||
}
|
||||
finally{
|
||||
running=false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void abort() {
|
||||
eventbus.post(new EndOfStreamMessage());
|
||||
cdump.stop();
|
||||
running = false;
|
||||
|
||||
eventbus.unregister(serializer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return running;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
abort();
|
||||
}
|
||||
|
||||
}
|
||||
26
src/main/java/ch/psi/fda/cdump/CdumpEContainerFactory.java
Normal file
26
src/main/java/ch/psi/fda/cdump/CdumpEContainerFactory.java
Normal file
@@ -0,0 +1,26 @@
|
||||
package ch.psi.fda.cdump;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.google.common.eventbus.EventBus;
|
||||
|
||||
import ch.psi.fda.EContainer;
|
||||
import ch.psi.fda.EContainerFactory;
|
||||
import ch.psi.fda.edescriptor.EDescriptor;
|
||||
import ch.psi.jcae.ChannelService;
|
||||
|
||||
public class CdumpEContainerFactory implements EContainerFactory {
|
||||
|
||||
@Inject
|
||||
private ChannelService cservice;
|
||||
|
||||
@Override
|
||||
public boolean supportsEDescriptor(EDescriptor descriptor) {
|
||||
return descriptor instanceof CdumpEDescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EContainer getEContainer(EDescriptor descriptor, EventBus bus) {
|
||||
return new CdumpEContainer(cservice, bus, (CdumpEDescriptor) descriptor);
|
||||
}
|
||||
}
|
||||
25
src/main/java/ch/psi/fda/cdump/CdumpEDescriptor.java
Normal file
25
src/main/java/ch/psi/fda/cdump/CdumpEDescriptor.java
Normal file
@@ -0,0 +1,25 @@
|
||||
package ch.psi.fda.cdump;
|
||||
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
import ch.psi.fda.edescriptor.EDescriptor;
|
||||
|
||||
@XmlRootElement(name="cdump")
|
||||
public class CdumpEDescriptor implements EDescriptor {
|
||||
|
||||
private String samplingRate;
|
||||
private String fileName;
|
||||
|
||||
public String getSamplingRate() {
|
||||
return samplingRate;
|
||||
}
|
||||
public void setSamplingRate(String samplingRate) {
|
||||
this.samplingRate = samplingRate;
|
||||
}
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
public void setFileName(String fileName) {
|
||||
this.fileName = fileName;
|
||||
}
|
||||
}
|
||||
44
src/main/java/ch/psi/fda/cdump/CdumpEDescriptorProvider.java
Normal file
44
src/main/java/ch/psi/fda/cdump/CdumpEDescriptorProvider.java
Normal file
@@ -0,0 +1,44 @@
|
||||
package ch.psi.fda.cdump;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.JAXBException;
|
||||
import javax.xml.bind.Unmarshaller;
|
||||
|
||||
import ch.psi.fda.DescriptorProvider;
|
||||
import ch.psi.fda.edescriptor.EDescriptor;
|
||||
import ch.psi.fda.vdescriptor.VDescriptor;
|
||||
|
||||
public class CdumpEDescriptorProvider implements DescriptorProvider {
|
||||
|
||||
private EDescriptor edescriptor;
|
||||
|
||||
@Override
|
||||
public void load(File... files) {
|
||||
try {
|
||||
JAXBContext context = JAXBContext.newInstance(CdumpEDescriptor.class);
|
||||
Unmarshaller u = context.createUnmarshaller();
|
||||
edescriptor = (EDescriptor) u.unmarshal(files[0]);
|
||||
} catch (JAXBException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public EDescriptor getEDescriptor() {
|
||||
return edescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VDescriptor getVDescriptor() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getEDescriptorClass() {
|
||||
return CdumpEDescriptor.class;
|
||||
}
|
||||
|
||||
}
|
||||
102
src/main/java/ch/psi/fda/cdump/CdumpListener.java
Normal file
102
src/main/java/ch/psi/fda/cdump/CdumpListener.java
Normal file
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2013 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but without any
|
||||
* warranty; without even the implied warranty of merchantability or fitness for
|
||||
* a particular purpose. See the GNU Lesser General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package ch.psi.fda.cdump;
|
||||
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import ch.psi.fda.messages.DataMessage;
|
||||
import ch.psi.fda.messages.EndOfStreamMessage;
|
||||
import ch.psi.fda.messages.Metadata;
|
||||
|
||||
import com.google.common.eventbus.EventBus;
|
||||
|
||||
/**
|
||||
* Listener that monitors the adc data channel and splitting up the data
|
||||
*/
|
||||
public class CdumpListener implements PropertyChangeListener {
|
||||
|
||||
private final EventBus bus;
|
||||
private final int numberOfElements;
|
||||
|
||||
private boolean first = true;
|
||||
private int numberOfWaveforms = 0;
|
||||
private final List<Metadata> metadata = new ArrayList<>();
|
||||
|
||||
public CdumpListener(EventBus bus, int numberOfElements){
|
||||
this.bus = bus;
|
||||
this.numberOfElements = numberOfElements;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
if(evt.getPropertyName().equals("value")){
|
||||
transform((int[]) evt.getNewValue());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Transform received waveform
|
||||
* 1. Take channel waveform
|
||||
* [wavefrom .......................................................]
|
||||
* 2. Split up waveform
|
||||
* [1-number of elements][2-number of elements][3-number of elements]
|
||||
* 3. Rotate splitted waveforms
|
||||
* [1-0,2-0,3-0] thats one message
|
||||
* [1-1,2-1,3-1]
|
||||
* ...
|
||||
* [1-noe, 2-noe, 3-noe]
|
||||
*
|
||||
*/
|
||||
public void transform(int[] value){
|
||||
|
||||
// The first time check whether received waveform is a multiple of the specified number of elements number
|
||||
// Calculate how many waveforms are within the received waveform
|
||||
if(first){
|
||||
first=false;
|
||||
int nelements = value.length;
|
||||
int n = nelements % numberOfElements;
|
||||
if (n != 0) {
|
||||
throw new RuntimeException("Array size is not a multiple of "+numberOfElements);
|
||||
}
|
||||
numberOfWaveforms = nelements / numberOfElements;
|
||||
for(int i=0;i<numberOfWaveforms;i++){
|
||||
metadata.add(new Metadata("waveform-"+i));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Split and rotate waveform
|
||||
for (int x = 0; x < numberOfElements; x++) {
|
||||
|
||||
DataMessage m = new DataMessage(metadata);
|
||||
for (int t = 0; t < numberOfWaveforms; t++) {
|
||||
m.getData().add(value[x + t * numberOfElements]);
|
||||
}
|
||||
bus.post(m);
|
||||
}
|
||||
}
|
||||
|
||||
public void terminate(){
|
||||
bus.post(new EndOfStreamMessage());
|
||||
}
|
||||
}
|
||||
107
src/main/java/ch/psi/fda/cdump/ui/CdumpMain.java
Normal file
107
src/main/java/ch/psi/fda/cdump/ui/CdumpMain.java
Normal file
@@ -0,0 +1,107 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.cdump.ui;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import com.google.common.eventbus.AsyncEventBus;
|
||||
import com.google.common.eventbus.EventBus;
|
||||
|
||||
import ch.psi.fda.cdump.Cdump;
|
||||
import ch.psi.fda.cdump.CdumpConfiguration;
|
||||
import ch.psi.fda.serializer.SerializerTXT;
|
||||
import ch.psi.jcae.ChannelService;
|
||||
import ch.psi.jcae.impl.DefaultChannelService;
|
||||
import sun.misc.Signal;
|
||||
import sun.misc.SignalHandler;
|
||||
|
||||
@SuppressWarnings("restriction")
|
||||
public class CdumpMain {
|
||||
|
||||
public static void main(String[] args){
|
||||
|
||||
final ChannelService cservice = new DefaultChannelService();
|
||||
|
||||
if(args.length != 2){
|
||||
System.err.println("Usage: cdump <samplingRate> <datafilename>");
|
||||
StringBuilder b = new StringBuilder();
|
||||
for(String s: Cdump.SAMPLING_RATES){
|
||||
b.append(s);
|
||||
b.append(" ");
|
||||
}
|
||||
System.err.println("Supported rates: "+b.toString());
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
String samplingRate = args[0];
|
||||
|
||||
// Calculate data file location/name
|
||||
String fname = args[1];
|
||||
|
||||
File f = new File(fname);
|
||||
f.getParentFile().mkdirs(); // Create data base directory
|
||||
|
||||
// Create execution service
|
||||
EventBus eventbus = new AsyncEventBus(Executors.newSingleThreadExecutor());
|
||||
final Cdump service = new Cdump(cservice, eventbus, new CdumpConfiguration());
|
||||
|
||||
SerializerTXT serializer = new SerializerTXT(f);
|
||||
serializer.setShowDimensionHeader(false);
|
||||
|
||||
eventbus.register(serializer);
|
||||
|
||||
// Stop/abort handling of acquisition
|
||||
Signal.handle(new Signal("INT"), new SignalHandler() {
|
||||
/**
|
||||
* Thread save signal counter
|
||||
*/
|
||||
private AtomicInteger signalCount= new AtomicInteger(0);
|
||||
|
||||
/**
|
||||
* Testing signal handler (in Eclipse) use this after starting scan:
|
||||
*
|
||||
* SL5: A=`ps -ef | tail -10 | grep jav[a] | awk '{printf $2}'`;kill -2 $A
|
||||
* MacOS X: A=`ps -ef | grep AcquisitionMai[n] | awk '{printf $2}'`;kill -2 $A
|
||||
*
|
||||
* on the command line use CTRL-C
|
||||
*/
|
||||
@Override
|
||||
public void handle(Signal signal) {
|
||||
|
||||
|
||||
int count = signalCount.incrementAndGet();
|
||||
|
||||
// If signal is received more than 1 time forcefully abort application
|
||||
if(count>1){
|
||||
System.exit(2);
|
||||
}
|
||||
|
||||
service.stop();
|
||||
|
||||
cservice.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
service.acquire(samplingRate);
|
||||
}
|
||||
|
||||
}
|
||||
33
src/main/java/ch/psi/fda/core/Action.java
Normal file
33
src/main/java/ch/psi/fda/core/Action.java
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.core;
|
||||
|
||||
public interface Action {
|
||||
|
||||
/**
|
||||
* Execute logic of the action
|
||||
*/
|
||||
public void execute() throws InterruptedException;
|
||||
|
||||
/**
|
||||
* Abort the execution logic of the action
|
||||
*/
|
||||
public void abort();
|
||||
}
|
||||
66
src/main/java/ch/psi/fda/core/ActionLoop.java
Normal file
66
src/main/java/ch/psi/fda/core/ActionLoop.java
Normal file
@@ -0,0 +1,66 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.core;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.google.common.eventbus.EventBus;
|
||||
|
||||
/**
|
||||
* Loop of actions to accomplish a task. Depending on the loop
|
||||
* actions may be executed in a different way.
|
||||
*/
|
||||
public interface ActionLoop extends Action {
|
||||
|
||||
/**
|
||||
* Prepare ActionLoop for execution.
|
||||
*/
|
||||
public void prepare();
|
||||
|
||||
/**
|
||||
* Cleanup resources used by this ActionLoop while it was executed.
|
||||
*/
|
||||
public void cleanup();
|
||||
|
||||
/**
|
||||
* Get the pre actions of the Loop
|
||||
* @return pre actions
|
||||
*/
|
||||
public List<Action> getPreActions();
|
||||
|
||||
/**
|
||||
* Get the post actions of the loop
|
||||
* @return post actions
|
||||
*/
|
||||
public List<Action> getPostActions();
|
||||
|
||||
/**
|
||||
* @return is a datagroup
|
||||
*/
|
||||
public boolean isDataGroup();
|
||||
|
||||
/**
|
||||
* Set whether data of the loop belongs to a own data group
|
||||
* @param dataGroup
|
||||
*/
|
||||
public void setDataGroup(boolean dataGroup);
|
||||
|
||||
public EventBus getEventBus();
|
||||
}
|
||||
49
src/main/java/ch/psi/fda/core/Actor.java
Normal file
49
src/main/java/ch/psi/fda/core/Actor.java
Normal file
@@ -0,0 +1,49 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.core;
|
||||
|
||||
public interface Actor {
|
||||
/**
|
||||
* Set actor value
|
||||
*/
|
||||
public void set() throws InterruptedException;
|
||||
|
||||
/**
|
||||
* Function to check whether the actor has a next set value
|
||||
* @return Returns true if there is an actor value for the next iteration.
|
||||
* False if there is no actor value (i.e. this will be the last iteration in the ActionLoop)
|
||||
*/
|
||||
public boolean hasNext();
|
||||
|
||||
/**
|
||||
* Initialize the actor to the start
|
||||
*/
|
||||
public void init();
|
||||
|
||||
/**
|
||||
* Reverse the set values of the actor
|
||||
*/
|
||||
public void reverse();
|
||||
|
||||
/**
|
||||
* Reset the actuator to its initial configuration
|
||||
*/
|
||||
public void reset();
|
||||
}
|
||||
40
src/main/java/ch/psi/fda/core/ActorSetCallable.java
Normal file
40
src/main/java/ch/psi/fda/core/ActorSetCallable.java
Normal file
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.core;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
/**
|
||||
* Callable used for parallel execution of the set method of an actor
|
||||
*/
|
||||
public class ActorSetCallable implements Callable<Object> {
|
||||
|
||||
private Actor actor;
|
||||
|
||||
public ActorSetCallable(Actor actor){
|
||||
this.actor = actor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object call() throws Exception {
|
||||
actor.set();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
41
src/main/java/ch/psi/fda/core/Guard.java
Normal file
41
src/main/java/ch/psi/fda/core/Guard.java
Normal file
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.core;
|
||||
|
||||
/**
|
||||
* Guard to protect specific activities. A guard can be used to check for environment changes while a
|
||||
* certain activity was executed.
|
||||
*
|
||||
* Example:
|
||||
* An Guard can be used to check whether an injection happened while the the detector were read.
|
||||
*/
|
||||
public interface Guard {
|
||||
|
||||
/**
|
||||
* Initialize guard object and its internal state.
|
||||
*/
|
||||
public void init();
|
||||
|
||||
/**
|
||||
* Check the status of the guard.
|
||||
* @return Returns true if the guard condition was not constrainted since the last init call. False otherwise.
|
||||
*/
|
||||
public boolean check();
|
||||
}
|
||||
47
src/main/java/ch/psi/fda/core/Manipulation.java
Normal file
47
src/main/java/ch/psi/fda/core/Manipulation.java
Normal file
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.core;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import ch.psi.fda.messages.DataMessage;
|
||||
import ch.psi.fda.messages.Metadata;
|
||||
|
||||
public interface Manipulation {
|
||||
|
||||
/**
|
||||
* Get the id of the manipulation
|
||||
* @return id of manipulation
|
||||
*/
|
||||
public String getId();
|
||||
|
||||
/**
|
||||
* Initialize the manipulation
|
||||
* @param metadata Metadata of the incomming data message
|
||||
*/
|
||||
public void initialize(List<Metadata> metadata);
|
||||
|
||||
/**
|
||||
* Execute the manipulation on the passed data message
|
||||
* @param message Message to manipulate
|
||||
* @return Result of the manipulation
|
||||
*/
|
||||
public Object execute(DataMessage message);
|
||||
}
|
||||
39
src/main/java/ch/psi/fda/core/Sensor.java
Normal file
39
src/main/java/ch/psi/fda/core/Sensor.java
Normal file
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.core;
|
||||
|
||||
/**
|
||||
* The sensor interface describes an entity that can be read out like a
|
||||
* simple channel or (image) detector. Depending on the sensor type the
|
||||
* returned data is of a certain type.
|
||||
*/
|
||||
public interface Sensor {
|
||||
/**
|
||||
* Readout sensor.
|
||||
* @return Sensor value. The type of the returned value depends on the sensor type.
|
||||
*/
|
||||
public Object read() throws InterruptedException;
|
||||
|
||||
/**
|
||||
* Get the global id of the sensor
|
||||
* @return id of sensor
|
||||
*/
|
||||
public String getId();
|
||||
}
|
||||
87
src/main/java/ch/psi/fda/core/TestConfiguration.java
Normal file
87
src/main/java/ch/psi/fda/core/TestConfiguration.java
Normal file
@@ -0,0 +1,87 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2012 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but without any
|
||||
* warranty; without even the implied warranty of merchantability or fitness for
|
||||
* a particular purpose. See the GNU Lesser General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package ch.psi.fda.core;
|
||||
|
||||
public class TestConfiguration {
|
||||
private static final TestConfiguration instance = new TestConfiguration();
|
||||
|
||||
private final String otfPrefix = "MTEST-HW3-OTFX";
|
||||
private final String crlogicPrefix = "MTEST-HW3-CRL";
|
||||
private final String prefixScaler = "MTEST-HW3:JS";
|
||||
private final String server = "MTEST-VME-HW3";
|
||||
|
||||
private final String motor1 = "MTEST-HW3:MOT1";
|
||||
private final String analogIn1 = "MTEST-HW3-AI1:AI_01";
|
||||
|
||||
private final String ioc = "MTEST-VME-HW3.psi.ch";
|
||||
|
||||
private TestConfiguration(){
|
||||
}
|
||||
|
||||
public static TestConfiguration getInstance(){
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the prefix
|
||||
*/
|
||||
public String getCrlogicPrefix() {
|
||||
return crlogicPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the prefixScaler
|
||||
*/
|
||||
public String getPrefixScaler() {
|
||||
return prefixScaler;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the server
|
||||
*/
|
||||
public String getServer() {
|
||||
return server;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the motor1
|
||||
*/
|
||||
public String getMotor1() {
|
||||
return motor1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the analogIn1
|
||||
*/
|
||||
public String getAnalogIn1() {
|
||||
return analogIn1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the otfPrefix
|
||||
*/
|
||||
public String getOtfPrefix() {
|
||||
return otfPrefix;
|
||||
}
|
||||
|
||||
public String getIoc() {
|
||||
return ioc;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.core.actions;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import ch.psi.fda.core.Action;
|
||||
import ch.psi.jcae.Channel;
|
||||
import ch.psi.jcae.ChannelException;
|
||||
|
||||
/**
|
||||
* Perform a put on the specified Channel Access channel. The put can be done synchronous or
|
||||
* asynchronously.
|
||||
*/
|
||||
public class ChannelAccessCondition<E> implements Action {
|
||||
|
||||
private static Logger logger = Logger.getLogger(ChannelAccessCondition.class.getName());
|
||||
|
||||
private final Channel<E> channel;
|
||||
private final E expectedValue;
|
||||
private final Comparator<E> comparator;
|
||||
private final Long timeout;
|
||||
|
||||
private volatile boolean abort = false;
|
||||
private volatile Thread waitT = null;
|
||||
|
||||
/**
|
||||
* @param channel Channel to wait value for
|
||||
* @param expectedValue Value to wait for
|
||||
* @param timeout Timeout of the condition in milliseconds (null accepted - will take default wait timeout for channels ch.psi.jcae.ChannelBeanFactory.waitTimeout)
|
||||
*
|
||||
* @throws IllegalArgumentException Timeout specified is not >=0
|
||||
*/
|
||||
public ChannelAccessCondition(Channel<E> channel, E expectedValue, Long timeout){
|
||||
|
||||
if(timeout != null && timeout<=0){
|
||||
throw new IllegalArgumentException("Timeout must be > 0");
|
||||
}
|
||||
|
||||
this.channel = channel;
|
||||
this.expectedValue = expectedValue;
|
||||
this.comparator = null;
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
public ChannelAccessCondition(Channel<E> channel, E expectedValue, Comparator<E> comparator, Long timeout){
|
||||
|
||||
if(timeout != null && timeout<=0){
|
||||
throw new IllegalArgumentException("Timeout must be > 0");
|
||||
}
|
||||
|
||||
this.channel = channel;
|
||||
this.expectedValue = expectedValue;
|
||||
this.comparator = comparator;
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @throws RuntimeException Channel value did not reach expected value (within the specified timeout period)
|
||||
*/
|
||||
@Override
|
||||
public void execute() throws InterruptedException {
|
||||
abort=false;
|
||||
logger.finest("Checking channel "+channel.getName()+" for value "+expectedValue+" [timeout: "+timeout+"]" );
|
||||
try{
|
||||
waitT = Thread.currentThread();
|
||||
try {
|
||||
if(comparator==null){
|
||||
if(timeout == null){
|
||||
channel.waitForValue(expectedValue);
|
||||
}
|
||||
else{
|
||||
channel.waitForValue(expectedValue, timeout);
|
||||
}
|
||||
}
|
||||
else{
|
||||
if(timeout == null){
|
||||
channel.waitForValue(expectedValue, comparator);
|
||||
}
|
||||
else{
|
||||
channel.waitForValue(expectedValue, comparator, timeout);
|
||||
}
|
||||
}
|
||||
} catch (ExecutionException | ChannelException | TimeoutException | InterruptedException e) {
|
||||
if(abort && e instanceof InterruptedException){
|
||||
return;
|
||||
}
|
||||
throw new RuntimeException("Channel [name:"+channel.getName()+"] did not reach expected value "+expectedValue+" ", e);
|
||||
}
|
||||
}
|
||||
finally{
|
||||
waitT=null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void abort() {
|
||||
abort=true;
|
||||
if(waitT!=null){
|
||||
waitT.interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
94
src/main/java/ch/psi/fda/core/actions/ChannelAccessPut.java
Normal file
94
src/main/java/ch/psi/fda/core/actions/ChannelAccessPut.java
Normal file
@@ -0,0 +1,94 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.core.actions;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import ch.psi.fda.core.Action;
|
||||
import ch.psi.jcae.Channel;
|
||||
import ch.psi.jcae.ChannelException;
|
||||
|
||||
/**
|
||||
* Perform a put on the specified Channel Access channel. The put can be done synchronous or
|
||||
* asynchronously.
|
||||
*/
|
||||
public class ChannelAccessPut<E> implements Action {
|
||||
|
||||
private static Logger logger = Logger.getLogger(ChannelAccessPut.class.getName());
|
||||
|
||||
private final Channel<E> channel;
|
||||
private final E value;
|
||||
private final boolean asynchronous;
|
||||
|
||||
private final Long timeout;
|
||||
|
||||
/**
|
||||
* @param channel
|
||||
* @param value Value to set
|
||||
* @param asynchronous Flag whether to set the value synchronous (wait for response) or asynchronously (fire and forget)
|
||||
* @param timeout Timeout used for set operation (time that set need to come back)
|
||||
*/
|
||||
public ChannelAccessPut(Channel<E> channel, E value, boolean asynchronous, Long timeout){
|
||||
|
||||
this.channel = channel;
|
||||
this.value = value;
|
||||
this.asynchronous = asynchronous;
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Additional constructor for convenience. This constructor defaults the operation type to synchronous put.
|
||||
* @param channel
|
||||
* @param value Value to set
|
||||
*/
|
||||
public ChannelAccessPut(Channel<E> channel, E value){
|
||||
this(channel, value, false, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws RuntimeException Cannot set value on channel
|
||||
*/
|
||||
@Override
|
||||
public void execute() throws InterruptedException {
|
||||
logger.finest("Put to channel: "+channel.getName()+ " asynchronous: "+asynchronous);
|
||||
try{
|
||||
if(asynchronous){
|
||||
channel.setValueNoWait(value);
|
||||
}
|
||||
else{
|
||||
if(timeout==null){
|
||||
channel.setValue(value);
|
||||
}
|
||||
else{
|
||||
channel.setValueAsync(value).get(timeout, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
} catch (ExecutionException | TimeoutException | ChannelException e) {
|
||||
throw new RuntimeException("Unable to set channel [name:"+channel.getName()+"] to value "+value, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void abort() {
|
||||
}
|
||||
}
|
||||
52
src/main/java/ch/psi/fda/core/actions/Delay.java
Normal file
52
src/main/java/ch/psi/fda/core/actions/Delay.java
Normal file
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.core.actions;
|
||||
|
||||
import ch.psi.fda.core.Action;
|
||||
|
||||
/**
|
||||
* Wait a specific time until executing the next action ...
|
||||
*/
|
||||
public class Delay implements Action {
|
||||
|
||||
private final long time;
|
||||
|
||||
/**
|
||||
* @param time Time to wait in milliseconds
|
||||
*/
|
||||
public Delay(long time){
|
||||
|
||||
// Check if delay time is positive and >0
|
||||
if(time<=0){
|
||||
throw new IllegalArgumentException("Wait time must be >0");
|
||||
}
|
||||
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws InterruptedException {
|
||||
Thread.sleep(time);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void abort() {
|
||||
}
|
||||
}
|
||||
142
src/main/java/ch/psi/fda/core/actions/JythonAction.java
Normal file
142
src/main/java/ch/psi/fda/core/actions/JythonAction.java
Normal file
@@ -0,0 +1,142 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.core.actions;
|
||||
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineManager;
|
||||
import javax.script.ScriptException;
|
||||
|
||||
import ch.psi.fda.core.Action;
|
||||
|
||||
/**
|
||||
* Executes a python script inside a Jython interpreter
|
||||
*/
|
||||
public class JythonAction implements Action {
|
||||
|
||||
private static Logger logger = Logger.getLogger(JythonAction.class.getName());
|
||||
|
||||
/**
|
||||
* Pattern of the entry function of the jython script
|
||||
*/
|
||||
private static final String entryFunction = "process";
|
||||
private static final String entryFunctionPattern = "def "+entryFunction+"\\((.*)\\):";
|
||||
|
||||
private ScriptEngine engine;
|
||||
|
||||
private String jythonCall; // entry call to script - including all parameters, etc.
|
||||
|
||||
private final Map<String,Object> globalObjects;
|
||||
|
||||
public JythonAction(String script, Map<String, ?> mapping){
|
||||
this(script, mapping, new HashMap<String,Object>());
|
||||
}
|
||||
|
||||
public JythonAction(String script, Map<String, ?> mapping, Map<String,Object> globalObjects){
|
||||
|
||||
this.globalObjects = globalObjects;
|
||||
|
||||
// Workaround for Jython memory leak
|
||||
// http://blog.hillbrecht.de/2009/07/11/jython-memory-leakout-of-memory-problem/
|
||||
System.setProperty("python.options.internalTablesImpl","weak");
|
||||
|
||||
// Create new script engine
|
||||
this.engine = new ScriptEngineManager().getEngineByName("python");
|
||||
|
||||
// Determine script entry function and the function parameters
|
||||
Pattern pattern = Pattern.compile(entryFunctionPattern);
|
||||
Matcher matcher = pattern.matcher(script);
|
||||
String[] functionParameters = null;
|
||||
if(matcher.find() && matcher.groupCount()==1){
|
||||
logger.finest("Entry function '"+entryFunctionPattern+"' found - Identified parameters: "+matcher.group(1));
|
||||
jythonCall = entryFunction+"("+matcher.group(1)+")";
|
||||
if(matcher.group(1).matches(" *")){
|
||||
functionParameters = new String[0];
|
||||
}
|
||||
else{
|
||||
functionParameters = matcher.group(1).split(" *, *");
|
||||
}
|
||||
}
|
||||
else{
|
||||
throw new IllegalArgumentException("Cannot determine entry function: "+entryFunctionPattern);
|
||||
}
|
||||
|
||||
// Check whether all function parameters have a mapping
|
||||
for(int i=0;i<functionParameters.length; i++){
|
||||
String p = functionParameters[i];
|
||||
p = p.trim();
|
||||
boolean found = false;
|
||||
for(String pm: mapping.keySet()){
|
||||
if(pm.equals(p)){
|
||||
found=true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!found){
|
||||
throw new IllegalArgumentException("No mapping compontent found for parameter "+p);
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether more mappings are specified than function parameters
|
||||
if(functionParameters.length<mapping.size()){
|
||||
throw new IllegalArgumentException("More mappings than function parameters are specified");
|
||||
}
|
||||
|
||||
// Load manipulation script
|
||||
try {
|
||||
engine.eval(script);
|
||||
} catch (ScriptException e) {
|
||||
throw new RuntimeException("Unable to load manipulation script", e);
|
||||
}
|
||||
|
||||
for(String b: mapping.keySet()){
|
||||
// Assign channel bean to variable
|
||||
engine.put(b, mapping.get(b));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
|
||||
// Set global objects
|
||||
// This block is not in initialization as we want to assure that all invocations
|
||||
// of this manipulation will get the same value (i.e. to prevent inconsistent behavior
|
||||
// if variable was changed during an execution of the manipulation)
|
||||
for(String k: globalObjects.keySet()){
|
||||
engine.put(k, globalObjects.get(k));
|
||||
}
|
||||
|
||||
try {
|
||||
engine.eval(jythonCall+"\n");
|
||||
} catch (ScriptException e) {
|
||||
throw new RuntimeException("Action failed while executing the Jython script",e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void abort() {
|
||||
}
|
||||
}
|
||||
133
src/main/java/ch/psi/fda/core/actions/ShellAction.java
Normal file
133
src/main/java/ch/psi/fda/core/actions/ShellAction.java
Normal file
@@ -0,0 +1,133 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.core.actions;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import ch.psi.fda.core.Action;
|
||||
|
||||
/**
|
||||
* Action that executes a specified command when it is executed.
|
||||
*/
|
||||
public class ShellAction implements Action{
|
||||
|
||||
private static Logger logger = Logger.getLogger(ShellAction.class.getName());
|
||||
|
||||
private volatile Process process;
|
||||
private volatile boolean abort = false;
|
||||
private boolean checkExitValue = true;
|
||||
private int exitValue = 0;
|
||||
|
||||
/**
|
||||
* Name (full path if it is not in the system path) of the script to execute when
|
||||
* the execute() function of this action is invoked.
|
||||
*/
|
||||
private final String script;
|
||||
|
||||
/**
|
||||
* @param script Name of the command to execute when this action is invoked
|
||||
*
|
||||
* @throws IllegalArgumentException Specified script does not exist
|
||||
*/
|
||||
public ShellAction(String script){
|
||||
String[] scri = script.split("[ ,\t]");
|
||||
File s = new File(scri[0]);
|
||||
if(!s.exists()){
|
||||
throw new IllegalArgumentException("Script "+script+" does not exist.");
|
||||
}
|
||||
this.script = script;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws InterruptedException {
|
||||
try{
|
||||
abort = false;
|
||||
logger.fine("Execute script "+script);
|
||||
process = Runtime.getRuntime().exec(new String[]{"/bin/bash","-c",script});
|
||||
int exitVal = process.waitFor();
|
||||
|
||||
// Log output of the shell script if loglevel is finest
|
||||
if(logger.isLoggable(Level.FINEST)){
|
||||
logger.finest("STDOUT [BEGIN]");
|
||||
// Ideally the readout of the stream should be in parallel to the processing of the script! I.e. the output appears in the log as it is generated by the script!
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
|
||||
String line = null;
|
||||
while((line=reader.readLine()) != null){
|
||||
logger.finest(line);
|
||||
}
|
||||
logger.finest("STDOUT [END]");
|
||||
|
||||
logger.finest("STDERR [BEGIN]");
|
||||
// Ideally the readout of the stream should be in parallel to the processing of the script! I.e. the output appears in the log as it is generated by the script!
|
||||
reader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
|
||||
line = null;
|
||||
while((line=reader.readLine()) != null){
|
||||
logger.finest(line);
|
||||
}
|
||||
logger.finest("STDERR [END]");
|
||||
}
|
||||
|
||||
logger.fine("Script ["+script+"] return value: "+exitVal);
|
||||
|
||||
if(abort){
|
||||
throw new RuntimeException("Script ["+script+"] was aborted");
|
||||
}
|
||||
else{
|
||||
// Check script exit value to 0 if != 0 then throw an runtime exception
|
||||
if(checkExitValue && exitVal != exitValue){
|
||||
throw new RuntimeException("Script ["+script+"] returned with an exit value not equal to 0");
|
||||
}
|
||||
}
|
||||
process = null;
|
||||
}
|
||||
catch(IOException e){
|
||||
throw new RuntimeException("Unable to execute script: "+script,e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void abort() {
|
||||
abort=true;
|
||||
if(process!=null){
|
||||
process.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isCheckExitValue() {
|
||||
return checkExitValue;
|
||||
}
|
||||
|
||||
public void setCheckExitValue(boolean checkExitValue) {
|
||||
this.checkExitValue = checkExitValue;
|
||||
}
|
||||
|
||||
public int getExitValue() {
|
||||
return exitValue;
|
||||
}
|
||||
|
||||
public void setExitValue(int exitValue) {
|
||||
this.exitValue = exitValue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.core.actors;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import ch.psi.fda.core.Actor;
|
||||
import ch.psi.jcae.Channel;
|
||||
import ch.psi.jcae.ChannelException;
|
||||
|
||||
public class ChannelAccessFunctionActuator<T> implements Actor {
|
||||
|
||||
private static Logger logger = Logger.getLogger(ChannelAccessFunctionActuator.class.getName());
|
||||
|
||||
private boolean asynchronous = false;
|
||||
private double start;
|
||||
private double end;
|
||||
private double stepSize;
|
||||
private int direction; // Move direction (start<end = 1, start>end = -1)
|
||||
|
||||
/**
|
||||
* Execution count of actuator. This variable is used to minimize the floating point
|
||||
* rounding errors for calculating the next step.
|
||||
*/
|
||||
private int count;
|
||||
|
||||
/**
|
||||
* Flag that indicates whether there is a next set value for the Actor
|
||||
*/
|
||||
private boolean next;
|
||||
|
||||
/**
|
||||
* Value to set at next @see ch.psi.fda.engine.Actor#set() call
|
||||
*/
|
||||
private double value;
|
||||
|
||||
/**
|
||||
* Level of accuracy the positioner need to have (e.g. if a positioner is set to 1 the readback set value
|
||||
* of the positioner need to have at lease 1+/-accuracy)
|
||||
* Default is stepSize/2
|
||||
*/
|
||||
private double accuracy;
|
||||
|
||||
/**
|
||||
* Move timeout
|
||||
*/
|
||||
private Long timeout;
|
||||
|
||||
private final T doneValue;
|
||||
private final long doneDelay;
|
||||
|
||||
private final double originalStart;
|
||||
private final double originalEnd;
|
||||
private final int originalDirection;
|
||||
|
||||
private Channel<Double> channel;
|
||||
private Channel<T> doneChannel = null;
|
||||
|
||||
private final Function function;
|
||||
private boolean checkActorSet = true;
|
||||
|
||||
/**
|
||||
* Constructor - Initialize actor
|
||||
* @param channelName
|
||||
* @param start
|
||||
* @param end
|
||||
* @param stepSize
|
||||
* @param timeout Maximum move time (in milliseconds)
|
||||
*/
|
||||
public ChannelAccessFunctionActuator(Channel<Double> channel, Function function, double start, double end, double stepSize, Long timeout){
|
||||
this(channel, null, null, 0, function, start, end, stepSize, timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param channel
|
||||
* @param doneChannel If null actor will not wait (for this channel) to continue
|
||||
* @param doneValue
|
||||
* @param doneDelay Delay in seconds before checking the done channel
|
||||
* @param start
|
||||
* @param end
|
||||
* @param stepSize
|
||||
* @param timeout Maximum move time (in milliseconds)
|
||||
*/
|
||||
public ChannelAccessFunctionActuator(Channel<Double> channel, Channel<T> doneChannel, T doneValue, double doneDelay, Function function, double start, double end, double stepSize, Long timeout){
|
||||
|
||||
this.doneValue = doneValue;
|
||||
this.doneDelay = (long) Math.floor((doneDelay*1000));
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
|
||||
if(stepSize <= 0){
|
||||
throw new IllegalArgumentException("Step size ["+stepSize+"] must be > 0");
|
||||
}
|
||||
this.stepSize = stepSize;
|
||||
|
||||
this.accuracy = stepSize/2;
|
||||
|
||||
// Validate and save timeout parameter
|
||||
if(timeout!=null && timeout<=0){
|
||||
throw new IllegalArgumentException("Timeout must be >0 or null");
|
||||
}
|
||||
else{
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
if(function==null){
|
||||
throw new IllegalArgumentException("Function must not be null");
|
||||
}
|
||||
this.function = function;
|
||||
|
||||
|
||||
init();
|
||||
|
||||
// Save original settings
|
||||
this.originalStart = start;
|
||||
this.originalEnd = end;
|
||||
this.originalDirection = direction;
|
||||
|
||||
this.channel = channel;
|
||||
this.doneChannel = doneChannel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set() throws InterruptedException {
|
||||
|
||||
// Throw an IllegalStateException in the case that set is called although there is no next step.
|
||||
if(!next){
|
||||
throw new IllegalStateException("The actuator does not have any next step.");
|
||||
}
|
||||
|
||||
// Set actuator channel
|
||||
logger.finest("Set actuator channel "+channel.getName()+" to value: "+value);
|
||||
try {
|
||||
double fvalue = function.calculate(value);
|
||||
|
||||
if(!asynchronous){
|
||||
if(timeout==null){
|
||||
channel.setValue(fvalue);
|
||||
}
|
||||
else{
|
||||
channel.setValueAsync(fvalue).get(timeout, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
else{
|
||||
channel.setValueNoWait(fvalue);
|
||||
}
|
||||
|
||||
if(doneChannel != null){
|
||||
Thread.sleep(doneDelay);
|
||||
doneChannel.waitForValue(doneValue);
|
||||
}
|
||||
|
||||
// Check whether the set value is really on the value that was set before.
|
||||
if(checkActorSet){
|
||||
double c = channel.getValue(true);
|
||||
double a = Math.abs( c - fvalue );
|
||||
if ( a > accuracy ){
|
||||
throw new RuntimeException("Actor could not be set to the value "+fvalue+" The readback of the set value does not match the value that was set [value: "+c+" delta: "+a+" accuracy: "+accuracy+"]");
|
||||
}
|
||||
}
|
||||
|
||||
} catch (ExecutionException | TimeoutException | ChannelException e) {
|
||||
throw new RuntimeException("Unable to move actuator [channel: "+channel.getName()+"] to value "+value,e);
|
||||
}
|
||||
|
||||
|
||||
count++;
|
||||
double nextValue = start+(count*stepSize*direction); // Done like this to keep floating point rounding errors minimal
|
||||
|
||||
if((direction==1&&nextValue<=end)||(direction==-1&nextValue>=end)){
|
||||
|
||||
// Apply function
|
||||
// nextValue = function.calculate(nextValue);
|
||||
|
||||
logger.fine("Next actor value: "+nextValue);
|
||||
value=nextValue;
|
||||
this.next = true;
|
||||
}
|
||||
else{
|
||||
// There is no next set value
|
||||
this.next = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return next;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
this.count = 0;
|
||||
|
||||
// Determine move direction
|
||||
this.direction = 1;
|
||||
if(start>end){
|
||||
direction=-1; // Move in negative direction
|
||||
}
|
||||
|
||||
// Set first set value to the start value
|
||||
this.value = start;
|
||||
this.next = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void reverse() {
|
||||
double oldStart = start;
|
||||
this.start = this.end;
|
||||
this.end = oldStart;
|
||||
|
||||
// Determine move direction
|
||||
this.direction = 1;
|
||||
if(this.start>this.end){
|
||||
direction=-1; // Move in negative direction
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
this.start = this.originalStart;
|
||||
this.end = this.originalEnd;
|
||||
this.direction = this.originalDirection;
|
||||
}
|
||||
|
||||
public boolean isAsynchronous() {
|
||||
return asynchronous;
|
||||
}
|
||||
public void setAsynchronous(boolean asynchronous) {
|
||||
this.asynchronous = asynchronous;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,234 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.core.actors;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import ch.psi.fda.core.Actor;
|
||||
import ch.psi.jcae.Channel;
|
||||
import ch.psi.jcae.ChannelException;
|
||||
|
||||
/**
|
||||
* This actuator sets an Channel Access channel from a start to an end value by doing discrete steps.
|
||||
*/
|
||||
public class ChannelAccessLinearActuator<T> implements Actor {
|
||||
|
||||
private static Logger logger = Logger.getLogger(ChannelAccessLinearActuator.class.getName());
|
||||
|
||||
private boolean asynchronous = false;
|
||||
|
||||
private double start;
|
||||
private double end;
|
||||
private double stepSize;
|
||||
private int direction;
|
||||
|
||||
/**
|
||||
* Execution count of actuator. This variable is used to minimize the floating point
|
||||
* rounding errors for calculating the next step.
|
||||
*/
|
||||
private int count;
|
||||
|
||||
/**
|
||||
* Flag that indicates whether there is a next set value for the Actor
|
||||
*/
|
||||
private boolean next;
|
||||
|
||||
/**
|
||||
* Value to set at next @see ch.psi.fda.engine.Actor#set() call
|
||||
*/
|
||||
private double value;
|
||||
|
||||
/**
|
||||
* Level of accuracy the positioner need to have (e.g. if a positioner is set to 1 the readback set value
|
||||
* of the positioner need to have at lease 1+/-accuracy)
|
||||
* Default is stepSize/2
|
||||
*/
|
||||
private double accuracy;
|
||||
|
||||
|
||||
|
||||
private final T doneValue;
|
||||
private final long doneDelay;
|
||||
|
||||
private final double originalStart;
|
||||
private final double originalEnd;
|
||||
private final int originalDirection;
|
||||
|
||||
private Long timeout; // Set timeout
|
||||
|
||||
private final Channel<Double> channel;
|
||||
private final Channel<T> doneChannel;
|
||||
|
||||
private boolean checkActorSet = true;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param channel
|
||||
* @param doneChannel If null actor will not wait (for this channel) to continue
|
||||
* @param doneValue
|
||||
* @param doneDelay Delay in seconds before checking the done channel
|
||||
* @param start
|
||||
* @param end
|
||||
* @param stepSize
|
||||
* @param timeout Maximum move time (in milliseconds)
|
||||
*/
|
||||
public ChannelAccessLinearActuator(Channel<Double> channel, Channel<T> doneChannel, T doneValue, double doneDelay, double start, double end, double stepSize, Long timeout){
|
||||
|
||||
this.doneValue = doneValue;
|
||||
this.doneDelay = (long) Math.floor((doneDelay*1000));
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
|
||||
if(stepSize <= 0){
|
||||
throw new IllegalArgumentException("Step size ["+stepSize+"] must be > 0");
|
||||
}
|
||||
this.stepSize = stepSize;
|
||||
|
||||
this.accuracy = stepSize/2;
|
||||
|
||||
// Validate and save timeout parameter
|
||||
if(timeout!=null && timeout<=0){
|
||||
throw new IllegalArgumentException("Timeout must be >0 or null");
|
||||
}
|
||||
else{
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
|
||||
init();
|
||||
|
||||
// Save original settings
|
||||
this.originalStart = start;
|
||||
this.originalEnd = end;
|
||||
this.originalDirection = direction;
|
||||
|
||||
this.channel = channel;
|
||||
this.doneChannel = doneChannel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set() throws InterruptedException {
|
||||
|
||||
// Throw an IllegalStateException in the case that set is called although there is no next step.
|
||||
if(!next){
|
||||
throw new IllegalStateException("The actuator does not have any next step.");
|
||||
}
|
||||
|
||||
// Set actuator channel
|
||||
logger.finest("Set actuator channel "+channel.getName()+" to value: "+value);
|
||||
try {
|
||||
|
||||
if(!asynchronous){
|
||||
if(timeout==null){
|
||||
channel.setValue(value);
|
||||
}
|
||||
else{
|
||||
channel.setValueAsync(value).get(timeout, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
else{
|
||||
channel.setValueNoWait(value);
|
||||
}
|
||||
|
||||
if(doneChannel != null){
|
||||
Thread.sleep(doneDelay);
|
||||
doneChannel.waitForValue(doneValue);
|
||||
}
|
||||
|
||||
// Check whether the set value is really on the value that was set before.
|
||||
if(checkActorSet){
|
||||
double c = channel.getValue(true);
|
||||
double a = Math.abs( c - value );
|
||||
if ( a > accuracy ){
|
||||
throw new RuntimeException("Actor could not be set to the value "+value+" The readback of the set value does not match the value that was set [value: "+c+" delta: "+a+" accuracy: "+accuracy+"]");
|
||||
}
|
||||
}
|
||||
|
||||
} catch (ExecutionException | TimeoutException | ChannelException e) {
|
||||
throw new RuntimeException("Unable to move actuator [channel: "+channel.getName()+"] to value "+value,e);
|
||||
}
|
||||
|
||||
|
||||
count++;
|
||||
double nextValue = start+(count*stepSize*direction); // Done like this to keep floating point rounding errors minimal
|
||||
|
||||
if((direction==1&&nextValue<=end)||(direction==-1&nextValue>=end)){
|
||||
logger.fine("Next actor value: "+nextValue);
|
||||
value=nextValue;
|
||||
this.next = true;
|
||||
}
|
||||
else{
|
||||
// There is no next set value
|
||||
this.next = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return next;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
this.count = 0;
|
||||
|
||||
// Determine move direction
|
||||
this.direction = 1;
|
||||
if(start>end){
|
||||
direction=-1; // Move in negative direction
|
||||
}
|
||||
|
||||
|
||||
// Set first set value to the start value
|
||||
this.value = start;
|
||||
this.next = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void reverse() {
|
||||
double oldStart = start;
|
||||
this.start = this.end;
|
||||
this.end = oldStart;
|
||||
|
||||
// Determine move direction
|
||||
this.direction = 1;
|
||||
if(this.start>this.end){
|
||||
direction=-1; // Move in negative direction
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
this.start = this.originalStart;
|
||||
this.end = this.originalEnd;
|
||||
this.direction = this.originalDirection;
|
||||
}
|
||||
|
||||
public boolean isAsynchronous() {
|
||||
return asynchronous;
|
||||
}
|
||||
public void setAsynchronous(boolean asynchronous) {
|
||||
this.asynchronous = asynchronous;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,236 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.core.actors;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import ch.psi.fda.core.Actor;
|
||||
import ch.psi.jcae.Channel;
|
||||
import ch.psi.jcae.ChannelException;
|
||||
|
||||
/**
|
||||
* This actuator sets an Channel Access channel by using the positions from the given table.
|
||||
*/
|
||||
public class ChannelAccessTableActuator<T> implements Actor {
|
||||
|
||||
private static Logger logger = Logger.getLogger(ChannelAccessTableActuator.class.getName());
|
||||
|
||||
private boolean asynchronous = false;
|
||||
|
||||
/**
|
||||
* Position table
|
||||
*/
|
||||
private final double[] table;
|
||||
|
||||
/**
|
||||
* Level of accuracy the positioner need to have (e.g. if a positioner is set to 1 the readback set value
|
||||
* of the positioner need to have at lease 1+/-accuracy)
|
||||
*/
|
||||
private double accuracy = 0.1;
|
||||
|
||||
/**
|
||||
* Execution count of actuator. This variable is used to minimize the floating point
|
||||
* rounding errors for calculating the next step.
|
||||
*/
|
||||
private int count;
|
||||
|
||||
/**
|
||||
* Flag that indicates whether there is a next set value for the Actor
|
||||
*/
|
||||
private boolean next;
|
||||
|
||||
|
||||
private Channel<Double> channel;
|
||||
private Channel<T> doneChannel = null;
|
||||
|
||||
private final T doneValue;
|
||||
private final long doneDelay;
|
||||
|
||||
/**
|
||||
* Flag that indicates whether the actor moves in the positive direction
|
||||
*/
|
||||
private boolean positiveDirection = true;
|
||||
private final boolean originalPositiveDirection;
|
||||
|
||||
/**
|
||||
* Maximum move time (in milliseconds)
|
||||
*/
|
||||
private Long timeout = null;
|
||||
|
||||
private boolean checkActorSet = true;
|
||||
|
||||
/**
|
||||
* Constructor - Initialize actor
|
||||
* @param channelName Name of the channel to set
|
||||
* @param table Position table with the explicit positions for each step
|
||||
* @param timeout Maximum move time (in milliseconds)
|
||||
*/
|
||||
public ChannelAccessTableActuator(Channel<Double> channel, double[] table, Long timeout){
|
||||
this(channel, null, null, 0, table, timeout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param channel
|
||||
* @param doneChannel
|
||||
* @param doneValue
|
||||
* @param doneDelay
|
||||
* @param table
|
||||
* @param timeout Maximum move time (in milliseconds)
|
||||
*/
|
||||
public ChannelAccessTableActuator(Channel<Double> channel, Channel<T> doneChannel, T doneValue, double doneDelay, double[] table, Long timeout){
|
||||
|
||||
this.doneValue = doneValue;
|
||||
this.doneDelay = (long) Math.floor((doneDelay*1000));
|
||||
|
||||
if(table==null){
|
||||
throw new IllegalArgumentException("Null table is not accepted");
|
||||
}
|
||||
if(table.length==0){
|
||||
throw new IllegalArgumentException("Position table need to have at least one position");
|
||||
}
|
||||
|
||||
this.table = table;
|
||||
|
||||
// Validate and save timeout parameter
|
||||
if(timeout!=null && timeout<=0){
|
||||
throw new IllegalArgumentException("Timeout must be >0 or null");
|
||||
}
|
||||
else{
|
||||
this.timeout = timeout;
|
||||
}
|
||||
|
||||
init();
|
||||
|
||||
// Save the initial direction
|
||||
this.originalPositiveDirection = positiveDirection;
|
||||
|
||||
this.channel = channel;
|
||||
this.doneChannel = doneChannel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set() throws InterruptedException {
|
||||
|
||||
// Throw an IllegalStateException in the case that set is called although there is no next step.
|
||||
if(!next){
|
||||
throw new IllegalStateException("The actuator does not have any next step.");
|
||||
}
|
||||
|
||||
// Set actuator channel
|
||||
logger.finest("Set actuator channel "+channel.getName()+" to value: "+table[count]);
|
||||
try {
|
||||
if(!asynchronous){
|
||||
if(timeout==null){
|
||||
channel.setValue(table[count]);
|
||||
}
|
||||
else{
|
||||
channel.setValueAsync(table[count]).get(timeout, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
else{
|
||||
channel.setValueNoWait(table[count]);
|
||||
}
|
||||
|
||||
|
||||
if(doneChannel != null){
|
||||
Thread.sleep(doneDelay);
|
||||
doneChannel.waitForValue(doneValue);
|
||||
}
|
||||
|
||||
// Check whether the set value is really on the value that was set before.
|
||||
if(doneChannel==null && !asynchronous && checkActorSet){
|
||||
if ( Math.abs( channel.getValue() - table[count] ) > accuracy ){
|
||||
throw new RuntimeException("Actor could not be set to the value "+table[count]+" The readback of the set value does not match the value that was set");
|
||||
}
|
||||
}
|
||||
|
||||
} catch (ExecutionException | ChannelException | TimeoutException e) {
|
||||
throw new RuntimeException("Move actuator [channel: "+channel.getName()+"] to value "+table[count],e);
|
||||
}
|
||||
|
||||
if(positiveDirection){
|
||||
count++;
|
||||
if(count<table.length){
|
||||
this.next = true;
|
||||
}
|
||||
else{
|
||||
// There is no next set value
|
||||
this.next = false;
|
||||
}
|
||||
}
|
||||
else{
|
||||
count--;
|
||||
if(count>=0){
|
||||
this.next = true;
|
||||
}
|
||||
else{
|
||||
// There is no next set value
|
||||
this.next = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return next;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
|
||||
// Set first set value to the start value
|
||||
if(positiveDirection){
|
||||
this.count = 0; // Set count to the first element
|
||||
}
|
||||
else{
|
||||
this.count = table.length-1; // Set count to the last element
|
||||
}
|
||||
|
||||
if(table.length>0){
|
||||
this.next = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reverse() {
|
||||
if(positiveDirection){
|
||||
positiveDirection=false;
|
||||
}
|
||||
else{
|
||||
positiveDirection=true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
this.positiveDirection = this.originalPositiveDirection;
|
||||
}
|
||||
|
||||
public boolean isAsynchronous() {
|
||||
return asynchronous;
|
||||
}
|
||||
public void setAsynchronous(boolean asynchronous) {
|
||||
this.asynchronous = asynchronous;
|
||||
}
|
||||
}
|
||||
201
src/main/java/ch/psi/fda/core/actors/ComplexActuator.java
Normal file
201
src/main/java/ch/psi/fda/core/actors/ComplexActuator.java
Normal file
@@ -0,0 +1,201 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.core.actors;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import ch.psi.fda.core.Action;
|
||||
import ch.psi.fda.core.Actor;
|
||||
|
||||
/**
|
||||
* Complex actuator that consists of a list of other actuators. The steps of this actor are composed out of the
|
||||
* steps of the actors that this actuator consists of.
|
||||
* First all the steps of the first actor are used, after that the steps of the next actor are done.
|
||||
* Before the first step of an actor pre actions are available and after the last step of an actor a post action is available.
|
||||
*/
|
||||
public class ComplexActuator implements Actor {
|
||||
|
||||
private static Logger logger = Logger.getLogger(ComplexActuator.class.getName());
|
||||
|
||||
/**
|
||||
* List of actors this actor is made of
|
||||
*/
|
||||
private final List<Actor> actors;
|
||||
|
||||
/**
|
||||
* Actions that are executed directly before the first step of this actor
|
||||
*/
|
||||
private final List<Action> preActions;
|
||||
|
||||
/**
|
||||
* Actions that are executed directly after the last step of this actor
|
||||
*/
|
||||
private final List<Action> postActions;
|
||||
|
||||
/**
|
||||
* Flag that indicates whether there is a next set value for the Actor
|
||||
*/
|
||||
private boolean next;
|
||||
|
||||
/**
|
||||
* Index of the actor currently used
|
||||
*/
|
||||
private int actualActorCount;
|
||||
|
||||
/**
|
||||
* Actor currently used to perform steps
|
||||
*/
|
||||
private Actor actualActor;
|
||||
|
||||
/**
|
||||
* Flag to indicate the first set() execution of this actor.
|
||||
* This flag is used to decide whether to execute the pre actions.
|
||||
*/
|
||||
private boolean firstrun;
|
||||
|
||||
public ComplexActuator(){
|
||||
this.actors = new ArrayList<Actor>();
|
||||
this.preActions = new ArrayList<Action>();
|
||||
this.postActions = new ArrayList<Action>();
|
||||
|
||||
this.next = false;
|
||||
this.actualActorCount = 0;
|
||||
|
||||
this.firstrun = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void set() throws InterruptedException {
|
||||
if(!next){
|
||||
throw new IllegalStateException("The actuator does not have any next step.");
|
||||
}
|
||||
|
||||
// PRE actions
|
||||
if(firstrun){
|
||||
this.firstrun = false;
|
||||
|
||||
// Execute pre actions
|
||||
logger.finest("Execute pre actions");
|
||||
for(Action action: preActions){
|
||||
action.execute();
|
||||
}
|
||||
}
|
||||
|
||||
actualActor.set(); // If the actor has no next step then something is wrong in the init/next step logic
|
||||
|
||||
// If the last point of the actual actuator is set take the next actuator in the list if there is one available
|
||||
while(!actualActor.hasNext()&&actualActorCount<actors.size()){
|
||||
actualActorCount++;
|
||||
if(actualActorCount<actors.size()){
|
||||
actualActor = actors.get(actualActorCount);
|
||||
}
|
||||
else{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(actualActor.hasNext()){
|
||||
this.next = true;
|
||||
}
|
||||
else{
|
||||
this.next = false;
|
||||
|
||||
// Execute post actions
|
||||
logger.finest("Execute post actions");
|
||||
for(Action action: postActions){
|
||||
action.execute();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return next;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
// Initialize all actors this actor consist of
|
||||
for(Actor a: actors){
|
||||
a.init();
|
||||
}
|
||||
|
||||
// Set the actual actor to the first actor in the list
|
||||
actualActorCount=0;
|
||||
if(actualActorCount<actors.size()){
|
||||
actualActor = actors.get(actualActorCount);
|
||||
|
||||
// Check whether the actor has a next step, if not increase to the next one
|
||||
// This is needed because the first actor might not have any step ... (very unlikely but bad things happens some time)
|
||||
while(!actualActor.hasNext()&&actualActorCount<actors.size()){
|
||||
actualActorCount++;
|
||||
if(actualActorCount<actors.size()){
|
||||
actualActor = actors.get(actualActorCount);
|
||||
}
|
||||
else{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(actualActor.hasNext()){
|
||||
this.next = true;
|
||||
}
|
||||
else{
|
||||
this.next = false;
|
||||
}
|
||||
|
||||
}
|
||||
else{
|
||||
actualActor=null;
|
||||
this.next=false;
|
||||
}
|
||||
|
||||
this.firstrun = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reverse() {
|
||||
// Reverse all actors this actor consist of
|
||||
for(Actor a: actors){
|
||||
a.reverse();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
// Reset all actors this actor consist of
|
||||
for(Actor a: actors){
|
||||
a.reset();
|
||||
}
|
||||
}
|
||||
|
||||
public List<Action> getPreActions() {
|
||||
return preActions;
|
||||
}
|
||||
public List<Actor> getActors() {
|
||||
return actors;
|
||||
}
|
||||
public List<Action> getPostActions() {
|
||||
return postActions;
|
||||
}
|
||||
}
|
||||
24
src/main/java/ch/psi/fda/core/actors/Function.java
Normal file
24
src/main/java/ch/psi/fda/core/actors/Function.java
Normal file
@@ -0,0 +1,24 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.core.actors;
|
||||
|
||||
public interface Function {
|
||||
public double calculate(double parameter);
|
||||
}
|
||||
106
src/main/java/ch/psi/fda/core/actors/JythonFunction.java
Normal file
106
src/main/java/ch/psi/fda/core/actors/JythonFunction.java
Normal file
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.core.actors;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineManager;
|
||||
import javax.script.ScriptException;
|
||||
|
||||
import ch.psi.fda.core.scripting.JythonGlobalVariable;
|
||||
|
||||
public class JythonFunction implements Function {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(JythonFunction.class.getName());
|
||||
|
||||
public static final String ENTRY_FUNCTION_NAME = "calculate";
|
||||
private static final String ENTRY_FUNCTION_PATTERN = "def "+ENTRY_FUNCTION_NAME+"\\((.*)\\):";
|
||||
|
||||
private ScriptEngine engine;
|
||||
private String additionalParameter = "";
|
||||
|
||||
|
||||
public JythonFunction(String script, Map<String, JythonGlobalVariable> map){
|
||||
|
||||
// Workaround for Jython memory leak
|
||||
// http://blog.hillbrecht.de/2009/07/11/jython-memory-leakout-of-memory-problem/
|
||||
System.setProperty("python.options.internalTablesImpl","weak");
|
||||
|
||||
this.engine = new ScriptEngineManager().getEngineByName("python");
|
||||
|
||||
// Determine script entry function and the function parameters
|
||||
String[] parameter;
|
||||
Pattern pattern = Pattern.compile(ENTRY_FUNCTION_PATTERN);
|
||||
Matcher matcher = pattern.matcher(script);
|
||||
if(matcher.find() && matcher.groupCount()==1){
|
||||
if(!matcher.group(1).trim().equals("")){
|
||||
logger.finest("Entry function '"+ENTRY_FUNCTION_PATTERN+"' found - Identified parameters: "+matcher.group(1));
|
||||
parameter = matcher.group(1).split(" *, *");
|
||||
}
|
||||
else{
|
||||
parameter = new String[0];
|
||||
}
|
||||
}
|
||||
else{
|
||||
throw new IllegalArgumentException("Cannot determine entry function: "+ENTRY_FUNCTION_PATTERN);
|
||||
}
|
||||
|
||||
// Check whether all parameters are mapped
|
||||
StringBuilder b = new StringBuilder();
|
||||
for(int i=1;i<parameter.length; i++){ // Starting at 1 because first argument is implicit.
|
||||
if(!map.containsKey(parameter[i])){
|
||||
throw new IllegalArgumentException("Function parameter "+parameter[i]+" is not mapped");
|
||||
}
|
||||
b.append(",");
|
||||
b.append(parameter[i]);
|
||||
}
|
||||
additionalParameter = b.toString();
|
||||
|
||||
// Set variables in Jython engine
|
||||
for(String k: map.keySet()){
|
||||
engine.put(k, map.get(k));
|
||||
}
|
||||
|
||||
// Load manipulation script
|
||||
try {
|
||||
engine.eval(script);
|
||||
} catch (ScriptException e) {
|
||||
throw new RuntimeException("Unable to load manipulation script", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public double calculate(double parameter) {
|
||||
logger.fine("Function called");
|
||||
|
||||
try {
|
||||
logger.info("calculate( "+parameter+""+additionalParameter+" )");
|
||||
return ((Double) engine.eval("calculate( "+parameter+""+additionalParameter+" )"));
|
||||
} catch (ScriptException e) {
|
||||
throw new RuntimeException("Calculating actuator step failed while executing the Jython script",e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.core.actors;
|
||||
|
||||
import ch.psi.fda.core.Actor;
|
||||
import ch.psi.fda.core.Sensor;
|
||||
|
||||
/**
|
||||
* Pseudo actor that is literally doing nothing for n times
|
||||
*/
|
||||
public class PseudoActuatorSensor implements Actor, Sensor {
|
||||
|
||||
/**
|
||||
* Execution count of actuator.
|
||||
*/
|
||||
private int count;
|
||||
|
||||
/**
|
||||
* Number of counts for this actuator
|
||||
*/
|
||||
private final int counts;
|
||||
|
||||
private final String id;
|
||||
|
||||
/**
|
||||
* @param counts
|
||||
* @param id Id of the Actor/Sensor
|
||||
*/
|
||||
public PseudoActuatorSensor(String id, int counts){
|
||||
if(counts < 1){
|
||||
throw new IllegalArgumentException("Count ["+counts+"] must be > 0");
|
||||
}
|
||||
this.id = id;
|
||||
this.counts = counts;
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void set() {
|
||||
if(!hasNext()){
|
||||
throw new IllegalStateException("The actuator does not have any next step.");
|
||||
}
|
||||
|
||||
count++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return (count<counts);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
count=0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reverse() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object read() {
|
||||
return new Double(count); // Return actual count
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return this.id;
|
||||
}
|
||||
}
|
||||
89
src/main/java/ch/psi/fda/core/guard/ChannelAccessGuard.java
Normal file
89
src/main/java/ch/psi/fda/core/guard/ChannelAccessGuard.java
Normal file
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.core.guard;
|
||||
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import ch.psi.fda.core.Guard;
|
||||
import ch.psi.jcae.ChannelException;
|
||||
|
||||
/**
|
||||
* Guard checking channels to meet a certain condition
|
||||
*/
|
||||
public class ChannelAccessGuard implements Guard {
|
||||
|
||||
private static Logger logger = Logger.getLogger(ChannelAccessGuard.class.getName());
|
||||
|
||||
/**
|
||||
* Flag to indicate whether a guard condition failed since the last init call
|
||||
* true: all conditions met, false: at least one condition failed
|
||||
*/
|
||||
private volatile boolean check = true;
|
||||
|
||||
private final List<ChannelAccessGuardCondition<?>> conditions;
|
||||
|
||||
public ChannelAccessGuard(List<ChannelAccessGuardCondition<?>> conditions){
|
||||
|
||||
this.conditions = conditions;
|
||||
|
||||
for(final ChannelAccessGuardCondition<?> condition: conditions){
|
||||
condition.getChannel().addPropertyChangeListener(new PropertyChangeListener() {
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
if(! evt.getNewValue().equals(condition.getValue())){
|
||||
check=false;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
check = true;
|
||||
|
||||
// Check one time if all conditions are met
|
||||
for(ChannelAccessGuardCondition<?> condition: conditions){
|
||||
try{
|
||||
if(! (condition.getChannel().getValue(true)).equals(condition.getValue()) ){
|
||||
check=false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (InterruptedException e) {
|
||||
throw new RuntimeException("Guard interrupted ",e);
|
||||
} catch (TimeoutException | ChannelException | ExecutionException e) {
|
||||
logger.log(Level.WARNING, "Unable ", e);
|
||||
check=false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean check() {
|
||||
return check;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.core.guard;
|
||||
|
||||
import ch.psi.jcae.Channel;
|
||||
|
||||
public class ChannelAccessGuardCondition<T> {
|
||||
|
||||
private final Channel<T> channel;
|
||||
private final T value; // Value of the channel to meet condition
|
||||
|
||||
public ChannelAccessGuardCondition(Channel<T> channel, T value){
|
||||
this.channel = channel;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public Channel<T> getChannel() {
|
||||
return channel;
|
||||
}
|
||||
|
||||
public T getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
436
src/main/java/ch/psi/fda/core/loops/ActorSensorLoop.java
Normal file
436
src/main/java/ch/psi/fda/core/loops/ActorSensorLoop.java
Normal file
@@ -0,0 +1,436 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.core.loops;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.google.common.eventbus.EventBus;
|
||||
|
||||
import ch.psi.fda.core.Action;
|
||||
import ch.psi.fda.core.ActionLoop;
|
||||
import ch.psi.fda.core.Actor;
|
||||
import ch.psi.fda.core.ActorSetCallable;
|
||||
import ch.psi.fda.core.Guard;
|
||||
import ch.psi.fda.core.Sensor;
|
||||
import ch.psi.fda.messages.DataMessage;
|
||||
import ch.psi.fda.messages.EndOfStreamMessage;
|
||||
import ch.psi.fda.messages.Metadata;
|
||||
|
||||
/**
|
||||
* Loop of actions to accomplish a task or test.
|
||||
*/
|
||||
public class ActorSensorLoop implements ActionLoop {
|
||||
|
||||
private static Logger logger = Logger.getLogger(ActorSensorLoop.class.getName());
|
||||
|
||||
/**
|
||||
* Flag to indicate whether the data of this loop will be grouped
|
||||
* According to this flag the dataGroup flag in EndOfStream will be set.
|
||||
*/
|
||||
private boolean dataGroup = false;
|
||||
|
||||
/**
|
||||
* List of additional action loops that are executed at the end of each
|
||||
* iteration of the loop
|
||||
*/
|
||||
private List<ActionLoop> actionLoops;
|
||||
/**
|
||||
* List of actions that are executed at the beginning of the loop.
|
||||
*/
|
||||
private List<Action> preActions;
|
||||
/**
|
||||
* List of actions that are executed at the end of the loop.
|
||||
*/
|
||||
private List<Action> postActions;
|
||||
|
||||
/**
|
||||
* List of actions that are executed before the actors are set
|
||||
*/
|
||||
private List<Action> preActorActions;
|
||||
|
||||
/**
|
||||
* List of actions that are executed after all actors have been set
|
||||
*/
|
||||
private List<Action> postActorActions;
|
||||
|
||||
/**
|
||||
* List of actions that are executed before the sensors are read out
|
||||
*/
|
||||
private List<Action> preSensorActions;
|
||||
/**
|
||||
* List of actions that are executed after all sensors have been read out
|
||||
*/
|
||||
private List<Action> postSensorActions;
|
||||
|
||||
/**
|
||||
* List of actors of this loop
|
||||
*/
|
||||
private List<Actor> actors;
|
||||
|
||||
/**
|
||||
* List of sensors of this loop
|
||||
*/
|
||||
private List<Sensor> sensors;
|
||||
|
||||
/**
|
||||
* Guard used to check whether the environment was ok while reading out the sensors
|
||||
*/
|
||||
private Guard guard = null;
|
||||
|
||||
private volatile boolean loop = false;
|
||||
private volatile boolean abort = false;
|
||||
|
||||
private final boolean zigZag;
|
||||
|
||||
private List<ActorSetCallable> pactors;
|
||||
|
||||
private EventBus eventBus = new EventBus();
|
||||
|
||||
|
||||
public ActorSensorLoop(){
|
||||
this(false);
|
||||
}
|
||||
|
||||
public ActorSensorLoop(boolean zigZag){
|
||||
this.zigZag = zigZag;
|
||||
this.actionLoops = new ArrayList<ActionLoop>();
|
||||
this.preActions = new ArrayList<Action>();
|
||||
this.postActions = new ArrayList<Action>();
|
||||
this.preActorActions = new ArrayList<Action>();
|
||||
this.postActorActions = new ArrayList<Action>();
|
||||
this.preSensorActions = new ArrayList<Action>();
|
||||
this.postSensorActions = new ArrayList<Action>();
|
||||
|
||||
this.actors = new ArrayList<Actor>();
|
||||
this.pactors = new ArrayList<ActorSetCallable>();
|
||||
this.sensors = new ArrayList<Sensor>();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Executes the actor sensor loop. The actor sensor loop is build up as follows:
|
||||
* preActions
|
||||
* loop{
|
||||
* check actors - abort if there are no new steps
|
||||
* pre actor actions
|
||||
* set actors
|
||||
* post actor actions
|
||||
* pre sensor actions
|
||||
* read sensors
|
||||
* post sensor actions
|
||||
* execute additional registered action loops
|
||||
* }
|
||||
* postActions
|
||||
* @throws InterruptedException
|
||||
*/
|
||||
@Override
|
||||
public void execute() throws InterruptedException {
|
||||
abort = false;
|
||||
|
||||
List<Metadata> metadata = new ArrayList<>();
|
||||
|
||||
// Build up data message metadata based on the sensors currently registered.
|
||||
for(Sensor s: sensors){
|
||||
metadata.add(new Metadata(s.getId()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Thread pool for parallel execution of tasks
|
||||
*/
|
||||
ExecutorService executorService = Executors.newCachedThreadPool();
|
||||
|
||||
loop = true;
|
||||
|
||||
// Execute pre actions
|
||||
for(Action action: preActions){
|
||||
action.execute();
|
||||
}
|
||||
|
||||
// Initialize actors of Loop
|
||||
for(Actor actor: actors){
|
||||
actor.init();
|
||||
}
|
||||
|
||||
// Variable to store the last guard status
|
||||
boolean guardOK=true;
|
||||
|
||||
try{
|
||||
|
||||
// Execute loop logic
|
||||
while(loop){
|
||||
|
||||
if(guardOK){
|
||||
|
||||
|
||||
// If actors are defined for the loop check whether all of them
|
||||
// have a next step defined if there is no actor defined only run this loop once
|
||||
if(actors.size()>0){
|
||||
// Check whether the actors of this loop have a next step. If not
|
||||
// abort the loop
|
||||
boolean hasNext = true;
|
||||
for(Actor actor: actors){
|
||||
if(!actor.hasNext()){
|
||||
hasNext=false;
|
||||
break; // Stop actor check loop
|
||||
}
|
||||
}
|
||||
|
||||
// If not all actors have a next step abort the loop
|
||||
if(!hasNext){
|
||||
break; // Stop action loop
|
||||
}
|
||||
}
|
||||
else{
|
||||
// No actors defined, only run loop once
|
||||
loop = false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Execute pre actor actions
|
||||
for(Action action: preActorActions){
|
||||
action.execute();
|
||||
}
|
||||
|
||||
if(abort){ // End loop if abort was issued
|
||||
break;
|
||||
}
|
||||
|
||||
// Set actors
|
||||
// for(Actor actor: actors){
|
||||
// actor.set();
|
||||
// }
|
||||
// Parallel set of the actors
|
||||
try {
|
||||
for (Future<Object> f : executorService.invokeAll(pactors)) {
|
||||
f.get(); //Blocks until the async set() is finished
|
||||
}
|
||||
} catch (ExecutionException e) {
|
||||
throw new RuntimeException("Setting the actors failed",e);
|
||||
}
|
||||
|
||||
// Execute post actor actions
|
||||
for(Action action: postActorActions){
|
||||
action.execute();
|
||||
}
|
||||
|
||||
if(abort){ // End loop if abort was issued
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(guard!=null){
|
||||
// Initialize guard
|
||||
guard.init();
|
||||
guardOK=guard.check();
|
||||
|
||||
// Wait until guard is ok
|
||||
while(!guardOK){
|
||||
logger.info("Waiting for guard condition(s) to be met");
|
||||
// Sleep 100 milliseconds before next check
|
||||
Thread.sleep(1000);
|
||||
|
||||
// Check whether the loop is not aborted, if it is aborted
|
||||
// break the wait loop (afterwards also the loop loop is aborted)
|
||||
if(!loop){
|
||||
break;
|
||||
}
|
||||
|
||||
guard.init();
|
||||
guardOK=guard.check();
|
||||
}
|
||||
|
||||
// If loop is aborted proceed to next iteration an abort loop
|
||||
if(!loop){
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Execute pre sensor actions
|
||||
for(Action action: preSensorActions){
|
||||
action.execute();
|
||||
}
|
||||
|
||||
if(abort){ // End loop if abort was issued
|
||||
break;
|
||||
}
|
||||
|
||||
// Read sensors
|
||||
DataMessage message = new DataMessage(metadata);
|
||||
for(Sensor sensor: sensors){
|
||||
// Readout sensor
|
||||
Object o = sensor.read();
|
||||
// Add sensor data item to message
|
||||
message.getData().add(o);
|
||||
}
|
||||
|
||||
// Execute post sensor actions
|
||||
for(Action action: postSensorActions){
|
||||
action.execute();
|
||||
}
|
||||
|
||||
if(abort){ // End loop if abort was issued
|
||||
break;
|
||||
}
|
||||
|
||||
// Check guard if one is registered
|
||||
if(guard!=null){
|
||||
guardOK=guard.check();
|
||||
}
|
||||
|
||||
if(guardOK){
|
||||
|
||||
// Post a message with the sensor data
|
||||
eventBus.post(message);
|
||||
|
||||
// Loop all configured ActionLoop objects
|
||||
for(ActionLoop actionLoop: actionLoops){
|
||||
actionLoop.execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Execute post actions
|
||||
for(Action action: postActions){
|
||||
action.execute();
|
||||
}
|
||||
|
||||
}
|
||||
finally{
|
||||
// Ensure that data stream is terminated ...
|
||||
|
||||
// Issue end of loop control message
|
||||
// Set iflag of the EndOfStreamMessage according to dataGroup flag of this loop
|
||||
eventBus.post(new EndOfStreamMessage(dataGroup));
|
||||
}
|
||||
|
||||
if(zigZag){
|
||||
// Reverse actors for the next run
|
||||
for(Actor actor: actors){
|
||||
actor.reverse();
|
||||
}
|
||||
}
|
||||
|
||||
executorService.shutdownNow();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void abort(){
|
||||
abort = true;
|
||||
loop = false;
|
||||
|
||||
// To abort all wait actions interrupt this thread
|
||||
for(Action a: preSensorActions){
|
||||
a.abort();
|
||||
}
|
||||
|
||||
// Recursively abort all registered action loops
|
||||
for(ActionLoop actionLoop: actionLoops){
|
||||
actionLoop.abort();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepare() {
|
||||
// Create callable for all actors
|
||||
pactors.clear();
|
||||
for(final Actor a: actors){
|
||||
pactors.add(new ActorSetCallable(a));
|
||||
}
|
||||
|
||||
// Reset all actuators
|
||||
for(Actor a: actors){
|
||||
a.reset();
|
||||
}
|
||||
|
||||
// Recursively call prepare() method of all registered action loops
|
||||
for(ActionLoop actionLoop: actionLoops){
|
||||
actionLoop.prepare();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanup() {
|
||||
// Recursively call cleanup() method of all registered action loops
|
||||
for(ActionLoop actionLoop: actionLoops){
|
||||
actionLoop.cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Action> getPreActions() {
|
||||
return preActions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Action> getPostActions() {
|
||||
return postActions;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public EventBus getEventBus(){
|
||||
return eventBus;
|
||||
}
|
||||
|
||||
|
||||
public List<ActionLoop> getActionLoops() {
|
||||
return actionLoops;
|
||||
}
|
||||
public List<Action> getPreActorActions() {
|
||||
return preActorActions;
|
||||
}
|
||||
public List<Action> getPostActorActions() {
|
||||
return postActorActions;
|
||||
}
|
||||
public List<Action> getPreSensorActions() {
|
||||
return preSensorActions;
|
||||
}
|
||||
public List<Action> getPostSensorActions() {
|
||||
return postSensorActions;
|
||||
}
|
||||
public List<Actor> getActors() {
|
||||
return actors;
|
||||
}
|
||||
public List<Sensor> getSensors() {
|
||||
return sensors;
|
||||
}
|
||||
public Guard getGuard() {
|
||||
return guard;
|
||||
}
|
||||
public void setGuard(Guard guard) {
|
||||
this.guard = guard;
|
||||
}
|
||||
public boolean isDataGroup() {
|
||||
return dataGroup;
|
||||
}
|
||||
public void setDataGroup(boolean dataGroup) {
|
||||
this.dataGroup = dataGroup;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.core.loops.cr;
|
||||
|
||||
/**
|
||||
* Filter calculating the delta of two subsequent values
|
||||
*/
|
||||
public class CrlogicDeltaDataFilter {
|
||||
|
||||
private Double lastValue = null;
|
||||
|
||||
public void reset(){
|
||||
lastValue = null;
|
||||
}
|
||||
|
||||
public Double delta(Double value){
|
||||
|
||||
Double lvalue = lastValue;
|
||||
lastValue = value;
|
||||
|
||||
if(lvalue==null){
|
||||
return Double.NaN;
|
||||
}
|
||||
else{
|
||||
// Return delta
|
||||
return(value-lvalue);
|
||||
}
|
||||
}
|
||||
}
|
||||
723
src/main/java/ch/psi/fda/core/loops/cr/CrlogicLoopStream.java
Normal file
723
src/main/java/ch/psi/fda/core/loops/cr/CrlogicLoopStream.java
Normal file
@@ -0,0 +1,723 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.core.loops.cr;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.zeromq.ZMQ;
|
||||
import org.zeromq.ZMQ.Context;
|
||||
import org.zeromq.ZMQ.Socket;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.google.common.eventbus.EventBus;
|
||||
|
||||
import ch.psi.fda.core.Action;
|
||||
import ch.psi.fda.core.ActionLoop;
|
||||
import ch.psi.fda.messages.DataMessage;
|
||||
import ch.psi.fda.messages.EndOfStreamMessage;
|
||||
import ch.psi.fda.messages.Metadata;
|
||||
import ch.psi.jcae.ChannelException;
|
||||
import ch.psi.jcae.ChannelService;
|
||||
|
||||
/**
|
||||
* While using Crlogic the IOC system clock rate should/must be set to 1000 (default 60)
|
||||
*
|
||||
* sysClkRateSet 1000
|
||||
*
|
||||
*/
|
||||
public class CrlogicLoopStream implements ActionLoop {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(CrlogicLoopStream.class.getName());
|
||||
|
||||
public static final int HIGH_WATER_MARK = 100;
|
||||
private Context context;
|
||||
private Socket socket;
|
||||
|
||||
private ObjectMapper mapper = new ObjectMapper();
|
||||
|
||||
/**
|
||||
* Flag to indicate whether the data of this loop will be grouped
|
||||
* According to this flag the dataGroup flag in EndOfStream will be set.
|
||||
*/
|
||||
private boolean dataGroup = false;
|
||||
|
||||
// Default timeout (in milliseconds) for wait operations
|
||||
private long startStopTimeout = 8000;
|
||||
|
||||
|
||||
private String ioc;
|
||||
|
||||
/**
|
||||
* Flag whether the actor of this loop should move in zig zag mode
|
||||
*/
|
||||
private final boolean zigZag;
|
||||
|
||||
private boolean useReadback;
|
||||
private boolean useEncoder;
|
||||
|
||||
|
||||
private List<Action> preActions;
|
||||
private List<Action> postActions;
|
||||
private List<CrlogicResource> sensors;
|
||||
|
||||
// Prefix for the CRLOGIC channels
|
||||
private final String prefix;
|
||||
|
||||
private TemplateCrlogic template;
|
||||
private TemplateMotor motortemplate;
|
||||
|
||||
private List<String> readoutResources;
|
||||
private Map<Integer, CrlogicDeltaDataFilter> scalerIndices;
|
||||
private CrlogicRangeDataFilter crlogicDataFilter;
|
||||
|
||||
private boolean abort = false;
|
||||
|
||||
private final ChannelService cservice;
|
||||
|
||||
private String id;
|
||||
private String name; // name of the motor channel
|
||||
private String readback; // name of the encoder channel
|
||||
private double start;
|
||||
private double end;
|
||||
private double stepSize;
|
||||
private double integrationTime;
|
||||
private double additionalBacklash;
|
||||
|
||||
|
||||
private final EventBus eventbus;
|
||||
private List<Metadata> metadata;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param cservice Channel Access context
|
||||
* @param prefix Prefix of the IOC channels
|
||||
* @param ioc Name of the IOC running CRLOGIC
|
||||
* @param zigZag Do zigZag scan
|
||||
*/
|
||||
public CrlogicLoopStream(ChannelService cservice, String prefix, String ioc, boolean zigZag){
|
||||
eventbus = new EventBus();
|
||||
this.cservice = cservice;
|
||||
this.prefix = prefix;
|
||||
this.ioc = ioc;
|
||||
this.zigZag = zigZag;
|
||||
|
||||
// Initialize lists used by the loop
|
||||
this.preActions = new ArrayList<Action>();
|
||||
this.postActions = new ArrayList<Action>();
|
||||
this.sensors = new ArrayList<>();
|
||||
this.readoutResources = new ArrayList<String>();
|
||||
this.scalerIndices = new HashMap<Integer, CrlogicDeltaDataFilter>();
|
||||
|
||||
this.crlogicDataFilter = new CrlogicRangeDataFilter();
|
||||
}
|
||||
|
||||
|
||||
public void setActuator(String id, String name, String readback, double start, double end, double stepSize, double integrationTime, double additionalBacklash){
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.readback = readback;
|
||||
this.start = start;
|
||||
this.end = end;
|
||||
this.stepSize = stepSize;
|
||||
this.integrationTime = integrationTime;
|
||||
this.additionalBacklash = additionalBacklash;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Receive a ZMQ message
|
||||
*/
|
||||
private void receive() {
|
||||
|
||||
MainHeader mainHeader;
|
||||
|
||||
try {
|
||||
byte[] bytes = socket.recv(ZMQ.NOBLOCK);
|
||||
if(bytes == null){
|
||||
// There is no message hanging
|
||||
return;
|
||||
}
|
||||
mainHeader = mapper.readValue(bytes, MainHeader.class);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Unable to decode main header", e);
|
||||
}
|
||||
|
||||
if(!socket.hasReceiveMore()){
|
||||
throw new RuntimeException("There is no data submessage");
|
||||
}
|
||||
|
||||
byte[] bytes = socket.recv();
|
||||
ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
|
||||
|
||||
DataMessage message = new DataMessage(metadata);
|
||||
boolean use = true;
|
||||
|
||||
for(int i=0; i<mainHeader.getElements(); i++){
|
||||
Double raw = byteBuffer.getDouble();
|
||||
Double val;
|
||||
|
||||
if(i==0){
|
||||
if(useEncoder){
|
||||
val = crlogicDataFilter.calculatePositionMotorUseEncoder(raw);
|
||||
}
|
||||
else if(useReadback){
|
||||
val = crlogicDataFilter.calculatePositionMotorUseReadback(raw);
|
||||
}
|
||||
else{
|
||||
val = crlogicDataFilter.calculatePositionMotor(raw);
|
||||
}
|
||||
|
||||
// Check whether data is within the configured range - otherwise drop data
|
||||
use = crlogicDataFilter.filter(val);
|
||||
}
|
||||
else if(scalerIndices.containsKey(i)){
|
||||
CrlogicDeltaDataFilter f = scalerIndices.get(i);
|
||||
val = f.delta(raw);
|
||||
}
|
||||
else{
|
||||
val = raw;
|
||||
}
|
||||
|
||||
message.getData().add(val);
|
||||
}
|
||||
|
||||
// Drain remaining messages
|
||||
int n = drainHangingSubmessages();
|
||||
if(n>1){
|
||||
throw new RuntimeException("More than 1 message drained from stream: "+n);
|
||||
}
|
||||
|
||||
if(use){
|
||||
eventbus.post(message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void execute() throws InterruptedException {
|
||||
try{
|
||||
|
||||
// Set values for the datafilter
|
||||
crlogicDataFilter.setStart(start);
|
||||
crlogicDataFilter.setEnd(end);
|
||||
|
||||
// Reset data filter
|
||||
for(Integer k: scalerIndices.keySet()){
|
||||
scalerIndices.get(k).reset();
|
||||
}
|
||||
|
||||
// Set abort state to false
|
||||
abort = false;
|
||||
|
||||
Long timeout = 600000l; // 10 minutes move timeout
|
||||
|
||||
// Check if logic is inactive, otherwise return early
|
||||
if(!template.getStatus().getValue().equals(TemplateCrlogic.Status.INACTIVE.toString())){
|
||||
logger.info("CRLOGIC is not inactive!");
|
||||
// TODO Decide what to do in this situation
|
||||
|
||||
if(template.getStatus().getValue().equals(TemplateCrlogic.Status.FAULT.toString())){
|
||||
// If in fault show message and recover
|
||||
logger.info("CRLOGIC in FAULT state");
|
||||
logger.info("Error message: "+template.getMessage().getValue());
|
||||
logger.info("Recover logic and set it to INACTIVE");
|
||||
template.getStatus().setValue(TemplateCrlogic.Status.INACTIVE.toString());
|
||||
}
|
||||
else if(template.getStatus().getValue().equals(TemplateCrlogic.Status.ACTIVE.toString())){
|
||||
template.getStatus().setValue(TemplateCrlogic.Status.STOP.toString());
|
||||
template.getStatus().waitForValue(TemplateCrlogic.Status.INACTIVE.toString(), startStopTimeout);
|
||||
}
|
||||
else{
|
||||
throw new RuntimeException("CRLOGIC is not inactive");
|
||||
}
|
||||
}
|
||||
|
||||
logger.info("Set parameters");
|
||||
template.getNfsServer().setValue("");
|
||||
template.getNfsShare().setValue("");
|
||||
template.getDataFile().setValue("");
|
||||
|
||||
int tps = template.getTicksPerSecond().getValue();
|
||||
logger.info("Ticks per second: "+tps);
|
||||
|
||||
logger.info("Set readout resources");
|
||||
|
||||
template.getReadoutResources().setValue(readoutResources.toArray(new String[readoutResources.size()]));
|
||||
|
||||
|
||||
|
||||
// Set ticks between interrupt to integration time
|
||||
int ticks = (int)(tps*integrationTime);
|
||||
template.getTicksBetweenInterrupts().setValue(ticks);
|
||||
|
||||
// Prepare motor
|
||||
double totalTimeSeconds = Math.abs((end-start)/stepSize*integrationTime);
|
||||
int hours = (int) Math.floor(totalTimeSeconds/60/60);
|
||||
int minutes = (int) Math.floor(totalTimeSeconds/60-hours*60);
|
||||
int seconds = (int) Math.floor(totalTimeSeconds-hours*60*60-minutes*60);
|
||||
|
||||
logger.info("Estimated time: "+hours+":"+minutes+":"+seconds);
|
||||
|
||||
int direction = 1;
|
||||
if(end-start<0){
|
||||
direction = -1;
|
||||
}
|
||||
|
||||
double motorBaseSpeed = motortemplate.getBaseSpeed().getValue();
|
||||
double motorHighLimit = motortemplate.getHighLimit().getValue();
|
||||
double motorLowLimit = motortemplate.getLowLimit().getValue();
|
||||
double motorBacklash = motortemplate.getBacklashDistance().getValue();
|
||||
|
||||
boolean respectMotorMinSpeed = false; // if false set min speed to 0
|
||||
double motorMinSpeed = 0;
|
||||
if(respectMotorMinSpeed){
|
||||
motorMinSpeed = motorBaseSpeed;
|
||||
}
|
||||
|
||||
// Check user parameters
|
||||
// TODO start and end values must be between the motor high and low value - otherwise fail
|
||||
if(start>motorHighLimit || start<motorLowLimit){
|
||||
// Start value is outside motor high and/or low value
|
||||
logger.info("Start value is outside motor high and/or low value");
|
||||
throw new IllegalArgumentException("Start value is outside motor high and/or low value");
|
||||
}
|
||||
if(end>motorHighLimit || end<motorLowLimit){
|
||||
// End value is outside motor high and/or low value
|
||||
logger.info("End value is outside motor high and/or low value");
|
||||
throw new IllegalArgumentException("End value is outside motor high and/or low value");
|
||||
}
|
||||
// TODO Check minimum step size
|
||||
int minimumTicks = 10;
|
||||
double minStepSize = motorMinSpeed*(minimumTicks/tps);
|
||||
if(stepSize<minStepSize){
|
||||
// Step size is too small
|
||||
logger.info("Step size is too small");
|
||||
throw new IllegalArgumentException("Step size is too small");
|
||||
}
|
||||
// TODO Check integration time
|
||||
if(motorMinSpeed>0){
|
||||
double maxIntegrationTime = stepSize/motorMinSpeed;
|
||||
if(integrationTime>maxIntegrationTime){
|
||||
logger.info("Integration time is too big");
|
||||
// Integration time is too big
|
||||
throw new IllegalArgumentException("Integration time is too big");
|
||||
}
|
||||
}
|
||||
double motorMaxSpeed = motortemplate.getVelocity().getValue();
|
||||
double minIntegrationTime = Math.min( (stepSize/motorMaxSpeed), ((double)minimumTicks/(double)tps) );
|
||||
if(integrationTime<minIntegrationTime){
|
||||
// Integration time is too small
|
||||
logger.info("Integration time is too small [min integration time: "+minIntegrationTime+"]");
|
||||
throw new IllegalArgumentException("Integration time is too small [min integration time: "+minIntegrationTime+"]");
|
||||
}
|
||||
|
||||
|
||||
// TODO Calculate and set motor speed, backlash, etc.
|
||||
double motorSpeed = stepSize/integrationTime;
|
||||
double backlash = (0.5*motorSpeed*motortemplate.getAccelerationTime().getValue())+motorBacklash+additionalBacklash;
|
||||
double realEnd = end+(backlash*direction);
|
||||
double realStart = start-(backlash*direction);
|
||||
|
||||
|
||||
// Move to start
|
||||
logger.info("Move motor to start ["+realStart+"]");
|
||||
motortemplate.getSetValue().setValueAsync(realStart).get(timeout, TimeUnit.MILLISECONDS); // Will block until move is done
|
||||
|
||||
|
||||
// Set motor paramters
|
||||
// Backup settings
|
||||
logger.info("Backup motor settings");
|
||||
double backupSpeed = motortemplate.getVelocity().getValue();
|
||||
double backupBacklash = motorBacklash;
|
||||
double backupMinSpeed = motorBaseSpeed;
|
||||
|
||||
try{
|
||||
// Set motor settings
|
||||
logger.info("Update motor settings");
|
||||
// if(!respectMotorMinSpeed){
|
||||
// motortemplate.getBaseSpeed().setValue(0d);
|
||||
// }
|
||||
// Set base speed as fast as possible but not faster than the original base speed.
|
||||
double base = motorBaseSpeed;
|
||||
if(motorSpeed<base){
|
||||
base = motorSpeed;
|
||||
}
|
||||
motortemplate.getBaseSpeed().setValue(base);
|
||||
motortemplate.getVelocity().setValue(motorSpeed);
|
||||
motortemplate.getBacklashDistance().setValue(0d);
|
||||
|
||||
|
||||
// Execute pre actions
|
||||
for(Action action: preActions){
|
||||
action.execute();
|
||||
}
|
||||
|
||||
|
||||
// Start crlogic logic
|
||||
logger.info("Start CRLOGIC");
|
||||
template.getStatus().setValue(TemplateCrlogic.Status.INITIALIZE.toString());
|
||||
try{
|
||||
template.getStatus().waitForValue(TemplateCrlogic.Status.ACTIVE.toString(), startStopTimeout);
|
||||
}
|
||||
catch(ChannelException | ExecutionException | TimeoutException e){
|
||||
logger.info( "Failed to start CRLOGIC. Logic in status: "+template.getStatus().getValue() );
|
||||
if(template.getStatus().getValue().equals(TemplateCrlogic.Status.FAULT.toString())){
|
||||
logger.info("Error message: "+template.getMessage().getValue());
|
||||
}
|
||||
// Recover to inactive
|
||||
template.getStatus().setValue(TemplateCrlogic.Status.INACTIVE.toString());
|
||||
// TODO Improve error handling
|
||||
throw new RuntimeException("Failed to start CRLOGIC. Logic in status: "+template.getStatus().getValue()+ " Error message: "+template.getMessage().getValue(), e);
|
||||
|
||||
}
|
||||
|
||||
// Connect ZMQ stream
|
||||
connect(ioc);
|
||||
// drainHangingSubmessages();
|
||||
|
||||
// Move motor(s) to end / wait until motor is stopped
|
||||
logger.info("Move motor to end ["+realEnd+"]");
|
||||
try{
|
||||
|
||||
// This is the continuous motor move
|
||||
Future<Double> future = motortemplate.getSetValue().setValueAsync(realEnd);
|
||||
|
||||
long timeoutTime = System.currentTimeMillis()+timeout;
|
||||
|
||||
// This loop will keep spinning until the motor reached the final position
|
||||
while(!future.isDone()){
|
||||
if(System.currentTimeMillis()>timeoutTime){
|
||||
throw new TimeoutException("Motion timed out");
|
||||
}
|
||||
|
||||
receive(); // TODO Problem still blocking
|
||||
|
||||
if(abort){
|
||||
// Abort motor move
|
||||
motortemplate.getCommand().setValue(TemplateMotor.Commands.Stop.ordinal());
|
||||
motortemplate.getCommand().setValue(TemplateMotor.Commands.Go.ordinal());
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
finally {
|
||||
receive();
|
||||
|
||||
// Send end of stream message
|
||||
logger.info("Sending - End of Line - Data Group: "+ dataGroup);
|
||||
eventbus.post(new EndOfStreamMessage(dataGroup));
|
||||
|
||||
// Close ZMQ stream
|
||||
close();
|
||||
}
|
||||
logger.info("Motor reached end position");
|
||||
|
||||
// Stop crlogic logic
|
||||
logger.info("Stop CRLOGIC");
|
||||
template.getStatus().setValue(TemplateCrlogic.Status.STOP.toString());
|
||||
// Wait until stopped
|
||||
logger.info("Wait until stopped");
|
||||
try{
|
||||
template.getStatus().waitForValue(TemplateCrlogic.Status.INACTIVE.toString(), startStopTimeout);
|
||||
}
|
||||
catch(ChannelException | ExecutionException | TimeoutException e){
|
||||
logger.info( "Failed to stop CRLOGIC. Logic in status: "+template.getStatus().getValue() );
|
||||
// TODO Improve error handling
|
||||
throw new RuntimeException("Failed to stop CRLOGIC. Logic in status: "+template.getStatus().getValue(), e);
|
||||
}
|
||||
logger.info("CRLOGIC is now stopped");
|
||||
|
||||
|
||||
// Execute post actions
|
||||
for(Action action: postActions){
|
||||
action.execute();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
finally{
|
||||
logger.info("Restore motor settings");
|
||||
motortemplate.getBaseSpeed().setValue(backupMinSpeed);
|
||||
motortemplate.getVelocity().setValue(backupSpeed);
|
||||
motortemplate.getBacklashDistance().setValue(backupBacklash);
|
||||
}
|
||||
|
||||
|
||||
if(zigZag){
|
||||
// reverse start/end
|
||||
double aend = end;
|
||||
end=start;
|
||||
start=aend;
|
||||
}
|
||||
|
||||
}
|
||||
catch(ChannelException | ExecutionException | TimeoutException e){
|
||||
throw new RuntimeException("Unable to execute crloop", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Abort execution
|
||||
*/
|
||||
@Override
|
||||
public void abort() {
|
||||
abort = true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prepare this loop for execution
|
||||
*/
|
||||
@Override
|
||||
public void prepare() {
|
||||
|
||||
metadata = new ArrayList<>();
|
||||
|
||||
// Build up metadata
|
||||
metadata.add(new Metadata(this.id));
|
||||
for(CrlogicResource s: sensors){
|
||||
metadata.add(new Metadata(s.getId()));
|
||||
}
|
||||
|
||||
try{
|
||||
// Connect crlogic channels
|
||||
template = new TemplateCrlogic();
|
||||
logger.info("Connect channels");
|
||||
Map<String,String> map = new HashMap<>();
|
||||
map.put("PREFIX", prefix);
|
||||
cservice.createAnnotatedChannels(template, map);
|
||||
|
||||
// Connect motor channels
|
||||
motortemplate = new TemplateMotor();
|
||||
map = new HashMap<>();
|
||||
map.put("PREFIX", this.name);
|
||||
cservice.createAnnotatedChannels(motortemplate, map);
|
||||
|
||||
useReadback = motortemplate.getUseReadback().getValue();
|
||||
useEncoder = motortemplate.getUseEncoder().getValue();
|
||||
|
||||
logger.info("Motor type: "+ TemplateMotor.Type.values()[motortemplate.getType().getValue()]);
|
||||
logger.info("Motor use readback: "+useReadback);
|
||||
logger.info("Motor use encoder: "+useEncoder);
|
||||
|
||||
// TODO build up list of readout resources (based on sensors)
|
||||
readoutResources.clear();
|
||||
// first sensor is the actuator
|
||||
|
||||
// Determine mode of motor
|
||||
if((!useReadback) && (!useEncoder)){
|
||||
// Open loop
|
||||
if(this.readback!=null){
|
||||
throw new IllegalArgumentException("Readback not supported if motor is configured in open loop");
|
||||
}
|
||||
else{
|
||||
readoutResources.add(this.name);
|
||||
}
|
||||
|
||||
}
|
||||
else if(useReadback && (!useEncoder)){
|
||||
String readback;
|
||||
// use readback link
|
||||
if(this.readback!=null){
|
||||
// Use specified readback
|
||||
readback = this.readback;
|
||||
}
|
||||
else{
|
||||
// Set resouce to readback link
|
||||
readback = (motortemplate.getReadbackLink().getValue());
|
||||
readback = readback.replaceAll(" +.*", ""); // remove NPP etc at the end
|
||||
}
|
||||
|
||||
readoutResources.add(readback);
|
||||
|
||||
// Fill readback encoder settings
|
||||
// Connect to encoder
|
||||
TemplateEncoder encodertemplate = new TemplateEncoder();
|
||||
map = new HashMap<>();
|
||||
map.put("PREFIX", readback);
|
||||
cservice.createAnnotatedChannels(encodertemplate, map);
|
||||
|
||||
// Read encoder settings
|
||||
if(encodertemplate.getDirection().getValue()==TemplateEncoder.Direction.Positive.ordinal()){
|
||||
crlogicDataFilter.setEncoderDirection(1);
|
||||
}
|
||||
else{
|
||||
crlogicDataFilter.setEncoderDirection(-1);
|
||||
}
|
||||
crlogicDataFilter.setEncoderOffset(encodertemplate.getOffset().getValue());
|
||||
crlogicDataFilter.setEncoderResolution(encodertemplate.getResolution().getValue());
|
||||
|
||||
// Disconnect from encoder
|
||||
cservice.destroyAnnotatedChannels(encodertemplate);
|
||||
|
||||
}
|
||||
else if (useEncoder && (!useReadback)){
|
||||
// use readback link
|
||||
if(this.readback!=null){
|
||||
throw new IllegalArgumentException("Readback not supported if motor is configured to use encoder");
|
||||
}
|
||||
else{
|
||||
// Set resouce to readback link
|
||||
readoutResources.add(this.name+"_ENC");
|
||||
}
|
||||
}
|
||||
else{
|
||||
throw new IllegalArgumentException("Motor configuration not supportet: use readback - "+useReadback+" use encoder - "+useEncoder);
|
||||
}
|
||||
|
||||
// Fill Motor specific settings
|
||||
if(motortemplate.getDirection().getValue()==TemplateMotor.Direction.Positive.ordinal()){
|
||||
crlogicDataFilter.setMotorDirection(1);
|
||||
}
|
||||
else{
|
||||
crlogicDataFilter.setMotorDirection(-1);
|
||||
}
|
||||
crlogicDataFilter.setMotorEncoderResolution(motortemplate.getEncoderResolution().getValue());
|
||||
crlogicDataFilter.setMotorOffset(motortemplate.getOffset().getValue());
|
||||
crlogicDataFilter.setMotorReadbackResolution(motortemplate.getReadbackResolution().getValue());
|
||||
crlogicDataFilter.setMotorResolution(motortemplate.getMotorResolution().getValue());
|
||||
|
||||
// Clear all indices
|
||||
scalerIndices.clear();
|
||||
|
||||
int c = 1; // We start at 1 because the actuator right now is an implicit sensor
|
||||
for(CrlogicResource s: sensors){
|
||||
readoutResources.add(s.getKey());
|
||||
if(s.isDelta()){
|
||||
scalerIndices.put(c, new CrlogicDeltaDataFilter());
|
||||
}
|
||||
c++;
|
||||
}
|
||||
|
||||
// Workaround - somehow one has to add an empty thing to the value otherwise the c logic
|
||||
// does not pick up the end
|
||||
readoutResources.add("");
|
||||
}
|
||||
catch(Exception e){
|
||||
throw new RuntimeException("Unable to prepare crloop: ",e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Connect ZMQ stream
|
||||
* @param address ZMQ endpoint address
|
||||
*/
|
||||
private void connect(String address) {
|
||||
// Clear interrupted state
|
||||
Thread.interrupted();
|
||||
|
||||
logger.info("Connecting with IOC"+address);
|
||||
context = ZMQ.context(1);
|
||||
socket = context.socket(ZMQ.PULL);
|
||||
socket.setRcvHWM(HIGH_WATER_MARK);
|
||||
socket.connect("tcp://"+address+":9999");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Close ZMQ stream
|
||||
*/
|
||||
private void close() {
|
||||
logger.info("Closing stream from IOC "+ioc);
|
||||
|
||||
socket.close();
|
||||
context.close();
|
||||
socket = null;
|
||||
context = null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Drain sub-messages
|
||||
* @return Number of sub-messages drained
|
||||
*/
|
||||
private int drainHangingSubmessages() {
|
||||
int count = 0;
|
||||
while (socket.hasReceiveMore()) {
|
||||
// is there a way to avoid copying data to user space here?
|
||||
socket.recv();
|
||||
count++;
|
||||
}
|
||||
if(count>0){
|
||||
logger.info("Drain hanging submessages");
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Cleanup loop
|
||||
*/
|
||||
@Override
|
||||
public void cleanup() {
|
||||
logger.info("Cleanup");
|
||||
try {
|
||||
cservice.destroyAnnotatedChannels(template);
|
||||
cservice.destroyAnnotatedChannels(motortemplate);
|
||||
template = null;
|
||||
motortemplate = null;
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("Unable to destroy Channel Access channels", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<Action> getPreActions() {
|
||||
return preActions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Action> getPostActions() {
|
||||
return postActions;
|
||||
}
|
||||
|
||||
public List<CrlogicResource> getSensors() {
|
||||
return sensors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDataGroup() {
|
||||
return dataGroup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDataGroup(boolean dataGroup) {
|
||||
this.dataGroup = dataGroup;
|
||||
}
|
||||
|
||||
public EventBus getEventBus(){
|
||||
return eventbus;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.core.loops.cr;
|
||||
|
||||
public class CrlogicRangeDataFilter {
|
||||
|
||||
private double motorResolution = 1;
|
||||
private int motorDirection = 1;
|
||||
private double motorOffset = 0;
|
||||
private double motorReadbackResolution = 1;
|
||||
private double motorEncoderResolution = 1;
|
||||
|
||||
private double encoderOffset = 0;
|
||||
private double encoderResolution = 1;
|
||||
private int encoderDirection = 1;
|
||||
|
||||
private double start = 0;
|
||||
private double end = 0;
|
||||
private boolean positive = true;
|
||||
|
||||
/**
|
||||
* Marker whether the value was equal
|
||||
*/
|
||||
private boolean wasEqualBefore = false;
|
||||
|
||||
/**
|
||||
* @return the motorResolution
|
||||
*/
|
||||
public double getMotorResolution() {
|
||||
return motorResolution;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param motorResolution the motorResolution to set
|
||||
*/
|
||||
public void setMotorResolution(double motorResolution) {
|
||||
this.motorResolution = motorResolution;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the motorDirection
|
||||
*/
|
||||
public int getMotorDirection() {
|
||||
return motorDirection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param motorDirection the motorDirection to set
|
||||
*/
|
||||
public void setMotorDirection(int motorDirection) {
|
||||
this.motorDirection = motorDirection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the motorOffset
|
||||
*/
|
||||
public double getMotorOffset() {
|
||||
return motorOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param motorOffset the motorOffset to set
|
||||
*/
|
||||
public void setMotorOffset(double motorOffset) {
|
||||
this.motorOffset = motorOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the motorReadbackResolution
|
||||
*/
|
||||
public double getMotorReadbackResolution() {
|
||||
return motorReadbackResolution;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param motorReadbackResolution the motorReadbackResolution to set
|
||||
*/
|
||||
public void setMotorReadbackResolution(double motorReadbackResolution) {
|
||||
this.motorReadbackResolution = motorReadbackResolution;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the motorEncoderResolution
|
||||
*/
|
||||
public double getMotorEncoderResolution() {
|
||||
return motorEncoderResolution;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param motorEncoderResolution the motorEncoderResolution to set
|
||||
*/
|
||||
public void setMotorEncoderResolution(double motorEncoderResolution) {
|
||||
this.motorEncoderResolution = motorEncoderResolution;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the encoderOffset
|
||||
*/
|
||||
public double getEncoderOffset() {
|
||||
return encoderOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param encoderOffset the encoderOffset to set
|
||||
*/
|
||||
public void setEncoderOffset(double encoderOffset) {
|
||||
this.encoderOffset = encoderOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the encoderResolution
|
||||
*/
|
||||
public double getEncoderResolution() {
|
||||
return encoderResolution;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param encoderResolution the encoderResolution to set
|
||||
*/
|
||||
public void setEncoderResolution(double encoderResolution) {
|
||||
this.encoderResolution = encoderResolution;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the encoderDirection
|
||||
*/
|
||||
public int getEncoderDirection() {
|
||||
return encoderDirection;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param encoderDirection the encoderDirection to set
|
||||
*/
|
||||
public void setEncoderDirection(int encoderDirection) {
|
||||
this.encoderDirection = encoderDirection;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return the start
|
||||
*/
|
||||
public double getStart() {
|
||||
return start;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param start the start to set
|
||||
*/
|
||||
public void setStart(double start) {
|
||||
this.start = start;
|
||||
if(start<=end){
|
||||
positive=true;
|
||||
}
|
||||
else{
|
||||
positive=false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the end
|
||||
*/
|
||||
public double getEnd() {
|
||||
return end;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param end the end to set
|
||||
*/
|
||||
public void setEnd(double end) {
|
||||
this.end = end;
|
||||
if(end>=start){
|
||||
positive=true;
|
||||
}
|
||||
else{
|
||||
positive=false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate real position
|
||||
* @param raw
|
||||
* @return
|
||||
*/
|
||||
public double calculatePositionMotor(double raw){
|
||||
return(((raw * motorResolution * motorReadbackResolution)/motorDirection)+motorOffset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate real motor position using the readback link
|
||||
* @param raw
|
||||
* @return
|
||||
*/
|
||||
public double calculatePositionMotorUseReadback(double raw){
|
||||
return((((raw - encoderOffset) * encoderResolution * encoderDirection * motorReadbackResolution) / motorDirection) + motorOffset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate real motor position using encoder
|
||||
* @param raw
|
||||
* @return
|
||||
*/
|
||||
public double calculatePositionMotorUseEncoder(double raw){
|
||||
return(raw * motorEncoderResolution * motorReadbackResolution/motorDirection + motorOffset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter whether value is with the range
|
||||
* @param value
|
||||
* @return
|
||||
*/
|
||||
public boolean filter(Double value){
|
||||
if(positive){
|
||||
if(start<=value && value<=end){
|
||||
|
||||
// If motor is very accurete and user backlash==null it might be that value is exactly
|
||||
// the end value. To prevent that unnecessary data is captured execute this check
|
||||
if(wasEqualBefore){
|
||||
wasEqualBefore=false; // Reset flag
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check whether was equal
|
||||
if(value==end){
|
||||
wasEqualBefore=true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else{
|
||||
if(end<=value && value <=start){
|
||||
|
||||
if(wasEqualBefore){
|
||||
wasEqualBefore=false; // Reset flag
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check whether was equal
|
||||
if(value==start){
|
||||
wasEqualBefore=true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
71
src/main/java/ch/psi/fda/core/loops/cr/CrlogicResource.java
Normal file
71
src/main/java/ch/psi/fda/core/loops/cr/CrlogicResource.java
Normal file
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2013 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but without any
|
||||
* warranty; without even the implied warranty of merchantability or fitness for
|
||||
* a particular purpose. See the GNU Lesser General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package ch.psi.fda.core.loops.cr;
|
||||
|
||||
/**
|
||||
* Readout resource of crlogic
|
||||
*/
|
||||
public class CrlogicResource {
|
||||
|
||||
/**
|
||||
* Id of the data
|
||||
*/
|
||||
private String id;
|
||||
/**
|
||||
* Id of the crlogic resource to be read out. As configured in the CRLOGIC part of the IOC startup script
|
||||
*/
|
||||
private String key;
|
||||
/**
|
||||
* Flag whether to read back the delta of the values of this resource. (delta is done in software)
|
||||
*/
|
||||
private boolean delta = false;
|
||||
|
||||
public CrlogicResource(){
|
||||
}
|
||||
|
||||
public CrlogicResource(String id, String key){
|
||||
this.id = id;
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
public CrlogicResource(String id, String key, boolean delta){
|
||||
this.id = id;
|
||||
this.key = key;
|
||||
this.delta = delta;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
public void setKey(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
public boolean isDelta() {
|
||||
return delta;
|
||||
}
|
||||
public void setDelta(boolean delta) {
|
||||
this.delta = delta;
|
||||
}
|
||||
}
|
||||
23
src/main/java/ch/psi/fda/core/loops/cr/MainHeader.java
Normal file
23
src/main/java/ch/psi/fda/core/loops/cr/MainHeader.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package ch.psi.fda.core.loops.cr;
|
||||
|
||||
/**
|
||||
* Header of the ZMQ message
|
||||
*/
|
||||
public class MainHeader {
|
||||
private String htype;
|
||||
private int elements;
|
||||
|
||||
public void setElements(int elements) {
|
||||
this.elements = elements;
|
||||
}
|
||||
public int getElements() {
|
||||
return elements;
|
||||
}
|
||||
|
||||
public void setHtype(String htype) {
|
||||
this.htype = htype;
|
||||
}
|
||||
public String getHtype() {
|
||||
return htype;
|
||||
}
|
||||
}
|
||||
209
src/main/java/ch/psi/fda/core/loops/cr/MergeLogic.java
Normal file
209
src/main/java/ch/psi/fda/core/loops/cr/MergeLogic.java
Normal file
@@ -0,0 +1,209 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.core.loops.cr;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.google.common.eventbus.EventBus;
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
|
||||
import ch.psi.fda.messages.DataMessage;
|
||||
import ch.psi.fda.messages.EndOfStreamMessage;
|
||||
import ch.psi.fda.messages.Message;
|
||||
import ch.psi.fda.messages.Metadata;
|
||||
|
||||
/**
|
||||
* Class to merge two data streams into one. The secondary queues data is added to the primary queues data.
|
||||
* The resulting queue therefor will hold the same amount of elements as the primary queue does.
|
||||
* The datagroup flag set in the end of stream message will be set according to the flag set in the primary queue.
|
||||
*/
|
||||
public class MergeLogic {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(MergeLogic.class.getName());
|
||||
|
||||
private final EventBus primaryEventBus;
|
||||
private final EventBus secondaryEventBus;
|
||||
private final EventBus outputEventBus;
|
||||
|
||||
private List<Metadata> metadata;
|
||||
|
||||
// Handler of the secondary event bus. We need to keep the reference on this
|
||||
// to be able to unregister ...
|
||||
private Object handler;
|
||||
|
||||
|
||||
boolean firstPrimary = true;
|
||||
boolean firstSecondary = true;
|
||||
|
||||
private BlockingQueue<DataMessage> queue = new LinkedBlockingQueue<>();
|
||||
private List<Object> currentData = null;
|
||||
|
||||
|
||||
public MergeLogic(EventBus primaryEventBus, EventBus secondaryEventBus, EventBus outputEventBus){
|
||||
this.primaryEventBus = primaryEventBus;
|
||||
this.secondaryEventBus = secondaryEventBus;
|
||||
this.outputEventBus = outputEventBus;
|
||||
}
|
||||
|
||||
// Enables the merge logic
|
||||
public void enable(){
|
||||
|
||||
firstPrimary = true;
|
||||
firstSecondary = true;
|
||||
|
||||
queue.clear();
|
||||
|
||||
handler = new Object(){
|
||||
@Subscribe
|
||||
public void onMessage(DataMessage m){
|
||||
// Only queue data messages
|
||||
queue.add(m);
|
||||
}
|
||||
};
|
||||
|
||||
primaryEventBus.register(this);
|
||||
secondaryEventBus.register(handler);
|
||||
}
|
||||
|
||||
// Disables the merge logic
|
||||
public void disable(){
|
||||
primaryEventBus.unregister(this);
|
||||
secondaryEventBus.unregister(handler);
|
||||
|
||||
queue.clear();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This is the master merging logic
|
||||
* @param m Data message from primary event bus
|
||||
*/
|
||||
@Subscribe
|
||||
public void onMessage(Message rawMessage){
|
||||
if(rawMessage instanceof EndOfStreamMessage){
|
||||
outputEventBus.post(rawMessage);
|
||||
|
||||
// Clear queue of secondary event bus
|
||||
queue.clear();
|
||||
return;
|
||||
}
|
||||
else if(!(rawMessage instanceof DataMessage)){
|
||||
logger.warning("Message type not supported - ignore");
|
||||
return;
|
||||
}
|
||||
|
||||
DataMessage message = (DataMessage) rawMessage;
|
||||
|
||||
// Hack to remove the synchronization timestamp from the primary event bus
|
||||
if(firstPrimary){
|
||||
firstPrimary = false;
|
||||
|
||||
metadata = new ArrayList<>();
|
||||
List<Metadata> pqm = message.getMetadata();
|
||||
metadata.add(pqm.get(0)); // add first component (this is the actuator)
|
||||
// Skip the next component as this is the timestamp used to merge the data
|
||||
for(int i=2;i<pqm.size();i++){
|
||||
metadata.add(pqm.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
// Merge logic
|
||||
// Get and remove merge timestamp from the data of the message
|
||||
Double timestamp = (Double) message.getData().remove(1);
|
||||
long milliseconds = (long) (timestamp*1000);
|
||||
long nanoOffset = (long)((timestamp*1000-milliseconds)*1000000);
|
||||
|
||||
int count = 10;
|
||||
while(true){ // TODO Wait only for a limited amount of time ...
|
||||
// Assumption: the secondary Queue holds at least the data up to the timestamp of the message handled by this function
|
||||
DataMessage msCheck = queue.peek();
|
||||
|
||||
// No data is available need to wait for some time
|
||||
// If still no data is available, throw an exception
|
||||
if(msCheck == null){
|
||||
if(count==0){
|
||||
throw new RuntimeException("No data comming in from SCR channels");
|
||||
}
|
||||
|
||||
if(currentData == null){
|
||||
// Need to wait until data comes in ... (this case is for the beginning only)
|
||||
logger.info("No data available");
|
||||
try {
|
||||
Thread.sleep(10);
|
||||
} catch (InterruptedException e) {
|
||||
logger.log(Level.INFO, "", e);
|
||||
}
|
||||
count--;
|
||||
continue;
|
||||
}
|
||||
else{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// If we handle the first message of the secondary event bus, we copy its metadata
|
||||
if(firstSecondary){
|
||||
firstSecondary = false;
|
||||
|
||||
List<Metadata> sqm = msCheck.getMetadata();
|
||||
// Skip first two components of the message as this is the timestamp
|
||||
for(int i=2;i<sqm.size();i++){
|
||||
metadata.add(sqm.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether timestamp of the next message is bigger than the timestamp of the
|
||||
// message from the primary queue - if the timestamp is bigger do not take message out of the queue
|
||||
|
||||
long currMilliCheck = ((Double) msCheck.getData().get(0)).longValue();
|
||||
long currNanoCheck = ((Double) msCheck.getData().get(1)).longValue();
|
||||
|
||||
if(currMilliCheck>milliseconds || (currMilliCheck==milliseconds && currNanoCheck>nanoOffset)){
|
||||
break;
|
||||
}
|
||||
|
||||
try {
|
||||
DataMessage currentDataMessage = queue.take();
|
||||
currentData = currentDataMessage.getData();
|
||||
// Remove timestamps used for synchronization
|
||||
currentData.remove(0);
|
||||
currentData.remove(0);
|
||||
} catch (InterruptedException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(currentData == null){
|
||||
throw new RuntimeException("No SCR data arrived");
|
||||
}
|
||||
|
||||
// Add data to primary data queue message and put it into the out queue
|
||||
message.getData().addAll(currentData);
|
||||
message.setMetadata(metadata);
|
||||
outputEventBus.post(message);
|
||||
}
|
||||
}
|
||||
211
src/main/java/ch/psi/fda/core/loops/cr/ParallelCrlogic.java
Normal file
211
src/main/java/ch/psi/fda/core/loops/cr/ParallelCrlogic.java
Normal file
@@ -0,0 +1,211 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.core.loops.cr;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CyclicBarrier;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.google.common.eventbus.EventBus;
|
||||
|
||||
import ch.psi.fda.core.Action;
|
||||
import ch.psi.fda.core.ActionLoop;
|
||||
|
||||
public class ParallelCrlogic implements ActionLoop {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ParallelCrlogic.class.getName());
|
||||
|
||||
/**
|
||||
* Flag to indicate whether the data of this loop will be grouped
|
||||
* According to this flag the dataGroup flag in EndOfStream will be set.
|
||||
*/
|
||||
private boolean dataGroup = false;
|
||||
|
||||
private CrlogicLoopStream crlogic;
|
||||
private ScrlogicLoop scrlogic;
|
||||
|
||||
/**
|
||||
* List of actions that are executed at the beginning of the loop.
|
||||
*/
|
||||
private List<Action> preActions;
|
||||
/**
|
||||
* List of actions that are executed at the end of the loop.
|
||||
*/
|
||||
private List<Action> postActions;
|
||||
|
||||
private MergeLogic mergeLogic;
|
||||
|
||||
|
||||
private final EventBus eventbus;
|
||||
|
||||
public ParallelCrlogic(CrlogicLoopStream crlogic, ScrlogicLoop scrlogic){
|
||||
|
||||
if(crlogic==null){
|
||||
throw new IllegalArgumentException("No Crloop specified");
|
||||
}
|
||||
if(scrlogic==null){
|
||||
throw new IllegalArgumentException("No Scrloop specified");
|
||||
}
|
||||
|
||||
|
||||
this.eventbus = new EventBus();
|
||||
|
||||
this.crlogic = crlogic;
|
||||
// Add timestamp to sensor at the beginning of the sensor list as this is required for merging the data
|
||||
// Timestamp will be at the second position of a message in the queue!
|
||||
this.crlogic.getSensors().add(0, new CrlogicResource("tmp_timestamp","TIMESTAMP"));
|
||||
this.scrlogic = scrlogic;
|
||||
|
||||
// Initialize lists used by the loop
|
||||
this.preActions = new ArrayList<Action>();
|
||||
this.postActions = new ArrayList<Action>();
|
||||
|
||||
|
||||
|
||||
this.mergeLogic = new MergeLogic(crlogic.getEventBus(), scrlogic.getEventBus(), eventbus);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepare() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws InterruptedException {
|
||||
|
||||
mergeLogic.enable();
|
||||
|
||||
// Execute pre actions
|
||||
for(Action action: preActions){
|
||||
action.execute();
|
||||
}
|
||||
|
||||
|
||||
ExecutorService service = Executors.newFixedThreadPool(3); // 2 for the parallel data acquisition and 1 for the merger thread
|
||||
final CyclicBarrier b = new CyclicBarrier(2);
|
||||
|
||||
List<Future<Boolean>> list = new ArrayList<Future<Boolean>>();
|
||||
|
||||
// Start a thread for each logic
|
||||
logger.info("Submit logic");
|
||||
Future<Boolean> f = service.submit(new Callable<Boolean>(){
|
||||
@Override
|
||||
public Boolean call() throws Exception {
|
||||
|
||||
// Prepare logic
|
||||
crlogic.prepare();
|
||||
|
||||
// Ensure that really all parallel logics start in parallel
|
||||
b.await();
|
||||
|
||||
// Execute the logic of this path
|
||||
crlogic.execute();
|
||||
|
||||
// crlogic.cleanup();
|
||||
|
||||
// Need to stop the scrlogic logic (otherwise it would keep going to take data)
|
||||
scrlogic.abort();
|
||||
return true;
|
||||
}});
|
||||
list.add(f);
|
||||
|
||||
//Start a thread for the scrlogic
|
||||
logger.info("Submit logic");
|
||||
f = service.submit(new Callable<Boolean>(){
|
||||
@Override
|
||||
public Boolean call() throws Exception {
|
||||
|
||||
// Prepare logic
|
||||
scrlogic.prepare();
|
||||
|
||||
// Ensure that really all parallel logics start in parallel
|
||||
b.await();
|
||||
|
||||
// Execute the logic of this path
|
||||
scrlogic.execute(); // This actually just starts the collection ...
|
||||
|
||||
// scrlogic.cleanup();
|
||||
return true;
|
||||
}});
|
||||
list.add(f);
|
||||
|
||||
for(Future<Boolean> bf: list){
|
||||
// Wait for completion of the thread
|
||||
try {
|
||||
bf.get();
|
||||
} catch (ExecutionException e) {
|
||||
logger.log(Level.WARNING, "Something went wrong while waiting for crthreads to finish");
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Wait until all threads have finished
|
||||
service.shutdown();
|
||||
service.awaitTermination(1, TimeUnit.MINUTES);
|
||||
|
||||
|
||||
// Execute post actions
|
||||
for(Action action: postActions){
|
||||
action.execute();
|
||||
}
|
||||
|
||||
mergeLogic.disable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void abort() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanup() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Action> getPreActions() {
|
||||
return preActions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Action> getPostActions() {
|
||||
return postActions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDataGroup() {
|
||||
return dataGroup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDataGroup(boolean dataGroup) {
|
||||
this.dataGroup = dataGroup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventBus getEventBus(){
|
||||
return eventbus;
|
||||
}
|
||||
}
|
||||
224
src/main/java/ch/psi/fda/core/loops/cr/ScrlogicLoop.java
Normal file
224
src/main/java/ch/psi/fda/core/loops/cr/ScrlogicLoop.java
Normal file
@@ -0,0 +1,224 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but without any
|
||||
* warranty; without even the implied warranty of merchantability or fitness for
|
||||
* a particular purpose. See the GNU Lesser General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.core.loops.cr;
|
||||
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.google.common.eventbus.EventBus;
|
||||
|
||||
import ch.psi.fda.core.Action;
|
||||
import ch.psi.fda.core.ActionLoop;
|
||||
import ch.psi.fda.messages.DataMessage;
|
||||
import ch.psi.fda.messages.EndOfStreamMessage;
|
||||
import ch.psi.fda.messages.Metadata;
|
||||
import ch.psi.jcae.Channel;
|
||||
import ch.psi.jcae.ChannelException;
|
||||
import ch.psi.jcae.impl.type.DoubleTimestamp;
|
||||
|
||||
/**
|
||||
* Assumptions: - The delay between the monitor writing the value to the monitor
|
||||
* queue and the readout of all the queues is sufficient to prevent the
|
||||
* situation that some monitors of events close to each other on different IOC's
|
||||
* have not arrived yet. - The sequence of monitors fired for one channel is
|
||||
* according to the sequence of the causes. No monitor package is overtaking an
|
||||
* other package on the network.
|
||||
*
|
||||
* - No monitor events are lost on the network (while using monitors you cannot
|
||||
* guarantee this)
|
||||
*
|
||||
* The data queue returned by this logic includes two items for the timestamp
|
||||
* and nanoseconds offset. These two items are the first two items of a message
|
||||
* The id's are: crTimestampMilliseconds crTimestampOffsetNanoseconds
|
||||
*/
|
||||
public class ScrlogicLoop implements ActionLoop {
|
||||
|
||||
private static String ID_TIMESTAMP_MILLISECONDS = "crTimestampMilliseconds";
|
||||
private static String ID_TIMESTAMP_OFFSET_NANOSECONDS = "crTimestampOffsetNanoseconds";
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ScrlogicLoop.class.getName());
|
||||
|
||||
private boolean dataGroup = false;
|
||||
private final List<Action> preActions = new ArrayList<Action>();
|
||||
private final List<Action> postActions = new ArrayList<Action>();
|
||||
|
||||
private final List<Channel<DoubleTimestamp>> sensors;
|
||||
private final List<String> sensorIds;
|
||||
|
||||
/**
|
||||
* List of monitors that were attached to the sensor channels (i.e
|
||||
* workaround)
|
||||
*/
|
||||
private final List<PropertyChangeListener> monitors = new ArrayList<>();
|
||||
|
||||
private List<Object> currentValues;
|
||||
|
||||
private CountDownLatch latch;
|
||||
|
||||
private final EventBus eventbus;
|
||||
private List<Metadata> metadata;
|
||||
|
||||
public ScrlogicLoop(List<String> sensorIds, List<Channel<DoubleTimestamp>> sensors) {
|
||||
this.eventbus = new EventBus();
|
||||
this.sensors = sensors;
|
||||
this.sensorIds = sensorIds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() throws InterruptedException {
|
||||
|
||||
latch = new CountDownLatch(1);
|
||||
|
||||
// Initialize current values
|
||||
currentValues = new ArrayList<Object>(sensors.size() + 2);
|
||||
// Initialize values
|
||||
currentValues.add(0.0);
|
||||
currentValues.add(0.0);
|
||||
for (Channel<DoubleTimestamp> sensor : sensors) {
|
||||
try {
|
||||
DoubleTimestamp value = sensor.getValue();
|
||||
double timestamp = value.getTimestamp().getTime();
|
||||
double noffset = value.getNanosecondOffset();
|
||||
if (timestamp > ((Double) currentValues.get(0))) {
|
||||
// We don't care about the nanoseconds offset so far
|
||||
currentValues.set(0, timestamp);
|
||||
currentValues.set(1, noffset);
|
||||
}
|
||||
currentValues.add(value.getValue());
|
||||
} catch (InterruptedException | TimeoutException | ChannelException | ExecutionException e) {
|
||||
throw new RuntimeException("Unable to retrieve initial value");
|
||||
}
|
||||
// Initialize current value with NAN
|
||||
}
|
||||
|
||||
// Create metadata
|
||||
metadata = new ArrayList<>(sensors.size());
|
||||
|
||||
// Build up data message metadata based on the channels registered.
|
||||
metadata.add(new Metadata(ID_TIMESTAMP_MILLISECONDS));
|
||||
metadata.add(new Metadata(ID_TIMESTAMP_OFFSET_NANOSECONDS));
|
||||
for (String id : sensorIds) {
|
||||
metadata.add(new Metadata(id));
|
||||
}
|
||||
|
||||
// Send a first data message to be sure that there is a message inside
|
||||
// the queue
|
||||
DataMessage message = new DataMessage(metadata);
|
||||
message.getData().addAll(currentValues);
|
||||
|
||||
eventbus.post(message);
|
||||
|
||||
// Attach monitors to the channels (this is actually a workaround)
|
||||
int counter = 2;
|
||||
for (Channel<DoubleTimestamp> sensor : sensors) {
|
||||
final int currentCount = counter;
|
||||
|
||||
PropertyChangeListener listener = new PropertyChangeListener() {
|
||||
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
if (evt.getPropertyName().equals("value")) {
|
||||
|
||||
// Merging values this way it is not 100% accurate as
|
||||
// theoretically values does not need to come in
|
||||
// in the correct time sequence. However we tradeoff
|
||||
// this accuracy as we are not precise anyway
|
||||
|
||||
DoubleTimestamp v = (DoubleTimestamp) evt.getNewValue();
|
||||
double timestamp = v.getTimestamp().getTime();
|
||||
double noffset = v.getNanosecondOffset();
|
||||
currentValues.set(0, timestamp);
|
||||
currentValues.set(1, noffset);
|
||||
currentValues.set(currentCount, v.getValue());
|
||||
|
||||
DataMessage message = new DataMessage(metadata);
|
||||
message.getData().addAll(currentValues);
|
||||
// logger.info("update"+v.getValue());
|
||||
eventbus.post(message);
|
||||
}
|
||||
}
|
||||
};
|
||||
sensor.addPropertyChangeListener(listener);
|
||||
monitors.add(listener);
|
||||
|
||||
counter++;
|
||||
}
|
||||
|
||||
logger.info("Start data acquisition");
|
||||
|
||||
latch.await();
|
||||
|
||||
// Remove monitors
|
||||
|
||||
logger.info("Remove monitors");
|
||||
for (int i = 0; i < sensors.size(); i++) {
|
||||
Channel<DoubleTimestamp> sensor = sensors.get(i);
|
||||
sensor.removePropertyChangeListener(monitors.get(i));
|
||||
}
|
||||
|
||||
// Put end of stream to the queue
|
||||
eventbus.post(new EndOfStreamMessage(dataGroup));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void abort() {
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepare() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cleanup() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Action> getPreActions() {
|
||||
return preActions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Action> getPostActions() {
|
||||
return postActions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDataGroup() {
|
||||
return dataGroup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDataGroup(boolean dataGroup) {
|
||||
this.dataGroup = dataGroup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EventBus getEventBus() {
|
||||
return eventbus;
|
||||
}
|
||||
}
|
||||
114
src/main/java/ch/psi/fda/core/loops/cr/TemplateCrlogic.java
Normal file
114
src/main/java/ch/psi/fda/core/loops/cr/TemplateCrlogic.java
Normal file
@@ -0,0 +1,114 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.core.loops.cr;
|
||||
|
||||
import ch.psi.jcae.Channel;
|
||||
import ch.psi.jcae.annotation.CaChannel;
|
||||
|
||||
public class TemplateCrlogic {
|
||||
|
||||
/**
|
||||
* Ticks per second - IOC setting
|
||||
* ATTENTION - This field must only be set bu the IOC - ATTENTION
|
||||
*/
|
||||
@CaChannel(type=Integer.class, name ="${PREFIX}:TPS")
|
||||
private Channel<Integer> ticksPerSecond;
|
||||
|
||||
/**
|
||||
* Status of the OTFSCAN IOC logic
|
||||
*/
|
||||
public enum Status { SETUP, INACTIVE, INITIALIZE, ACTIVE, STOP, FAULT, ERROR };
|
||||
@CaChannel(type=String.class, name ="${PREFIX}:STATUS")
|
||||
private Channel<String> status;
|
||||
|
||||
/**
|
||||
* Message from the OTFSCAN IOC logic
|
||||
*/
|
||||
@CaChannel(type=String.class, name ="${PREFIX}:MSG")
|
||||
private Channel<String> message;
|
||||
|
||||
/**
|
||||
* IOC ticks between data acquisition interrupts
|
||||
*/
|
||||
@CaChannel(type=Integer.class, name ="${PREFIX}:TBINT")
|
||||
private Channel<Integer> ticksBetweenInterrupts;
|
||||
|
||||
/**
|
||||
* Name or ip address of the NFS server to save the data to
|
||||
* (depending on the IOC setup)
|
||||
*/
|
||||
@CaChannel(type=String.class, name ="${PREFIX}:NFSSE")
|
||||
private Channel<String> nfsServer;
|
||||
|
||||
/**
|
||||
* Name of the NFS share on the NFS server
|
||||
*/
|
||||
@CaChannel(type=String.class, name ="${PREFIX}:NFSSH")
|
||||
private Channel<String> nfsShare;
|
||||
|
||||
/**
|
||||
* Name of the data file
|
||||
*/
|
||||
@CaChannel(type=String.class, name ="${PREFIX}:DFNAM")
|
||||
private Channel<String> dataFile;
|
||||
|
||||
/**
|
||||
* Flag to identify whether the data file should be appended
|
||||
*/
|
||||
@CaChannel(type=Boolean.class, name ="${PREFIX}:FAPPE")
|
||||
private Channel<Boolean> appendFile;
|
||||
|
||||
/**
|
||||
* Readout resources
|
||||
*/
|
||||
@CaChannel(type=String[].class, name ="${PREFIX}:RRES")
|
||||
private Channel<String[]> readoutResources;
|
||||
|
||||
|
||||
|
||||
public Channel<Integer> getTicksPerSecond() {
|
||||
return ticksPerSecond;
|
||||
}
|
||||
public Channel<String> getStatus() {
|
||||
return status;
|
||||
}
|
||||
public Channel<String> getMessage() {
|
||||
return message;
|
||||
}
|
||||
public Channel<Integer> getTicksBetweenInterrupts() {
|
||||
return ticksBetweenInterrupts;
|
||||
}
|
||||
public Channel<String> getNfsServer() {
|
||||
return nfsServer;
|
||||
}
|
||||
public Channel<String> getNfsShare() {
|
||||
return nfsShare;
|
||||
}
|
||||
public Channel<String> getDataFile() {
|
||||
return dataFile;
|
||||
}
|
||||
public Channel<Boolean> getAppendFile() {
|
||||
return appendFile;
|
||||
}
|
||||
public Channel<String[]> getReadoutResources() {
|
||||
return readoutResources;
|
||||
}
|
||||
|
||||
}
|
||||
59
src/main/java/ch/psi/fda/core/loops/cr/TemplateEncoder.java
Normal file
59
src/main/java/ch/psi/fda/core/loops/cr/TemplateEncoder.java
Normal file
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.core.loops.cr;
|
||||
|
||||
import ch.psi.jcae.Channel;
|
||||
import ch.psi.jcae.annotation.CaChannel;
|
||||
|
||||
public class TemplateEncoder {
|
||||
|
||||
/**
|
||||
* Resolution - $(P)$(E)_SCL
|
||||
*/
|
||||
@CaChannel(type=Double.class, name="${PREFIX}_SCL")
|
||||
private Channel<Double> resolution;
|
||||
|
||||
/**
|
||||
* Offset - $(P)$(E)_OFF
|
||||
*/
|
||||
@CaChannel(type=Double.class, name ="${PREFIX}_OFF")
|
||||
private Channel<Double> offset;
|
||||
|
||||
/**
|
||||
* Direction - $(P)$(E)_DIR
|
||||
*/
|
||||
public enum Direction {Negative, Positive};
|
||||
@CaChannel(type=Integer.class, name ="${PREFIX}_DIR")
|
||||
private Channel<Integer> direction;
|
||||
|
||||
|
||||
|
||||
public Channel<Double> getResolution() {
|
||||
return resolution;
|
||||
}
|
||||
public Channel<Double> getOffset() {
|
||||
return offset;
|
||||
}
|
||||
public Channel<Integer> getDirection() {
|
||||
return direction;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
471
src/main/java/ch/psi/fda/core/loops/cr/TemplateMotor.java
Normal file
471
src/main/java/ch/psi/fda/core/loops/cr/TemplateMotor.java
Normal file
@@ -0,0 +1,471 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.core.loops.cr;
|
||||
|
||||
import ch.psi.jcae.Channel;
|
||||
import ch.psi.jcae.annotation.CaChannel;
|
||||
|
||||
public class TemplateMotor {
|
||||
|
||||
public enum Type {SOFT_CHANNEL, MOTOR_SIMULATION, OMS_VME58, OMS_MAXv};
|
||||
|
||||
/**
|
||||
* ## Drive section ##
|
||||
* # User coordinates #
|
||||
*/
|
||||
|
||||
/**
|
||||
* .HLM High limit - double
|
||||
*/
|
||||
@CaChannel(type=Double.class, name ="${PREFIX}.HLM")
|
||||
private Channel<Double> highLimit;
|
||||
|
||||
/**
|
||||
* .LLM Low limit - double
|
||||
*/
|
||||
@CaChannel(type=Double.class, name ="${PREFIX}.LLM")
|
||||
private Channel<Double> lowLimit;
|
||||
|
||||
/**
|
||||
* .RBV Readback value - double
|
||||
*/
|
||||
@CaChannel(type=Double.class, name ="${PREFIX}.RBV", monitor=true)
|
||||
private Channel<Double> readbackValue;
|
||||
|
||||
/**
|
||||
* .VAL Set value - double
|
||||
*/
|
||||
@CaChannel(type=Double.class, name ="${PREFIX}.VAL", monitor=true)
|
||||
private Channel<Double> setValue;
|
||||
|
||||
/**
|
||||
* .RLV Relative move value - double
|
||||
*/
|
||||
@CaChannel(type=Double.class, name ="${PREFIX}.RLV")
|
||||
private Channel<Double> relativeMoveValue;
|
||||
|
||||
/**
|
||||
* .TWV Teak value - double
|
||||
*/
|
||||
@CaChannel(type=Double.class, name ="${PREFIX}.TWV")
|
||||
private Channel<Double> tweakValue;
|
||||
|
||||
/**
|
||||
* .TWR Tweak reverse - move left - int
|
||||
*/
|
||||
@CaChannel(type=Integer.class, name ="${PREFIX}.TWR")
|
||||
private Channel<Integer> tweakReverse;
|
||||
|
||||
/**
|
||||
* .TWF Tweak forward - move right - int
|
||||
*/
|
||||
@CaChannel(type=Integer.class, name ="${PREFIX}.TWF")
|
||||
private Channel<Integer> tweakForward;
|
||||
|
||||
/**
|
||||
* .JOGR Jog reverse - int
|
||||
*/
|
||||
@CaChannel(type=Integer.class, name ="${PREFIX}.JOGR")
|
||||
private Channel<Integer> jogReverse;
|
||||
|
||||
/**
|
||||
* .JOGF Jog forward - int
|
||||
*/
|
||||
@CaChannel(type=Integer.class, name ="${PREFIX}.JOGF")
|
||||
private Channel<Integer> jogForward;
|
||||
|
||||
/**
|
||||
* .HOMR Home reverse - int
|
||||
*/
|
||||
@CaChannel(type=Integer.class, name ="${PREFIX}.HOMR")
|
||||
private Channel<Integer> homeReverse;
|
||||
|
||||
/**
|
||||
* .HOMF Home forward - int
|
||||
*/
|
||||
@CaChannel(type=Integer.class, name ="${PREFIX}.HOMF")
|
||||
private Channel<Integer> homeForward;
|
||||
|
||||
/**
|
||||
* .EGU Engineering unit - String
|
||||
*/
|
||||
@CaChannel(type=String.class, name ="${PREFIX}.EGU")
|
||||
private Channel<String> engineeringUnit;
|
||||
|
||||
/**
|
||||
* .DTYP Type - String (e.g. "OMS MAXv") see enum Type
|
||||
*/
|
||||
@CaChannel(type=Integer.class, name ="${PREFIX}.DTYP")
|
||||
private Channel<Integer> type;
|
||||
|
||||
/**
|
||||
* .DESC Description - String
|
||||
*/
|
||||
@CaChannel(type=String.class, name ="${PREFIX}.DESC")
|
||||
private Channel<String> description;
|
||||
|
||||
|
||||
/**
|
||||
* # Dial coordinates #
|
||||
*/
|
||||
|
||||
/**
|
||||
* .DHLM Dial high limit - double
|
||||
*/
|
||||
@CaChannel(type=Double.class, name ="${PREFIX}.DHLM")
|
||||
private Channel<Double> dialHighLimit;
|
||||
|
||||
/**
|
||||
* .DLLM Dial low limit - double
|
||||
*/
|
||||
@CaChannel(type=Double.class, name ="${PREFIX}.DLLM")
|
||||
private Channel<Double> dialLowLimit;
|
||||
|
||||
/**
|
||||
* .DRBV Dial readback value - double
|
||||
*/
|
||||
@CaChannel(type=Double.class, name ="${PREFIX}.DRBV", monitor=true)
|
||||
private Channel<Double> dialReadbackValue;
|
||||
|
||||
/**
|
||||
* .DVAL Dial set value - double
|
||||
*/
|
||||
@CaChannel(type=Double.class, name ="${PREFIX}.DVAL", monitor=true)
|
||||
private Channel<Double> dialSetValue;
|
||||
|
||||
/**
|
||||
* .RVAL Raw value - int
|
||||
*/
|
||||
@CaChannel(type=Integer.class, name ="${PREFIX}.RVAL", monitor=true)
|
||||
private Channel<Integer> rawValue;
|
||||
|
||||
/**
|
||||
* .RRBV Raw readback value - int
|
||||
*/
|
||||
@CaChannel(type=Integer.class, name ="${PREFIX}.RRBV", monitor=true)
|
||||
private Channel<Integer> rawReadbackValue;
|
||||
|
||||
/**
|
||||
* .SPMG Stop/Pause/Move/Go - (0:"Stop", 1:"Pause", 2:"Move", 3:"Go") - 3
|
||||
*/
|
||||
public enum Commands { Stop, Pause, Move, Go };
|
||||
@CaChannel(type=Integer.class, name ="${PREFIX}.SPMG")
|
||||
private Channel<Integer> command;
|
||||
|
||||
|
||||
/**
|
||||
* ## Calibration section ##
|
||||
*/
|
||||
|
||||
/**
|
||||
* .SET Set/Use Switch - (0:"Use", 1:"Set") - 0
|
||||
*/
|
||||
public enum Calibration {Use, Set};
|
||||
@CaChannel(type=Integer.class, name ="${PREFIX}.SET")
|
||||
private Channel<Integer> calibration;
|
||||
|
||||
/**
|
||||
* .OFF User offset (EGU) - double
|
||||
*/
|
||||
@CaChannel(type=Double.class, name ="${PREFIX}.OFF")
|
||||
private Channel<Double> offset;
|
||||
|
||||
/**
|
||||
* .FOFF Offset-Freeze Switch - (0:"Variable", 1:"Frozen") - 1
|
||||
*/
|
||||
public enum OffsetMode {Variable, Frozen};
|
||||
@CaChannel(type=Integer.class, name ="${PREFIX}.FOFF")
|
||||
private Channel<Integer> offsetMode;
|
||||
|
||||
/**
|
||||
* .DIR User direction - (0:"Pos", 1:"Neg")
|
||||
*/
|
||||
public enum Direction {Positive, Negative};
|
||||
@CaChannel(type=Integer.class, name ="${PREFIX}.DIR")
|
||||
private Channel<Integer> direction;
|
||||
|
||||
|
||||
/**
|
||||
* ## Dynamics ##
|
||||
*/
|
||||
|
||||
/**
|
||||
* .VELO Velocity (EGU/s) - double
|
||||
*/
|
||||
@CaChannel(type=Double.class, name ="${PREFIX}.VELO")
|
||||
private Channel<Double> velocity;
|
||||
|
||||
/**
|
||||
* .BVEL Backlash velocity (EGU/s) - double
|
||||
*/
|
||||
@CaChannel(type=Double.class, name ="${PREFIX}.BVEL")
|
||||
private Channel<Double> backlashVelocity;
|
||||
|
||||
/**
|
||||
* .VBAS Base speed (EGU/s) - double
|
||||
*/
|
||||
@CaChannel(type=Double.class, name ="${PREFIX}.VBAS")
|
||||
private Channel<Double> baseSpeed;
|
||||
|
||||
/**
|
||||
* .ACCL Acceleration time / seconds to velocity - double
|
||||
*/
|
||||
@CaChannel(type=Double.class, name ="${PREFIX}.ACCL")
|
||||
private Channel<Double> accelerationTime;
|
||||
|
||||
/**
|
||||
* .BACC Backlash acceleration time / seconds to velocity - double
|
||||
*/
|
||||
@CaChannel(type=Double.class, name ="${PREFIX}.BACC")
|
||||
private Channel<Double> backlashAccelerationTime;
|
||||
|
||||
/**
|
||||
* .BDST Backlash distance (EGU) - double
|
||||
*/
|
||||
@CaChannel(type=Double.class, name ="${PREFIX}.BDST")
|
||||
private Channel<Double> backlashDistance;
|
||||
|
||||
/**
|
||||
* .FRAC Move fraction - double
|
||||
*/
|
||||
@CaChannel(type=Double.class, name ="${PREFIX}.FRAC")
|
||||
private Channel<Double> moveFracion;
|
||||
|
||||
|
||||
/**
|
||||
* ## Resolution ##
|
||||
*/
|
||||
|
||||
/**
|
||||
* .MRES Motor resolution - double
|
||||
*/
|
||||
@CaChannel(type=Double.class, name ="${PREFIX}.MRES")
|
||||
private Channel<Double> motorResolution;
|
||||
|
||||
/**
|
||||
* .ERES Encoder resolution - double
|
||||
*/
|
||||
@CaChannel(type=Double.class, name ="${PREFIX}.ERES")
|
||||
private Channel<Double> encoderResolution;
|
||||
|
||||
/**
|
||||
* .RRES Readback resolution - double
|
||||
*/
|
||||
@CaChannel(type=Double.class, name ="${PREFIX}.RRES")
|
||||
private Channel<Double> readbackResolution;
|
||||
|
||||
/**
|
||||
* .RDBD Retry deadband (EGU) - double
|
||||
*/
|
||||
@CaChannel(type=Double.class, name ="${PREFIX}.RDBD")
|
||||
private Channel<Double> retryDeadband;
|
||||
|
||||
/**
|
||||
* .RTRY Max retry count - int
|
||||
*/
|
||||
@CaChannel(type=Integer.class, name ="${PREFIX}.RTRY")
|
||||
private Channel<Integer> maxRetryCount;
|
||||
|
||||
/**
|
||||
* .RCNT Retry count - int
|
||||
*/
|
||||
@CaChannel(type=Integer.class, name ="${PREFIX}.RCNT", monitor=true)
|
||||
private Channel<Integer> retryCount;
|
||||
|
||||
/**
|
||||
* .UEIP Use encoder (if present) - (0:"No", 1:"Yes")
|
||||
*/
|
||||
@CaChannel(type=Boolean.class, name ="${PREFIX}.UEIP")
|
||||
private Channel<Boolean> useEncoder;
|
||||
|
||||
/**
|
||||
* .URIP Use readback link (if present) - (0:"No", 1:"Yes")
|
||||
*/
|
||||
@CaChannel(type=Boolean.class, name ="${PREFIX}.URIP")
|
||||
private Channel<Boolean> useReadback;
|
||||
|
||||
/**
|
||||
* .DLY Readback delay (s) - double
|
||||
*/
|
||||
@CaChannel(type=Double.class, name ="${PREFIX}.DLY")
|
||||
private Channel<Double> readbackDelay;
|
||||
|
||||
/**
|
||||
* .RDBL Readback link - String
|
||||
*/
|
||||
@CaChannel(type=String.class, name ="${PREFIX}.RDBL")
|
||||
private Channel<String> readbackLink;
|
||||
|
||||
/**
|
||||
* .OMSL Output mode select - (0:"supervisory", 1:"closed_loop")
|
||||
*/
|
||||
public enum OutputMode {Supervisory, Closed_Loop};
|
||||
@CaChannel(type=Integer.class, name ="${PREFIX}.OMSL")
|
||||
private Channel<Integer> outputMode;
|
||||
|
||||
/**
|
||||
* ## Status ##
|
||||
*/
|
||||
|
||||
/**
|
||||
* .DMOV Done move - int
|
||||
*/
|
||||
@CaChannel(type=Boolean.class, name ="${PREFIX}.DMOV", monitor=true)
|
||||
private Channel<Boolean> moveDone;
|
||||
|
||||
|
||||
|
||||
public Channel<Double> getHighLimit() {
|
||||
return highLimit;
|
||||
}
|
||||
public Channel<Double> getLowLimit() {
|
||||
return lowLimit;
|
||||
}
|
||||
public Channel<Double> getReadbackValue() {
|
||||
return readbackValue;
|
||||
}
|
||||
public Channel<Double> getSetValue() {
|
||||
return setValue;
|
||||
}
|
||||
public Channel<Double> getRelativeMoveValue() {
|
||||
return relativeMoveValue;
|
||||
}
|
||||
public Channel<Double> getTweakValue() {
|
||||
return tweakValue;
|
||||
}
|
||||
public Channel<Integer> getTweakReverse() {
|
||||
return tweakReverse;
|
||||
}
|
||||
public Channel<Integer> getTweakForward() {
|
||||
return tweakForward;
|
||||
}
|
||||
public Channel<Integer> getJogReverse() {
|
||||
return jogReverse;
|
||||
}
|
||||
public Channel<Integer> getJogForward() {
|
||||
return jogForward;
|
||||
}
|
||||
public Channel<Integer> getHomeReverse() {
|
||||
return homeReverse;
|
||||
}
|
||||
public Channel<Integer> getHomeForward() {
|
||||
return homeForward;
|
||||
}
|
||||
public Channel<String> getEngineeringUnit() {
|
||||
return engineeringUnit;
|
||||
}
|
||||
public Channel<Integer> getType() {
|
||||
return type;
|
||||
}
|
||||
public Channel<String> getDescription() {
|
||||
return description;
|
||||
}
|
||||
public Channel<Double> getDialHighLimit() {
|
||||
return dialHighLimit;
|
||||
}
|
||||
public Channel<Double> getDialLowLimit() {
|
||||
return dialLowLimit;
|
||||
}
|
||||
public Channel<Double> getDialReadbackValue() {
|
||||
return dialReadbackValue;
|
||||
}
|
||||
public Channel<Double> getDialSetValue() {
|
||||
return dialSetValue;
|
||||
}
|
||||
public Channel<Integer> getRawValue() {
|
||||
return rawValue;
|
||||
}
|
||||
public Channel<Integer> getRawReadbackValue() {
|
||||
return rawReadbackValue;
|
||||
}
|
||||
public Channel<Integer> getCommand() {
|
||||
return command;
|
||||
}
|
||||
public Channel<Integer> getCalibration() {
|
||||
return calibration;
|
||||
}
|
||||
public Channel<Double> getOffset() {
|
||||
return offset;
|
||||
}
|
||||
public Channel<Integer> getOffsetMode() {
|
||||
return offsetMode;
|
||||
}
|
||||
public Channel<Integer> getDirection() {
|
||||
return direction;
|
||||
}
|
||||
public Channel<Double> getVelocity() {
|
||||
return velocity;
|
||||
}
|
||||
public Channel<Double> getBacklashVelocity() {
|
||||
return backlashVelocity;
|
||||
}
|
||||
public Channel<Double> getBaseSpeed() {
|
||||
return baseSpeed;
|
||||
}
|
||||
public Channel<Double> getAccelerationTime() {
|
||||
return accelerationTime;
|
||||
}
|
||||
public Channel<Double> getBacklashAccelerationTime() {
|
||||
return backlashAccelerationTime;
|
||||
}
|
||||
public Channel<Double> getBacklashDistance() {
|
||||
return backlashDistance;
|
||||
}
|
||||
public Channel<Double> getMoveFracion() {
|
||||
return moveFracion;
|
||||
}
|
||||
public Channel<Double> getMotorResolution() {
|
||||
return motorResolution;
|
||||
}
|
||||
public Channel<Double> getEncoderResolution() {
|
||||
return encoderResolution;
|
||||
}
|
||||
public Channel<Double> getReadbackResolution() {
|
||||
return readbackResolution;
|
||||
}
|
||||
public Channel<Double> getRetryDeadband() {
|
||||
return retryDeadband;
|
||||
}
|
||||
public Channel<Integer> getMaxRetryCount() {
|
||||
return maxRetryCount;
|
||||
}
|
||||
public Channel<Integer> getRetryCount() {
|
||||
return retryCount;
|
||||
}
|
||||
public Channel<Boolean> getUseEncoder() {
|
||||
return useEncoder;
|
||||
}
|
||||
public Channel<Boolean> getUseReadback() {
|
||||
return useReadback;
|
||||
}
|
||||
public Channel<Double> getReadbackDelay() {
|
||||
return readbackDelay;
|
||||
}
|
||||
public Channel<String> getReadbackLink() {
|
||||
return readbackLink;
|
||||
}
|
||||
public Channel<Integer> getOutputMode() {
|
||||
return outputMode;
|
||||
}
|
||||
public Channel<Boolean> getMoveDone() {
|
||||
return moveDone;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.core.loops.cr;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import ch.psi.jcae.Channel;
|
||||
import ch.psi.jcae.annotation.CaChannel;
|
||||
|
||||
public class TemplateVSC16Scaler {
|
||||
|
||||
/**
|
||||
* Command
|
||||
*/
|
||||
public enum Command {Done, Count};
|
||||
@CaChannel(type=Integer.class, name ="${PREFIX}.CNT")
|
||||
private Channel<Integer> command;
|
||||
|
||||
|
||||
public enum Mode {OneShot, AutoCount};
|
||||
/**
|
||||
* Count mode
|
||||
*/
|
||||
@CaChannel(type=Integer.class, name ="${PREFIX}.CONT")
|
||||
private Channel<Integer> mode;
|
||||
|
||||
/**
|
||||
* Channel description
|
||||
*/
|
||||
@CaChannel(type=Boolean.class, name={"${PREFIX}.NM1", "${PREFIX}.NM2", "${PREFIX}.NM3", "${PREFIX}.NM4", "${PREFIX}.NM5", "${PREFIX}.NM6", "${PREFIX}.NM7", "${PREFIX}.NM8", "${PREFIX}.NM9", "${PREFIX}.NM10", "${PREFIX}.NM11", "${PREFIX}.NM12", "${PREFIX}.NM13", "${PREFIX}.NM14", "${PREFIX}.NM15", "${PREFIX}.NM16"})
|
||||
private List<Channel<Boolean>> channelDescription;
|
||||
|
||||
/**
|
||||
* Channel gate
|
||||
*/
|
||||
@CaChannel(type=Boolean.class, name={"${PREFIX}.G1", "${PREFIX}.G2", "${PREFIX}.G3", "${PREFIX}.G4", "${PREFIX}.G5", "${PREFIX}.G6", "${PREFIX}.G7", "${PREFIX}.G8", "${PREFIX}.G9", "${PREFIX}.G10", "${PREFIX}.G11", "${PREFIX}.G12", "${PREFIX}.G13", "${PREFIX}.G14", "${PREFIX}.G15", "${PREFIX}.G16"})
|
||||
private List<Channel<Boolean>> channelGate;
|
||||
|
||||
/**
|
||||
* Channel preset count
|
||||
* If gate is on scaler will only count until this value
|
||||
*/
|
||||
@CaChannel(type=Integer.class, name={"${PREFIX}.PR1", "${PREFIX}.PR2", "${PREFIX}.PR3", "${PREFIX}.PR4", "${PREFIX}.PR5", "${PREFIX}.PR6", "${PREFIX}.PR7", "${PREFIX}.PR8", "${PREFIX}.PR9", "${PREFIX}.PR10", "${PREFIX}.PR11", "${PREFIX}.PR12", "${PREFIX}.PR13", "${PREFIX}.PR14", "${PREFIX}.PR15", "${PREFIX}.PR16"})
|
||||
private List<Channel<Integer>> channelPresetCount;
|
||||
|
||||
|
||||
|
||||
public Channel<Integer> getCommand() {
|
||||
return command;
|
||||
}
|
||||
public Channel<Integer> getMode() {
|
||||
return mode;
|
||||
}
|
||||
public List<Channel<Boolean>> getChannelDescription() {
|
||||
return channelDescription;
|
||||
}
|
||||
public List<Channel<Boolean>> getChannelGate() {
|
||||
return channelGate;
|
||||
}
|
||||
public List<Channel<Integer>> getChannelPresetCount() {
|
||||
return channelPresetCount;
|
||||
}
|
||||
}
|
||||
43
src/main/java/ch/psi/fda/core/loops/cr/TimestampedValue.java
Normal file
43
src/main/java/ch/psi/fda/core/loops/cr/TimestampedValue.java
Normal file
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.core.loops.cr;
|
||||
|
||||
public class TimestampedValue {
|
||||
|
||||
private final Double value;
|
||||
private final long timestamp;
|
||||
private final long nanosecondsOffset;
|
||||
|
||||
public TimestampedValue(Double value, long timestamp, long nanosecondsOffset){
|
||||
this.value = value;
|
||||
this.timestamp = timestamp;
|
||||
this.nanosecondsOffset = nanosecondsOffset;
|
||||
}
|
||||
|
||||
public Double getValue() {
|
||||
return value;
|
||||
}
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
public long getNanosecondsOffset() {
|
||||
return nanosecondsOffset;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.core.loops.cr;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* Comparator for comparint 2 timestamped values
|
||||
*/
|
||||
public class TimestampedValueComparator implements Comparator<TimestampedValue> {
|
||||
|
||||
@Override
|
||||
public int compare(TimestampedValue o1, TimestampedValue o2) {
|
||||
if (o1.getTimestamp() < o2.getTimestamp()) {
|
||||
return -1;
|
||||
} else if (o1.getTimestamp() > o2.getTimestamp()) {
|
||||
return 1;
|
||||
} else if (o1.getTimestamp() == o2.getTimestamp()) {
|
||||
if (o1.getNanosecondsOffset() < o2.getNanosecondsOffset()) {
|
||||
return -1;
|
||||
} else if (o1.getNanosecondsOffset() > o2.getNanosecondsOffset()) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,283 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.core.manipulator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineManager;
|
||||
import javax.script.ScriptException;
|
||||
|
||||
import ch.psi.fda.core.Manipulation;
|
||||
import ch.psi.fda.core.scripting.JythonParameterMapping;
|
||||
import ch.psi.fda.core.scripting.JythonParameterMappingChannel;
|
||||
import ch.psi.fda.core.scripting.JythonParameterMappingGlobalVariable;
|
||||
import ch.psi.fda.core.scripting.JythonParameterMappingID;
|
||||
import ch.psi.fda.messages.DataMessage;
|
||||
import ch.psi.fda.messages.Metadata;
|
||||
|
||||
public class JythonManipulation implements Manipulation{
|
||||
|
||||
private static Logger logger = Logger.getLogger(JythonManipulation.class.getName());
|
||||
|
||||
/**
|
||||
* Pattern of the entry function of the jython script
|
||||
*/
|
||||
private static final String entryFunction = "process";
|
||||
private static final String entryFunctionPattern = "def "+entryFunction+"\\((.*)\\):";
|
||||
|
||||
/**
|
||||
* Code of the manipulation. The script need to implement a function of the type:
|
||||
* <code>
|
||||
* def process():
|
||||
* # ... your code
|
||||
* return value
|
||||
* </code>
|
||||
*
|
||||
* The number of parameters of the script are not limited. So <code>process(a)</code> is valid
|
||||
* as well as <code>process(a,b,c,d,e,f)</code>.
|
||||
* However for each parameter there need to be a mapping inside the mapping list! If there is no
|
||||
* mapping inside the Manipulator evaluating this Manipulation will throw an Exception.
|
||||
*/
|
||||
private final String script;
|
||||
|
||||
/**
|
||||
* Id of the resulting component
|
||||
*/
|
||||
private final String id;
|
||||
|
||||
/**
|
||||
* Mapping of components to manipulation code variables
|
||||
*/
|
||||
private final List<JythonParameterMapping> mapping;
|
||||
|
||||
private final boolean returnArray;
|
||||
|
||||
/**
|
||||
* Script engine of the manipulator
|
||||
*/
|
||||
private ScriptEngine engine;
|
||||
/**
|
||||
* Component index of the script parameter. The sequence of the indexes in this array correspond to the script
|
||||
* parameter position, i.e. the first index corresponds to the first parameter.
|
||||
*/
|
||||
// private Integer[] parameterIndex;
|
||||
private List<String> parameterIds = new ArrayList<>();
|
||||
/**
|
||||
* Parameter array of the entry function
|
||||
*/
|
||||
private String[] parameter;
|
||||
|
||||
/**
|
||||
* Jython entry call
|
||||
*/
|
||||
private String jythonCall;
|
||||
|
||||
|
||||
private Map<String,Object> gvariables = new HashMap<String,Object>();
|
||||
|
||||
|
||||
public JythonManipulation(String id, String script, List<JythonParameterMapping> mapping){
|
||||
this(id, script, mapping, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param id
|
||||
* @param script
|
||||
* @param mapping
|
||||
*/
|
||||
public JythonManipulation(String id, String script, List<JythonParameterMapping> mapping, boolean returnArray){
|
||||
this.id = id;
|
||||
this.script = script;
|
||||
this.mapping = mapping;
|
||||
this.returnArray = returnArray;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the script
|
||||
*/
|
||||
public String getScript() {
|
||||
return script;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the mapping
|
||||
*/
|
||||
public List<JythonParameterMapping> getMapping() {
|
||||
return mapping;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void initialize(List<Metadata> metadata){
|
||||
|
||||
// Workaround for Jython memory leak
|
||||
// http://blog.hillbrecht.de/2009/07/11/jython-memory-leakout-of-memory-problem/
|
||||
System.setProperty("python.options.internalTablesImpl","weak");
|
||||
|
||||
// Create new script engine
|
||||
this.engine = new ScriptEngineManager().getEngineByName("python");
|
||||
|
||||
// Determine script entry function and the function parameters
|
||||
Pattern pattern = Pattern.compile(entryFunctionPattern);
|
||||
Matcher matcher = pattern.matcher(this.script);
|
||||
if(matcher.find() && matcher.groupCount()==1){
|
||||
if(!matcher.group(1).trim().equals("")){
|
||||
logger.finest("Entry function '"+entryFunctionPattern+"' found - Identified parameters: "+matcher.group(1));
|
||||
parameter = matcher.group(1).split(" *, *");
|
||||
}
|
||||
else{
|
||||
parameter = new String[0];
|
||||
}
|
||||
}
|
||||
else{
|
||||
throw new IllegalArgumentException("Cannot determine entry function: "+entryFunctionPattern);
|
||||
}
|
||||
|
||||
// Load manipulation script
|
||||
try {
|
||||
engine.eval(this.script);
|
||||
} catch (ScriptException e) {
|
||||
throw new RuntimeException("Unable to load manipulation script", e);
|
||||
}
|
||||
|
||||
// Determine component index of the needed parameters
|
||||
// If the component index of the parameter cannot be determined an IllegalArgumentException is thrown
|
||||
// parameterIndex = new Integer[parameter.length];
|
||||
parameterIds = new ArrayList<>();
|
||||
for(int i=0;i<parameter.length; i++){
|
||||
String p = parameter[i];
|
||||
p = p.trim();
|
||||
boolean found = false;
|
||||
|
||||
for(JythonParameterMapping jpm: this.mapping){
|
||||
if(jpm.getVariable().equals(p)){
|
||||
if(jpm instanceof JythonParameterMappingID){
|
||||
JythonParameterMappingID pm = (JythonParameterMappingID)jpm;
|
||||
// Mapping for parameter found, determine index of the corresponding component
|
||||
// parameterIndex[i] = metadata.getIndex(pm.getRefid());
|
||||
parameterIds.add(pm.getRefid());
|
||||
}
|
||||
else if (jpm instanceof JythonParameterMappingChannel){
|
||||
JythonParameterMappingChannel<?> pm = (JythonParameterMappingChannel<?>) jpm;
|
||||
// parameterIndex[i] = null;
|
||||
parameterIds.add(null);
|
||||
engine.put(pm.getVariable(), pm.getChannel());
|
||||
}
|
||||
else if (jpm instanceof JythonParameterMappingGlobalVariable){
|
||||
JythonParameterMappingGlobalVariable pm = (JythonParameterMappingGlobalVariable)jpm;
|
||||
parameterIds.add(null);
|
||||
// parameterIndex[i] = null;
|
||||
|
||||
engine.put(pm.getVariable(), pm.getGlobalVariable());
|
||||
}
|
||||
found=true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// If there is no mapping nothing can be found
|
||||
// If there are mappings everything need to be found
|
||||
if(!found){
|
||||
throw new IllegalArgumentException("No mapping compontent found for parameter "+p);
|
||||
}
|
||||
}
|
||||
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
buffer.append(entryFunction);
|
||||
buffer.append("(");
|
||||
|
||||
for(String p: parameter){
|
||||
buffer.append(p);
|
||||
buffer.append(",");
|
||||
}
|
||||
|
||||
buffer.setCharAt(buffer.length()-1, ')');
|
||||
|
||||
jythonCall = buffer.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object execute(DataMessage message){
|
||||
|
||||
// Set global variables - WORKAROUND gvariables
|
||||
// This block is not in initialization as we want to assure that all invocations
|
||||
// of this manipulation will get the same value (i.e. to prevent inconsistent behaviour
|
||||
// if variable was changed during an execution of the manipulation)
|
||||
for(String k: gvariables.keySet()){
|
||||
engine.put(k, gvariables.get(k));
|
||||
}
|
||||
|
||||
// Manipulate data
|
||||
for(int i=0;i<parameterIds.size();i++){
|
||||
if(parameterIds.get(i) != null){
|
||||
engine.put(parameter[i], message.getData(parameterIds.get(i)));
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if(returnArray){
|
||||
return((double[]) engine.eval(jythonCall));
|
||||
}
|
||||
else{
|
||||
// Due to the typeless nature of Python an Integer of Float/Double might be returned from the
|
||||
// Python code (e.g. depending on the factors in a calculation)
|
||||
// Always return the return value to a double. If this cannot be done return NaN
|
||||
Object r = engine.eval(jythonCall);
|
||||
if(r instanceof Double){
|
||||
return((Double) r);
|
||||
}
|
||||
else if( r instanceof Integer){
|
||||
return(((Integer)r).doubleValue());
|
||||
}
|
||||
else{
|
||||
// return Double.NaN;
|
||||
return r;
|
||||
}
|
||||
}
|
||||
} catch (ScriptException e) {
|
||||
// throw new RuntimeException("Data manipulaton [id: "+id+"] failed while executing the manipulation script",e);
|
||||
logger.log(Level.WARNING, "Data manipulaton [id: "+id+"] failed while executing the manipulation script");
|
||||
return Double.NaN;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Workaround to put variables into the jython engine.
|
||||
* @param name
|
||||
* @param value
|
||||
*/
|
||||
public void setVariable(String name, Object value){
|
||||
gvariables.put(name, value);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.core.scripting;
|
||||
|
||||
public class JythonGlobalVariable {
|
||||
private String name;
|
||||
private double value;
|
||||
|
||||
/**
|
||||
* @return the name
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
/**
|
||||
* @param name the name to set
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
/**
|
||||
* @return the value
|
||||
*/
|
||||
public double getValue() {
|
||||
return value;
|
||||
}
|
||||
/**
|
||||
* @param value the value to set
|
||||
*/
|
||||
public void setValue(double value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.core.scripting;
|
||||
|
||||
/**
|
||||
* Mapping of a parameter to something
|
||||
*/
|
||||
public abstract class JythonParameterMapping {
|
||||
|
||||
/**
|
||||
* Variable name inside the script
|
||||
*/
|
||||
private String variable;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
* @param variable
|
||||
*/
|
||||
public JythonParameterMapping(String variable){
|
||||
this.variable = variable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the variable
|
||||
*/
|
||||
public String getVariable() {
|
||||
return variable;
|
||||
}
|
||||
/**
|
||||
* @param variable the variable to set
|
||||
*/
|
||||
public void setVariable(String variable) {
|
||||
this.variable = variable;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.core.scripting;
|
||||
|
||||
import ch.psi.jcae.Channel;
|
||||
|
||||
|
||||
/**
|
||||
* Mapping of a script parameter to a channel bean.
|
||||
*/
|
||||
public class JythonParameterMappingChannel<T> extends JythonParameterMapping {
|
||||
|
||||
private Channel<T> channel;
|
||||
|
||||
public JythonParameterMappingChannel(String variable, Channel<T> channel){
|
||||
super(variable);
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
public Channel<T> getChannel() {
|
||||
return channel;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.core.scripting;
|
||||
|
||||
|
||||
/**
|
||||
* Mapping of a script parameter to a component via the Id.
|
||||
*/
|
||||
public class JythonParameterMappingGlobalVariable extends JythonParameterMapping {
|
||||
|
||||
private JythonGlobalVariable globalVariable;
|
||||
|
||||
/**
|
||||
* Constuctor
|
||||
* @param variable
|
||||
* @param globalVariable
|
||||
*/
|
||||
public JythonParameterMappingGlobalVariable(String variable, JythonGlobalVariable globalVariable){
|
||||
super(variable);
|
||||
this.globalVariable = globalVariable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the globalVariable
|
||||
*/
|
||||
public JythonGlobalVariable getGlobalVariable() {
|
||||
return globalVariable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param globalVariable the globalVariable to set
|
||||
*/
|
||||
public void setGlobalVariable(JythonGlobalVariable globalVariable) {
|
||||
this.globalVariable = globalVariable;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.core.scripting;
|
||||
|
||||
|
||||
/**
|
||||
* Mapping of a script parameter to a component via the Id.
|
||||
*/
|
||||
public class JythonParameterMappingID extends JythonParameterMapping {
|
||||
|
||||
/**
|
||||
* Id of the component to map to this variable
|
||||
*/
|
||||
private String refid;
|
||||
|
||||
/**
|
||||
* Constructor accepting varible/id pair
|
||||
* @param variable
|
||||
* @param refid
|
||||
*/
|
||||
public JythonParameterMappingID(String variable, String refid){
|
||||
super(variable);
|
||||
this.refid = refid;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the refid
|
||||
*/
|
||||
public String getRefid() {
|
||||
return refid;
|
||||
}
|
||||
/**
|
||||
* @param refid the refid to set
|
||||
*/
|
||||
public void setRefid(String refid) {
|
||||
this.refid = refid;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.core.sensors;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import ch.psi.fda.core.Sensor;
|
||||
import ch.psi.jcae.Channel;
|
||||
import ch.psi.jcae.ChannelException;
|
||||
|
||||
/**
|
||||
* Channel Access sensor capable of reading a channel access channel
|
||||
*/
|
||||
public class ChannelAccessSensor<T> implements Sensor {
|
||||
|
||||
private static Logger logger = Logger.getLogger(ChannelAccessSensor.class.getName());
|
||||
|
||||
private Channel<T> channel;
|
||||
private final String id;
|
||||
private boolean failOnSensorError = true;
|
||||
|
||||
public ChannelAccessSensor(String id, Channel<T> channel){
|
||||
this.id = id;
|
||||
this.channel = channel;
|
||||
}
|
||||
|
||||
public ChannelAccessSensor(String id, Channel<T> channel, boolean failOnSensorError){
|
||||
this.id = id;
|
||||
this.channel = channel;
|
||||
this.failOnSensorError = failOnSensorError;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object read() throws InterruptedException {
|
||||
logger.finest("Read sensor "+channel.getName());
|
||||
|
||||
T v;
|
||||
try {
|
||||
v = channel.getValue();
|
||||
} catch (TimeoutException | ChannelException | ExecutionException e) {
|
||||
if(failOnSensorError){
|
||||
throw new RuntimeException("Unable to get value from channel [name:"+channel.getName()+"]",e);
|
||||
}
|
||||
v = null;
|
||||
}
|
||||
return(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
56
src/main/java/ch/psi/fda/core/sensors/ComplexSensor.java
Normal file
56
src/main/java/ch/psi/fda/core/sensors/ComplexSensor.java
Normal file
@@ -0,0 +1,56 @@
|
||||
package ch.psi.fda.core.sensors;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
import ch.psi.fda.core.Action;
|
||||
import ch.psi.fda.core.Sensor;
|
||||
/**
|
||||
* Complex sensor that complements an other sensor with pre and post actions.
|
||||
* Before reading out the complemented sensor the pre actions are executed. After the
|
||||
* readout the post actions.
|
||||
*/
|
||||
public class ComplexSensor implements Sensor {
|
||||
|
||||
private static Logger logger = Logger.getLogger(ComplexSensor.class.getName());
|
||||
|
||||
private String id;
|
||||
private final Sensor sensor;
|
||||
private final List<Action> preActions;
|
||||
private final List<Action> postActions;
|
||||
|
||||
public ComplexSensor(String id, Sensor sensor){
|
||||
this.id = id;
|
||||
this.sensor = sensor;
|
||||
this.preActions = new ArrayList<Action>();
|
||||
this.postActions = new ArrayList<Action>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object read() throws InterruptedException {
|
||||
logger.finest("Execute pre actions");
|
||||
for(Action action: preActions){
|
||||
action.execute();
|
||||
}
|
||||
|
||||
Object value = sensor.read();
|
||||
|
||||
logger.finest("Execute post actions");
|
||||
for(Action action: postActions){
|
||||
action.execute();
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public List<Action> getPreActions() {
|
||||
return preActions;
|
||||
}
|
||||
public List<Action> getPostActions() {
|
||||
return postActions;
|
||||
}
|
||||
}
|
||||
46
src/main/java/ch/psi/fda/core/sensors/TimestampSensor.java
Normal file
46
src/main/java/ch/psi/fda/core/sensors/TimestampSensor.java
Normal file
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.core.sensors;
|
||||
|
||||
import ch.psi.fda.core.Sensor;
|
||||
|
||||
/**
|
||||
* Sensor to read the current time in milliseconds
|
||||
*/
|
||||
public class TimestampSensor implements Sensor {
|
||||
|
||||
private final String id; // Global id of the sensor
|
||||
|
||||
public TimestampSensor(String id){
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object read() {
|
||||
// Return current time in milliseconds
|
||||
return new Double(System.currentTimeMillis());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
}
|
||||
15
src/main/java/ch/psi/fda/edescriptor/EDescriptor.java
Normal file
15
src/main/java/ch/psi/fda/edescriptor/EDescriptor.java
Normal file
@@ -0,0 +1,15 @@
|
||||
package ch.psi.fda.edescriptor;
|
||||
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import javax.xml.bind.annotation.XmlTransient;
|
||||
|
||||
/**
|
||||
* Execution container descriptor
|
||||
*/
|
||||
@XmlRootElement
|
||||
@XmlTransient
|
||||
public interface EDescriptor {
|
||||
|
||||
|
||||
// TODO Need to contain what need to be streamed
|
||||
}
|
||||
198
src/main/java/ch/psi/fda/fdaq/Fdaq.java
Normal file
198
src/main/java/ch/psi/fda/fdaq/Fdaq.java
Normal file
@@ -0,0 +1,198 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2013 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but without any
|
||||
* warranty; without even the implied warranty of merchantability or fitness for
|
||||
* a particular purpose. See the GNU Lesser General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package ch.psi.fda.fdaq;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.google.common.eventbus.EventBus;
|
||||
|
||||
import ch.psi.fda.messages.DataMessage;
|
||||
import ch.psi.fda.messages.EndOfStreamMessage;
|
||||
import ch.psi.fda.messages.Metadata;
|
||||
|
||||
public class Fdaq {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(Fdaq.class.getName());
|
||||
|
||||
private volatile boolean stopAcquisition = false;
|
||||
private volatile boolean running = false;
|
||||
|
||||
private final EventBus bus;
|
||||
|
||||
private FdaqConfiguration configuration;
|
||||
private final int numberOfElements = Integer.MAX_VALUE/2;
|
||||
|
||||
public Fdaq(EventBus bus, FdaqConfiguration configuration){
|
||||
this.bus = bus;
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquire data from the fdaq box
|
||||
* @param hostname
|
||||
* @param port
|
||||
* @param numberOfElements
|
||||
*/
|
||||
public void acquire() {
|
||||
running = true; // potential threading problem
|
||||
|
||||
final List<Metadata> metadata = new ArrayList<>();
|
||||
metadata.add(new Metadata("counter"));
|
||||
metadata.add(new Metadata("ain1"));
|
||||
metadata.add(new Metadata("ain2"));
|
||||
metadata.add(new Metadata("ain3"));
|
||||
metadata.add(new Metadata("ain4"));
|
||||
metadata.add(new Metadata("enc1"));
|
||||
|
||||
Socket echoSocket = null;
|
||||
DataOutputStream out = null;
|
||||
DataInputStream in = null;
|
||||
|
||||
boolean first = true;
|
||||
while(running){
|
||||
|
||||
try {
|
||||
|
||||
|
||||
stopAcquisition = false;
|
||||
echoSocket = new Socket(configuration.getHostname(), configuration.getPort());
|
||||
out = new DataOutputStream(echoSocket.getOutputStream());
|
||||
in = new DataInputStream(new BufferedInputStream(echoSocket.getInputStream()));
|
||||
|
||||
ByteBuffer bytebuffer = ByteBuffer.allocate(3 * 4); // 3 times Integer
|
||||
bytebuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
bytebuffer.putInt(26); // Function number (index of pointer to function)
|
||||
bytebuffer.putInt(numberOfElements);
|
||||
|
||||
if(first){
|
||||
bytebuffer.putInt(1); // Reset FIFO flag - The first time there need to be a 1 in int to reset the fifo buffer on the box
|
||||
first=false;
|
||||
}
|
||||
else{
|
||||
bytebuffer.putInt(0);
|
||||
}
|
||||
|
||||
out.write(bytebuffer.array());
|
||||
out.flush();
|
||||
|
||||
for (int t = 0; t < numberOfElements; t++) {
|
||||
byte[] cbuffer = new byte[4*4]; // 4 times Integers a 4 bytes
|
||||
|
||||
try{
|
||||
in.readFully(cbuffer);
|
||||
}
|
||||
catch(EOFException e){
|
||||
logger.info("End of Stream");
|
||||
break;
|
||||
}
|
||||
|
||||
if(t<1028){
|
||||
// The first 1028 are wrong (not really wrong but there are repeated values/holes)
|
||||
// It has to do with the fact that we asynchronously reset the FIFO.
|
||||
// This is independent of the frequency
|
||||
continue;
|
||||
}
|
||||
|
||||
ByteBuffer buffer = ByteBuffer.wrap(cbuffer);
|
||||
buffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
int a = buffer.getInt();
|
||||
int b = buffer.getInt();
|
||||
int b1 = b & 0xffff;
|
||||
int b2 = (b >> 16) & 0xffff;
|
||||
int c = buffer.getInt();
|
||||
int c1 = c & 0xffff;
|
||||
int c2 = (c >> 16) & 0xffff;
|
||||
int d = buffer.getInt();
|
||||
|
||||
DataMessage message = new DataMessage(metadata);
|
||||
message.getData().add(a);
|
||||
message.getData().add(b1);
|
||||
message.getData().add(b2);
|
||||
message.getData().add(c1);
|
||||
message.getData().add(c2);
|
||||
message.getData().add(d);
|
||||
bus.post(message);
|
||||
}
|
||||
|
||||
logger.info("Done ...");
|
||||
|
||||
} catch (IOException e) {
|
||||
// Ignore potential exceptions if stop was triggered before all messages were retrieved
|
||||
if (!stopAcquisition) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
} finally {
|
||||
try {
|
||||
if (out!=null)
|
||||
out.close();
|
||||
if (in!=null)
|
||||
in.close();
|
||||
if (echoSocket!=null)
|
||||
echoSocket.close();
|
||||
} catch (IOException e) {
|
||||
// Ignore because not relevant at this stage
|
||||
}
|
||||
running = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bus.post(new EndOfStreamMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sending termination command to fdaq box
|
||||
*/
|
||||
public void stop() {
|
||||
try {
|
||||
running=false;
|
||||
stopAcquisition = true;
|
||||
Socket echoSocket = new Socket(configuration.getHostname(), configuration.getKillPort());
|
||||
DataOutputStream out = new DataOutputStream(echoSocket.getOutputStream());
|
||||
|
||||
ByteBuffer bytebuffer = ByteBuffer.allocate(1 * 4); // 2
|
||||
// times
|
||||
// Integers
|
||||
bytebuffer.order(ByteOrder.LITTLE_ENDIAN);
|
||||
bytebuffer.putInt(666);
|
||||
out.write(bytebuffer.array());
|
||||
out.flush();
|
||||
|
||||
out.close();
|
||||
echoSocket.close();
|
||||
} catch (IOException e) {
|
||||
logger.log(Level.SEVERE, "", e);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isRunning() {
|
||||
return running;
|
||||
}
|
||||
}
|
||||
78
src/main/java/ch/psi/fda/fdaq/FdaqConfiguration.java
Normal file
78
src/main/java/ch/psi/fda/fdaq/FdaqConfiguration.java
Normal file
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2014 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but without any
|
||||
* warranty; without even the implied warranty of merchantability or fitness for
|
||||
* a particular purpose. See the GNU Lesser General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package ch.psi.fda.fdaq;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.util.Properties;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
public class FdaqConfiguration {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(FdaqConfiguration.class.getName());
|
||||
private final static String FDAQ_CONFIG = "ch.psi.fda.fdaq.config.file";
|
||||
|
||||
private String hostname = "mchip015.psi.ch";
|
||||
private int port = 2233;
|
||||
private int killPort = 2234;
|
||||
|
||||
public FdaqConfiguration(){
|
||||
String config = System.getProperty(FDAQ_CONFIG);
|
||||
|
||||
if(config != null){
|
||||
loadFile(new File(config));
|
||||
}
|
||||
else{
|
||||
logger.warning("No configuration file specified via -D"+FDAQ_CONFIG+"=... - using defaults");
|
||||
}
|
||||
}
|
||||
|
||||
public void loadFile(File file) {
|
||||
Properties properties = new Properties();
|
||||
try {
|
||||
properties.load(new FileReader(file));
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Cannot read file "+file, e);
|
||||
}
|
||||
|
||||
hostname = properties.getProperty(FdaqConfiguration.class.getPackage().getName()+".hostname", hostname);
|
||||
port = Integer.parseInt(properties.getProperty(FdaqConfiguration.class.getPackage().getName()+".port", port+""));
|
||||
killPort = Integer.parseInt(properties.getProperty(FdaqConfiguration.class.getPackage().getName()+".killPort", killPort+""));
|
||||
}
|
||||
|
||||
public String getHostname() {
|
||||
return hostname;
|
||||
}
|
||||
public void setHostname(String hostname) {
|
||||
this.hostname = hostname;
|
||||
}
|
||||
public int getPort() {
|
||||
return port;
|
||||
}
|
||||
public void setPort(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
public int getKillPort() {
|
||||
return killPort;
|
||||
}
|
||||
public void setKillPort(int killPort) {
|
||||
this.killPort = killPort;
|
||||
}
|
||||
}
|
||||
80
src/main/java/ch/psi/fda/fdaq/FdaqEContainer.java
Normal file
80
src/main/java/ch/psi/fda/fdaq/FdaqEContainer.java
Normal file
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2014 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but without any
|
||||
* warranty; without even the implied warranty of merchantability or fitness for
|
||||
* a particular purpose. See the GNU Lesser General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package ch.psi.fda.fdaq;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import ch.psi.fda.EContainer;
|
||||
import ch.psi.fda.fdaq.FdaqConfiguration;
|
||||
import ch.psi.fda.serializer.SerializerTXT;
|
||||
|
||||
import com.google.common.eventbus.EventBus;
|
||||
|
||||
public class FdaqEContainer implements EContainer {
|
||||
|
||||
private Fdaq fdaq;
|
||||
private EventBus bus;
|
||||
|
||||
private FdaqEDescriptor edescriptor;
|
||||
|
||||
@Inject
|
||||
public FdaqEContainer(FdaqEDescriptor edescriptor, EventBus ebus){
|
||||
this.bus = ebus;
|
||||
this.edescriptor = edescriptor;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void initialize() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
if(fdaq!=null && fdaq.isRunning()){
|
||||
throw new IllegalStateException("FDAQ is already running");
|
||||
}
|
||||
|
||||
fdaq = new Fdaq(bus, new FdaqConfiguration());
|
||||
|
||||
File file = new File(edescriptor.getFileName());
|
||||
file.getParentFile().mkdirs(); // Create data base directory
|
||||
|
||||
SerializerTXT serializer = new SerializerTXT(file);
|
||||
serializer.setShowDimensionHeader(false);
|
||||
bus.register(serializer);
|
||||
|
||||
fdaq.acquire();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void abort() {
|
||||
fdaq.stop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return fdaq.isRunning();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
}
|
||||
}
|
||||
20
src/main/java/ch/psi/fda/fdaq/FdaqEContainerFactory.java
Normal file
20
src/main/java/ch/psi/fda/fdaq/FdaqEContainerFactory.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package ch.psi.fda.fdaq;
|
||||
|
||||
import com.google.common.eventbus.EventBus;
|
||||
|
||||
import ch.psi.fda.EContainer;
|
||||
import ch.psi.fda.EContainerFactory;
|
||||
import ch.psi.fda.edescriptor.EDescriptor;
|
||||
|
||||
public class FdaqEContainerFactory implements EContainerFactory {
|
||||
|
||||
@Override
|
||||
public boolean supportsEDescriptor(EDescriptor descriptor) {
|
||||
return descriptor instanceof FdaqEDescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EContainer getEContainer(EDescriptor descriptor, EventBus bus) {
|
||||
return new FdaqEContainer((FdaqEDescriptor) descriptor, bus);
|
||||
}
|
||||
}
|
||||
18
src/main/java/ch/psi/fda/fdaq/FdaqEDescriptor.java
Normal file
18
src/main/java/ch/psi/fda/fdaq/FdaqEDescriptor.java
Normal file
@@ -0,0 +1,18 @@
|
||||
package ch.psi.fda.fdaq;
|
||||
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
import ch.psi.fda.edescriptor.EDescriptor;
|
||||
|
||||
@XmlRootElement(name="fdaq")
|
||||
public class FdaqEDescriptor implements EDescriptor {
|
||||
|
||||
private String fileName;
|
||||
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
public void setFileName(String fileName) {
|
||||
this.fileName = fileName;
|
||||
}
|
||||
}
|
||||
44
src/main/java/ch/psi/fda/fdaq/FdaqEDescriptorProvider.java
Normal file
44
src/main/java/ch/psi/fda/fdaq/FdaqEDescriptorProvider.java
Normal file
@@ -0,0 +1,44 @@
|
||||
package ch.psi.fda.fdaq;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.JAXBException;
|
||||
import javax.xml.bind.Unmarshaller;
|
||||
|
||||
import ch.psi.fda.DescriptorProvider;
|
||||
import ch.psi.fda.edescriptor.EDescriptor;
|
||||
import ch.psi.fda.vdescriptor.VDescriptor;
|
||||
|
||||
public class FdaqEDescriptorProvider implements DescriptorProvider {
|
||||
|
||||
private EDescriptor edescriptor;
|
||||
|
||||
@Override
|
||||
public void load(File... files) {
|
||||
try {
|
||||
JAXBContext context = JAXBContext.newInstance(FdaqEDescriptor.class);
|
||||
Unmarshaller u = context.createUnmarshaller();
|
||||
edescriptor = (EDescriptor) u.unmarshal(files[0]);
|
||||
} catch (JAXBException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public EDescriptor getEDescriptor() {
|
||||
return edescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public VDescriptor getVDescriptor() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getEDescriptorClass() {
|
||||
return FdaqEDescriptor.class;
|
||||
}
|
||||
|
||||
}
|
||||
83
src/main/java/ch/psi/fda/fdaq/ui/FdaqMain.java
Normal file
83
src/main/java/ch/psi/fda/fdaq/ui/FdaqMain.java
Normal file
@@ -0,0 +1,83 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2013 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU Lesser General Public License as published by the Free
|
||||
* Software Foundation, either version 3 of the License, or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but without any
|
||||
* warranty; without even the implied warranty of merchantability or fitness for
|
||||
* a particular purpose. See the GNU Lesser General Public License for more
|
||||
* details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package ch.psi.fda.fdaq.ui;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import sun.misc.Signal;
|
||||
import sun.misc.SignalHandler;
|
||||
import ch.psi.fda.fdaq.Fdaq;
|
||||
import ch.psi.fda.fdaq.FdaqConfiguration;
|
||||
import ch.psi.fda.serializer.SerializerTXT;
|
||||
|
||||
import com.google.common.eventbus.AsyncEventBus;
|
||||
import com.google.common.eventbus.EventBus;
|
||||
|
||||
@SuppressWarnings("restriction")
|
||||
public class FdaqMain {
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
if(args.length != 1){
|
||||
System.err.println("Usage:\n fdaq <file>");
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
File file = new File(args[0]);
|
||||
|
||||
if(file.exists()){
|
||||
System.err.println("File "+file.getName()+ " already exists.");
|
||||
System.exit(-1);
|
||||
}
|
||||
|
||||
FdaqConfiguration configuration = new FdaqConfiguration();
|
||||
|
||||
EventBus bus = new AsyncEventBus(Executors.newSingleThreadExecutor());
|
||||
final Fdaq fdaq = new Fdaq(bus, configuration);
|
||||
|
||||
Signal.handle(new Signal("INT"), new SignalHandler() {
|
||||
int count = 0;
|
||||
public void handle(Signal sig) {
|
||||
if(count < 1){
|
||||
fdaq.stop();
|
||||
count++;
|
||||
return;
|
||||
}
|
||||
System.exit(0);
|
||||
}
|
||||
});
|
||||
|
||||
SerializerTXT serializer = new SerializerTXT(file, false);
|
||||
serializer.setShowDimensionHeader(false);
|
||||
bus.register(serializer);
|
||||
|
||||
// This stop ensures that the data server is in a good shape (i.e. gets restarted)
|
||||
// We need to wait a certain amount of time to have the server restarted.
|
||||
fdaq.stop();
|
||||
try {
|
||||
Thread.sleep(1000); // TODO check whether this sleep is really necessary
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
fdaq.acquire();
|
||||
}
|
||||
|
||||
}
|
||||
29
src/main/java/ch/psi/fda/messages/ControlMessage.java
Normal file
29
src/main/java/ch/psi/fda/messages/ControlMessage.java
Normal file
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.messages;
|
||||
|
||||
|
||||
/**
|
||||
* A control message that is not holding any data but
|
||||
* control information (like end of loop, etc.)
|
||||
*/
|
||||
public abstract class ControlMessage extends Message{
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
||||
105
src/main/java/ch/psi/fda/messages/DataMessage.java
Normal file
105
src/main/java/ch/psi/fda/messages/DataMessage.java
Normal file
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.messages;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Message holding data
|
||||
*/
|
||||
public class DataMessage extends Message{
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final List<Object> data;
|
||||
private List<Metadata> metadata;
|
||||
|
||||
// public DataMessage(){
|
||||
// this.data = new ArrayList<Object>();
|
||||
// this.metadata = new ArrayList<>();
|
||||
// }
|
||||
//
|
||||
public DataMessage(List<Metadata> metadata){
|
||||
this.data = new ArrayList<Object>();
|
||||
this.metadata = metadata;
|
||||
}
|
||||
|
||||
public List<Object> getData(){
|
||||
return(data);
|
||||
}
|
||||
public List<Metadata> getMetadata(){
|
||||
return metadata;
|
||||
}
|
||||
public void setMetadata(List<Metadata> metadata){
|
||||
this.metadata = metadata;
|
||||
}
|
||||
|
||||
// Utility functions
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T getData(String id){
|
||||
int i=0;
|
||||
for(Metadata m: metadata){
|
||||
if(m.getId().equals(id)){
|
||||
return (T) data.get(i);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
throw new IllegalArgumentException("No data found for id: "+id);
|
||||
}
|
||||
|
||||
public Metadata getMetadata(String id){
|
||||
for(Metadata m: metadata){
|
||||
if(m.getId().equals(id)){
|
||||
return m;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("No data found for id: "+id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuffer b = new StringBuffer();
|
||||
b.append("Message [ ");
|
||||
for (Object o : data) {
|
||||
if (o.getClass().isArray()) {
|
||||
// If the array object is of type double[] display its content
|
||||
if (o instanceof double[]) {
|
||||
double[] oa = (double[]) o;
|
||||
b.append("[ ");
|
||||
for (double o1 : oa) {
|
||||
b.append(o1);
|
||||
b.append(" ");
|
||||
}
|
||||
b.append("]");
|
||||
} else {
|
||||
b.append(o.toString());
|
||||
}
|
||||
} else {
|
||||
b.append(o);
|
||||
}
|
||||
|
||||
b.append(" ");
|
||||
}
|
||||
b.append("]");
|
||||
return b.toString();
|
||||
}
|
||||
}
|
||||
52
src/main/java/ch/psi/fda/messages/EndOfStreamMessage.java
Normal file
52
src/main/java/ch/psi/fda/messages/EndOfStreamMessage.java
Normal file
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.messages;
|
||||
|
||||
/**
|
||||
* Message that is send at the end of the action loop inside an ActionLoop implementation
|
||||
* of just to indicate that a particular stream has finished
|
||||
*/
|
||||
public class EndOfStreamMessage extends ControlMessage {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Intersect flag - flag to indicate that stream should be intersected
|
||||
* after this message.
|
||||
*/
|
||||
private final boolean iflag;
|
||||
|
||||
public EndOfStreamMessage(){
|
||||
this(false);
|
||||
}
|
||||
|
||||
public EndOfStreamMessage(boolean iflag){
|
||||
this.iflag = iflag;
|
||||
}
|
||||
|
||||
public boolean isIflag(){
|
||||
return(iflag);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Message[ c message: end of stream ]";
|
||||
}
|
||||
}
|
||||
29
src/main/java/ch/psi/fda/messages/Message.java
Normal file
29
src/main/java/ch/psi/fda/messages/Message.java
Normal file
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.messages;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Message that can be put to the data queue
|
||||
*/
|
||||
public abstract class Message implements Serializable{
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
||||
58
src/main/java/ch/psi/fda/messages/Metadata.java
Normal file
58
src/main/java/ch/psi/fda/messages/Metadata.java
Normal file
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.messages;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Metadata of a component of a message. Each component has a global id.
|
||||
* Optionally the component can also belong to a dimension. However, depending on the
|
||||
* view the number of the dimension might vary. Therefore the dimension number
|
||||
* might change during the lifetime of a message (component).
|
||||
*/
|
||||
public class Metadata implements Serializable{
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final String id;
|
||||
private int dimension;
|
||||
|
||||
public Metadata(String id){
|
||||
this.id = id;
|
||||
this.dimension = 0;
|
||||
}
|
||||
|
||||
public Metadata(String id, int dimension){
|
||||
this.id = id;
|
||||
this.dimension = dimension;
|
||||
}
|
||||
|
||||
public void setDimension(int dimension){
|
||||
this.dimension = dimension;
|
||||
}
|
||||
|
||||
public int getDimension() {
|
||||
return dimension;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.messages;
|
||||
|
||||
/**
|
||||
* Message that is send at the end of the action loop inside an ActionLoop implementation
|
||||
*/
|
||||
public class StreamDelimiterMessage extends ControlMessage{
|
||||
private static final long serialVersionUID = 1L;
|
||||
/**
|
||||
* Number of the dimension this delimiter belongs to.
|
||||
*/
|
||||
private final int number;
|
||||
|
||||
/**
|
||||
* Intersect flag - flag to indicate that stream should be intersected
|
||||
* after this message.
|
||||
*/
|
||||
private final boolean iflag;
|
||||
|
||||
/**
|
||||
* @param number Number of the dimension this delimiter belongs to
|
||||
*/
|
||||
public StreamDelimiterMessage(int number){
|
||||
this(number, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param number
|
||||
* @param iflag Flag to indicate that data is grouped
|
||||
*/
|
||||
public StreamDelimiterMessage(int number, boolean iflag){
|
||||
this.number = number;
|
||||
this.iflag = iflag;
|
||||
}
|
||||
|
||||
public int getNumber() {
|
||||
return number;
|
||||
}
|
||||
|
||||
public boolean isIflag(){
|
||||
return iflag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
// TODO Auto-generated method stub
|
||||
return "Message [ c message: delimiter dimension "+number+" ]";
|
||||
}
|
||||
}
|
||||
1
src/main/java/ch/psi/fda/model/.gitignore
vendored
Normal file
1
src/main/java/ch/psi/fda/model/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/v1/
|
||||
100
src/main/java/ch/psi/fda/model/ModelManager.java
Normal file
100
src/main/java/ch/psi/fda/model/ModelManager.java
Normal file
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.model;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.JAXBElement;
|
||||
import javax.xml.bind.JAXBException;
|
||||
import javax.xml.bind.Marshaller;
|
||||
import javax.xml.bind.UnmarshalException;
|
||||
import javax.xml.bind.Unmarshaller;
|
||||
import javax.xml.namespace.QName;
|
||||
import javax.xml.transform.Source;
|
||||
import javax.xml.transform.stream.StreamSource;
|
||||
import javax.xml.validation.Schema;
|
||||
import javax.xml.validation.SchemaFactory;
|
||||
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.SAXParseException;
|
||||
|
||||
import ch.psi.fda.model.v1.Configuration;
|
||||
|
||||
/**
|
||||
* Manage the serialization and deserialization of the FDA data model
|
||||
*/
|
||||
public class ModelManager {
|
||||
|
||||
/**
|
||||
* De-serialize an instance of the FDA data model
|
||||
*
|
||||
* @param file File to deserialize
|
||||
* @throws JAXBException Something went wrong while unmarshalling
|
||||
* @throws SAXException Cannot read model schema file
|
||||
*/
|
||||
public static Configuration unmarshall(File file) throws JAXBException, SAXException {
|
||||
|
||||
JAXBContext context = JAXBContext.newInstance(Configuration.class);
|
||||
Unmarshaller u = context.createUnmarshaller();
|
||||
|
||||
// Validation
|
||||
SchemaFactory sf = SchemaFactory.newInstance(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI);
|
||||
Source s = new StreamSource(Configuration.class.getResourceAsStream("/model-v1.xsd"));
|
||||
Schema schema = sf.newSchema(new Source[]{s}); // Use schema reference provided in XML
|
||||
u.setSchema(schema);
|
||||
|
||||
try{
|
||||
Configuration model = (Configuration) u.unmarshal(new StreamSource(file), Configuration.class).getValue();
|
||||
return (model);
|
||||
}
|
||||
catch(UnmarshalException e){
|
||||
// Check
|
||||
if(e.getLinkedException() instanceof SAXParseException){
|
||||
throw new RuntimeException("Configuration file does not comply to required model specification\nCause: "+e.getLinkedException().getMessage(), e);
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize an instance of the FDA data model
|
||||
*
|
||||
* @param model Model datastructure
|
||||
* @param file File to write the model data into
|
||||
* @throws JAXBException Something went wrong while marshalling model
|
||||
* @throws SAXException Cannot read model schema files
|
||||
*/
|
||||
public static void marshall(Configuration model, File file) throws JAXBException, SAXException{
|
||||
QName qname = new QName("http://www.psi.ch/~ebner/models/scan/1.0", "configuration");
|
||||
|
||||
JAXBContext context = JAXBContext.newInstance(Configuration.class);
|
||||
Marshaller m = context.createMarshaller();
|
||||
m.setProperty("jaxb.formatted.output", true);
|
||||
|
||||
// Validation
|
||||
SchemaFactory sf = SchemaFactory.newInstance(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI);
|
||||
Source s = new StreamSource(Configuration.class.getResourceAsStream("/model-v1.xsd"));
|
||||
Schema schema = sf.newSchema(new Source[]{s}); // Use schema reference provided in XML
|
||||
m.setSchema(schema);
|
||||
|
||||
m.marshal( new JAXBElement<Configuration>(qname, Configuration.class, model ), file);
|
||||
}
|
||||
}
|
||||
224
src/main/java/ch/psi/fda/serializer/SerializerTXT.java
Normal file
224
src/main/java/ch/psi/fda/serializer/SerializerTXT.java
Normal file
@@ -0,0 +1,224 @@
|
||||
/**
|
||||
*
|
||||
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
|
||||
*
|
||||
* This code is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful,
|
||||
* but without any warranty; without even the implied warranty of
|
||||
* merchantability or fitness for a particular purpose. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this code. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package ch.psi.fda.serializer;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
|
||||
import ch.psi.fda.messages.DataMessage;
|
||||
import ch.psi.fda.messages.EndOfStreamMessage;
|
||||
import ch.psi.fda.messages.Message;
|
||||
import ch.psi.fda.messages.Metadata;
|
||||
import ch.psi.fda.messages.StreamDelimiterMessage;
|
||||
|
||||
/**
|
||||
* Serialize data received by a DataQueue
|
||||
*/
|
||||
public class SerializerTXT {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(SerializerTXT.class.getName());
|
||||
|
||||
private File file;
|
||||
private boolean appendSuffix = true;
|
||||
|
||||
private boolean first = true;
|
||||
private File outfile;
|
||||
|
||||
private int icount;
|
||||
private String basename;
|
||||
private String extension;
|
||||
private boolean newfile;
|
||||
private boolean dataInBetween;
|
||||
private BufferedWriter writer;
|
||||
private StringBuffer b;
|
||||
private StringBuffer b1;
|
||||
private boolean showDimensionHeader = true;
|
||||
|
||||
public SerializerTXT(File file) {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param metadata
|
||||
* @param file
|
||||
* @param appendSuffix
|
||||
* Flag whether to append a _0000 suffix after the original file
|
||||
* name
|
||||
*/
|
||||
public SerializerTXT(File file, boolean appendSuffix) {
|
||||
this.file = file;
|
||||
this.appendSuffix = appendSuffix;
|
||||
}
|
||||
|
||||
@Subscribe
|
||||
public void onMessage(Message message) {
|
||||
try {
|
||||
if (first) {
|
||||
first = false;
|
||||
// Write header
|
||||
|
||||
icount = 0;
|
||||
newfile = true;
|
||||
dataInBetween = false;
|
||||
writer = null;
|
||||
|
||||
// Get basename of the file
|
||||
basename = this.file.getAbsolutePath(); // Determine file name
|
||||
extension = basename.replaceAll("^.*\\.", ""); // Determine
|
||||
// extension
|
||||
basename = basename.replaceAll("\\." + extension + "$", "");
|
||||
}
|
||||
|
||||
if (message instanceof DataMessage) {
|
||||
dataInBetween = true;
|
||||
if (newfile) {
|
||||
|
||||
b = new StringBuffer();
|
||||
b1 = new StringBuffer();
|
||||
b.append("#");
|
||||
b1.append("#");
|
||||
for (Metadata c : ((DataMessage) message).getMetadata()) {
|
||||
|
||||
b.append(c.getId());
|
||||
b.append("\t");
|
||||
|
||||
b1.append(c.getDimension());
|
||||
b1.append("\t");
|
||||
}
|
||||
b.setCharAt(b.length() - 1, '\n');
|
||||
b1.setCharAt(b1.length() - 1, '\n');
|
||||
|
||||
// Open new file and write header
|
||||
// Construct file name
|
||||
if (appendSuffix) {
|
||||
outfile = new File(String.format("%s_%04d.%s", basename, icount, extension));
|
||||
}
|
||||
else {
|
||||
outfile = new File(String.format("%s.%s", basename, extension));
|
||||
}
|
||||
|
||||
// Open file
|
||||
logger.fine("Open new data file: " + outfile.getAbsolutePath());
|
||||
writer = new BufferedWriter(new FileWriter(outfile));
|
||||
|
||||
// Write header
|
||||
writer.write(b.toString());
|
||||
if (showDimensionHeader) {
|
||||
writer.write(b1.toString());
|
||||
}
|
||||
|
||||
newfile = false;
|
||||
}
|
||||
|
||||
// Write message to file - each message will result in one line
|
||||
DataMessage m = (DataMessage) message;
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
for (Object o : m.getData()) {
|
||||
if (o.getClass().isArray()) {
|
||||
// If the array object is of type double[] display its
|
||||
// content
|
||||
if (o instanceof double[]) {
|
||||
double[] oa = (double[]) o;
|
||||
for (double o1 : oa) {
|
||||
buffer.append(o1);
|
||||
buffer.append(" "); // Use space instead of tab
|
||||
}
|
||||
buffer.replace(buffer.length() - 1, buffer.length() - 1, "\t"); // Replace
|
||||
// last
|
||||
// space
|
||||
// with
|
||||
// tab
|
||||
}
|
||||
else if (o instanceof Object[]) {
|
||||
// TODO need to be recursive ...
|
||||
Object[] oa = (Object[]) o;
|
||||
for (Object o1 : oa) {
|
||||
buffer.append(o1);
|
||||
buffer.append(" "); // Use space instead of tab
|
||||
}
|
||||
buffer.replace(buffer.length() - 1, buffer.length() - 1, "\t"); // Replace
|
||||
// last
|
||||
// space
|
||||
// with
|
||||
// tab
|
||||
}
|
||||
else {
|
||||
buffer.append("-"); // Not supported
|
||||
}
|
||||
}
|
||||
else {
|
||||
buffer.append(o);
|
||||
buffer.append("\t");
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer.length() > 0) {
|
||||
buffer.deleteCharAt(buffer.length() - 1); // Remove last
|
||||
// character
|
||||
// (i.e. \t)
|
||||
buffer.append("\n"); // Append newline
|
||||
}
|
||||
writer.write(buffer.toString());
|
||||
}
|
||||
else if (message instanceof StreamDelimiterMessage) {
|
||||
StreamDelimiterMessage m = (StreamDelimiterMessage) message;
|
||||
logger.info("Delimiter - number: " + m.getNumber() + " iflag: " + m.isIflag());
|
||||
if (m.isIflag() && appendSuffix) {
|
||||
// Only increase iflag counter if there was data in between
|
||||
// subsequent StreamDelimiterMessages.
|
||||
if (dataInBetween) {
|
||||
icount++;
|
||||
}
|
||||
dataInBetween = false;
|
||||
|
||||
// Set flag to open new file
|
||||
newfile = true;
|
||||
|
||||
// Close file
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
else if (message instanceof EndOfStreamMessage) {
|
||||
if (writer != null) {
|
||||
// Close file
|
||||
writer.close(); // If the stream was closed previously this
|
||||
// has no effect
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.log(Level.SEVERE, "Data serializer had a problem writing to the specified file", e);
|
||||
throw new RuntimeException("Data serializer had a problem writing to the specified file", e);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isShowDimensionHeader() {
|
||||
return showDimensionHeader;
|
||||
}
|
||||
|
||||
public void setShowDimensionHeader(boolean showDimensionHeader) {
|
||||
this.showDimensionHeader = showDimensionHeader;
|
||||
}
|
||||
}
|
||||
53
src/main/java/ch/psi/fda/vdescriptor/LinePlot.java
Normal file
53
src/main/java/ch/psi/fda/vdescriptor/LinePlot.java
Normal file
@@ -0,0 +1,53 @@
|
||||
package ch.psi.fda.vdescriptor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
@XmlRootElement(name="lineplot")
|
||||
public class LinePlot extends Plot {
|
||||
private List<Series> data = new ArrayList<>();
|
||||
|
||||
private Double minX;
|
||||
private Double maxX;
|
||||
private Integer maxSeries;
|
||||
|
||||
public LinePlot(){
|
||||
}
|
||||
|
||||
public LinePlot(String title){
|
||||
setTitle(title);
|
||||
}
|
||||
|
||||
@XmlElement
|
||||
public List<Series> getData() {
|
||||
return data;
|
||||
}
|
||||
public void setData(List<Series> data) {
|
||||
this.data = data;
|
||||
}
|
||||
@XmlAttribute
|
||||
public Double getMinX() {
|
||||
return minX;
|
||||
}
|
||||
public void setMinX(Double minX) {
|
||||
this.minX = minX;
|
||||
}
|
||||
@XmlAttribute
|
||||
public Double getMaxX() {
|
||||
return maxX;
|
||||
}
|
||||
public void setMaxX(Double maxX) {
|
||||
this.maxX = maxX;
|
||||
}
|
||||
@XmlAttribute
|
||||
public Integer getMaxSeries() {
|
||||
return maxSeries;
|
||||
}
|
||||
public void setMaxSeries(Integer maxSeries) {
|
||||
this.maxSeries = maxSeries;
|
||||
}
|
||||
}
|
||||
87
src/main/java/ch/psi/fda/vdescriptor/MatrixPlot.java
Normal file
87
src/main/java/ch/psi/fda/vdescriptor/MatrixPlot.java
Normal file
@@ -0,0 +1,87 @@
|
||||
package ch.psi.fda.vdescriptor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import javax.xml.bind.annotation.XmlSchemaType;
|
||||
|
||||
@XmlRootElement(name="matrixplot")
|
||||
public class MatrixPlot extends Plot {
|
||||
|
||||
private List<Series> data = new ArrayList<>();
|
||||
private Double minX;
|
||||
private Double maxX;
|
||||
private Integer nX;
|
||||
|
||||
private Double minY;
|
||||
private Double maxY;
|
||||
private Integer nY;
|
||||
|
||||
private String type;
|
||||
|
||||
public MatrixPlot(){
|
||||
}
|
||||
|
||||
public MatrixPlot(String title){
|
||||
setTitle(title);
|
||||
}
|
||||
|
||||
public List<Series> getData() {
|
||||
return data;
|
||||
}
|
||||
public void setData(List<Series> data) {
|
||||
this.data = data;
|
||||
}
|
||||
@XmlAttribute
|
||||
@XmlSchemaType(name = "float")
|
||||
public Double getMinX() {
|
||||
return minX;
|
||||
}
|
||||
public void setMinX(Double minX) {
|
||||
this.minX = minX;
|
||||
}
|
||||
@XmlAttribute
|
||||
public Double getMaxX() {
|
||||
return maxX;
|
||||
}
|
||||
public void setMaxX(Double maxX) {
|
||||
this.maxX = maxX;
|
||||
}
|
||||
@XmlAttribute
|
||||
public Integer getnX() {
|
||||
return nX;
|
||||
}
|
||||
public void setnX(Integer nX) {
|
||||
this.nX = nX;
|
||||
}
|
||||
@XmlAttribute
|
||||
public Double getMinY() {
|
||||
return minY;
|
||||
}
|
||||
public void setMinY(Double minY) {
|
||||
this.minY = minY;
|
||||
}
|
||||
@XmlAttribute
|
||||
public Double getMaxY() {
|
||||
return maxY;
|
||||
}
|
||||
public void setMaxY(Double maxY) {
|
||||
this.maxY = maxY;
|
||||
}
|
||||
@XmlAttribute
|
||||
public Integer getnY() {
|
||||
return nY;
|
||||
}
|
||||
public void setnY(Integer nY) {
|
||||
this.nY = nY;
|
||||
}
|
||||
@XmlAttribute
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
20
src/main/java/ch/psi/fda/vdescriptor/Plot.java
Normal file
20
src/main/java/ch/psi/fda/vdescriptor/Plot.java
Normal file
@@ -0,0 +1,20 @@
|
||||
package ch.psi.fda.vdescriptor;
|
||||
|
||||
import javax.xml.bind.annotation.XmlSeeAlso;
|
||||
import javax.xml.bind.annotation.XmlTransient;
|
||||
|
||||
|
||||
@XmlSeeAlso({LinePlot.class, MatrixPlot.class})
|
||||
@XmlTransient
|
||||
public abstract class Plot {
|
||||
|
||||
private String title;
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
}
|
||||
12
src/main/java/ch/psi/fda/vdescriptor/Series.java
Normal file
12
src/main/java/ch/psi/fda/vdescriptor/Series.java
Normal file
@@ -0,0 +1,12 @@
|
||||
package ch.psi.fda.vdescriptor;
|
||||
|
||||
import javax.xml.bind.annotation.XmlSeeAlso;
|
||||
import javax.xml.bind.annotation.XmlTransient;
|
||||
|
||||
|
||||
@XmlSeeAlso({XYSeries.class, XYZSeries.class})
|
||||
@XmlTransient
|
||||
public abstract class Series {
|
||||
|
||||
|
||||
}
|
||||
28
src/main/java/ch/psi/fda/vdescriptor/VDescriptor.java
Normal file
28
src/main/java/ch/psi/fda/vdescriptor/VDescriptor.java
Normal file
@@ -0,0 +1,28 @@
|
||||
package ch.psi.fda.vdescriptor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
import javax.xml.bind.annotation.XmlElementWrapper;
|
||||
import javax.xml.bind.annotation.XmlElements;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
@XmlRootElement(name="vdescriptor")
|
||||
public class VDescriptor {
|
||||
|
||||
private List<Plot> plots = new ArrayList<>();
|
||||
|
||||
@XmlElementWrapper
|
||||
@XmlElements({
|
||||
@XmlElement(name="lineplot",type=LinePlot.class),
|
||||
@XmlElement(name="matrixplot",type=MatrixPlot.class),
|
||||
})
|
||||
public List<Plot> getPlots() {
|
||||
return plots;
|
||||
}
|
||||
|
||||
public void setPlots(List<Plot> plots) {
|
||||
this.plots = plots;
|
||||
}
|
||||
}
|
||||
42
src/main/java/ch/psi/fda/vdescriptor/XYSeries.java
Normal file
42
src/main/java/ch/psi/fda/vdescriptor/XYSeries.java
Normal file
@@ -0,0 +1,42 @@
|
||||
package ch.psi.fda.vdescriptor;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
@XmlRootElement(name="xyseries")
|
||||
public class XYSeries extends Series {
|
||||
|
||||
private String x = null;
|
||||
private String y = null;
|
||||
private Integer maxItemCount = -1;
|
||||
|
||||
public XYSeries(){
|
||||
}
|
||||
|
||||
public XYSeries(String x, String y){
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
@XmlAttribute
|
||||
public String getX() {
|
||||
return x;
|
||||
}
|
||||
public void setX(String x) {
|
||||
this.x = x;
|
||||
}
|
||||
@XmlAttribute
|
||||
public String getY() {
|
||||
return y;
|
||||
}
|
||||
public void setY(String y) {
|
||||
this.y = y;
|
||||
}
|
||||
@XmlAttribute
|
||||
public Integer getMaxItemCount() {
|
||||
return maxItemCount;
|
||||
}
|
||||
public void setMaxItemCount(Integer maxCount) {
|
||||
this.maxItemCount = maxCount;
|
||||
}
|
||||
}
|
||||
43
src/main/java/ch/psi/fda/vdescriptor/XYZSeries.java
Normal file
43
src/main/java/ch/psi/fda/vdescriptor/XYZSeries.java
Normal file
@@ -0,0 +1,43 @@
|
||||
package ch.psi.fda.vdescriptor;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
@XmlRootElement(name="xyzseries")
|
||||
public class XYZSeries extends Series {
|
||||
|
||||
private String x = null;
|
||||
private String y = null;
|
||||
private String z = null;
|
||||
|
||||
public XYZSeries(){
|
||||
}
|
||||
|
||||
public XYZSeries(String x, String y, String z){
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
@XmlAttribute
|
||||
public String getX() {
|
||||
return x;
|
||||
}
|
||||
public void setX(String x) {
|
||||
this.x = x;
|
||||
}
|
||||
@XmlAttribute
|
||||
public String getY() {
|
||||
return y;
|
||||
}
|
||||
public void setY(String y) {
|
||||
this.y = y;
|
||||
}
|
||||
@XmlAttribute
|
||||
public String getZ() {
|
||||
return z;
|
||||
}
|
||||
public void setZ(String z) {
|
||||
this.z = z;
|
||||
}
|
||||
}
|
||||
28
src/main/java/ch/psi/fda/vdescriptor/YSeries.java
Normal file
28
src/main/java/ch/psi/fda/vdescriptor/YSeries.java
Normal file
@@ -0,0 +1,28 @@
|
||||
package ch.psi.fda.vdescriptor;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
/**
|
||||
* In a YSeries the Y components hold an data array in which the index i the x and the value the y value.
|
||||
*/
|
||||
@XmlRootElement(name="yseries")
|
||||
public class YSeries extends Series {
|
||||
|
||||
private String y = null;
|
||||
|
||||
public YSeries(){
|
||||
}
|
||||
|
||||
public YSeries(String y){
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
@XmlAttribute
|
||||
public String getY() {
|
||||
return y;
|
||||
}
|
||||
public void setY(String y) {
|
||||
this.y = y;
|
||||
}
|
||||
}
|
||||
38
src/main/java/ch/psi/fda/vdescriptor/YZSeries.java
Normal file
38
src/main/java/ch/psi/fda/vdescriptor/YZSeries.java
Normal file
@@ -0,0 +1,38 @@
|
||||
package ch.psi.fda.vdescriptor;
|
||||
|
||||
import javax.xml.bind.annotation.XmlAttribute;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
|
||||
/**
|
||||
* In a YZSeries the Y components hold an data array in which the index i the x and the value the y value.
|
||||
*/
|
||||
@XmlRootElement(name="yzseries")
|
||||
public class YZSeries extends Series {
|
||||
|
||||
private String y = null;
|
||||
private String z = null;
|
||||
|
||||
public YZSeries(){
|
||||
}
|
||||
|
||||
public YZSeries(String y, String z){
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
@XmlAttribute
|
||||
public String getY() {
|
||||
return y;
|
||||
}
|
||||
public void setY(String y) {
|
||||
this.y = y;
|
||||
}
|
||||
@XmlAttribute
|
||||
public String getZ() {
|
||||
return z;
|
||||
}
|
||||
public void setZ(String z) {
|
||||
this.z = z;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
ch.psi.fda.cdump.CdumpEDescriptorProvider
|
||||
ch.psi.fda.fdaq.FdaqEDescriptorProvider
|
||||
ch.psi.fda.aq.XScanDescriptorProvider
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user