9 Commits

Author SHA1 Message Date
d1e8913dd4 new version 2016-03-21 15:55:40 +01:00
c5d845e7ff added simulated epics server for testing 2016-03-21 15:47:04 +01:00
5f836e7856 merged core into project 2016-03-17 08:52:34 +01:00
8c13c1c85d updated gradle build 2016-03-17 08:22:19 +01:00
e1ff42d464 merged xscan into project 2016-03-17 08:17:05 +01:00
b014dd6afd merged fdaq code 2016-03-16 15:09:26 +01:00
e412ae7092 merged cdump code which this project 2016-03-16 13:29:02 +01:00
e63d2e3052 Updated readme to remember that there are 2 more properties in the fda.properties file for CRLOGIC 2015-08-21 08:53:10 +02:00
09ead2eb64 updated version 2015-08-20 08:38:51 +02:00
138 changed files with 17002 additions and 10 deletions

1
.gitignore vendored
View File

@@ -5,3 +5,4 @@
.settings
.project
.classpath
/schema/

View File

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

View File

@@ -3,7 +3,20 @@ apply plugin: 'eclipse'
sourceCompatibility = 1.7
version = '2.6.2'
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.2'
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,23 +58,31 @@ 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'
uploadArchives {
repositories {
mavenDeployer {
repository(url: "http://artifacts.psi.ch/artifactory/libs-releases-local"){
repository(url: "http://artifacts.psi.ch/artifactory/libs-snapshots-local"){
authentication(userName: "upload", password: "{DESede}eWKHxAtQ2Dc=")
}
pom.groupId = 'ch.psi'

BIN
gradle/wrapper/gradle-wrapper.jar vendored Normal file

Binary file not shown.

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

View 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();
}

View 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();
}

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

File diff suppressed because it is too large Load Diff

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

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

View 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());
}
}

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

View 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();
}
}

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

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

View 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());
}
}

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

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

View 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();
}
}

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

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

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

View 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());
}
}

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

View 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();
}

View 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();
}

View 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();
}

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

View 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();
}

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

View 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();
}

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

View File

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

View 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() {
}
}

View 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() {
}
}

View 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() {
}
}

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

View File

@@ -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&lt;end = 1, start&gt;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;
}
}

View File

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

View File

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

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

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

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

View File

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

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

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

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

View 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.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);
}
}
}

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

View File

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

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

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

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

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

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

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

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

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View 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.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;
}
}

View File

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

View File

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

View File

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

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

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

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

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

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

View 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() {
}
}

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

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

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

View 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();
}
}

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

View 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();
}
}

View 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 ]";
}
}

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

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

View File

@@ -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+" ]";
}
}

View File

@@ -0,0 +1 @@
/v1/

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

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

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

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

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

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

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

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

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

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

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

View File

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