Migrated from Mercurial to Git (without keeping history)

This commit is contained in:
ebner
2011-10-21 09:04:00 +02:00
commit 1c0dd06b6e
228 changed files with 37631 additions and 0 deletions
+15
View File
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java"/>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources"/>
<classpathentry kind="src" output="target/test-classes" path="src/test/java"/>
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources"/>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"/>
<classpathentry kind="src" output="target/classes" path="target/generated-sources/xjc">
<attributes>
<attribute name="optional" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="output" path="target/classes"/>
</classpath>
+1
View File
@@ -0,0 +1 @@
/target
+23
View File
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>ch.psi.fda</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>
@@ -0,0 +1,3 @@
#Wed Oct 19 12:49:33 CEST 2011
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
@@ -0,0 +1,5 @@
#Tue Oct 18 15:52:42 CEST 2011
activeProfiles=
eclipse.preferences.version=1
resolveWorkspaceProjects=true
version=1
+159
View File
@@ -0,0 +1,159 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>ch.psi</groupId>
<artifactId>fda</artifactId>
<version>1.1.27</version>
<dependencies>
<dependency>
<groupId>ch.psi</groupId>
<artifactId>jcae</artifactId>
<version>1.0.28</version>
</dependency>
<!-- Plotting library -->
<dependency>
<groupId>ch.psi</groupId>
<artifactId>plot</artifactId>
<version>1.1.30</version>
</dependency>
<!-- Java SMB/CIFS library -->
<dependency>
<groupId>org.samba</groupId>
<artifactId>jcifs</artifactId>
<version>1.3.14</version>
</dependency>
<!-- JAXB implementation -->
<!-- <dependency> <groupId>com.sun.xml.bind</groupId> <artifactId>jaxb-impl</artifactId>
<version>2.2.1</version> </dependency> <dependency> <groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-xjc</artifactId> <version>2.2.1</version> </dependency> -->
<!-- Jython Library -->
<dependency>
<groupId>org.jython</groupId>
<artifactId>jython</artifactId>
<version>2.5.1</version>
</dependency>
<!-- CLI Libraries -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>cli</artifactId>
<version>1.2</version>
</dependency>
<!-- Library to Read Matlab Files -->
<dependency>
<groupId>com.jmatio</groupId>
<artifactId>jmatio</artifactId>
<version>0.2u2psi1</version>
</dependency>
<!-- Mail Libraries -->
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4.1</version>
</dependency>
<!-- Library for reading/writing XDR Format (MDA) -->
<dependency>
<groupId>org.freehep.io</groupId>
<artifactId>xdr</artifactId>
<version>2.0.4</version>
</dependency>
<!-- JUnit test library -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.2</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Generate Javadoc Jar -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.8</version>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Generate Source Jar -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>2.1.2</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Schema generation -->
<plugin>
<groupId>org.jvnet.jaxb2.maven2</groupId>
<artifactId>maven-jaxb2-plugin</artifactId>
<version>0.8.0</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
<configuration>
<generatePackage>ch.psi.fda.model.v1</generatePackage>
</configuration>
</plugin>
<!-- Schema Documentation Generation -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>xml-maven-plugin</artifactId>
<version>1.0</version>
<executions>
<execution>
<phase>transform</phase>
<goals>
<goal>transform</goal>
</goals>
</execution>
</executions>
<configuration>
<transformationSets>
<transformationSet>
<!-- <outputDir>target</outputDir> -->
<dir>src/main/resources</dir>
<stylesheet>src/main/documentation/xs3p.xsl</stylesheet>
<fileMappers>
<fileMapper implementation="org.codehaus.plexus.components.io.filemappers.RegExpFileMapper">
<pattern>\.xsd$</pattern>
<replacement>.html</replacement>
</fileMapper>
</fileMappers>
</transformationSet>
</transformationSets>
</configuration>
</plugin>
</plugins>
</build>
<distributionManagement>
<snapshotRepository>
<id>i.snapshots</id>
<name>Artifactory Snapshots</name>
<url>http://slsyoke1/artifactory/libs-snapshots-local</url>
</snapshotRepository>
<repository>
<id>i.releases</id>
<name>Atrifactory Releases</name>
<url>http://slsyoke1/artifactory/libs-releases-local</url>
</repository>
</distributionManagement>
</project>
+237
View File
@@ -0,0 +1,237 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="744.09448819"
height="1052.3622047"
id="svg2"
version="1.1"
inkscape:version="0.48.1 r9760"
sodipodi:docname="fda_small.svg"
inkscape:export-filename="/Users/ebner/Desktop/fda_splashScreen.png"
inkscape:export-xdpi="100"
inkscape:export-ydpi="100">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.49497475"
inkscape:cx="400.20885"
inkscape:cy="559.53899"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
inkscape:window-width="908"
inkscape:window-height="780"
inkscape:window-x="174"
inkscape:window-y="41"
inkscape:window-maximized="0" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<rect
style="fill:#ffffff;stroke:none"
id="rect4058"
width="642.45703"
height="496.99506"
x="6.0609155"
y="90.69696" />
<path
sodipodi:type="arc"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path3849"
sodipodi:cx="77.14286"
sodipodi:cy="232.36218"
sodipodi:rx="20"
sodipodi:ry="20"
d="m 97.14286,232.36218 a 20,20 0 1 1 -40,0 20,20 0 1 1 40,0 z"
transform="matrix(1,0,0,-1,223.52035,508.61088)" />
<path
transform="matrix(1,0,0,-1,210.38837,614.6769)"
d="m 97.14286,232.36218 a 20,20 0 1 1 -40,0 20,20 0 1 1 40,0 z"
sodipodi:ry="20"
sodipodi:rx="20"
sodipodi:cy="232.36218"
sodipodi:cx="77.14286"
id="path3851"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
sodipodi:type="arc" />
<path
sodipodi:type="arc"
style="fill:#ccff00;fill-opacity:1;fill-rule:evenodd;stroke:#ccff00;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path3861"
sodipodi:cx="77.14286"
sodipodi:cy="232.36218"
sodipodi:rx="20"
sodipodi:ry="20"
d="m 97.14286,232.36218 a 20,20 0 1 1 -40,0 20,20 0 1 1 40,0 z"
transform="matrix(1,0,0,-1,253.82494,565.17943)" />
<path
transform="matrix(1,0,0,-1,292.21074,627.80889)"
d="m 97.14286,232.36218 a 20,20 0 1 1 -40,0 20,20 0 1 1 40,0 z"
sodipodi:ry="20"
sodipodi:rx="20"
sodipodi:cy="232.36218"
sodipodi:cx="77.14286"
id="path3871"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
sodipodi:type="arc" />
<path
sodipodi:type="arc"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path3873"
sodipodi:cx="77.14286"
sodipodi:cy="232.36218"
sodipodi:rx="20"
sodipodi:ry="20"
d="m 97.14286,232.36218 a 20,20 0 1 1 -40,0 20,20 0 1 1 40,0 z"
transform="matrix(1,0,0,-1,317.46455,530.83425)" />
<path
sodipodi:type="arc"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path3885"
sodipodi:cx="77.14286"
sodipodi:cy="232.36218"
sodipodi:rx="20"
sodipodi:ry="20"
d="m 97.14286,232.36218 a 20,20 0 1 1 -40,0 20,20 0 1 1 40,0 z"
transform="matrix(0.630432,0,0,-0.630432,228.29133,474.76012)" />
<path
sodipodi:type="arc"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path3897"
sodipodi:cx="77.14286"
sodipodi:cy="232.36218"
sodipodi:rx="20"
sodipodi:ry="20"
d="m 97.14286,232.36218 a 20,20 0 1 1 -40,0 20,20 0 1 1 40,0 z"
transform="matrix(0.630432,0,0,-0.630432,297.99185,438.39462)" />
<path
transform="matrix(0.630432,0,0,-0.630432,317.18473,494.96314)"
d="m 97.14286,232.36218 a 20,20 0 1 1 -40,0 20,20 0 1 1 40,0 z"
sodipodi:ry="20"
sodipodi:rx="20"
sodipodi:cy="232.36218"
sodipodi:cx="77.14286"
id="path3915"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
sodipodi:type="arc" />
<path
sodipodi:type="arc"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path3917"
sodipodi:cx="77.14286"
sodipodi:cy="232.36218"
sodipodi:rx="20"
sodipodi:ry="20"
d="m 97.14286,232.36218 a 20,20 0 1 1 -40,0 20,20 0 1 1 40,0 z"
transform="matrix(0.630432,0,0,-0.630432,282.83954,526.27787)" />
<path
sodipodi:type="arc"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path3985"
sodipodi:cx="77.14286"
sodipodi:cy="232.36218"
sodipodi:rx="20"
sodipodi:ry="20"
d="m 97.14286,232.36218 a 20,20 0 1 1 -40,0 20,20 0 1 1 40,0 z"
transform="matrix(0.31013973,0,0,-0.31013973,247.44373,374.57741)" />
<path
transform="matrix(0.31013973,0,0,-0.31013973,272.69754,419.02412)"
d="m 97.14286,232.36218 a 20,20 0 1 1 -40,0 20,20 0 1 1 40,0 z"
sodipodi:ry="20"
sodipodi:rx="20"
sodipodi:cy="232.36218"
sodipodi:cx="77.14286"
id="path3987"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
sodipodi:type="arc" />
<path
sodipodi:type="arc"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path4001"
sodipodi:cx="77.14286"
sodipodi:cy="232.36218"
sodipodi:rx="20"
sodipodi:ry="20"
d="m 97.14286,232.36218 a 20,20 0 1 1 -40,0 20,20 0 1 1 40,0 z"
transform="matrix(0.31013973,0,0,-0.31013973,332.29653,441.24746)" />
<path
transform="matrix(0.31013973,0,0,-0.31013973,344.41836,392.76014)"
d="m 97.14286,232.36218 a 20,20 0 1 1 -40,0 20,20 0 1 1 40,0 z"
sodipodi:ry="20"
sodipodi:rx="20"
sodipodi:cy="232.36218"
sodipodi:cx="77.14286"
id="path4003"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
sodipodi:type="arc" />
<path
sodipodi:type="arc"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="path4005"
sodipodi:cx="77.14286"
sodipodi:cy="232.36218"
sodipodi:rx="20"
sodipodi:ry="20"
d="m 97.14286,232.36218 a 20,20 0 1 1 -40,0 20,20 0 1 1 40,0 z"
transform="matrix(0.31013973,0,0,-0.31013973,338.35744,343.26267)" />
<path
transform="matrix(0.31013973,0,0,-0.31013973,280.77874,379.62816)"
d="m 97.14286,232.36218 a 20,20 0 1 1 -40,0 20,20 0 1 1 40,0 z"
sodipodi:ry="20"
sodipodi:rx="20"
sodipodi:cy="232.36218"
sodipodi:cx="77.14286"
id="path4007"
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
sodipodi:type="arc" />
<text
xml:space="preserve"
style="font-size:50px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans"
x="425.27417"
y="363.43817"
id="text4033"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan4035"
x="425.27417"
y="363.43817">fda</tspan></text>
<text
sodipodi:linespacing="125%"
id="text3000"
y="329.09299"
x="501.96454"
style="font-size:24px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;text-align:start;line-height:125%;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#aa0000;fill-opacity:1;stroke:none;font-family:Sans;-inkscape-font-specification:Sans Bold"
xml:space="preserve"><tspan
y="329.09299"
x="501.96454"
id="tspan3002"
sodipodi:role="line">2.0</tspan></text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,233 @@
/**
*
* 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.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import ch.psi.fda.install.ApplicationConfigurator;
/**
* @author ebner
*
*/
public class AcquisitionEngineConfiguration {
private static final AcquisitionEngineConfiguration instance = new AcquisitionEngineConfiguration();
private String otfChannelPrefix = "";
private String otfNfsServer = "";
private String otfNfsShare = "";
private String otfSmbShare = "";
private String otfScalerPrefix = "";
private boolean otfUseCrlogic = false;
private String otfCrlogicPrefix = "";
/**
* 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.
*/
private AcquisitionEngineConfiguration(){
loadConfiguration();
}
/**
* Get instance of this configuration
* @return configuration
*/
public static AcquisitionEngineConfiguration getInstance(){
return(instance);
}
/**
* Load configuration from properties file
*/
private void loadConfiguration() {
String config = System.getProperty(ApplicationConfigurator.FDA_CONFIG_FILE_ARGUMENT);
if(config == null){
throw new RuntimeException("No configuration file specified via -D"+ApplicationConfigurator.FDA_CONFIG_FILE_ARGUMENT+"=...");
}
Properties properties = new Properties();
try {
properties.load(new FileReader(config));
} catch (FileNotFoundException e) {
throw new RuntimeException("Configuration file "+config+" not found", e);
} catch (IOException e) {
throw new RuntimeException("Cannot read configuration file "+config, e);
}
otfChannelPrefix = properties.getProperty(AcquisitionEngineConfiguration.class.getPackage().getName()+".otf.channelPrefix", "");
otfScalerPrefix = properties.getProperty(AcquisitionEngineConfiguration.class.getPackage().getName()+".otf.scalerPrefix", "");
otfNfsServer = properties.getProperty(AcquisitionEngineConfiguration.class.getPackage().getName()+".otf.nfsServer", "");
otfNfsShare = properties.getProperty(AcquisitionEngineConfiguration.class.getPackage().getName()+".otf.nfsShare", "");
otfSmbShare = properties.getProperty(AcquisitionEngineConfiguration.class.getPackage().getName()+".otf.smbShare", "");
otfUseCrlogic = new Boolean(properties.getProperty(AcquisitionEngineConfiguration.class.getPackage().getName()+".otf.useCrlogic", "false"));
otfCrlogicPrefix = properties.getProperty(AcquisitionEngineConfiguration.class.getPackage().getName()+".otf.crlogicPrefix", "");
// Workaround
// dataBaseDirectory = properties.getProperty(AcquisitionEngineConfiguration.class.getPackage().getName()+".data.baseDirectory", ".");
dataBaseDirectory = System.getProperty(ApplicationConfigurator.FDA_HOME_ARGUMENT)+"/data";
dataFilePrefix = properties.getProperty(AcquisitionEngineConfiguration.class.getPackage().getName()+".data.filePrefix","");
actorMoveTimeout = new Long(properties.getProperty(AcquisitionEngineConfiguration.class.getPackage().getName()+".actorMoveTimeout","600000"));
smptServer= properties.getProperty(AcquisitionEngineConfiguration.class.getPackage().getName()+".notification.host","mail.psi.ch");
}
/**
* Reload this configuration from the corresponding properties file
* @throws IOException
*/
public void reloadConfiguration() throws IOException{
loadConfiguration();
}
/**
* @return the otfChannelPrefix
*/
public String getOtfChannelPrefix() {
return otfChannelPrefix;
}
/**
* @return the otfScalerPrefix
*/
public String getOtfScalerPrefix() {
return otfScalerPrefix;
}
/**
* @return the otfNfsServer
*/
public String getOtfNfsServer() {
return otfNfsServer;
}
/**
* @return the otfNfsShare
*/
public String getOtfNfsShare() {
return otfNfsShare;
}
/**
* @return the otfSmbShare
*/
public String getOtfSmbShare() {
return otfSmbShare;
}
/**
* @return the otfUseCrlogic
*/
public boolean isOtfUseCrlogic() {
return otfUseCrlogic;
}
/**
* @return the dataBaseDirectory
*/
public String getDataBaseDirectory() {
return dataBaseDirectory;
}
/**
* @return the dataFilePrefix
*/
public String getDataFilePrefix() {
return dataFilePrefix;
}
/**
* @return the smptServer
*/
public String getSmptServer() {
return smptServer;
}
/**
* @return the otfCrlogicPrefix
*/
public String getOtfCrlogicPrefix() {
return otfCrlogicPrefix;
}
/**
* @return the actorMoveTimeout
*/
public Long getActorMoveTimeout() {
return actorMoveTimeout;
}
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;
}
}
@@ -0,0 +1,432 @@
/**
*
* 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.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.ScrollPaneLayout;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import sun.misc.Signal;
import sun.misc.SignalHandler;
import ch.psi.fda.core.messages.DataQueue;
import ch.psi.fda.gui.ProgressPanel;
import ch.psi.fda.gui.ScrollableFlowPanel;
import ch.psi.fda.install.ApplicationConfigurator;
import ch.psi.fda.model.ModelManager;
import ch.psi.fda.model.v1.Configuration;
import ch.psi.fda.model.v1.Data;
import ch.psi.fda.visualizer.Visualizer;
/**
* Main class for data acquisition
* @author ebner
*
*/
public class AcquisitionMain {
// Get Logger
private static Logger logger = Logger.getLogger(AcquisitionMain.class.getName());
/**
* Main Program
* Returns -1 if wrong number of arguments are passed
*
* @param args Arguments of the program
*/
public static void main(String[] args) {
String scriptname = "fda_scan";
Integer iterations = null;
boolean autoclose = false;
boolean nogui = false;
String files[] = null;
HashMap<String,String> varTable = new HashMap<String,String>();
// Iterations option
OptionBuilder.hasArg();
OptionBuilder.withArgName("iterations");
OptionBuilder.withDescription("Number of iterations");
OptionBuilder.withType(new Integer(1));
Option o_iterations = OptionBuilder.create( "iterations");
// Variables option
OptionBuilder.hasArg();
OptionBuilder.withArgName("variables");
OptionBuilder.withDescription("Scan variables - variables are specified in the form var=value,var2=value2");
OptionBuilder.withType(new Integer(1));
Option o_variables = OptionBuilder.create( "variables");
Option o_autoclose = new Option( "autoclose", "Close down application after scan" );
Option o_init = new Option( "initialize", "Initialize application directories and configuration files" );
Option o_nogui = new Option( "nogui", "Do not show scan GUI" );
Options options = new Options();
options.addOption(o_variables);
options.addOption(o_iterations);
options.addOption(o_autoclose);
options.addOption(o_init);
options.addOption(o_nogui);
CommandLineParser parser = new GnuParser();
// Parse the command line arguments
try {
CommandLine line = parser.parse( options, args );
// Initialize application
if( line.hasOption(o_init.getOpt()) ){
// Initialize application
ApplicationConfigurator ac = new ApplicationConfigurator();
ac.initializeApplication();
System.exit(0);
}
// Check whether exactly one file is specified
// if(line.getArgs().length>1){
// throw new ParseException("Only up to one argument is supported");
// }
if(line.getArgs().length<1){
throw new ParseException("One argument is required");
}
files=line.getArgs();
// Iterations option
if( line.hasOption(o_iterations.getOpt()) ){
iterations = Integer.parseInt(line.getOptionValue(o_iterations.getOpt()));
}
// Variables
if( line.hasOption(o_variables.getOpt()) ){
String variables = line.getOptionValue(o_variables.getOpt() );
String[] vars = variables.split(",");
for(String varp:vars){
String[] pair = varp.split("=");
if(pair.length!=2){
throw new ParseException("Variables are not specified the correct way. -variables var1=val1,var2=val2");
}
varTable.put(pair[0], pair[1]);
}
}
// Autoclose option
if( line.hasOption( o_autoclose.getOpt() ) ) {
autoclose = true;
}
// No GUI option
if( line.hasOption( o_nogui.getOpt() ) ) {
nogui = true;
}
} catch (ParseException e) {
System.err.println(e.getMessage());
HelpFormatter formatter = new HelpFormatter();
formatter.printUsage(new PrintWriter(System.out, true), HelpFormatter.DEFAULT_WIDTH, scriptname, options);
System.exit(-1);
}
// Run application
try{
for(String file: files){
run(new File(file), iterations, autoclose, nogui, varTable);
}
// Close application automatically if autoclose option is set (and visualizations are specified)
if(nogui || autoclose ){
System.exit(0);
}
}
catch(Exception ee){
System.out.println("Acquisition failed due to: "+ee.getMessage());
logger.log(Level.SEVERE, "Acquisition failed due to: ", ee); // Do not print stack trace
System.exit(-1);
}
}
/**
* Run scan
* @param file Scan file
* @param iterations Number of iterations
* @param autoclose Flag whether to close the application automatically after the scan
* @param nogui Flag whether to run the scan with a GUI
* @param variables Table of scan variables
*/
private static void run(File file, Integer iterations, boolean autoclose, boolean nogui, HashMap<String,String> variables){
// Initialize application
ApplicationConfigurator ac = new ApplicationConfigurator();
ac.initializeApplication();
// Read in base configuration
// AcquisitionEngineConfiguration configuration = AcquisitionEngineConfiguration.getInstance();
if(!file.exists()){
throw new RuntimeException("File "+file.getAbsolutePath()+" does not exist");
}
Configuration c;
try {
if(file.getName().endsWith(".xsl")){
c = ModelManager.unmarshall(file, variables);
}
else{
c = ModelManager.unmarshall(file);
}
} catch (Exception e) {
throw new RuntimeException("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);
}
// Create/get acquisition engine
final Acquisition acquisition = new Acquisition();
boolean vis = false;
// Only register data visualization task/processor if there are visualizations
if(c.getVisualization().size()>0 && !nogui){
vis=true;
}
DataQueue vdq = acquisition.initalize(c, vis);
Visualizer visualizer = null;
// Only register data visualization task/processor if there are visualizations
if(vis){
visualizer = new Visualizer(vdq, c.getVisualization());
// If there is a continous dimension only update plot at the end of a line
if(c.getScan() != null && c.getScan().getCdimension()!=null){
visualizer.setUpdateAtStreamElement(false);
visualizer.setUpdateAtStreamDelimiter(true);
visualizer.setUpdateAtEndOfStream(true);
}
}
// GUI GUI GUI GUI GUI GUI GUI
ProgressPanel progressPanel = null;
if(visualizer != null){ // Only bring up GUI if there are some plots ...
// Visualizations
JPanel opanel = new ScrollableFlowPanel();
opanel.setLayout(new FlowLayout());
JScrollPane spane = new JScrollPane(opanel, ScrollPaneLayout.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneLayout.HORIZONTAL_SCROLLBAR_NEVER);
JTabbedPane tpane = new JTabbedPane();
tpane.addTab("Overview", spane);
for(JPanel p: visualizer.getPlotPanels()){
opanel.add(p);
}
final JFrame frame = new JFrame("FDA: "+acquisition.getDatafileName());
frame.setSize(1200,800);
// Create progress panel
progressPanel = new ProgressPanel();
progressPanel.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
acquisition.abort();
} catch (Exception e1) {
logger.log(Level.SEVERE, "Exception occured while aborting scan", e1);
}
}
});
JSplitPane splitPane = new JSplitPane();
splitPane.setLeftComponent(progressPanel);
splitPane.setRightComponent(tpane);
frame.add(splitPane);
// frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.addWindowListener(new WindowAdapter(){
@Override
public void windowClosing(WindowEvent we){
if(acquisition.isActive()){
// Abort acquisition
acquisition.abort();
}
// Wait until acquisition is aborted. Maximum wait 10*100milliseconds before forcefully
// terminate application
int count=0;
while(acquisition.isActive()){
if(count == 10){
break;
}
// Sleep 100 milliseconds
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
count++;
}
// Terminate program
System.exit(0);
}
});
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);//.DO_NOTHING_ON_CLOSE);
// frame.setVisible(true);
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
frame.setVisible(true);
}
});
}
// GUI GUI GUI GUI GUI GUI GUI
// CLI CLI CLI CLI
// Register the scan engine as Signal Handler
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) {
logger.finest("Received signal: "+signal);
int count = signalCount.incrementAndGet();
// If signal is received more than 1 time forcefully abort application
if(count>1){
logger.info("Terminate application");
System.exit(2);
}
// Abort acquisition engine
if(acquisition.isActive()){
// Abort acquisition
acquisition.abort();
}
}
});
// CLI CLI CLI CLI
// Run acquisition engine
try {
if(visualizer != null){
// Start visualization
visualizer.startVisualization();
}
acquisition.execute();
if(visualizer != null){
// Stop visualization
visualizer.stopVisualization();
}
} catch (InterruptedException e1) {
throw new RuntimeException("Acquisition was interrupted",e1);
}
finally{
acquisition.destroy();
}
// GUI GUI GUI GUI GUI GUI GUI
// Set progress panel to done
if(progressPanel != null){
// Can this be done via a Listener?
progressPanel.done();
}
// GUI GUI GUI GUI GUI GUI GUI
}
}
@@ -0,0 +1,257 @@
/**
*
* 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.co;
import java.io.File;
import java.io.PrintWriter;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import ch.psi.fda.deserializer.DataDeserializer;
import ch.psi.fda.deserializer.DataDeserializerMDA;
import ch.psi.fda.deserializer.DataDeserializerTXT;
import ch.psi.fda.serializer.DataSerializer;
import ch.psi.fda.serializer.DataSerializerMAT;
import ch.psi.fda.serializer.DataSerializerMAT2D;
import ch.psi.fda.serializer.DataSerializerMAT2DZigZag;
import ch.psi.fda.serializer.DataSerializerMDA;
import ch.psi.fda.serializer.DataSerializerTXT;
import ch.psi.fda.serializer.DataSerializerTXT2D;
import ch.psi.fda.serializer.DataSerializerTXTSplit;
/**
* Visualize data according to the scan description
*
* @author ebner
*
*/
public class ConversionEngine {
// Get Logger
private static Logger logger = Logger.getLogger(ConversionEngine.class.getName());
public enum Writer {TXT, TXT_2D, TXT_SPLIT, MAT, MAT_2D, MDA, MAT_2D_Z};
public enum Reader {TXT, MDA};
/**
* Default constructor
*/
public ConversionEngine(){
}
/**
* Visualize data
* @param input
* @param output
* @param writer Type of writer to use
* @throws InterruptedException
*/
public void convert(File input, File output, Reader reader, Writer writer) throws InterruptedException{
// Check existence input file
if(input==null){
throw new IllegalArgumentException("Input file not specified (is null)");
}
else if(!input.exists()){
throw new IllegalArgumentException("Input file ["+input.getAbsolutePath()+"] does not exist");
}
// Check existence output file
if(output==null){
throw new IllegalArgumentException("Output file not specified (is null)");
}
else if(output.exists()){
throw new IllegalArgumentException("Output file ["+output.getAbsolutePath()+"] already exists");
}
// Create deserializer
DataDeserializer deserializer;
if(reader.equals(Reader.TXT)){
deserializer = new DataDeserializerTXT(input);
}
else if(reader.equals(Reader.MDA)){
deserializer = new DataDeserializerMDA(input);
}
else{
throw new IllegalArgumentException("Reader of type "+reader+" not supported.");
}
DataSerializer serializer;
if(writer.equals(Writer.MAT)){
serializer = new DataSerializerMAT(deserializer.getQueue(), output);
}
else if(writer.equals(Writer.MAT_2D)){
serializer = new DataSerializerMAT2D(deserializer.getQueue(), output);
}
else if(writer.equals(Writer.TXT)){
serializer = new DataSerializerTXT(deserializer.getQueue(), output, false);
}
else if(writer.equals(Writer.TXT_2D)){
serializer = new DataSerializerTXT2D(deserializer.getQueue(), output);
}
else if(writer.equals(Writer.TXT_SPLIT)){
serializer = new DataSerializerTXTSplit(deserializer.getQueue(), output);
}
else if(writer.equals(Writer.MDA)){
serializer = new DataSerializerMDA(deserializer.getQueue(), output);
}
else if(writer.equals(Writer.MAT_2D_Z)){
serializer = new DataSerializerMAT2DZigZag(deserializer.getQueue(), output);
}
else{
throw new IllegalArgumentException("Writer of type "+writer+" not supported.");
}
// Start deserializer and serializer
Thread td = new Thread(deserializer);
Thread ts = new Thread(serializer);
td.start();
ts.start();
td.join();
ts.join();
}
/**
* This method accepts a data file and an option (-file) specifying the scan configuration file
* used for creating the data file. If no option is specified the configuration file is derived by the
* data file name.
*
* @param args Command line arguments
*/
public static void main(String[] args) {
String scriptname = "fda_convert";
File outfile = null;
File infile = null;
Reader reader = Reader.TXT;
Writer writer = Writer.TXT;
OptionBuilder.hasArg();
OptionBuilder.withArgName("writer");
OptionBuilder.withDescription("Output format");
OptionBuilder.withType(new String());
Option o_writer = OptionBuilder.create( "writer");
OptionBuilder.hasArg();
OptionBuilder.withArgName("reader");
OptionBuilder.withDescription("Input format");
OptionBuilder.withType(new String());
Option o_reader = OptionBuilder.create( "reader");
Options options = new Options();
options.addOption(o_writer);
options.addOption(o_reader);
CommandLineParser parser = new GnuParser();
// Parse the command line arguments
try {
CommandLine line = parser.parse( options, args );
// Check whether exactly one file is specified
if(line.getArgs().length != 2){
throw new ParseException("Two arguments required");
}
infile = new File(line.getArgs()[0]);
outfile= new File(line.getArgs()[1]);
if( line.hasOption(o_reader.getOpt() )){
String wr = line.getOptionValue(o_reader.getOpt());
try{
Reader r = Reader.valueOf(wr.toUpperCase());
reader = r;
}
catch(IllegalArgumentException e){
throw new ParseException("Data type "+ wr + " not supported");
}
}
else{
String suffix = infile.getName().replaceAll("^.*\\.", "");
try{
Reader r = Reader.valueOf(suffix.toUpperCase());
reader = r;
}
catch(IllegalArgumentException e){
throw new ParseException("Data type "+ suffix + " not supported");
}
}
if( line.hasOption(o_writer.getOpt() )){
String wr = line.getOptionValue(o_writer.getOpt());
try{
Writer v = Writer.valueOf(wr.toUpperCase());
writer = v;
}
catch(IllegalArgumentException e){
throw new ParseException("Data type "+ wr + " not supported");
}
}
else{
String suffix = outfile.getName().replaceAll("^.*\\.", "");
try{
Writer v = Writer.valueOf(suffix.toUpperCase());
writer = v;
}
catch(IllegalArgumentException e){
throw new ParseException("Data type "+ suffix + " not supported");
}
}
// Debug output
logger.finest("Using writer: "+writer);
} catch (ParseException e) {
System.err.println(e.getMessage());
HelpFormatter formatter = new HelpFormatter();
formatter.printUsage(new PrintWriter(System.out, true), HelpFormatter.DEFAULT_WIDTH, scriptname, options);
StringBuffer b = new StringBuffer();
for(Writer w: Writer.values()){
b.append(" ");
b.append(w.toString().toLowerCase());
}
System.out.println("Supported writer(s):"+ b);
System.exit(-1);
}
ConversionEngine e = new ConversionEngine();
try{
e.convert(infile, outfile, reader, writer);
}
catch(Exception ee){
System.out.println("Conversion failed due to: "+ee.getMessage());
logger.log(Level.WARNING, "Conversion failed due to Exception:", ee);
System.exit(-1);
}
}
}
@@ -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;
/**
* @author ebner
*
*/
public interface Action {
/**
* Execute logic of the action
*/
public void execute() throws InterruptedException;
/**
* Abort the execution logic of the action
*/
public void abort();
/**
* Destroy action.
* Can be used for the cleanup of used resources of the action (e.g. to close connections, ...) if
* this cannot be done automatically by the GarbageCollector.
*
* After calling this method the action must not be executed any more!
*/
public void destroy();
}
@@ -0,0 +1,69 @@
/**
*
* 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.core.messages.DataQueue;
/**
* Loop of actions to accomplish a task. Depending on the loop
* actions may be executed in a different way.
*
* @author ebner
*
*/
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 DataQueue getDataQueue();
}
@@ -0,0 +1,64 @@
/**
*
* 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;
/**
*
* @author ebner
*
*/
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();
/**
* Destroy action.
* Can be used for the cleanup of used resources of the actor (e.g. to close connections, ...) if
* this cannot be done automatically by the GarbageCollector.
*
* After calling this method the actor must not be used any more!
*/
public void destroy();
}
@@ -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.concurrent.Callable;
/**
* Callable used for parallel execution of the set method of an actor
* @author ebner
*
*/
public class ActorSetCallable implements Callable<Object> {
// Callable actor
private Actor actor;
public ActorSetCallable(Actor actor){
this.actor = actor;
}
/* (non-Javadoc)
* @see java.util.concurrent.Callable#call()
*/
@Override
public Object call() throws Exception {
actor.set();
return null;
}
}
@@ -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.core;
/**
* Singleton object for holing the core engine configuration
*
* @author ebner
*/
public class EngineConfiguration {
/**
* Flag to indicate whether the engine should fail if there is an error while reading out a sensor.
*/
private boolean failOnSensorError = true;
/**
* Flag to indicate whether the set value of the actor should be verified after the set operation.
* Having this flag on true will enable detecting for example that a motor hit the end switch or
* did not move correctly.
*/
private boolean checkActorSet = true;
/**
* Singleton instance
*/
private final static EngineConfiguration instance = new EngineConfiguration();
private EngineConfiguration(){
}
/**
* Get singleton instance of this class
* @return Engine configuration
*/
public static EngineConfiguration getInstance(){
return instance;
}
/**
* @return the failOnSensorError
*/
public boolean isFailOnSensorError() {
return failOnSensorError;
}
/**
* @param failOnSensorError the failOnSensorError to set
*/
public void setFailOnSensorError(boolean failOnSensorError) {
this.failOnSensorError = failOnSensorError;
}
/**
* @return the checkActorSet
*/
public boolean isCheckActorSet() {
return checkActorSet;
}
/**
* @param checkActorSet the checkActorSet to set
*/
public void setCheckActorSet(boolean checkActorSet) {
this.checkActorSet = checkActorSet;
}
}
@@ -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;
/**
* 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.
*
* @author ebner
*
*/
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();
/**
* Destroy guard.
* Can be used for the cleanup of used resources of the guard (e.g. to close connections, ...) if
* this cannot be done automatically by the GarbageCollector.
*
* After calling this method the guard must not be used any more!
*/
public void destroy();
}
@@ -0,0 +1,50 @@
/**
*
* 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.
* @author ebner
*
*/
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();
/**
* Destroy sensor.
* Can be used for the cleanup of used resources of the sensor (e.g. to close connections, ...) if
* this cannot be done automatically by the GarbageCollector.
*
* After calling this method the sensor must not be used any more!
*/
public void destroy();
}
@@ -0,0 +1,127 @@
/**
*
* 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.logging.Logger;
import gov.aps.jca.CAException;
import ch.psi.fda.core.Action;
import ch.psi.jcae.ChannelBean;
import ch.psi.jcae.ChannelBeanFactory;
/**
* Perform a put on the specified Channel Access channel. The put can be done synchronous or
* asynchronously.
* @author ebner
*
*/
public class ChannelAccessCondition<E> implements Action {
// Get Logger
private static Logger logger = Logger.getLogger(ChannelAccessCondition.class.getName());
/**
* Channel to set
*/
private final ChannelBean<E> channel;
/**
* Value to wait for
*/
private final E expectedValue;
private final Long timeout;
/**
* Constructor
* @param channelName Name of the channel to set the value
* @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 Unable to initialize channel,
* Timeout specified is not >=0
*/
@SuppressWarnings("unchecked")
public ChannelAccessCondition(String channelName, E expectedValue, Long timeout){
if(timeout !=null && timeout<=0){
throw new IllegalArgumentException("Timeout must be > 0");
}
try {
this.channel = (ChannelBean<E>) ChannelBeanFactory.getFactory().createChannelBean( (Class<E>) expectedValue.getClass(), channelName, false);
} catch (CAException e) {
// Convert Exception into unchecked RuntimeException
throw new IllegalArgumentException("Unable to initialize actuator channel [name:"+channelName+"]",e);
} catch (InterruptedException e) {
throw new RuntimeException("Unable to initialize actuator channel [name:"+channelName+"]",e);
}
this.expectedValue = expectedValue;
if(timeout==null){
this.timeout = channel.getWaitTimeout();
}
else{
this.timeout = timeout;
}
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Action#execute()
*/
/**
* @throws InterruptedException
* @throws RuntimeException Channel value did not reach expected value (within the specified timeout period)
*/
@Override
public void execute() throws InterruptedException {
logger.finest("Checking channel "+channel.getName()+" for value "+expectedValue+" [timeout: "+timeout+"]" );
try{
channel.waitForValue(expectedValue, timeout); // Workaround use 10seconds default set timeout to check several times whether the channel has reached the value
} catch (CAException e) {
throw new RuntimeException("Channel [name:"+channel.getName()+"] did not reach expected value "+expectedValue+" ", e);
}
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Action#abort()
*/
@Override
public void abort() {
// This action cannot be aborted, therefore this method is not implemented
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Action#destroy()
*/
@Override
public void destroy() {
// Destroy channel
try {
logger.finest("Destroy action channel: "+channel.getName());
channel.destroy();
} catch (CAException e) {
throw new RuntimeException("Unable to destroy channel ["+channel.getName()+"]",e);
}
}
}
@@ -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.logging.Logger;
import gov.aps.jca.CAException;
import ch.psi.fda.core.Action;
import ch.psi.jcae.ChannelBean;
import ch.psi.jcae.ChannelBeanFactory;
/**
* Perform a put on the specified Channel Access channel. The put can be done synchronous or
* asynchronously.
* @author ebner
*
*/
public class ChannelAccessPut<E> implements Action {
// Get Logger
private static Logger logger = Logger.getLogger(ChannelAccessPut.class.getName());
/**
* Channel to set
*/
private final ChannelBean<E> channel;
/**
* Value to set
*/
private final E value;
/**
* Put mode, true = fire and forget, false = wait for response
*/
private final boolean asynchronous;
private final Long timeout;
/**
* Constructor
* @param channelName Name of the channel to set the value
* @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)
*
* @throws IllegalArgumentException Unable to initialize channel
*/
@SuppressWarnings("unchecked")
public ChannelAccessPut(String channelName, E value, boolean asynchronous, Long timeout){
try {
this.channel = ChannelBeanFactory.getFactory().createChannelBean((Class<E>)value.getClass(), channelName, false);
} catch (CAException e) {
// Convert Exception into unchecked RuntimeException
throw new IllegalArgumentException("Unable to initialize actuator channel [name:"+channelName+"]",e);
} catch (InterruptedException e) {
throw new RuntimeException("Unable to initialize actuator channel [name:"+channelName+"]",e);
}
this.value = value;
this.asynchronous = asynchronous;
this.timeout = timeout;
}
/**
* Additional constructor for convenience. This constructor defaults the operation type to synchronous put.
* @param channelName Name of the channel to set the value
* @param value Value to set
*
* @throws RuntimeException Unable to initialize channel
*/
public ChannelAccessPut(String channelName, E value){
this(channelName, value, false, null);
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Action#execute()
*/
/**
* @throws InterruptedException
* @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.setValue(value, timeout);
}
}
} catch (CAException e) {
// Convert Exception into unchecked RuntimeException
throw new RuntimeException("Unable to set channel [name:"+channel.getName()+"] to value "+value, e);
}
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Action#abort()
*/
@Override
public void abort() {
// This action cannot be aborted, therefore this method is not implemented
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Action#destroy()
*/
@Override
public void destroy() {
// Destroy channel
try {
logger.finest("Destroy action channel: "+channel.getName());
channel.destroy();
} catch (CAException e) {
throw new RuntimeException("Unable to destroy channel ["+channel.getName()+"]",e);
}
}
}
@@ -0,0 +1,74 @@
/**
*
* 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 ...
* @author ebner
*
*/
public class Delay implements Action {
/**
* Time to wait
*/
private final long time;
/**
* Constructor
* @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;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Action#execute()
*/
@Override
public void execute() throws InterruptedException {
Thread.sleep(time);
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Action#abort()
*/
@Override
public void abort() {
// Not implemented because not needed
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Action#destroy()
*/
@Override
public void destroy() {
// Nothing to be done
}
}
@@ -0,0 +1,169 @@
/**
*
* 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 gov.aps.jca.CAException;
import java.util.List;
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;
import ch.psi.fda.core.scripting.JythonParameterMapping;
import ch.psi.fda.core.scripting.JythonParameterMappingChannel;
import ch.psi.jcae.ChannelBean;
import ch.psi.jcae.ChannelBeanFactory;
/**
* @author ebner
*
*/
public class JythonAction implements Action {
// Get Logger
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+"\\((.*)\\):";
/**
* Script engine of the manipulator
*/
private ScriptEngine engine;
/**
* Jython entry call
*/
private String jythonCall;
public JythonAction(String script, List<JythonParameterMappingChannel> mapping){
// 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));
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(JythonParameterMapping pm: mapping){
if(pm.getVariable().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);
}
StringBuffer buffer = new StringBuffer();
buffer.append(entryFunction);
buffer.append("(");
for(JythonParameterMappingChannel b: mapping){
// Create channel
ChannelBean<?> cb;
try {
cb = ChannelBeanFactory.getFactory().createChannelBean(b.getType(), b.getChannel(), true);
} catch (CAException e) {
throw new IllegalArgumentException("Unable to establish channel: "+b.getChannel(), e);
} catch (InterruptedException e) {
throw new RuntimeException("Unable to establish channel: "+b.getChannel(), e);
}
// Assign channel bean to variable
engine.put(b.getVariable(), cb);
buffer.append(b.getVariable());
buffer.append(",");
}
buffer.setCharAt(buffer.length()-1, ')');
jythonCall = buffer.toString();
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Action#execute()
*/
@Override
public void execute() {
try {
engine.eval(jythonCall);
} catch (ScriptException e) {
throw new RuntimeException("Action failed while executing the Jython script",e);
}
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Action#abort()
*/
@Override
public void abort() {
// TODO need to find a way to abort script execution
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Action#destroy()
*/
@Override
public void destroy() {
// Nothing to be done
}
}
@@ -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.core.actions;
import java.io.File;
import java.io.IOException;
import java.util.logging.Logger;
import ch.psi.fda.core.Action;
/**
* Action that executes a specified script when it is executed.
* @author ebner
*
*/
public class ShellAction implements Action{
// Get Logger
private static Logger logger = Logger.getLogger(ShellAction.class.getName());
/**
* 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;
/**
* Constructor
* @param script Name of the script to execute when this action is invoked
*
* @throws IllegalArgumentException Specified script does not exist
*/
public ShellAction(String script){
File s = new File(script);
if(!s.exists()){
throw new IllegalArgumentException("Script "+script+" does not exist.");
}
this.script = script;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Action#execute()
*/
/**
* @throws InterruptedException
* @throws RuntimeException Script did not exit with 0,
* Execution was interrupted,
* IO problem occurred
*/
@Override
public void execute() throws InterruptedException {
try{
logger.fine("Execute script "+script);
Process process = Runtime.getRuntime().exec(script);
int exitValue = process.waitFor();
logger.fine("Script ["+script+"] return value: "+exitValue);
// Check script exit value to 0 if != 0 then throw an runtime exception
if(exitValue != 0){
throw new RuntimeException("Script ["+script+"] returned with an exit value not equal to 0");
}
}
catch(IOException e){
// Convert Exception into unchecked RuntimeException
throw new RuntimeException("Unable to execute script: "+script,e);
}
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Action#abort()
*/
@Override
public void abort() {
// This action cannot be aborted, therefore this function is not implemented.
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Action#destroy()
*/
@Override
public void destroy() {
// Nothing to be done
}
}
@@ -0,0 +1,350 @@
/**
*
* 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 gov.aps.jca.CAException;
import java.util.logging.Logger;
import ch.psi.fda.core.Actor;
import ch.psi.fda.core.EngineConfiguration;
import ch.psi.jcae.ChannelBean;
import ch.psi.jcae.ChannelBeanFactory;
/**
* This actuator sets an Channel Access channel from a start to an end value by doing discrete steps.
* @author ebner
*
*/
public class ChannelAccessFunctionActuator<T> implements Actor {
// Get Logger
private static Logger logger = Logger.getLogger(ChannelAccessFunctionActuator.class.getName());
private boolean asynchronous = false;
/**
* Start value of the actuator
*/
private double start;
/**
* End value of the actuator
*/
private double end;
/**
* Step size of the move
*/
private double stepSize;
/**
* Move direction (start&lt;end = 1, start&gt;end = -1)
*/
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;
/**
* Channel Access channel of the actuator
*/
private ChannelBean<Double> channel;
/**
* Channel Access channel of the actuator
*/
private ChannelBean<T> doneChannel = null;
private final T doneValue;
private final long doneDelay;
private final double originalStart;
private final double originalEnd;
private final int originalDirection;
/**
* Move timeout
*/
private Long timeout;
private final Function function;
/**
* Constructor - Initialize actor
* @param channelName
* @param start
* @param end
* @param stepSize
* @param timeout Maximum move time (in milliseconds)
*/
public ChannelAccessFunctionActuator(String channelName, Function function, double start, double end, double stepSize, Long timeout){
this(channelName, null, null, 0, function, start, end, stepSize, timeout);
}
/**
* Constructor
* @param channelName
* @param doneChannelName 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)
*/
@SuppressWarnings("unchecked")
public ChannelAccessFunctionActuator(String channelName, String doneChannelName, 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;
// Initialize/create Channel Access channel
try {
channel = ChannelBeanFactory.getFactory().createChannelBean(Double.class, channelName, false);
} catch (CAException e) {
// Convert Exception into unchecked RuntimeException
throw new IllegalArgumentException("Unable to initialize actuator channel [name:"+channelName+"]",e);
} catch (InterruptedException e) {
throw new RuntimeException("Unable to initialize actuator channel [name:"+channelName+"]",e);
}
if(doneChannelName != null){
try {
doneChannel = ChannelBeanFactory.getFactory().createChannelBean((Class<T>)doneValue.getClass(), doneChannelName, false);
} catch (CAException e) {
// Convert Exception into unchecked RuntimeException
throw new IllegalArgumentException("Unable to initialize actuator channel [name:"+doneChannelName+"]",e);
} catch (InterruptedException e) {
throw new RuntimeException("Unable to initialize actuator channel [name:"+doneChannelName+"]",e);
}
}
}
/* (non-Javadoc)
* @see ch.psi.fda.engine.Actor#set()
*/
@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.setValue(fvalue, timeout);
}
}
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(EngineConfiguration.getInstance().isCheckActorSet()){
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 (CAException e) {
// Convert Exception into unchecked RuntimeException
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;
}
}
/* (non-Javadoc)
* @see ch.psi.fda.engine.Actor#hasNext()
*/
@Override
public boolean hasNext() {
return next;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Actor#init()
*/
@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;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Actor#reverse()
*/
@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
}
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Actor#reset()
*/
@Override
public void reset() {
this.start = this.originalStart;
this.end = this.originalEnd;
this.direction = this.originalDirection;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Actor#destroy()
*/
@Override
public void destroy() {
// Destroy channel
try {
logger.finest("Destroy actor channel: "+channel.getName());
channel.destroy();
} catch (CAException e) {
throw new RuntimeException("Unable to destroy channel ["+channel.getName()+"]",e);
}
// Destroy done channel if exists
if(doneChannel != null){
try {
logger.finest("Destroy actor done channel: "+doneChannel.getName());
doneChannel.destroy();
} catch (CAException e) {
throw new RuntimeException("Unable to destroy channel ["+channel.getName()+"]",e);
}
}
}
/**
* @return the asynchronous
*/
public boolean isAsynchronous() {
return asynchronous;
}
/**
* @param asynchronous the asynchronous to set
*/
public void setAsynchronous(boolean asynchronous) {
this.asynchronous = asynchronous;
}
}
@@ -0,0 +1,329 @@
/**
*
* 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 gov.aps.jca.CAException;
import java.util.logging.Logger;
import ch.psi.fda.core.Actor;
import ch.psi.fda.core.EngineConfiguration;
import ch.psi.jcae.ChannelBean;
import ch.psi.jcae.ChannelBeanFactory;
/**
* This actuator sets an Channel Access channel from a start to an end value by doing discrete steps.
* @author ebner
*
*/
public class ChannelAccessLinearActuator<T> implements Actor {
// Get Logger
private static Logger logger = Logger.getLogger(ChannelAccessLinearActuator.class.getName());
private boolean asynchronous = false;
/**
* Start value of the actuator
*/
private double start;
/**
* End value of the actuator
*/
private double end;
/**
* Step size of the move
*/
private double stepSize;
/**
* Move direction (start&lt;end = 1, start&gt;end = -1)
*/
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;
/**
* Channel Access channel of the actuator
*/
private ChannelBean<Double> channel;
/**
* Channel Access channel of the actuator
*/
private ChannelBean<T> doneChannel = null;
private final T doneValue;
private final long doneDelay;
private final double originalStart;
private final double originalEnd;
private final int originalDirection;
/**
* Move timeout
*/
private Long timeout;
/**
* Constructor
* @param channelName
* @param doneChannelName 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)
*/
@SuppressWarnings("unchecked")
public ChannelAccessLinearActuator(String channelName, String doneChannelName, 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;
// Initialize/create Channel Access channel
try {
channel = ChannelBeanFactory.getFactory().createChannelBean(Double.class, channelName, false);
} catch (CAException e) {
// Convert Exception into unchecked RuntimeException
throw new IllegalArgumentException("Unable to initialize actuator channel [name:"+channelName+"]",e);
} catch (InterruptedException e) {
throw new RuntimeException("Unable to initialize actuator channel [name:"+channelName+"]",e);
}
if(doneChannelName != null){
try {
doneChannel = ChannelBeanFactory.getFactory().createChannelBean((Class<T>) doneValue.getClass(), doneChannelName, false);
} catch (CAException e) {
// Convert Exception into unchecked RuntimeException
throw new IllegalArgumentException("Unable to initialize actuator channel [name:"+doneChannelName+"]",e);
} catch (InterruptedException e) {
throw new RuntimeException("Unable to initialize actuator channel [name:"+doneChannelName+"]",e);
}
}
}
/* (non-Javadoc)
* @see ch.psi.fda.engine.Actor#set()
*/
@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.setValue(value, timeout);
}
}
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(EngineConfiguration.getInstance().isCheckActorSet()){
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 (CAException e) {
// Convert Exception into unchecked RuntimeException
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;
}
}
/* (non-Javadoc)
* @see ch.psi.fda.engine.Actor#hasNext()
*/
@Override
public boolean hasNext() {
return next;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Actor#init()
*/
@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;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Actor#reverse()
*/
@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
}
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Actor#reset()
*/
@Override
public void reset() {
this.start = this.originalStart;
this.end = this.originalEnd;
this.direction = this.originalDirection;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Actor#destroy()
*/
@Override
public void destroy() {
// Destroy channel
try {
logger.finest("Destroy actor channel: "+channel.getName());
channel.destroy();
} catch (CAException e) {
throw new RuntimeException("Unable to destroy channel ["+channel.getName()+"]",e);
}
// Destroy done channel if exists
if(doneChannel != null){
try {
logger.finest("Destroy actor done channel: "+doneChannel.getName());
doneChannel.destroy();
} catch (CAException e) {
throw new RuntimeException("Unable to destroy channel ["+channel.getName()+"]",e);
}
}
}
/**
* @return the asynchronous
*/
public boolean isAsynchronous() {
return asynchronous;
}
/**
* @param asynchronous the asynchronous to set
*/
public void setAsynchronous(boolean asynchronous) {
this.asynchronous = asynchronous;
}
}
@@ -0,0 +1,314 @@
/**
*
* 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 gov.aps.jca.CAException;
import java.util.logging.Logger;
import ch.psi.fda.core.Actor;
import ch.psi.fda.core.EngineConfiguration;
import ch.psi.jcae.ChannelBean;
import ch.psi.jcae.ChannelBeanFactory;
/**
* This actuator sets an Channel Access channel by using the positions from the given table.
* @author ebner
*
*/
public class ChannelAccessTableActuator<T> implements Actor {
// Get Logger
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;
/**
* Channel Access channel of the actuator
*/
private ChannelBean<Double> channel;
/**
* Channel Access channel of the actuator
*/
private ChannelBean<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;
/**
* 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(String channelName, double[] table, Long timeout){
this(channelName, null, null, 0, table, timeout);
}
/**
* Constructor
* @param channelName
* @param doneChannelName
* @param doneValue
* @param doneDelay
* @param table
* @param timeout Maximum move time (in milliseconds)
*/
@SuppressWarnings("unchecked")
public ChannelAccessTableActuator(String channelName, String doneChannelName, 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;
// Initialize/create Channel Access channel
try {
channel = ChannelBeanFactory.getFactory().createChannelBean(Double.class, channelName, false);
} catch (CAException e) {
// Convert Exception into unchecked RuntimeException
throw new IllegalArgumentException("Unable to initialize actuator channel [name:"+channelName+"]",e);
} catch (InterruptedException e) {
throw new RuntimeException("Unable to initialize actuator channel [name:"+channelName+"]",e);
}
if(doneChannelName != null){
try {
doneChannel = ChannelBeanFactory.getFactory().createChannelBean((Class<T>)doneValue.getClass(), doneChannelName, false);
} catch (CAException e) {
// Convert Exception into unchecked RuntimeException
throw new IllegalArgumentException("Unable to initialize actuator channel [name:"+doneChannelName+"]",e);
} catch (InterruptedException e) {
throw new RuntimeException("Unable to initialize actuator channel [name:"+doneChannelName+"]",e);
}
}
}
/* (non-Javadoc)
* @see ch.psi.fda.engine.Actor#set()
*/
@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.setValue(table[count], timeout);
}
}
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(EngineConfiguration.getInstance().isCheckActorSet()){
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 (CAException e) {
// Convert Exception into unchecked RuntimeException
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;
}
}
}
/* (non-Javadoc)
* @see ch.psi.fda.engine.Actor#hasNext()
*/
@Override
public boolean hasNext() {
return next;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Actor#init()
*/
@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;
}
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Actor#reverse()
*/
@Override
public void reverse() {
if(positiveDirection){
positiveDirection=false;
}
else{
positiveDirection=true;
}
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Actor#reset()
*/
@Override
public void reset() {
this.positiveDirection = this.originalPositiveDirection;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Actor#destroy()
*/
@Override
public void destroy() {
// Destroy channel
try {
logger.finest("Destroy actor channel: "+channel.getName());
channel.destroy();
} catch (CAException e) {
throw new RuntimeException("Unable to destroy channel ["+channel.getName()+"]",e);
}
// Destroy done channel if exists
if(doneChannel != null){
try {
logger.finest("Destroy actor done channel: "+doneChannel.getName());
doneChannel.destroy();
} catch (CAException e) {
throw new RuntimeException("Unable to destroy channel ["+channel.getName()+"]",e);
}
}
}
/**
* @return the asynchronous
*/
public boolean isAsynchronous() {
return asynchronous;
}
/**
* @param asynchronous the asynchronous to set
*/
public void setAsynchronous(boolean asynchronous) {
this.asynchronous = asynchronous;
}
}
@@ -0,0 +1,261 @@
/**
*
* 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.
*
* @author ebner
*
*/
public class ComplexActuator implements Actor {
// Get Logger
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;
/**
* Constructor
*/
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;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Actor#set()
*/
@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.");
}
// 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();
}
}
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Actor#hasNext()
*/
@Override
public boolean hasNext() {
return next;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Actor#init()
*/
@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;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Actor#reverse()
*/
@Override
public void reverse() {
// Reverse all actors this actor consist of
for(Actor a: actors){
a.reverse();
}
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Actor#reset()
*/
@Override
public void reset() {
// Reset all actors this actor consist of
for(Actor a: actors){
a.reset();
}
}
/**
* Get the list of actors of this ComplexActor
* @return the actors
*/
public List<Actor> getActors() {
return actors;
}
/**
* @return the preActions
*/
public List<Action> getPreActions() {
return preActions;
}
/**
* @return the postActions
*/
public List<Action> getPostActions() {
return postActions;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Actor#destroy()
*/
@Override
public void destroy() {
// Destroy preActions
for(Action a: preActions){
a.destroy();
}
// Destroy actors
for(Actor a: actors){
a.destroy();
}
// Destroy postActions
for(Action a: postActions){
a.destroy();
}
}
}
@@ -0,0 +1,28 @@
/**
*
* 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;
/**
* @author ebner
*
*/
public interface Function {
public double calculate(double parameter);
}
@@ -0,0 +1,113 @@
/**
*
* 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;
/**
* @author ebner
*
*/
public class JythonFunction implements Function {
// Get Logger
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+"\\((.*)\\):";
/**
* Script engine of the manipulator
*/
private ScriptEngine engine;
private String additionalParameter = "";
public JythonFunction(String script, Map<String, JythonGlobalVariable> map){
// Create new script engine
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);
}
}
/* (non-Javadoc)
* @see ch.psi.fda.core.actors.Function#calculate(double)
*/
@Override
public double calculate(double parameter) {
logger.fine("Function called");
try {
return ((Double) engine.eval("calculate( "+parameter+""+additionalParameter+" )"));
} catch (ScriptException e) {
throw new RuntimeException("Calculating actuator step failed while executing the Jython script",e);
}
}
}
@@ -0,0 +1,188 @@
/**
*
* 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;
/**
* Special actuator for the OTFLoop. This type of actor must not be used in any other
* type of loop than OTFLoop. If it is used it will, depending on the loop, immediately stop the loop
* as it no single step.
* @author ebner
*
*/
public class OTFActuator implements Actor {
/**
* Name of the motor channel
*/
private final String name;
/**
* Channel name of the encoder;
*/
private final String readback;
private double start;
private double end;
private final double stepSize;
private final double integrationTime;
private final String id;
/**
* Additional backlash for the motor
*/
private final double additionalBacklash;
public OTFActuator(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;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Actor#set()
*/
@Override
public void set() {
// Do nothing
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Actor#hasNext()
*/
@Override
public boolean hasNext() {
return false;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @return the readback
*/
public String getReadback() {
return readback;
}
/**
* @return the start
*/
public double getStart() {
return start;
}
/**
* @return the end
*/
public double getEnd() {
return end;
}
/**
* @return the stepSize
*/
public double getStepSize() {
return stepSize;
}
/**
* @return the integrationTime
*/
public double getIntegrationTime() {
return integrationTime;
}
/**
* @return the additionalBacklash
*/
public double getAdditionalBacklash() {
return additionalBacklash;
}
/**
* @return the id
*/
public String getId() {
return id;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Actor#init()
*/
@Override
public void init() {
// Not implemented
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Actor#reverse()
*/
@Override
public void reverse() {
// Not implemented
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Actor#reset()
*/
@Override
public void reset() {
// Not implemented
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Actor#destroy()
*/
@Override
public void destroy() {
// Nothing to be done
}
/**
* @param start the start to set
*/
public void setStart(double start) {
this.start = start;
}
/**
* @param end the end to set
*/
public void setEnd(double end) {
this.end = end;
}
}
@@ -0,0 +1,135 @@
/**
*
* 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
* @author ebner
*
*/
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;
/**
* Constructor
* @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();
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Actor#set()
*/
@Override
public void set() {
// Throw an IllegalStateException in the case that set is called although there is no next step.
if(!hasNext()){
throw new IllegalStateException("The actuator does not have any next step.");
}
count++;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Actor#hasNext()
*/
@Override
public boolean hasNext() {
return (count<counts);
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Actor#init()
*/
@Override
public void init() {
count=0; // Set count back to 0
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Actor#reverse()
*/
@Override
public void reverse() {
// Not implemented
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Actor#reset()
*/
@Override
public void reset() {
// NOt implemented
}
// Sensor implementation
/* (non-Javadoc)
* @see ch.psi.fda.core.Sensor#read()
*/
@Override
public Object read() {
return new Double(count); // Return actual count
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Sensor#getId()
*/
@Override
public String getId() {
return this.id;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Actor#destroy()
*/
@Override
public void destroy() {
// Nothing to be done
}
}
@@ -0,0 +1,165 @@
/**
*
* 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.collector;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.Logger;
import ch.psi.fda.core.messages.ComponentMetadata;
import ch.psi.fda.core.messages.DataMessage;
import ch.psi.fda.core.messages.DataMessageMetadata;
import ch.psi.fda.core.messages.DataQueue;
import ch.psi.fda.core.messages.StreamDelimiterMessage;
import ch.psi.fda.core.messages.EndOfStreamMessage;
import ch.psi.fda.core.messages.Message;
/**
* Collector class that is collecting and merging data from different Queues.
* @author ebner
*
*/
public class Collector implements Runnable{
// Get Logger
private static Logger logger = Logger.getLogger(Collector.class.getName());
/**
* List of queues and their metadata this collector is responsible to readout.
* The logic is as follows:
* Read out the last queue until the first queue in the list has the EndOfLoopMessage.
*/
private List<DataQueue> queues;
/**
* Outgoing queue of this collector
*/
private BlockingQueue<Message> outQueue;
/**
* Constructor
*/
public Collector(){
queues = new ArrayList<DataQueue>();
outQueue = new LinkedBlockingQueue<Message>(1000); // Create bounded queue to prevent running out of memory ...
}
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
if(queues.size()>0){
try{
readQueue(0, null);
}
catch(InterruptedException e){
// Readout aborted through interrupt
}
}
else{
// No queue registered for reading
}
try {
outQueue.put(new EndOfStreamMessage());
} catch (InterruptedException e) {
// TODO Stop loop and exit logic instead of throwing an Exception
throw new RuntimeException("Unable to terminate stream with and End of Stream Message",e);
}
logger.info("END");
}
private void readQueue(int index, DataMessage m) throws InterruptedException{
BlockingQueue<Message> q = queues.get(index).getQueue();
// Read Message
Message message = q.take();
while(message instanceof DataMessage){
// Create new data message
DataMessage dm = new DataMessage();
if(m!=null){
dm.getData().addAll(m.getData());
}
dm.getData().addAll(((DataMessage)message).getData());
if(index<(queues.size()-1)){
readQueue(index+1, dm);
}
else{
// Write message to outgoing queue
outQueue.put(dm);
}
// Read next message
message = q.take();
}
if(message instanceof EndOfStreamMessage){
// Translate EndOfStream to StreamDelimiter message
StreamDelimiterMessage ddm = new StreamDelimiterMessage(queues.size()-1-index, ((EndOfStreamMessage)message).isIflag());
// Write message to outgoing queue
outQueue.put(ddm);
}
}
/**
* @return the queues
*/
public List<DataQueue> getQueues() {
return queues;
}
/**
* Get the outgoing data queue.
* Attention, only call this method after all ingoing queues were registered! Otherwise the data returned
* by this method is not accurate.
* @return output queue of collector
*/
public DataQueue getOutQueue(){
DataMessageMetadata dataMessageMetadata = new DataMessageMetadata();
dataMessageMetadata.getComponents();
// Generate new combined metadata and add dimension information to the components
int nq = queues.size();
for(int i=0;i<nq;i++){
DataQueue q = queues.get(i);
for(ComponentMetadata cm: q.getDataMessageMetadata().getComponents()){
// The dimension number is the number of queues minus the index of the current queue minus 1 (because dimension index starts at 0)
dataMessageMetadata.getComponents().add(new ComponentMetadata(cm.getId(), nq-i-1));
}
}
return(new DataQueue(outQueue, dataMessageMetadata));
}
}
@@ -0,0 +1,88 @@
/**
*
* 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.collector;
import java.util.ArrayList;
import java.util.List;
import ch.psi.fda.core.messages.DataQueue;
import ch.psi.fda.core.messages.EndOfStreamMessage;
import ch.psi.fda.core.messages.Message;
/**
* Serialize data received by a DataQueue
* @author ebner
*
*/
public class DataDispatcher implements Runnable{
private DataQueue queue;
private List<DataQueue> outQueues;
public DataDispatcher(DataQueue queue){
this.queue = queue;
this.outQueues = new ArrayList<DataQueue>();
}
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
try{
// TODO Need to synchronize message metadata
// for(DataQueue q: outQueues){
// }
// Dispatch Messages
Message message = queue.getQueue().take();
while(!(message instanceof EndOfStreamMessage)){
// Clone message ...
for(DataQueue q: outQueues){
q.getQueue().put(message);
}
// Read next message
message = queue.getQueue().take();
}
// Write end of stream message
for(DataQueue q: outQueues){
q.getQueue().put(message);
}
} catch (InterruptedException e) {
// TODO Stop loop and exit logic instead of throwing an Exception
throw new RuntimeException("Data serializer was interrupted while writing data to file",e);
}
}
/**
* @return the outQueues
*/
public List<DataQueue> getOutQueues() {
return outQueues;
}
}
@@ -0,0 +1,117 @@
/**
*
* 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 gov.aps.jca.CAException;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import ch.psi.fda.core.Guard;
/**
* Guard checking channels to meet a certain condition
* @author ebner
*
*/
public class ChannelAccessGuard implements Guard {
// Get Logger
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 boolean check = true;
private final List<ChannelAccessGuardCondition> conditions;
/**
* Constructor
* @param conditions
*/
public ChannelAccessGuard(List<ChannelAccessGuardCondition> conditions){
this.conditions = conditions;
// Create channel that contribute to the status of the guard
for(final ChannelAccessGuardCondition condition: conditions){
condition.getChannel().addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if(! evt.getNewValue().equals(condition.getValue())){
check=false;
}
}
});
}
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Guard#init()
*/
@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;
// Early exit
break;
}
}
catch(CAException e){
logger.log(Level.WARNING, "Unable ", e);
check=false;
} catch (InterruptedException e) {
throw new RuntimeException("Guard interrupted ",e);
}
}
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Guard#check()
*/
@Override
public boolean check() {
return check;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Guard#destroy()
*/
@Override
public void destroy() {
// Destroy Guard Conditions
for(ChannelAccessGuardCondition condition: conditions){
condition.destroy();
}
}
}
@@ -0,0 +1,96 @@
/**
*
* 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.util.logging.Logger;
import gov.aps.jca.CAException;
import ch.psi.jcae.ChannelBean;
import ch.psi.jcae.ChannelBeanFactory;
/**
* Channel and condition that need to be met.
* @author ebner
*
*/
public class ChannelAccessGuardCondition {
// Get Logger
private static Logger logger = Logger.getLogger(ChannelAccessGuardCondition.class.getName());
/**
* Channel name
*/
private final ChannelBean<?> channel;
/**
* Value of the channel to meet condition
*/
private final Object value;
/**
* Constructor
* @param channel Name of the channel that contributes to a guard
* @param value
*/
public ChannelAccessGuardCondition(String channel, Object value){
try {
this.channel = ChannelBeanFactory.getFactory().createChannelBean(value.getClass(), channel, true);
} catch (CAException e) {
// Convert Exception into unchecked RuntimeException
throw new IllegalArgumentException("Unable to initialize actuator channel [name:"+channel+"]",e);
} catch (InterruptedException e) {
throw new RuntimeException("Unable to initialize actuator channel [name:"+channel+"]",e);
}
this.value = value;
}
/**
* @return the channel
*/
public ChannelBean<?> getChannel() {
return channel;
}
/**
* @return the value
*/
public Object getValue() {
return value;
}
/**
* Destroy guard condition.
* Can be used for the cleanup of used resources of the guard condition (e.g. to close connections, ...) if
* this cannot be done automatically by the GarbageCollector.
*
* After calling this method the guard condition must not be used any more!
*/
public void destroy(){
// Destroy channel
try {
logger.finest("Destroy guard condition channel: "+channel.getName());
channel.destroy();
} catch (CAException e) {
throw new RuntimeException("Unable to destroy channel ["+channel.getName()+"]",e);
}
}
}
@@ -0,0 +1,564 @@
/**
*
* 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.BlockingQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.Logger;
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.core.messages.ComponentMetadata;
import ch.psi.fda.core.messages.DataMessage;
import ch.psi.fda.core.messages.DataMessageMetadata;
import ch.psi.fda.core.messages.DataQueue;
import ch.psi.fda.core.messages.EndOfStreamMessage;
import ch.psi.fda.core.messages.Message;
/**
* Loop of actions to accomplish a task or test.
* @author ebner
*
*/
public class ActorSensorLoop implements ActionLoop {
// Get Logger
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;
/**
* Data queue sensor data is posted to. A message consists of a list of data objects
* that are read out of the sensors of this loop.
*/
private BlockingQueue<Message> dataQueue;
/**
* Guard used to check whether the environment was ok while reading out the sensors
*/
private Guard guard = null;
private boolean loop = false;
private final boolean zigZag;
private List<ActorSetCallable> pactors;
/**
* Default constructor
*/
public ActorSensorLoop(){
this(false);
}
/**
* Create instance of the
*/
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>();
this.dataQueue = new LinkedBlockingQueue<Message>(1000);
}
/* (non-Javadoc)
* @see ch.psi.fda.engine.ActionLoop#execute()
*/
/**
* 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 {
/**
* 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;
// 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();
}
// 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(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();
}
// Read sensors
DataMessage message = new DataMessage();
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();
}
// Check guard if one is registered
if(guard!=null){
guardOK=guard.check();
}
if(guardOK){
// Post a message with the sensor data
dataQueue.put(message);
// Loop all configured ActionLoop objects
for(ActionLoop actionLoop: actionLoops){
actionLoop.execute();
}
}
}
// Execute post actions
for(Action action: postActions){
action.execute();
}
// Issue end of loop control message
// Set iflag of the EndOfStreamMessage according to dataGroup flag of this loop
dataQueue.put(new EndOfStreamMessage(dataGroup));
if(zigZag){
// Reverse actors for the next run
for(Actor actor: actors){
actor.reverse();
}
}
executorService.shutdownNow();
}
/* (non-Javadoc)
* @see ch.psi.fda.engine.ActionLoop#abort()
*/
@Override
public void abort(){
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();
}
}
/* (non-Javadoc)
* @see ch.psi.fda.core.ActionLoop#prepare()
*/
@Override
public void prepare() {
// Put in prepare code here
// 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();
}
}
/* (non-Javadoc)
* @see ch.psi.fda.core.ActionLoop#cleanup()
*/
@Override
public void cleanup() {
// Put in cleanup code here
// Recursively call cleanup() method of all registered action loops
for(ActionLoop actionLoop: actionLoops){
actionLoop.cleanup();
}
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Action#destroy()
*/
@Override
public void destroy() {
// Call destroy method of sub components
for(Action a: preActions){
logger.finest("Destroy pre-action");
a.destroy();
}
for(Action a: postActions){
logger.finest("Destroy post-action");
a.destroy();
}
for(Action a: preActorActions){
logger.finest("Destroy pre-actor action");
a.destroy();
}
for(Action a: postActorActions){
logger.finest("Destroy post-actor action");
a.destroy();
}
for(Action a: preSensorActions){
logger.finest("Destroy pre-sensor action");
a.destroy();
}
for(Action a: postSensorActions){
logger.finest("Destroy post-sensor action");
a.destroy();
}
for(Actor a: actors){
logger.finest("Destroy actor");
a.destroy();
}
for(Sensor s: sensors){
logger.finest("Destroy sensor");
s.destroy();
}
if(guard != null){
logger.finest("Destroy guard");
guard.destroy();
}
// Recursively call cleanup() method of all registered action loops
for(ActionLoop actionLoop: actionLoops){
logger.finest("Destroy action loop");
actionLoop.destroy();
}
}
/* (non-Javadoc)
* @see ch.psi.fda.core.ActionLoop#getPreActions()
*/
@Override
public List<Action> getPreActions() {
return preActions;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.ActionLoop#getPostActions()
*/
@Override
public List<Action> getPostActions() {
return postActions;
}
/**
* The structure of the data message depends on the sensors registered at this loop
* at the time this method is called.
* @return the data queue and the metadata of the data messages
*/
public DataQueue getDataQueue() {
DataMessageMetadata m = new DataMessageMetadata();
// Build up data message metadata based on the sensors currently registered.
for(Sensor s: sensors){
m.getComponents().add(new ComponentMetadata(s.getId()));
}
return new DataQueue(dataQueue, m);
}
// Getter functions for variable parts
/**
* @return the actionLoops
*/
public List<ActionLoop> getActionLoops() {
return actionLoops;
}
/**
* @return the preActorActions
*/
public List<Action> getPreActorActions() {
return preActorActions;
}
/**
* @return the postActorActions
*/
public List<Action> getPostActorActions() {
return postActorActions;
}
/**
* @return the preSensorActions
*/
public List<Action> getPreSensorActions() {
return preSensorActions;
}
/**
* @return the postSensorActions
*/
public List<Action> getPostSensorActions() {
return postSensorActions;
}
/**
* @return the actors
*/
public List<Actor> getActors() {
return actors;
}
/**
* @return the sensors
*/
public List<Sensor> getSensors() {
return sensors;
}
/**
* @return the guard
*/
public Guard getGuard() {
return guard;
}
/**
* @param guard the guard to set
*/
public void setGuard(Guard guard) {
this.guard = guard;
}
/**
* @return the dataGroup
*/
public boolean isDataGroup() {
return dataGroup;
}
/**
* @param dataGroup the dataGroup to set
*/
public void setDataGroup(boolean dataGroup) {
this.dataGroup = dataGroup;
}
}
@@ -0,0 +1,770 @@
/**
*
* 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 gov.aps.jca.CAException;
import java.util.List;
import ch.psi.jcae.annotation.CaChannel;
import ch.psi.jcae.ChannelBean;
/**
* Bean holding all OTF channels and functionality
* @author ebner
*
*/
public class OTFBean {
public enum Status { SETUP, INACTIVE, INITIALIZE, ACTIVE, STOP, FAULT, ERROR };
public enum Command { NONE, START, STOP };
private long timeoutMotorOk = 8000;
private long commandTimeout = 20000; // Maximum time until a command should take effect
@CaChannel(type=String.class, name =":UMOT")
private ChannelBean<String> motor;
@CaChannel(type=String.class, name=":MENC")
private ChannelBean<String> encoder;
@CaChannel(type=Double.class, name=":UBEG")
private ChannelBean<Double> begin;
@CaChannel(type=Double.class, name=":UBEG.DRVL")
private ChannelBean<Double> beginMin;
@CaChannel(type=Double.class, name=":UBEG.DRVH")
private ChannelBean<Double> beginMax;
@CaChannel(type=Double.class, name=":UEND")
private ChannelBean<Double> end;
@CaChannel(type=Double.class, name=":UEND.DRVL")
private ChannelBean<Double> endMin;
@CaChannel(type=Double.class, name=":UEND.DRVH")
private ChannelBean<Double> endMax;
@CaChannel(type=Double.class, name=":USSIZ")
private ChannelBean<Double> stepSize;
@CaChannel(type=Double.class, name=":USSIZ.DRVL")
private ChannelBean<Double> stepSizeMin;
@CaChannel(type=Double.class, name=":UITIM")
private ChannelBean<Double> integrationTime;
@CaChannel(type=Double.class, name=":UITIM.DRVL")
private ChannelBean<Double> integrationTimeMin;
@CaChannel(type=Double.class, name=":UITIM.DRVH")
private ChannelBean<Double> integrationTimeMax;
@CaChannel(type=Double.class, name=":UBCL")
private ChannelBean<Double> userBacklash;
@CaChannel(type=String.class, name=":NFSSE")
private ChannelBean<String> nfsServer;
@CaChannel(type=String.class, name=":NFSSH")
private ChannelBean<String> nfsShare;
@CaChannel(type=String.class, name=":DFNAM")
private ChannelBean<String> fileName;
@CaChannel(type=String.class, name=":FFORM")
private ChannelBean<String> fileNameFormat;
@CaChannel(type=Integer.class, name=":FCNT")
private ChannelBean<Integer> fileCount;
@CaChannel(type=Integer.class, name=":FCNT.B")
private ChannelBean<Integer> resetFileCounter;
@CaChannel(type=Boolean.class, name=":FAPPE")
private ChannelBean<Boolean> appendFile;
@CaChannel(type=Boolean.class, name=":FUSE")
private ChannelBean<Boolean> fileNameGeneration;
@CaChannel(type=Boolean.class, name=":UZIGZ")
private ChannelBean<Boolean> zigZag;
@CaChannel(type=Integer.class, name=":UCOM")
private ChannelBean<Integer> command;
@CaChannel(type=Boolean.class, name=":SCRU", monitor=true)
private ChannelBean<Boolean> scanRunning;
@CaChannel(type=Boolean.class, name=":MUENC")
private ChannelBean<Boolean> useEncoder;
@CaChannel(type=String.class, name={":CTM0",":CTM1",":CTM2",":CTM3",":CTM4",":CTM5",":CTM6",":CTM7"})
private List<ChannelBean<String>> monitoredChannels;
@CaChannel(type=Boolean.class, name=":OTF", monitor=true)
private ChannelBean<Boolean> running;
@CaChannel(type=Integer.class, name=":USTAT", monitor=true)
private ChannelBean<Integer> status;
@CaChannel(type=Boolean.class, name=":MOK", monitor=true)
private ChannelBean<Boolean> motorOk;
@CaChannel(type=Boolean.class, name=":EOK", monitor=true)
private ChannelBean<Boolean> encoderOk;
@CaChannel(type=String.class, name=":MSG")
private ChannelBean<String> message;
/**
* Get the trigger name that can be used by the sscan record to trigger an OTFScan
* @return Name of the trigger that can be used by sscan record
*/
public String getSScanTriggerName(){
return(running.getName());
}
/**
* Start OTF scan
*/
public void start() {
try{
if(getStatus().equals(Status.INACTIVE)){
// Send START command
this.command.setValue(Command.START.ordinal());
// Wait until OtF logic is active
this.scanRunning.waitForValue(true, commandTimeout);
}
else{
throw new RuntimeException("Cannot start scan, status is not INACTIVE.\nThe current status is: "+getStatus()+" . The OTF logic need to be recovered manually [Message: "+getMessage()+"]");
}
}
catch(Exception e){
throw new RuntimeException("Unable to start OTF scan.",e);
}
}
/**
* Abort scan
*/
public void abort() {
try{
// Send STOP command
this.command.setValue(Command.STOP.ordinal());
// Do not wait for put to return
this.running.setValueNoWait(false);
}
catch(Exception e){
throw new RuntimeException("Unable to abort OTF logic" ,e);
}
}
/**
* Stop OTF scan
* @throws Exception
*/
public void stop() throws Exception{
if(!getStatus().equals(Status.INACTIVE) || !getStatus().equals(Status.FAULT)){
// Send STOP command
this.command.setValue(Command.STOP.ordinal());
// Wait until logic is stopped
status.waitForValue(Status.INACTIVE.ordinal(), commandTimeout);
}
}
/**
* Wait until scan has stopped
* @throws InterruptedException
*/
public void waitUntilStopped() throws InterruptedException {
try {
scanRunning.waitForValue(false); // Use of default wait timeout
// Check whether the status is INACTIVE otherwise get messge from OTF and throw an exception
if(status.getValue() != Status.INACTIVE.ordinal()){
throw new RuntimeException("OTFSCAN failed with message: "+message.getValue());
}
} catch (CAException e) {
throw new RuntimeException("An error occurred while waiting for the OTF logic to finish.", e);
}
}
/**
* Wait until scan has stopped
* @param waitTimeout
* @throws InterruptedException
*/
public void waitUntilStopped(Long waitTimeout) throws InterruptedException {
try {
scanRunning.waitForValue(false, waitTimeout);
// Check whether the status is INACTIVE otherwise get messge from OTF and throw an exception
if(status.getValue() != Status.INACTIVE.ordinal()){
throw new RuntimeException("OTFSCAN failed with message: "+message.getValue());
}
} catch (CAException e) {
throw new RuntimeException("An error occurred while waiting for the OTF logic to finish.", e);
}
}
/**
* Reset OTFScan records to defaults
* @throws CAException
* @throws InterruptedException
*/
public void resetToDefaults() throws CAException, InterruptedException{
setMonitoredChannels(new String[]{});
setMotor("");
begin.setValue(0d);
end.setValue(0d);
stepSize.setValue(0d);
integrationTime.setValue(0d);
zigZag.setValue(false);
setAppendFile(false);
setFileNameGeneration(true);
setFileName("");
setFileNameFormat("%06d.txt");
resetFileCounter();
setUserBacklash(0d);
// setNfsServer("");
// setNfsShare("");
waitUntilMotorNotOk(timeoutMotorOk);
}
/**
* Get motor of the OTFScan axis
* @return Name of the OTF motor
* @throws CAException
*/
public String getMotor() throws CAException, InterruptedException {
return(this.motor.getValue());
}
/**
* Set motor of the OTFScan axis
* @param motor
* @throws CAException
*/
public void setMotor(String motor) throws CAException, InterruptedException {
this.motor.setValue(motor);
}
/**
* Get encoder of the OTFScan axis
* @return Name of the used encoder
* @throws CAException
*/
public String getEncoder() throws CAException, InterruptedException {
return(this.encoder.getValue());
}
/**
* Set encoder to use of the OTFScan axis
* @param encoder
* @throws CAException
*/
public void setEncoder(String encoder) throws CAException, InterruptedException {
this.encoder.setValue(encoder);
}
/**
* Get begin position of the scan
* @return Begin position scan
* @throws CAException
*/
public Double getBegin() throws CAException, InterruptedException {
return(this.begin.getValue());
}
/**
* Set begin position of scan
* @param begin
* @throws Exception
*/
public void setBegin(Double begin) throws CAException, InterruptedException {
if(begin==null){
throw new IllegalArgumentException("Begin position must not be null");
}
if(begin < beginMin.getValue() || begin > beginMax.getValue()){
throw new IllegalArgumentException("Cannot set begin value to "+begin+ ". Value is outside range [min: "+beginMin.getValue()+" max: "+beginMax.getValue()+"]");
}
this.begin.setValue(begin);
}
/**
* Get minimum value of the begin position
* @return Min value for begin
* @throws CAException
*/
public Double getMinBegin() throws CAException, InterruptedException {
return(this.beginMin.getValue());
}
/**
* Get maximum value of the begin position
* @return Max value for begin
* @throws CAException
*/
public Double getMaxBegin() throws CAException, InterruptedException {
return(this.beginMax.getValue());
}
/**
* Get end position of the scan
* @return End position scan
* @throws CAException
*/
public Double getEnd() throws CAException, InterruptedException {
return(this.end.getValue());
}
/**
* Set end positon of scan
* @param end
* @throws CAException
*/
public void setEnd(Double end) throws CAException, InterruptedException {
if(end==null){
throw new IllegalArgumentException("End position must not be null");
}
if(end < endMin.getValue() || end > endMax.getValue()){
throw new IllegalArgumentException("Cannot set end value to "+end+ ". Value is outside range [min: "+endMin.getValue()+" max: "+endMax.getValue()+"]");
}
this.end.setValue(end);
}
/**
* Get minimum value of end position
* @return Min value for end
* @throws CAException
*/
public Double getMinEnd() throws CAException, InterruptedException {
return(this.endMin.getValue());
}
/**
* Get maximum value of end position
* @return Max value for end
* @throws CAException
*/
public Double getMaxEnd() throws CAException, InterruptedException {
return(this.endMax.getValue());
}
/**
* Get scan step size
* @return Step size
* @throws CAException
*/
public Double getStepSize() throws CAException, InterruptedException {
return(this.stepSize.getValue());
}
/**
* Set step size of scan
* @param stepSize
* @throws CAException
*/
public void setStepSize(Double stepSize) throws CAException, InterruptedException {
if(integrationTime==null){
throw new IllegalArgumentException("Step size must not be null");
}
// Check if step size is greater than min step size
if(stepSizeMin.getValue() != 0 && stepSize < stepSizeMin.getValue()){
throw new IllegalArgumentException("Step size value ["+stepSize+"] is less than minimum step size ["+stepSizeMin.getValue()+"]!");
}
this.stepSize.setValue(stepSize);
// TODO WORKAROUND - Wait to "ensure" that step size related fields are updated (i.e. min/max integration time)
Thread.sleep(1);
}
/**
* Get minimum integration time
* @return Min value for step size
* @throws CAException
*/
public double getMinStepSize() throws CAException, InterruptedException {
return(this.stepSizeMin.getValue());
}
/**
* Get scan integration time (time that is spend in one step)
* @return Integration time
* @throws CAException
*/
public Double getIntegrationTime() throws CAException, InterruptedException {
return(this.integrationTime.getValue());
}
/**
* Set integration time of scan
* @param integrationTime
* @throws CAException
*/
public void setIntegrationTime(Double integrationTime) throws CAException, InterruptedException {
if(integrationTime==null){
throw new IllegalArgumentException("Integration time must not be null");
}
// Check range (if limit is set to 0 then limit is not set)
double min = integrationTimeMin.getValue();
double max = integrationTimeMax.getValue();
if(min!= 0 && max!= 0){
if(integrationTime < min || integrationTime > max){
throw new IllegalArgumentException("Integration time ["+integrationTime+"] is outside range [min: "+min+" max: "+max+"]");
}
}
else {
if(min!= 0 && integrationTime<min){
throw new IllegalArgumentException("Integration time ["+integrationTime+"] is outside range [min: "+min+" max: - ]");
}
else if(max!= 0 && integrationTime>max){
throw new IllegalArgumentException("Integration time ["+integrationTime+"] is outside range [min: - max: "+max+"]");
}
}
this.integrationTime.setValue(integrationTime);
}
/**
* Get minimum integration time
* @return Min value for integration time
* @throws CAException
*/
public Double getMinIntegrationTime() throws CAException, InterruptedException {
return(this.integrationTimeMin.getValue());
}
/**
* Get maximum integration time
* @return Max value for integration time
* @throws CAException
*/
public Double getMaxIntegrationTime() throws CAException, InterruptedException {
return(this.integrationTimeMax.getValue());
}
/**
* Get additional user defined backlash
* @return User backlash
* @throws CAException
*/
public Double getUserBacklash() throws CAException, InterruptedException {
return(this.userBacklash.getValue());
}
/**
* Set additional user defined backlash
* @param userBacklash
* @throws CAException
*/
public void setUserBacklash(Double userBacklash) throws CAException, InterruptedException {
if(userBacklash==null){
throw new IllegalArgumentException("User backlash must not be null");
}
this.userBacklash.setValue(userBacklash);
}
/**
* Get the current NFS server the data is written to
* @return Name of NFS server
* @throws CAException
*/
public String getNfsServer() throws CAException, InterruptedException {
return(this.nfsServer.getValue());
}
/**
* Set name of the NFS server the data is written to
* @param nfsServer
* @throws CAException
*/
public void setNfsServer(String nfsServer) throws CAException, InterruptedException {
this.nfsServer.setValue(nfsServer);
}
/**
* Get the NFS share the data is written to
* @return Name of NFS share
* @throws CAException
*/
public String getNfsShare() throws CAException, InterruptedException {
return(this.nfsShare.getValue());
}
/**
* Set name of the NFS share the data is written to
* @param nfsShare
* @throws CAException
*/
public void setNfsShare(String nfsShare) throws CAException, InterruptedException {
this.nfsShare.setValue(nfsShare);
}
/**
* Get the name of the data file
* @return Name of data file name
* @throws CAException
*/
public String getFileName() throws CAException, InterruptedException {
return(this.fileName.getValue());
}
/**
* Set name of the data file
* @param filename
* @throws CAException
*/
public void setFileName(String filename) throws CAException, InterruptedException {
this.fileName.setValue(filename);
}
/**
* Get File name formate
* @return Get format for file name
* @throws CAException
*/
public String getFileNameFormat() throws CAException, InterruptedException {
return(this.fileNameFormat.getValue());
}
/**
* Set file name formate of the data file
* @param fileNameFormat
* @throws Exception
*/
public void setFileNameFormat(String fileNameFormat) throws CAException, InterruptedException {
this.fileNameFormat.setValue(fileNameFormat);
}
/**
* Get value of the IOC based file name counter
* @return File counter
* @throws CAException
*/
public int getFileCounter() throws CAException, InterruptedException {
return(this.fileCount.getValue());
}
/**
* Reset the IOC based file counter
* @throws CAException
*/
public void resetFileCounter() throws CAException, InterruptedException {
this.resetFileCounter.setValue(1);
}
/**
* Get if append file option is activated
* @return Append file flag
* @throws CAException
*/
public boolean isAppendFile() throws CAException, InterruptedException {
return(this.appendFile.getValue());
}
/**
* Set whether to append the specified file if the file exists
* @param append
* @throws CAException
*/
public void setAppendFile(boolean append) throws CAException, InterruptedException {
this.appendFile.setValue(append);
}
/**
* Get if file name generation is on or off
* @return File name generation flag
* @throws CAException
*/
public boolean isFileNameGeneration() throws CAException, InterruptedException {
return(this.fileNameGeneration.getValue());
}
/**
* Set Whether the file name should be generated out of the file name format and the file counter
* @param generation
* @throws CAException
*/
public void setFileNameGeneration(boolean generation) throws CAException, InterruptedException {
this.fileNameGeneration.setValue(generation);
}
/**
* Get if ZigZag scan option is on or off
* @return ZigZag flag
* @throws CAException
*/
public boolean isZigZag() throws CAException, InterruptedException {
return(this.zigZag.getValue());
}
/**
* Set ZigZag scan mode on/off
* @param zigZag ZigZag mode on = true, ZigZag mode off = false
* @throws CAException
*/
public void setZigZag(boolean zigZag) throws CAException, InterruptedException {
this.zigZag.setValue(zigZag);
}
/**
* Get whether encoder is used
*/
public boolean isUseEncoder() throws CAException, InterruptedException {
return(this.useEncoder.getValue());
}
/**
* Set flag to use encoder
* @throws CAException
*/
public void setUseEncoder(boolean flag) throws CAException, InterruptedException {
this.useEncoder.setValue(flag);
}
/**
* Get the channels that are currently monitored by the OTFScan logic
* @return Names of the monitored channels
* @throws CAException
*/
public String[] getMonitoredChannels() throws CAException, InterruptedException {
String[] values = new String[this.monitoredChannels.size()];
for(int i=0; i<this.monitoredChannels.size();i++){
values[i] = monitoredChannels.get(i).getValue();
}
return(values);
}
/**
* Set the channels that need to be monitored.
* Note: As OTF only supports 8 channels to be monitored, only the first 8
* values of the passed channelNames are considered.
* @param values Array of channel names to be monitored
* @throws CAException
*/
public void setMonitoredChannels(String[] values) throws CAException, InterruptedException {
if(values.length>monitoredChannels.size()){
throw new IllegalArgumentException("Only up to "+monitoredChannels.size()+" monitored channels are supported by OTF");
}
for(int i=0; i<this.monitoredChannels.size(); i++){
if(values != null && i<values.length){
this.monitoredChannels.get(i).setValue(values[i]);
}
else{
this.monitoredChannels.get(i).setValue("");
}
}
}
/**
* Returns whether an scan is running
* @return Running flag
* @throws CAException
*/
public boolean isRunning() throws CAException, InterruptedException {
return(running.getValue());
}
/**
* Get status of the scan
* @return Status of the scan
* @throws CAException
*/
public Status getStatus() throws CAException, InterruptedException {
return(Status.values()[this.status.getValue()]);
}
/**
* Get the (error) message from the OTF records
* @return Message from OTF C logic
* @throws CAException
*/
public String getMessage() throws CAException, InterruptedException {
return(message.getValue());
}
/**
* Check whether the specified motor is recognized as ok (i.e. it is registered as OTFScan motor)
* @return Flag whether motor is ok
* @throws CAException
*/
public boolean isMotorOk() throws CAException, InterruptedException {
return(motorOk.getValue());
}
/**
* Wait until the motor flag goes to ok
* @param timeout Timout in milliseconds
*
* @throws CAException If motor ok flag does not switch to ok within the specified timeout
*/
public void waitUntilMotorOk(long timeout) throws CAException, InterruptedException {
motorOk.waitForValue(true, timeout);
}
/**
* Wait until the motor flag goes to not ok
* @param timeout Timout in milliseconds
*
* @throws CAException If motor ok flag does not switch to ok within the specified timeout
*/
public void waitUntilMotorNotOk(long timeout) throws CAException, InterruptedException {
motorOk.waitForValue(false, timeout);
}
public void waitUntilEncoderOk(long timeout) throws CAException, InterruptedException {
if(!useEncoder.getValue()){
return;
}
encoderOk.waitForValue(true, timeout);
}
}
@@ -0,0 +1,521 @@
/**
*
* 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.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.Logger;
import jcifs.smb.SmbException;
import jcifs.smb.SmbFile;
import gov.aps.jca.CAException;
import ch.psi.fda.core.Action;
import ch.psi.fda.core.ActionLoop;
import ch.psi.fda.core.Sensor;
import ch.psi.fda.core.actors.OTFActuator;
import ch.psi.fda.core.messages.ComponentMetadata;
import ch.psi.fda.core.messages.DataMessage;
import ch.psi.fda.core.messages.DataMessageMetadata;
import ch.psi.fda.core.messages.DataQueue;
import ch.psi.fda.core.messages.EndOfStreamMessage;
import ch.psi.fda.core.messages.Message;
import ch.psi.fda.core.sensors.MillisecondTimestampSensor;
import ch.psi.fda.core.sensors.OTFNamedChannelSensor;
import ch.psi.fda.core.sensors.OTFReadbackSensor;
import ch.psi.fda.core.sensors.OTFScalerChannelSensor;
import ch.psi.jcae.ChannelBeanFactory;
/**
* ActionLoop that is implementing the OTF Scan logic.
* While executing the loop a full OTF scan procedure is executed.
* @author ebner
*
*/
public class OTFLoop implements ActionLoop {
// Get Logger
private static Logger logger = Logger.getLogger(OTFLoop.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;
// Constants
/**
* Maximum number of monitored channels
*/
private static final int numberOfMonitoredChannels = 8;
/**
* Maximum number of Scaler channels
*/
private static final int numberOfScalerChannels = 16;
/**
* Default timeout (in milliseconds) for wait operations
*/
private long timeout = 8000;
/**
* Name of the NFS server to place the data of the OTF logic
*/
private final String server;
/**
* Share on the NFS server to put the OTF data on to
*/
private final String share;
/**
* SMB share to access the data written by the OTF C logic
*/
private final String smbShare;
/**
* Flag whether the actor of this loop should move in zig zag mode
*/
private final boolean zigZag;
/**
* Bean holding all OTF channels and functionality.
*/
private OTFBean obean;
/**
* 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;
/**
* Special OTF Actuator
*/
private OTFActuator actor = null;
/**
* List of sensors of this loop
*/
private List<Sensor> sensors;
private List<Integer> dataIndexes;
/**
* Execution count of this loop. This count is used to determine the
* file name of the OTF file.
*/
private int executionCount;
/**
* Data queue sensor data is posted to. A message consists of a list of data objects
* that are read out of the sensors of this loop.
*/
private BlockingQueue<Message> dataQueue;
/**
* Flag that indicates that the loop was requested to abort.
*/
private volatile boolean abort = false;
/**
* Constructor
* @param channelPrefix Prefix of the OTF related records, e.g. MTEST-HW3-OTF
* @param server NFS server the OTF C Logic should put its data to
* @param share Share on NFS server to put the OTF C Logic data
* @param smbShare SMB share to get the data written by the OTF C Logic
* @param zigZag Operate loop in zig zag mode
*/
public OTFLoop(String channelPrefix, String server, String share, String smbShare, boolean zigZag){
// Initialize connection to the OTF records
try {
this.obean = new OTFBean();
ChannelBeanFactory.getFactory().createChannelBeans(obean, channelPrefix);
} catch (CAException e) {
throw new IllegalArgumentException("Unable to connect to the OTF channels",e);
} catch (InterruptedException e) {
throw new RuntimeException("Unable to connect to the OTF channels",e);
}
// Store loop configuration
this.server = server;
this.share = share;
this.smbShare = smbShare;
this.zigZag = zigZag;
// Initialize lists used by the loop
this.preActions = new ArrayList<Action>();
this.postActions = new ArrayList<Action>();
this.sensors = new ArrayList<Sensor>();
this.dataQueue = new LinkedBlockingQueue<Message>(2000);
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Action#execute()
*/
@Override
public void execute() throws InterruptedException {
// Execute pre actions
for(Action action: preActions){
action.execute();
}
// Start scan
obean.start();
// Wait for end of scan
obean.waitUntilStopped();
// Read data from file
collectData();
// Execute post actions
for(Action action: postActions){
action.execute();
}
// Issue end of loop control message
dataQueue.put(new EndOfStreamMessage(dataGroup));
// Increase execution count
executionCount++;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Action#abort()
*/
@Override
public void abort() {
// Abort otf scan logic
obean.abort();
abort=true;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.ActionLoop#prepare()
*/
@Override
public void prepare() {
executionCount = 0;
// Set abort flag to false
abort=false;
// Check whether actor is set for the loop
if(actor == null){
throw new IllegalStateException("No actor specified for this loop");
}
// list with all monitored channels
List<String> monitoredChannels = new ArrayList<String>();
dataIndexes = new ArrayList<Integer>();
int channelCount =0;
for(Sensor s: sensors){
if(s instanceof OTFNamedChannelSensor){
// Monitored channel
OTFNamedChannelSensor so = (OTFNamedChannelSensor) s;
if(channelCount>=numberOfMonitoredChannels){
throw new IllegalArgumentException("Only up to "+numberOfMonitoredChannels+" channels can be monitored by OTF");
}
monitoredChannels.add(so.getName());
dataIndexes.add(2+numberOfScalerChannels+channelCount);
channelCount++;
}
else if (s instanceof OTFScalerChannelSensor){
OTFScalerChannelSensor so = (OTFScalerChannelSensor) s;
if(so.getIndex()>=numberOfScalerChannels){
throw new IllegalArgumentException("Scaler index must be between 0<=index<"+numberOfScalerChannels);
}
dataIndexes.add(2+so.getIndex()); // scalers follow directly after the readback
}
else if (s instanceof MillisecondTimestampSensor){
dataIndexes.add(2+numberOfScalerChannels+numberOfMonitoredChannels);
}
else if (s instanceof OTFReadbackSensor){
dataIndexes.add(1);
}
else{
throw new IllegalArgumentException("Sensor type "+s.getClass()+" is not supported by this loop");
}
}
// Set OTF parameters
try{
obean.resetToDefaults();
// Set actor properties
obean.setMotor(actor.getName());
obean.waitUntilMotorOk(timeout);
obean.setBegin(actor.getStart());
obean.setEnd(actor.getEnd());
obean.setStepSize(actor.getStepSize());
obean.setIntegrationTime(actor.getIntegrationTime());
// Override encoder if specified
if(actor.getReadback()!=null){
obean.setUseEncoder(true);
obean.setEncoder(actor.getReadback());
obean.waitUntilEncoderOk(timeout);
}
// Set user backlash
obean.setUserBacklash(actor.getAdditionalBacklash());
// NFS settings
obean.setNfsServer(server);
obean.setNfsShare(share);
obean.setFileNameGeneration(true);
obean.setAppendFile(false);
obean.setZigZag(zigZag); // Set ZigZag because there might be iterations
obean.setFileNameFormat("%06d"); // Force an update of the filename/counter by setting file format twice with different values
obean.setFileNameFormat("%06d.txt");
// Set monitored channels
obean.setMonitoredChannels(monitoredChannels.toArray(new String[monitoredChannels.size()]));
}
catch(CAException e){
throw new RuntimeException("Unable to set OTF configuration parameters",e);
} catch (InterruptedException e) {
throw new RuntimeException("Unable to set OTF configuration parameters",e);
}
// Cleanup temporary directory
try{
SmbFile tmpDir = new SmbFile(smbShare);
if( !tmpDir.exists() || !tmpDir.isDirectory() ){
throw new RuntimeException("Cannot access OTF temporary directory "+tmpDir.getCanonicalPath());
}
SmbFile[] files = tmpDir.listFiles();
for(int i=0;i<files.length;i++){
// Only delete item if it is not a directory and if it matches the given pattern.
if(files[i].isFile() && files[i].getName().matches("[0-9]+.txt")){
files[i].delete();
logger.fine("Delete file: "+files[i].getName());
}
}
}
catch(MalformedURLException e){
throw new RuntimeException("Malformed URL of the SMB share", e);
} catch (SmbException e) {
throw new RuntimeException("Unable to access SMB share (temporary files)",e);
}
}
/* (non-Javadoc)
* @see ch.psi.fda.core.ActionLoop#cleanup()
*/
@Override
public void cleanup() {
// TODO Auto-generated method stub
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Action#destroy()
*/
@Override
public void destroy() {
// Close all connections used by the OTFBean
try {
ChannelBeanFactory.getFactory().destroyChannelBeans(obean);
} catch (CAException e) {
throw new RuntimeException("Unable to destroy channels",e);
} catch (InterruptedException e) {
throw new RuntimeException("Unable to destroy channels",e);
}
}
/* (non-Javadoc)
* @see ch.psi.fda.core.ActionLoop#getPreActions()
*/
@Override
public List<Action> getPreActions() {
return preActions;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.ActionLoop#getPostActions()
*/
@Override
public List<Action> getPostActions() {
return postActions;
}
/**
* The structure of the data message depends on the sensors registered at this loop
* at the time this method is called.
* @return the data queue and the metadata of the data messages
*/
public DataQueue getDataQueue() {
DataMessageMetadata m = new DataMessageMetadata();
// Build up data message metadata based on the sensors currently registered.
for(Sensor s: sensors){
m.getComponents().add(new ComponentMetadata(s.getId()));
}
return new DataQueue(dataQueue, m);
}
/**
* Collect data written by the OTFScan logic
* @param dataSet
* @param tmpFile
*/
private void collectData() {
try{
final int timestampIndex = 2+numberOfScalerChannels+numberOfMonitoredChannels;
SmbFile tmpFile = new SmbFile(smbShare, String.format("%06d.txt", executionCount));
logger.fine("Collect data from "+tmpFile.getCanonicalPath());
SmbFile lockfile = new SmbFile(tmpFile.getCanonicalPath() + ".lock");
logger.fine("Wait until file is written [lock file: "+lockfile.getCanonicalPath()+"]");
// Wait until file is created
while ((!tmpFile.exists()) || lockfile.exists()) {
Thread.sleep(500);
if(abort){
// If abort is issued while waiting for data immediately return without
// trying to read the data
return;
}
}
logger.fine("Read file " + tmpFile.getCanonicalPath());
InputStreamReader inreader = new InputStreamReader(tmpFile.getInputStream());
BufferedReader in = new BufferedReader(inreader);
String line;
boolean firstline = true;
while (true) {
line = in.readLine();
if (line == null) {
break;
} else {
if(line.matches("^\\[.*")){
// Skip header lines
}
else{
if(firstline){
firstline=false;
continue;
}
DataMessage message = new DataMessage();
// Add data to dataset
String[] tokens = line.split("\t");
for(Integer i: dataIndexes){
try{
if(i == timestampIndex) {
// Calculate time in milliseconds
Double seconds = new Double(tokens[i]);
Double nanoseconds = new Double(tokens[i+1]);
Double v = seconds*1000+Math.floor(nanoseconds*0.000001);
message.getData().add(v);
}
else {
message.getData().add(new Double(tokens[i]));
}
}
catch(NumberFormatException e){
logger.warning("Cannot parse component ["+tokens[i]+"] from source file - will add 0 for this component");
message.getData().add(new Double(0));
}
}
dataQueue.put(message);
}
}
}
in.close();
inreader.close();
} catch(InterruptedException e){
throw new RuntimeException("An interrupt occured while waiting for the file to show up",e);
} catch (IOException e) {
throw new RuntimeException("An IO Exception occured while reading the OTF data file",e);
}
}
// Getter functions for variable parts
/**
* @return the sensors
*/
public List<Sensor> getSensors() {
return sensors;
}
/**
* @return the actor
*/
public OTFActuator getActor() {
return actor;
}
/**
* @param actor the actor to set
*/
public void setActor(OTFActuator actor) {
this.actor = actor;
}
/**
* @return the dataGroup
*/
public boolean isDataGroup() {
return dataGroup;
}
/**
* @param dataGroup the dataGroup to set
*/
public void setDataGroup(boolean dataGroup) {
this.dataGroup = dataGroup;
}
}
@@ -0,0 +1,157 @@
/**
*
* 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.ChannelBean;
import ch.psi.jcae.annotation.CaChannel;
/**
* @author ebner
*
*/
public class CrlogicChannelsTemplate {
/**
* Status of the OTFSCAN IOC logic
*/
public enum Status { SETUP, INACTIVE, INITIALIZE, ACTIVE, STOP, FAULT, ERROR };
/**
* Ticks per second - IOC setting
* ATTENTION - This field must only be set bu the IOC - ATTENTION
*/
@CaChannel(type=Integer.class, name =":TPS")
private ChannelBean<Integer> ticksPerSecond;
/**
* Status of the OTFSCAN IOC logic
*/
@CaChannel(type=String.class, name =":STATUS")
private ChannelBean<String> status;
/**
* Message from the OTFSCAN IOC logic
*/
@CaChannel(type=String.class, name =":MSG")
private ChannelBean<String> message;
/**
* IOC ticks between data acquisition interrupts
*/
@CaChannel(type=Integer.class, name =":TBINT")
private ChannelBean<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 =":NFSSE")
private ChannelBean<String> nfsServer;
/**
* Name of the NFS share on the NFS server
*/
@CaChannel(type=String.class, name =":NFSSH")
private ChannelBean<String> nfsShare;
/**
* Name of the data file
*/
@CaChannel(type=String.class, name =":DFNAM")
private ChannelBean<String> dataFile;
/**
* Flag to identify whether the data file should be appended
*/
@CaChannel(type=Boolean.class, name =":FAPPE")
private ChannelBean<Boolean> appendFile;
/**
* Readout resources
*/
@CaChannel(type=String[].class, name =":RRES")
private ChannelBean<String[]> readoutResources;
/**
* @return the ticksPerSecond
*/
public ChannelBean<Integer> getTicksPerSecond() {
return ticksPerSecond;
}
/**
* @return the status
*/
public ChannelBean<String> getStatus() {
return status;
}
/**
* @return the message
*/
public ChannelBean<String> getMessage() {
return message;
}
/**
* @return the ticksBetweenInterrupts
*/
public ChannelBean<Integer> getTicksBetweenInterrupts() {
return ticksBetweenInterrupts;
}
/**
* @return the nfsServer
*/
public ChannelBean<String> getNfsServer() {
return nfsServer;
}
/**
* @return the nfsShare
*/
public ChannelBean<String> getNfsShare() {
return nfsShare;
}
/**
* @return the dataFile
*/
public ChannelBean<String> getDataFile() {
return dataFile;
}
/**
* @return the appendFile
*/
public ChannelBean<Boolean> getAppendFile() {
return appendFile;
}
/**
* @return the readoutResources
*/
public ChannelBean<String[]> getReadoutResources() {
return readoutResources;
}
}
@@ -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.loops.cr;
/**
* @author ebner
*
*/
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);
}
}
}
@@ -0,0 +1,824 @@
/**
*
* 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.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.Semaphore;
import java.util.logging.Logger;
import jcifs.smb.SmbFile;
import gov.aps.jca.CAException;
import ch.psi.fda.core.Action;
import ch.psi.fda.core.ActionLoop;
import ch.psi.fda.core.Sensor;
import ch.psi.fda.core.actors.OTFActuator;
import ch.psi.fda.core.messages.ComponentMetadata;
import ch.psi.fda.core.messages.DataMessage;
import ch.psi.fda.core.messages.DataMessageMetadata;
import ch.psi.fda.core.messages.DataQueue;
import ch.psi.fda.core.messages.EndOfStreamMessage;
import ch.psi.fda.core.messages.Message;
import ch.psi.fda.core.sensors.MillisecondTimestampSensor;
import ch.psi.fda.core.sensors.OTFNamedChannelSensor;
import ch.psi.fda.core.sensors.OTFScalerChannelSensor;
import ch.psi.jcae.ChannelBeanFactory;
/**
* @author ebner
*
* While using Crlogic the IOC system clock rate should/must be set to 1000 (default 60)
*
* sysClkRateSet 1000
*
*/
public class CrlogicLoop implements ActionLoop {
// Get Logger
private static final Logger logger = Logger.getLogger(CrlogicLoop.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;
// Constants
/**
* Default timeout (in milliseconds) for wait operations
*/
private long startStopTimeout = 8000;
/**
* Name of the NFS server to place the data of the OTF logic
*/
private final String server;
/**
* Share on the NFS server to put the OTF data on to
*/
private final String share;
/**
* SMB share to access the data written by the OTF C logic
*/
private final String smbShare;
/**
* Flag whether the actor of this loop should move in zig zag mode
*/
private final boolean zigZag;
boolean useReadback;
boolean useEncoder;
/**
* 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;
/**
* Prefix for the CRLOGIC channels
*/
private String prefix;
private CrlogicChannelsTemplate template;
private MotorChannelsTemplate motortemplate;
/**
* Semaphore to ensure that data is read in correct sequence
*/
private Semaphore semaphore = new Semaphore(1);
/**
* Special OTF Actuator
*/
private OTFActuator actuator = null;
/**
* List of sensors of this loop
*/
private List<Sensor> sensors;
private List<String> readoutResources;
private Map<Integer, CrlogicDeltaDataFilter> scalerIndices;
private CrlogicRangeDataFilter crlogicDataFilter;
/**
* Data queue sensor data is posted to. A message consists of a list of data objects
* that are read out of the sensors of this loop.
*/
private BlockingQueue<Message> dataQueue;
/**
* Abort status
*/
private boolean abort = false;
private boolean abortForce = false;
private Thread executionThread = null;
public CrlogicLoop(String prefix, String server, String share, String smbShare, boolean zigZag){
this.prefix = prefix;
this.server = server;
this.share = share;
this.smbShare = smbShare;
this.zigZag = zigZag;
// Initialize lists used by the loop
this.preActions = new ArrayList<Action>();
this.postActions = new ArrayList<Action>();
this.sensors = new ArrayList<Sensor>();
this.readoutResources = new ArrayList<String>();
this.scalerIndices = new HashMap<Integer, CrlogicDeltaDataFilter>();
this.crlogicDataFilter = new CrlogicRangeDataFilter();
this.dataQueue = new LinkedBlockingQueue<Message>(2000);
}
/**
* Collect data from share
* @param tmpFileName Name of the temporary file
* @throws InterruptedException
* @throws IOException
*/
private void collectData(String tmpDir, String tmpFileName) throws InterruptedException, IOException {
semaphore.acquire();
if (tmpDir.startsWith("smb:")) {
SmbFile tmpFile = new SmbFile(tmpDir, tmpFileName);
logger.info("Collect data from " + tmpFile.getCanonicalPath());
SmbFile lockfile = new SmbFile(tmpFile.getCanonicalPath() + ".lock");
logger.info("Wait until file is written [lock file: " + lockfile.getCanonicalPath() + "]");
// Wait until file is created
while ((!tmpFile.exists()) || lockfile.exists()) {
Thread.sleep(100);
if(abort){
// If abort is issued while waiting for data immediately return without
// trying to read the data
return;
}
}
InputStreamReader inreader = new InputStreamReader(tmpFile.getInputStream());
BufferedReader in = new BufferedReader(inreader);
String line;
boolean firstline = true;
boolean wasInRangeBefore = false;
boolean discardAnyway = false;
while (true) {
line = in.readLine();
if (line == null) {
break;
} else {
// if(line.matches("^\\[.*")){
if (line.matches("^ *#.*")) {
// Skip header/comment lines
// logger.info("HEADER: " + line);
} else {
if (firstline) {
firstline = false;
continue;
}
// logger.info(line);
// Write into queue
DataMessage message = new DataMessage();
String[] tokens = line.split("\t");
boolean use = true;
for(int i=0;i<tokens.length;i++){
String t = tokens[i];
Double val;
if(i==0){
Double raw = new Double(t);
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);
// if(!use){
// break;
// }
}
else if(scalerIndices.containsKey(i)){
CrlogicDeltaDataFilter f = scalerIndices.get(i);
val = f.delta(new Double(t));
}
else{
val = new Double(t);
}
message.getData().add(val);
}
// Use this to filter out motor retry movements at the end of the scan
wasInRangeBefore = wasInRangeBefore | use;
if(!use && wasInRangeBefore){
discardAnyway=true;
// Optimization - terminate read loop once range is left
break;
}
// Filter data
if(use && !discardAnyway){
dataQueue.put(message);
}
}
}
}
in.close();
inreader.close();
// Remove temporary file
tmpFile.delete();
}
else{
// TODO - File in local file system
}
// Issue end of loop control message
dataQueue.put(new EndOfStreamMessage(dataGroup));
semaphore.release();
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Action#execute()
*/
@Override
public void execute() throws InterruptedException {
try{
double stepSize = actuator.getStepSize();
double start = actuator.getStart();
double end = actuator.getEnd();
double integrationTime = actuator.getIntegrationTime();
double ubacklash = actuator.getAdditionalBacklash();
// Set values for the datafilter
crlogicDataFilter.setStart(actuator.getStart());
crlogicDataFilter.setEnd(actuator.getEnd());
// Reset data filter
for(Integer k: scalerIndices.keySet()){
scalerIndices.get(k).reset();
}
synchronized(this){
// Set abort state to false
abort = false;
abortForce = false;
// Set execution thread to current thread
executionThread = Thread.currentThread();
}
// TODO each actuator will result in an additional sensor (at the beginning)
// Dependent on actuator settings (readback use encoder, ...)
// TODO filename generation?
final String tmpFileName = "tmp-"+System.currentTimeMillis()+".txt";
Long timeout = 600000l; // 10 minutes move timeout
// Check if logic is inactive, otherwise return early
if(!template.getStatus().getValue().equals(CrlogicChannelsTemplate.Status.INACTIVE.toString())){
logger.info("CRLOGIC is not inactive!");
// TODO Decide what to do in this situation
if(template.getStatus().getValue().equals(CrlogicChannelsTemplate.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(CrlogicChannelsTemplate.Status.INACTIVE.toString());
}
else if(template.getStatus().getValue().equals(CrlogicChannelsTemplate.Status.ACTIVE.toString())){
template.getStatus().setValue(CrlogicChannelsTemplate.Status.STOP.toString());
template.getStatus().waitForValue(CrlogicChannelsTemplate.Status.INACTIVE.toString(), startStopTimeout);
}
else{
throw new RuntimeException("CRLOGIC is not inactive");
}
}
logger.info("Set parameters");
template.getNfsServer().setValue(server);
template.getNfsShare().setValue(share);
template.getDataFile().setValue(tmpFileName);
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+ubacklash;
double realEnd = end+(backlash*direction);
double realStart = start-(backlash*direction);
// Move to start
logger.info("Move motor to start ["+realStart+"]");
motortemplate.getSetValue().setValue(realStart, timeout); // 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(CrlogicChannelsTemplate.Status.INITIALIZE.toString());
try{
template.getStatus().waitForValue(CrlogicChannelsTemplate.Status.ACTIVE.toString(), startStopTimeout);
}
catch(CAException e){
logger.info( "Failed to start CRLOGIC. Logic in status: "+template.getStatus().getValue() );
if(template.getStatus().getValue().equals(CrlogicChannelsTemplate.Status.FAULT.toString())){
logger.info("Error message: "+template.getMessage().getValue());
}
// Recover to inactive
template.getStatus().setValue(CrlogicChannelsTemplate.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);
}
// Move motor(s) to end / wait until motor is stopped
logger.info("Move motor to end ["+realEnd+"]");
try{
motortemplate.getSetValue().setValue(realEnd, timeout); // Will block until move is done
}
catch (InterruptedException e) {
if(abort & (!abortForce)){
// Abort motor move
motortemplate.getCommand().setValue(MotorChannelsTemplate.Commands.Stop.ordinal());
motortemplate.getCommand().setValue(MotorChannelsTemplate.Commands.Go.ordinal());
}
else{
throw e;
}
}
logger.info("Motor reached end position");
// Stop crlogic logic
logger.info("Stop CRLOGIC");
template.getStatus().setValue(CrlogicChannelsTemplate.Status.STOP.toString());
// Wait until stopped
logger.info("Wait until stopped");
try{
template.getStatus().waitForValue(CrlogicChannelsTemplate.Status.INACTIVE.toString(), startStopTimeout);
}
catch(CAException 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);
}
// Read data
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
collectData(smbShare, tmpFileName);
} catch (InterruptedException e) {
throw new RuntimeException("Unable to read CRLOGIC raw data file",e);
} catch (IOException e) {
throw new RuntimeException("Unable to read CRLOGIC raw data file",e);
}
}
});
t.start();
if(zigZag){
// Swap start and end
double aend = actuator.getEnd();
actuator.setEnd(actuator.getStart());
actuator.setStart(aend);
}
synchronized(this){
executionThread = null;
}
}
catch(CAException e){
throw new RuntimeException("Unable to execute crloop", e);
}
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Action#abort()
*/
@Override
public void abort() {
abort(false);
}
/**
* Abort logic
* @param force
*/
public synchronized void abort(boolean force){
abort = true;
abortForce = force;
// executionThread variable guarded by "this"
if(executionThread != null){
executionThread.interrupt();
}
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Action#destroy()
*/
@Override
public void destroy() {
try {
ChannelBeanFactory.getFactory().destroyChannelBeans(template);
ChannelBeanFactory.getFactory().destroyChannelBeans(motortemplate);
template = null;
motortemplate = null;
} catch (Exception e) {
throw new RuntimeException("Unable to destroy CrlogicLoop", e);
}
}
/* (non-Javadoc)
* @see ch.psi.fda.core.ActionLoop#prepare()
*/
@Override
public void prepare() {
try{
// Connect crlogic channels
template = new CrlogicChannelsTemplate();
logger.info("Connect channels");
ChannelBeanFactory.getFactory().createChannelBeans(template, prefix);
// Connect motor channels
motortemplate = new MotorChannelsTemplate();
ChannelBeanFactory.getFactory().createChannelBeans(motortemplate, actuator.getName());
useReadback = motortemplate.getUseReadback().getValue();
useEncoder = motortemplate.getUseEncoder().getValue();
logger.info("Motor type: "+ MotorChannelsTemplate.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(actuator.getReadback()!=null){
throw new IllegalArgumentException("Readback not supported if motor is configured in open loop");
}
else{
readoutResources.add(actuator.getName());
}
}
else if(useReadback && (!useEncoder)){
String readback;
// use readback link
if(actuator.getReadback()!=null){
// Use specified readback
readback = (actuator.getReadback());
}
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
EncoderChannelsTemplate encodertemplate = new EncoderChannelsTemplate();
ChannelBeanFactory.getFactory().createChannelBeans(encodertemplate, readback);
// Read encoder settings
if(encodertemplate.getDirection().getValue()==EncoderChannelsTemplate.Direction.Positive.ordinal()){
crlogicDataFilter.setEncoderDirection(1);
}
else{
crlogicDataFilter.setEncoderDirection(-1);
}
crlogicDataFilter.setEncoderOffset(encodertemplate.getOffset().getValue());
crlogicDataFilter.setEncoderResolution(encodertemplate.getResolution().getValue());
// Disconnect from encoder
ChannelBeanFactory.getFactory().destroyChannelBeans(encodertemplate);
}
else if (useEncoder && (!useReadback)){
// use readback link
if(actuator.getReadback()!=null){
throw new IllegalArgumentException("Readback not supported if motor is configured to use encoder");
}
else{
// Set resouce to readback link
readoutResources.add(actuator.getName()+"_ENC");
}
}
else{
throw new IllegalArgumentException("Motor configuration not supportet: use readback - "+useReadback+" use encoder - "+useEncoder);
}
// Fill Motor specific settings
if(motortemplate.getDirection().getValue()==MotorChannelsTemplate.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(Sensor s: sensors){
if(s instanceof OTFNamedChannelSensor){
// Monitored channel (MUST be configured MODULE ID'S)
OTFNamedChannelSensor so = (OTFNamedChannelSensor) s;
readoutResources.add(so.getName());
}
else if (s instanceof OTFScalerChannelSensor){
OTFScalerChannelSensor so = (OTFScalerChannelSensor) s;
readoutResources.add("SCALER"+so.getIndex());
scalerIndices.put(c, new CrlogicDeltaDataFilter());
}
else if (s instanceof MillisecondTimestampSensor){
readoutResources.add("TIMESTAMP");
}
else{
throw new IllegalArgumentException("Sensor type "+s.getClass()+" is not supported by this loop");
}
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);
}
}
/* (non-Javadoc)
* @see ch.psi.fda.core.ActionLoop#cleanup()
*/
@Override
public void cleanup() {
// TODO Auto-generated method stub
}
/* (non-Javadoc)
* @see ch.psi.fda.core.ActionLoop#getPreActions()
*/
@Override
public List<Action> getPreActions() {
return preActions;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.ActionLoop#getPostActions()
*/
@Override
public List<Action> getPostActions() {
return postActions;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.ActionLoop#isDataGroup()
*/
@Override
public boolean isDataGroup() {
// TODO Auto-generated method stub
return dataGroup;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.ActionLoop#setDataGroup(boolean)
*/
@Override
public void setDataGroup(boolean dataGroup) {
this.dataGroup = dataGroup;
}
/**
* @return the sensors
*/
public List<Sensor> getSensors() {
return sensors;
}
/**
* @return the actor
*/
public OTFActuator getActor() {
return actuator;
}
/**
* @param actor the actor to set
*/
public void setActor(OTFActuator actor) {
this.actuator = actor;
}
/**
* The structure of the data message depends on the sensors registered at this loop
* at the time this method is called.
* @return the data queue and the metadata of the data messages
*/
public DataQueue getDataQueue() {
DataMessageMetadata m = new DataMessageMetadata();
// Build up data message metadata based on the sensors currently registered.
m.getComponents().add(new ComponentMetadata(actuator.getId()));
for(Sensor s: sensors){
m.getComponents().add(new ComponentMetadata(s.getId()));
}
return new DataQueue(dataQueue, m);
}
}
@@ -0,0 +1,274 @@
/**
*
* 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;
/**
* @author ebner
*
*/
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;
}
}
}
}
@@ -0,0 +1,73 @@
/**
*
* 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.ChannelBean;
import ch.psi.jcae.annotation.CaChannel;
/**
* @author ebner
*
*/
public class EncoderChannelsTemplate {
/**
* Resolution - $(P)$(E)_SCL
*/
@CaChannel(type=Double.class, name="_SCL")
private ChannelBean<Double> resolution;
/**
* Offset - $(P)$(E)_OFF
*/
@CaChannel(type=Double.class, name ="_OFF")
private ChannelBean<Double> offset;
/**
* Direction - $(P)$(E)_DIR
*/
public enum Direction {Negative, Positive};
@CaChannel(type=Integer.class, name ="_DIR")
private ChannelBean<Integer> direction;
/**
* @return the resolution
*/
public ChannelBean<Double> getResolution() {
return resolution;
}
/**
* @return the offset
*/
public ChannelBean<Double> getOffset() {
return offset;
}
/**
* @return the direction
*/
public ChannelBean<Integer> getDirection() {
return direction;
}
}
@@ -0,0 +1,652 @@
/**
*
* 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.ChannelBean;
import ch.psi.jcae.annotation.CaChannel;
/**
* @author ebner
*
*/
public class MotorChannelsTemplate {
public enum Type {SOFT_CHANNEL, MOTOR_SIMULATION, OMS_VME58, OMS_MAXv};
/**
* ## Drive section ##
* # User coordinates #
*/
/**
* .HLM High limit - double
*/
@CaChannel(type=Double.class, name =".HLM")
private ChannelBean<Double> highLimit;
/**
* .LLM Low limit - double
*/
@CaChannel(type=Double.class, name =".LLM")
private ChannelBean<Double> lowLimit;
/**
* .RBV Readback value - double
*/
@CaChannel(type=Double.class, name =".RBV", monitor=true)
private ChannelBean<Double> readbackValue;
/**
* .VAL Set value - double
*/
@CaChannel(type=Double.class, name =".VAL", monitor=true)
private ChannelBean<Double> setValue;
/**
* .RLV Relative move value - double
*/
@CaChannel(type=Double.class, name =".RLV")
private ChannelBean<Double> relativeMoveValue;
/**
* .TWV Teak value - double
*/
@CaChannel(type=Double.class, name =".TWV")
private ChannelBean<Double> tweakValue;
/**
* .TWR Tweak reverse - move left - int
*/
@CaChannel(type=Integer.class, name =".TWR")
private ChannelBean<Integer> tweakReverse;
/**
* .TWF Tweak forward - move right - int
*/
@CaChannel(type=Integer.class, name =".TWF")
private ChannelBean<Integer> tweakForward;
/**
* .JOGR Jog reverse - int
*/
@CaChannel(type=Integer.class, name =".JOGR")
private ChannelBean<Integer> jogReverse;
/**
* .JOGF Jog forward - int
*/
@CaChannel(type=Integer.class, name =".JOGF")
private ChannelBean<Integer> jogForward;
/**
* .HOMR Home reverse - int
*/
@CaChannel(type=Integer.class, name =".HOMR")
private ChannelBean<Integer> homeReverse;
/**
* .HOMF Home forward - int
*/
@CaChannel(type=Integer.class, name =".HOMF")
private ChannelBean<Integer> homeForward;
/**
* .EGU Engineering unit - String
*/
@CaChannel(type=String.class, name =".EGU")
private ChannelBean<String> engineeringUnit;
/**
* .DTYP Type - String (e.g. "OMS MAXv") see enum Type
*/
@CaChannel(type=Integer.class, name =".DTYP")
private ChannelBean<Integer> type;
/**
* .DESC Description - String
*/
@CaChannel(type=String.class, name =".DESC")
private ChannelBean<String> description;
/**
* # Dial coordinates #
*/
/**
* .DHLM Dial high limit - double
*/
@CaChannel(type=Double.class, name =".DHLM")
private ChannelBean<Double> dialHighLimit;
/**
* .DLLM Dial low limit - double
*/
@CaChannel(type=Double.class, name =".DLLM")
private ChannelBean<Double> dialLowLimit;
/**
* .DRBV Dial readback value - double
*/
@CaChannel(type=Double.class, name =".DRBV", monitor=true)
private ChannelBean<Double> dialReadbackValue;
/**
* .DVAL Dial set value - double
*/
@CaChannel(type=Double.class, name =".DVAL", monitor=true)
private ChannelBean<Double> dialSetValue;
/**
* .RVAL Raw value - int
*/
@CaChannel(type=Integer.class, name =".RVAL", monitor=true)
private ChannelBean<Integer> rawValue;
/**
* .RRBV Raw readback value - int
*/
@CaChannel(type=Integer.class, name =".RRBV", monitor=true)
private ChannelBean<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 =".SPMG")
private ChannelBean<Integer> command;
/**
* ## Calibration section ##
*/
/**
* .SET Set/Use Switch - (0:"Use", 1:"Set") - 0
*/
public enum Calibration {Use, Set};
@CaChannel(type=Integer.class, name =".SET")
private ChannelBean<Integer> calibration;
/**
* .OFF User offset (EGU) - double
*/
@CaChannel(type=Double.class, name =".OFF")
private ChannelBean<Double> offset;
/**
* .FOFF Offset-Freeze Switch - (0:"Variable", 1:"Frozen") - 1
*/
public enum OffsetMode {Variable, Frozen};
@CaChannel(type=Integer.class, name =".FOFF")
private ChannelBean<Integer> offsetMode;
/**
* .DIR User direction - (0:"Pos", 1:"Neg")
*/
public enum Direction {Positive, Negative};
@CaChannel(type=Integer.class, name =".DIR")
private ChannelBean<Integer> direction;
/**
* ## Dynamics ##
*/
/**
* .VELO Velocity (EGU/s) - double
*/
@CaChannel(type=Double.class, name =".VELO")
private ChannelBean<Double> velocity;
/**
* .BVEL Backlash velocity (EGU/s) - double
*/
@CaChannel(type=Double.class, name =".BVEL")
private ChannelBean<Double> backlashVelocity;
/**
* .VBAS Base speed (EGU/s) - double
*/
@CaChannel(type=Double.class, name =".VBAS")
private ChannelBean<Double> baseSpeed;
/**
* .ACCL Acceleration time / seconds to velocity - double
*/
@CaChannel(type=Double.class, name =".ACCL")
private ChannelBean<Double> accelerationTime;
/**
* .BACC Backlash acceleration time / seconds to velocity - double
*/
@CaChannel(type=Double.class, name =".BACC")
private ChannelBean<Double> backlashAccelerationTime;
/**
* .BDST Backlash distance (EGU) - double
*/
@CaChannel(type=Double.class, name =".BDST")
private ChannelBean<Double> backlashDistance;
/**
* .FRAC Move fraction - double
*/
@CaChannel(type=Double.class, name =".FRAC")
private ChannelBean<Double> moveFracion;
/**
* ## Resolution ##
*/
/**
* .MRES Motor resolution - double
*/
@CaChannel(type=Double.class, name =".MRES")
private ChannelBean<Double> motorResolution;
/**
* .ERES Encoder resolution - double
*/
@CaChannel(type=Double.class, name =".ERES")
private ChannelBean<Double> encoderResolution;
/**
* .RRES Readback resolution - double
*/
@CaChannel(type=Double.class, name =".RRES")
private ChannelBean<Double> readbackResolution;
/**
* .RDBD Retry deadband (EGU) - double
*/
@CaChannel(type=Double.class, name =".RDBD")
private ChannelBean<Double> retryDeadband;
/**
* .RTRY Max retry count - int
*/
@CaChannel(type=Integer.class, name =".RTRY")
private ChannelBean<Integer> maxRetryCount;
/**
* .RCNT Retry count - int
*/
@CaChannel(type=Integer.class, name =".RCNT", monitor=true)
private ChannelBean<Integer> retryCount;
/**
* .UEIP Use encoder (if present) - (0:"No", 1:"Yes")
*/
@CaChannel(type=Boolean.class, name =".UEIP")
private ChannelBean<Boolean> useEncoder;
/**
* .URIP Use readback link (if present) - (0:"No", 1:"Yes")
*/
@CaChannel(type=Boolean.class, name =".URIP")
private ChannelBean<Boolean> useReadback;
/**
* .DLY Readback delay (s) - double
*/
@CaChannel(type=Double.class, name =".DLY")
private ChannelBean<Double> readbackDelay;
/**
* .RDBL Readback link - String
*/
@CaChannel(type=String.class, name =".RDBL")
private ChannelBean<String> readbackLink;
/**
* .OMSL Output mode select - (0:"supervisory", 1:"closed_loop")
*/
public enum OutputMode {Supervisory, Closed_Loop};
@CaChannel(type=Integer.class, name =".OMSL")
private ChannelBean<Integer> outputMode;
/**
* ## Status ##
*/
/**
* .DMOV Done move - int
*/
@CaChannel(type=Boolean.class, name =".DMOV", monitor=true)
private ChannelBean<Boolean> moveDone;
/**
* @return the highLimit
*/
public ChannelBean<Double> getHighLimit() {
return highLimit;
}
/**
* @return the lowLimit
*/
public ChannelBean<Double> getLowLimit() {
return lowLimit;
}
/**
* @return the readbackValue
*/
public ChannelBean<Double> getReadbackValue() {
return readbackValue;
}
/**
* @return the setValue
*/
public ChannelBean<Double> getSetValue() {
return setValue;
}
/**
* @return the relativeMoveValue
*/
public ChannelBean<Double> getRelativeMoveValue() {
return relativeMoveValue;
}
/**
* @return the tweakValue
*/
public ChannelBean<Double> getTweakValue() {
return tweakValue;
}
/**
* @return the tweakReverse
*/
public ChannelBean<Integer> getTweakReverse() {
return tweakReverse;
}
/**
* @return the tweakForward
*/
public ChannelBean<Integer> getTweakForward() {
return tweakForward;
}
/**
* @return the jogReverse
*/
public ChannelBean<Integer> getJogReverse() {
return jogReverse;
}
/**
* @return the jogForward
*/
public ChannelBean<Integer> getJogForward() {
return jogForward;
}
/**
* @return the homeReverse
*/
public ChannelBean<Integer> getHomeReverse() {
return homeReverse;
}
/**
* @return the homeForward
*/
public ChannelBean<Integer> getHomeForward() {
return homeForward;
}
/**
* @return the engineeringUnit
*/
public ChannelBean<String> getEngineeringUnit() {
return engineeringUnit;
}
/**
* @return the type
*/
public ChannelBean<Integer> getType() {
return type;
}
/**
* @return the description
*/
public ChannelBean<String> getDescription() {
return description;
}
/**
* @return the dialHighLimit
*/
public ChannelBean<Double> getDialHighLimit() {
return dialHighLimit;
}
/**
* @return the dialLowLimit
*/
public ChannelBean<Double> getDialLowLimit() {
return dialLowLimit;
}
/**
* @return the dialReadbackValue
*/
public ChannelBean<Double> getDialReadbackValue() {
return dialReadbackValue;
}
/**
* @return the dialSetValue
*/
public ChannelBean<Double> getDialSetValue() {
return dialSetValue;
}
/**
* @return the rawValue
*/
public ChannelBean<Integer> getRawValue() {
return rawValue;
}
/**
* @return the rawReadbackValue
*/
public ChannelBean<Integer> getRawReadbackValue() {
return rawReadbackValue;
}
/**
* @return the command
*/
public ChannelBean<Integer> getCommand() {
return command;
}
/**
* @return the calibration
*/
public ChannelBean<Integer> getCalibration() {
return calibration;
}
/**
* @return the userOffset
*/
public ChannelBean<Double> getOffset() {
return offset;
}
/**
* @return the offsetMode
*/
public ChannelBean<Integer> getOffsetMode() {
return offsetMode;
}
/**
* @return the direction
*/
public ChannelBean<Integer> getDirection() {
return direction;
}
/**
* @return the velocity
*/
public ChannelBean<Double> getVelocity() {
return velocity;
}
/**
* @return the backlashVelocity
*/
public ChannelBean<Double> getBacklashVelocity() {
return backlashVelocity;
}
/**
* @return the baseSpeed
*/
public ChannelBean<Double> getBaseSpeed() {
return baseSpeed;
}
/**
* @return the accelerationTime
*/
public ChannelBean<Double> getAccelerationTime() {
return accelerationTime;
}
/**
* @return the backlashAccelerationTime
*/
public ChannelBean<Double> getBacklashAccelerationTime() {
return backlashAccelerationTime;
}
/**
* @return the backlashDistance
*/
public ChannelBean<Double> getBacklashDistance() {
return backlashDistance;
}
/**
* @return the moveFracion
*/
public ChannelBean<Double> getMoveFracion() {
return moveFracion;
}
/**
* @return the motorResolution
*/
public ChannelBean<Double> getMotorResolution() {
return motorResolution;
}
/**
* @return the encoderResolution
*/
public ChannelBean<Double> getEncoderResolution() {
return encoderResolution;
}
/**
* @return the readbackResolution
*/
public ChannelBean<Double> getReadbackResolution() {
return readbackResolution;
}
/**
* @return the retryDeadband
*/
public ChannelBean<Double> getRetryDeadband() {
return retryDeadband;
}
/**
* @return the maxRetryCount
*/
public ChannelBean<Integer> getMaxRetryCount() {
return maxRetryCount;
}
/**
* @return the retryCount
*/
public ChannelBean<Integer> getRetryCount() {
return retryCount;
}
/**
* @return the useEncoder
*/
public ChannelBean<Boolean> getUseEncoder() {
return useEncoder;
}
/**
* @return the useReadback
*/
public ChannelBean<Boolean> getUseReadback() {
return useReadback;
}
/**
* @return the readbackDelay
*/
public ChannelBean<Double> getReadbackDelay() {
return readbackDelay;
}
/**
* @return the readbackLink
*/
public ChannelBean<String> getReadbackLink() {
return readbackLink;
}
/**
* @return the outputMode
*/
public ChannelBean<Integer> getOutputMode() {
return outputMode;
}
/**
* @return the moveDone
*/
public ChannelBean<Boolean> getMoveDone() {
return moveDone;
}
}
@@ -0,0 +1,265 @@
/**
*
* 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.Logger;
import ch.psi.fda.core.Action;
import ch.psi.fda.core.ActionLoop;
import ch.psi.fda.core.messages.DataQueue;
import ch.psi.fda.core.sensors.MillisecondTimestampSensor;
/**
* @author ebner
*
*/
public class ParallelCrlogic implements ActionLoop {
// Get Logger
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 CrlogicLoop 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 ParallelCrlogicStreamMerge merger;
public ParallelCrlogic(CrlogicLoop crlogic, ScrlogicLoop scrlogic){
if(crlogic==null){
throw new IllegalArgumentException("No Crloop specified");
}
if(scrlogic==null){
throw new IllegalArgumentException("No Scrloop specified");
}
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 MillisecondTimestampSensor("tmp_timestamp"));
this.scrlogic = scrlogic;
// Initialize lists used by the loop
this.preActions = new ArrayList<Action>();
this.postActions = new ArrayList<Action>();
this.merger = new ParallelCrlogicStreamMerge(crlogic.getDataQueue(), scrlogic.getDataQueue());
}
/* (non-Javadoc)
* @see ch.psi.fda.logic.Logic#prepare()
*/
@Override
public void prepare() {
// TODO Auto-generated method stub
}
/* (non-Javadoc)
* @see ch.psi.fda.logic.Logic#execute()
*/
@Override
public void execute() throws InterruptedException {
// 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();
// Stop scrlogic
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();
return true;
}});
list.add(f);
// Start data merge thread
logger.info("Start data merge");
f = service.submit(new Callable<Boolean>(){
@Override
public Boolean call() throws Exception {
merger.merge();
return true;
}});
list.add(f);
for(Future<Boolean> bf: list){
// Wait for completion of the thread
try {
bf.get();
} catch (ExecutionException e) {
// TODO Handle exception the correct way
e.printStackTrace();
}
}
// Wait until all threads have finished
service.shutdown();
service.awaitTermination(1, TimeUnit.MINUTES);
// Execute post actions
for(Action action: postActions){
action.execute();
}
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Action#abort()
*/
@Override
public void abort() {
// TODO Auto-generated method stub
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Action#destroy()
*/
@Override
public void destroy() {
crlogic.destroy();
scrlogic.destroy();
}
/* (non-Javadoc)
* @see ch.psi.fda.core.ActionLoop#cleanup()
*/
@Override
public void cleanup() {
// TODO Auto-generated method stub
}
/* (non-Javadoc)
* @see ch.psi.fda.core.ActionLoop#getPreActions()
*/
@Override
public List<Action> getPreActions() {
return preActions;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.ActionLoop#getPostActions()
*/
@Override
public List<Action> getPostActions() {
return postActions;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.ActionLoop#isDataGroup()
*/
@Override
public boolean isDataGroup() {
return dataGroup;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.ActionLoop#setDataGroup(boolean)
*/
@Override
public void setDataGroup(boolean dataGroup) {
this.dataGroup = dataGroup;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.ActionLoop#getDataQueue()
*/
@Override
public DataQueue getDataQueue() {
return(merger.getDataQueue());
}
}
@@ -0,0 +1,160 @@
/**
*
* 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 java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import ch.psi.fda.core.messages.DataMessage;
import ch.psi.fda.core.messages.DataMessageMetadata;
import ch.psi.fda.core.messages.DataQueue;
import ch.psi.fda.core.messages.EndOfStreamMessage;
import ch.psi.fda.core.messages.Message;
/**
* @author ebner
*
*/
public class ParallelCrlogicStreamMerge {
private BlockingQueue<Message> dataQueue;
private DataQueue primaryQueue;
private DataQueue secondaryQueue;
/**
* 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.
*
* @param pqueue Primary/master queue
* @param squeue Secondary queue
*/
public ParallelCrlogicStreamMerge(DataQueue pqueue, DataQueue squeue){
this.primaryQueue = pqueue;
this.secondaryQueue = squeue;
this.dataQueue = new LinkedBlockingQueue<Message>();
}
/**
* Merge the streams based on the timestamp
* @throws InterruptedException
*/
public void merge() throws InterruptedException{
// Actual data of the secondary queue
List<Object> currData = null;
// Take first element of the primary queue (wait until message is available)
Message m = primaryQueue.getQueue().take();
while(! (m instanceof EndOfStreamMessage)){
if(m instanceof DataMessage){
DataMessage dm = (DataMessage) m;
// Get and remove merge timestamp from the data of the message
Double timestamp = (Double) dm.getData().remove(1);
long milliseconds = (long) (timestamp*1000);
long nanoOffset = (long)((timestamp*1000-milliseconds)*1000000);
// System.out.println("timestamp: "+new Date(milliseconds)+" "+milliseconds+" ."+nanoOffset);
while(true){
// Assumption: the secondary Queue holds at least the data up to the
// timestamp of the primary queue
Message mess = secondaryQueue.getQueue().peek();
if(mess == null){
break;
}
if(mess instanceof DataMessage){
// 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
DataMessage msCheck = (DataMessage) mess;
// System.out.println("Mess: "+mess);
long currMilliCheck = ((Double) msCheck.getData().get(0)).longValue();
long currNanoCheck = ((Double) msCheck.getData().get(1)).longValue();
// Check
if(currMilliCheck>milliseconds || (currMilliCheck==milliseconds && currNanoCheck>nanoOffset)){
break;
}
DataMessage ms = (DataMessage) secondaryQueue.getQueue().take();
// System.out.println("Ms: "+ms);
currData = ms.getData();
// Remove timestamps
currData.remove(0);
currData.remove(0);
}
else{
// No DataMessage
break;
}
}
// Add data to primary data queue message and put it into the out queue
// System.out.println(currData);
dm.getData().addAll(currData);
dataQueue.add(dm);
}
m = primaryQueue.getQueue().take();
}
// Add the end of stream message of the primary queue
dataQueue.add(m);
}
/**
* The structure of the data message depends on the sensors registered at this loop
* at the time this method is called.
* @return the data queue and the metadata of the data messages
*/
public DataQueue getDataQueue() {
DataMessageMetadata m = new DataMessageMetadata();
DataMessageMetadata pqm = primaryQueue.getDataMessageMetadata();
m.getComponents().add(pqm.getComponents().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.getComponents().size();i++){
m.getComponents().add(pqm.getComponents().get(i));
}
DataMessageMetadata sqm = secondaryQueue.getDataMessageMetadata();
// Skip first two components of the message as this is the timestamp
for(int i=2;i<sqm.getComponents().size();i++){
m.getComponents().add(sqm.getComponents().get(i));
}
return new DataQueue(dataQueue, m);
}
}
@@ -0,0 +1,378 @@
/**
*
* 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 gov.aps.jca.CAException;
import gov.aps.jca.Monitor;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.Semaphore;
import java.util.logging.Logger;
import ch.psi.fda.core.Action;
import ch.psi.fda.core.ActionLoop;
import ch.psi.fda.core.Sensor;
import ch.psi.fda.core.messages.ComponentMetadata;
import ch.psi.fda.core.messages.DataMessage;
import ch.psi.fda.core.messages.DataMessageMetadata;
import ch.psi.fda.core.messages.DataQueue;
import ch.psi.fda.core.messages.EndOfStreamMessage;
import ch.psi.fda.core.messages.Message;
import ch.psi.fda.core.sensors.ChannelAccessDoubleSensor;
import ch.psi.jcae.ChannelBean;
import ch.psi.jcae.MonitorListenerDoubleTimestamp;
/**
* @author ebner
*
* 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";
// Get Logger
private static final Logger logger = Logger.getLogger(ScrlogicLoop.class.getName());
private static Semaphore semaphore = new Semaphore(0);
/**
* Data queue sensor data is posted to. A message consists of a list of data objects
* that are read out of the sensors of this loop.
*/
private BlockingQueue<Message> dataQueue;
private boolean dataGroup = false;
private List<Action> preActions;
private List<Action> postActions;
private List<Sensor> sensors;
private List<Monitor> monitors;
private List<BlockingQueue<TimestampedValue>> queues;
private Thread mergeThread = null;
public ScrlogicLoop(List<Sensor> sensors){
queues = new ArrayList<BlockingQueue<TimestampedValue>>();
preActions = new ArrayList<Action>();
postActions = new ArrayList<Action>();
monitors = new ArrayList<Monitor>();
this.sensors = sensors;
this.dataQueue = new LinkedBlockingQueue<Message>();
}
/**
* @return the queues
*/
public List<BlockingQueue<TimestampedValue>> getQueues() {
return queues;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Action#execute()
*/
@Override
public void execute() throws InterruptedException {
try{
// Attach monitors to the channels
for(Sensor sensor: sensors){
if(sensor instanceof ChannelAccessDoubleSensor){
ChannelAccessDoubleSensor s = (ChannelAccessDoubleSensor) sensor;
ChannelBean<Double> b = s.getChannel();
// Create data queue for the channel
final BlockingQueue<TimestampedValue> q = new LinkedBlockingQueue<TimestampedValue>();
queues.add(q);
Monitor m = b.attachMonitor(new MonitorListenerDoubleTimestamp() {
@Override
public void valueChanged(Double value, Date timestamp, long nanosecondsOffset) {
// Add values to channel queue
q.add(new TimestampedValue(value, timestamp.getTime(), nanosecondsOffset));
// Increase semaphore count (used for merging thread of the queues)
semaphore.release();
}
});
monitors.add(m);
}
}
}
catch(CAException e){
new RuntimeException("Unable to create monitor for channels",e);
}
logger.info("Start data acquisition");
// Start merge thread
mergeThread = new Thread(new Runnable() {
@Override
public void run() {
try {
merge();
} catch (InterruptedException e) {
// Normal termination
}
}
});
mergeThread.start();
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Action#abort()
*/
@Override
public void abort() {
// Actually this is stopping the logic
// Remove monitors
try{
for(int i=0;i<sensors.size();i++){
Sensor sensor = sensors.get(i);
if(sensor instanceof ChannelAccessDoubleSensor){
ChannelAccessDoubleSensor s = (ChannelAccessDoubleSensor) sensor;
ChannelBean<Double> b = s.getChannel();
b.removeMonitor(monitors.get(i));
}
}
}
catch(CAException e){
new RuntimeException(e);
}
// Stop merge thread
mergeThread.interrupt();
// Clear data queues
for(BlockingQueue<TimestampedValue> q: queues){
q.clear();
}
// Put end of stream to the queue
dataQueue.add(new EndOfStreamMessage(dataGroup));
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Action#destroy()
*/
@Override
public void destroy() {
// Destroy all sensors
for(Sensor s: sensors){
s.destroy();
}
sensors.clear();
}
/* (non-Javadoc)
* @see ch.psi.fda.core.ActionLoop#prepare()
*/
@Override
public void prepare() {
// do nothing
}
/* (non-Javadoc)
* @see ch.psi.fda.core.ActionLoop#cleanup()
*/
@Override
public void cleanup() {
// Do nothing
}
/* (non-Javadoc)
* @see ch.psi.fda.core.ActionLoop#getPreActions()
*/
@Override
public List<Action> getPreActions() {
return preActions;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.ActionLoop#getPostActions()
*/
@Override
public List<Action> getPostActions() {
return postActions;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.ActionLoop#isDataGroup()
*/
@Override
public boolean isDataGroup() {
return dataGroup;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.ActionLoop#setDataGroup(boolean)
*/
@Override
public void setDataGroup(boolean dataGroup) {
this.dataGroup = dataGroup;
}
/**
* The structure of the data message depends on the sensors registered at this loop
* at the time this method is called.
* @return the data queue and the metadata of the data messages
*/
public DataQueue getDataQueue() {
DataMessageMetadata m = new DataMessageMetadata();
// Build up data message metadata based on the channels registered.
m.getComponents().add(new ComponentMetadata(ID_TIMESTAMP_MILLISECONDS));
m.getComponents().add(new ComponentMetadata(ID_TIMESTAMP_OFFSET_NANOSECONDS));
for(Sensor s: sensors){
m.getComponents().add(new ComponentMetadata(s.getId()));
}
return new DataQueue(dataQueue, m);
}
/**
* Merge data collected by different monitor
*
* @throws InterruptedException
*/
private void merge() throws InterruptedException{
// Array to hold temporary channel values
TimestampedValue[] cvalues = new TimestampedValue[queues.size()];
TimestampedValueComparator comparator = new TimestampedValueComparator();
// Oldest value written
TimestampedValue globalOldest = null;
List<Integer> indexes = new ArrayList<Integer>();
while(!Thread.currentThread().isInterrupted()){
semaphore.acquire();
Thread.sleep(10); // Ensure that close by monitors have time to catch up / also ensure context switch
// Oldest value of this run
TimestampedValue oldest = null;
// Queue index of the oldest value of this run
indexes.clear();
// Find oldest element in any of the queues
for(int i=0;i<queues.size();i++){
BlockingQueue<TimestampedValue> q = queues.get(i);
TimestampedValue ttcheck = q.peek();
if(ttcheck != null){
if(oldest==null){
// Update the oldest variable with current element
oldest = ttcheck;
indexes.clear();
indexes.add(i);
}
else if(comparator.compare(ttcheck, oldest)<0){ // Check whether timestamp is less (older) than the current oldest timestamp.
oldest = ttcheck;
indexes.clear();
indexes.add(i);
}
else if(comparator.compare(ttcheck, oldest) == 0){
// SAME TIMESTAMP
indexes.add(i);
}
else{
}
}
}
// logger.info("Index: "+index+" Permits: "+semaphore.availablePermits());
// System.out.println("indexes: "+indexes.size());
if(indexes.size()>0){
long timestamp = 0l;
long nanoOffset =0l;
for(Integer index: indexes){
// Get next older value
cvalues[index] = queues.get(index).poll();
if(globalOldest != null){
if(comparator.compare(cvalues[index], globalOldest)>=0){
// Update the global oldest variable
globalOldest = cvalues[index];
timestamp = cvalues[index].getTimestamp();
nanoOffset = cvalues[index].getNanosecondsOffset();
}
else{
// Monitors did not fire in sequence (an newer monitor overtook an older (from an other IOC))
logger.warning("Timestamped value out of sequence - discard value !!!!");
// Continue with next value ...
continue;
}
}
else{
globalOldest = cvalues[index];
}
}
DataMessage message = new DataMessage();
message.getData().add(new Double(timestamp));
message.getData().add(new Double(nanoOffset));
for(int y=0;y<cvalues.length;y++){
if(cvalues[y]!=null){
message.getData().add(new Double(cvalues[y].getValue()));
}
else{
message.getData().add(Double.NaN);
}
}
dataQueue.add(message);
}
}
}
}
@@ -0,0 +1,81 @@
/**
*
* 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;
/**
* @author ebner
*
*/
public class TimestampedValue {
private Double value;
private long timestamp;
private long nanosecondsOffset;
public TimestampedValue(Double value, long timestamp, long nanosecondsOffset){
this.value = value;
this.timestamp = timestamp;
this.nanosecondsOffset = nanosecondsOffset;
}
/**
* @return the value
*/
public Double getValue() {
return value;
}
/**
* @param value the value to set
*/
public void setValue(Double value) {
this.value = value;
}
/**
* @return the timestamp
*/
public long getTimestamp() {
return timestamp;
}
/**
* @param timestamp the timestamp to set
*/
public void setTimestamp(long timestamp) {
this.timestamp = timestamp;
}
/**
* @return the nanosecondsOffset
*/
public long getNanosecondsOffset() {
return nanosecondsOffset;
}
/**
* @param nanosecondsOffset the nanosecondsOffset to set
*/
public void setNanosecondsOffset(long nanosecondsOffset) {
this.nanosecondsOffset = nanosecondsOffset;
}
}
@@ -0,0 +1,45 @@
/**
*
* 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;
/**
* @author ebner
*
*/
public class TimestampedValueComparator implements Comparator<TimestampedValue> {
@Override
public int compare(TimestampedValue o1, TimestampedValue o2) {
if (o1.getTimestamp() < o2.getTimestamp()) {
return -1;
} else if (o1.getTimestamp() > o2.getTimestamp()) {
return 1;
} else if (o1.getTimestamp() == o2.getTimestamp()) {
if (o1.getNanosecondsOffset() < o2.getNanosecondsOffset()) {
return -1;
} else if (o1.getNanosecondsOffset() > o2.getNanosecondsOffset()) {
return 1;
}
}
return 0;
}
}
@@ -0,0 +1,103 @@
/**
*
* 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.ChannelBean;
import ch.psi.jcae.annotation.CaChannel;
/**
* @author ebner
*
*/
public class VSC16ScalerChannelsTemplate {
public enum Command {Done, Count};
/**
* Command
*/
@CaChannel(type=Integer.class, name =".CNT")
private ChannelBean<Integer> command;
public enum Mode {OneShot, AutoCount};
/**
* Count mode
*/
@CaChannel(type=Integer.class, name =".CONT")
private ChannelBean<Integer> mode;
/**
* Channel description
*/
@CaChannel(type=Boolean.class, name={".NM1", ".NM2", ".NM3", ".NM4", ".NM5", ".NM6", ".NM7", ".NM8", ".NM9", ".NM10", ".NM11", ".NM12", ".NM13", ".NM14", ".NM15", ".NM16"})
private List<ChannelBean<Boolean>> channelDescription;
/**
* Channel gate
*/
@CaChannel(type=Boolean.class, name={".G1", ".G2", ".G3", ".G4", ".G5", ".G6", ".G7", ".G8", ".G9", ".G10", ".G11", ".G12", ".G13", ".G14", ".G15", ".G16"})
private List<ChannelBean<Boolean>> channelGate;
/**
* Channel preset count
* If gate is on scaler will only count until this value
*/
@CaChannel(type=Integer.class, name={".PR1", ".PR2", ".PR3", ".PR4", ".PR5", ".PR6", ".PR7", ".PR8", ".PR9", ".PR10", ".PR11", ".PR12", ".PR13", ".PR14", ".PR15", ".PR16"})
private List<ChannelBean<Integer>> channelPresetCount;
/**
* @return the command
*/
public ChannelBean<Integer> getCommand() {
return command;
}
/**
* @return the mode
*/
public ChannelBean<Integer> getMode() {
return mode;
}
/**
* @return the channelDescription
*/
public List<ChannelBean<Boolean>> getChannelDescription() {
return channelDescription;
}
/**
* @return the channelGate
*/
public List<ChannelBean<Boolean>> getChannelGate() {
return channelGate;
}
/**
* @return the channelPresetCount
*/
public List<ChannelBean<Integer>> getChannelPresetCount() {
return channelPresetCount;
}
}
@@ -0,0 +1,261 @@
/**
*
* 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 gov.aps.jca.CAException;
import java.util.List;
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.messages.DataMessage;
import ch.psi.fda.core.messages.DataMessageMetadata;
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.jcae.ChannelBean;
import ch.psi.jcae.ChannelBeanFactory;
/**
* Manipulation
* @author ebner
*/
public class JythonManipulation implements Manipulation{
// Get Logger
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;
/**
* Parameter array of the entry function
*/
private String[] parameter;
/**
* Jython entry call
*/
private String jythonCall;
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(DataMessageMetadata 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];
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());
}
else if (jpm instanceof JythonParameterMappingChannel){
JythonParameterMappingChannel pm = (JythonParameterMappingChannel)jpm;
parameterIndex[i] = null;
ChannelBean<?> cb;
try {
cb = ChannelBeanFactory.getFactory().createChannelBean(pm.getType(), pm.getChannel(), true);
} catch (CAException e) {
throw new IllegalArgumentException("Unable to establish channel: "+pm.getChannel(), e);
} catch (InterruptedException e) {
throw new RuntimeException("Unable to establish channel: "+pm.getChannel(), e);
}
engine.put(pm.getVariable(), cb);
}
else if (jpm instanceof JythonParameterMappingGlobalVariable){
JythonParameterMappingGlobalVariable pm = (JythonParameterMappingGlobalVariable)jpm;
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){
// Manipulate data
for(int i=0;i<parameterIndex.length;i++){
if(parameterIndex[i] != null){
engine.put(parameter[i], message.getData().get(parameterIndex[i]));
}
}
try {
if(returnArray){
return((double[]) engine.eval(jythonCall));
}
else{
return( (Double) engine.eval(jythonCall));
}
} 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;
}
}
}
@@ -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.manipulator;
import ch.psi.fda.core.messages.DataMessage;
import ch.psi.fda.core.messages.DataMessageMetadata;
/**
* @author ebner
*
*/
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(DataMessageMetadata metadata);
/**
* Execute the manipulation on the passed data message
* @param message Message to manipulate
* @return Result of the manipulation
*/
public Object execute(DataMessage message);
}
@@ -0,0 +1,123 @@
/**
*
* 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.List;
import java.util.concurrent.LinkedBlockingQueue;
import ch.psi.fda.core.messages.ComponentMetadata;
import ch.psi.fda.core.messages.DataMessage;
import ch.psi.fda.core.messages.DataMessageMetadata;
import ch.psi.fda.core.messages.DataQueue;
import ch.psi.fda.core.messages.EndOfStreamMessage;
import ch.psi.fda.core.messages.Message;
/**
* @author ebner
*
*/
public class Manipulator implements Runnable{
/**
* Outgoing data queue
*/
private final DataQueue outQueue;
/**
* Incomming data queue
*/
private final DataQueue queue;
/**
* List of manipulations
*/
private final List<Manipulation> manipulations;
/**
* Constructor
* @param queue
* @param manipulations
*/
// TODO need to support multiple (a list of) manipulation(s)
public Manipulator(DataQueue queue, List<Manipulation> manipulations){
this.manipulations = manipulations;
// Create outgoing data metadata
DataMessageMetadata dmetadata = queue.getDataMessageMetadata().clone();
// Initialize manipulations and create outgoing metadata
for(Manipulation manipulation: this.manipulations){
// Initialize manipulation
// manipulation.initialize(queue.getDataMessageMetadata());
manipulation.initialize(dmetadata);
// Add manipulation id to metadata
dmetadata.getComponents().add(new ComponentMetadata(manipulation.getId(),0)); // Calculated component always belongs to lowes dimension
}
this.queue = queue;
this.outQueue = new DataQueue(new LinkedBlockingQueue<Message>(1000) , dmetadata ); // Create bounded queue to prevent running out of memory ...
}
/**
* @return the outQueue
*/
public DataQueue getOutQueue() {
return outQueue;
}
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
try{
// Dispatch Messages
Message message = queue.getQueue().take();
while(!(message instanceof EndOfStreamMessage)){
if(message instanceof DataMessage){
DataMessage dm = (DataMessage) message;
for(Manipulation manipulation: manipulations){
dm.getData().add(manipulation.execute(dm));
}
}
// Put message to outgoing queue ...
outQueue.getQueue().put(message);
// Read next message
message = queue.getQueue().take();
}
// Write end of stream message
outQueue.getQueue().put(message);
} catch (InterruptedException e) {
// TODO Stop loop and exit logic instead of throwing an Exception
throw new RuntimeException("Data manipulator was interrupted while writing data to file",e);
}
}
}
@@ -0,0 +1,82 @@
/**
*
* 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.messages;
/**
* 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).
*
* @author ebner
*
*/
public class ComponentMetadata {
/**
* Global id of the component
*/
private final String id;
/**
* Dimension of the component (optional)
*/
private final int dimension;
/**
* Default constructor - Create component metadata
* @param id Global id of the component
*/
public ComponentMetadata(String id){
this.id = id;
this.dimension = 0;
}
/**
* Constructor that initializes id and the dimension number
* @param id
* @param dimension
*/
public ComponentMetadata(String id, int dimension){
this.id = id;
this.dimension = dimension;
}
/**
* @return the dimension
*/
public int getDimension() {
return dimension;
}
/**
* @return the id
*/
public String getId() {
return id;
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "Component Metadata[id="+id+" dimension="+dimension+"]";
}
}
@@ -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.core.messages;
/**
* A control message that is not holding any data but
* control information (like end of loop, etc.)
* @author ebner
*
*/
public abstract class ControlMessage extends Message {
}
@@ -0,0 +1,83 @@
/**
*
* 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.messages;
import java.util.ArrayList;
import java.util.List;
/**
* Message holding data
* @author ebner
*
*/
public class DataMessage extends Message {
/**
* Data payload of the message
*/
private List<Object> data;
/**
* Constructor - Create message object with data payload
*/
public DataMessage(){
this.data = new ArrayList<Object>();
}
/**
* Get data object of the message
* @return Data object of the message
*/
public List<Object> getData(){
return(data);
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@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();
}
}
@@ -0,0 +1,81 @@
/**
*
* 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.messages;
import java.util.ArrayList;
import java.util.List;
/**
* Structure to hold the metadata of a component of a message.
* @author ebner
*
*/
public class DataMessageMetadata {
/**
* List of the metadata of the message components
*/
private List<ComponentMetadata> components;
/**
* Default constructor
*/
public DataMessageMetadata(){
components = new ArrayList<ComponentMetadata>();
}
/**
* @return the list of component metadata of a message described by this object.
*/
public List<ComponentMetadata> getComponents() {
return components;
}
/**
* Get the index of the component with the specified Id
* @param componentId Id of the component to look for
* @return Index of the component
* @throws IllegalArgumentException There is no component with the specified Id
*/
public int getIndex(String componentId) throws IllegalArgumentException {
for(int i=0;i<components.size();i++){
ComponentMetadata c =components.get(i);
if(c.getId().equals(componentId)){
return(i);
}
}
throw new IllegalArgumentException("There is no message component with Id "+ componentId);
}
/**
* Clone data structure
* @return Cloned data structure
*/
public DataMessageMetadata clone(){
DataMessageMetadata metadata = new DataMessageMetadata();
for(int i=0;i<components.size();i++){
ComponentMetadata c =components.get(i);
metadata.getComponents().add(new ComponentMetadata(c.getId(), c.getDimension()));
}
return(metadata);
}
}
@@ -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.messages;
import java.util.concurrent.BlockingQueue;
/**
* Container holding for holding a queue and the metadata of the
* data messages of that queue.
* @author ebner
*
*/
public class DataQueue {
/**
* Queue serving the messages
*/
private final BlockingQueue<Message> queue;
/**
* Metadata of the data messages of the queue
*/
private final DataMessageMetadata dataMessageMetadata;
/**
* Constructor
* @param queue Data queue
* @param dataMessageMetadata Metadata of the data messages of the queue
*/
public DataQueue(BlockingQueue<Message> queue, DataMessageMetadata dataMessageMetadata){
this.queue = queue;
this.dataMessageMetadata = dataMessageMetadata;
}
/**
* @return the queue
*/
public BlockingQueue<Message> getQueue() {
return queue;
}
/**
* @return the metadata
*/
public DataMessageMetadata getDataMessageMetadata() {
return dataMessageMetadata;
}
}
@@ -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.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
* @author ebner
*
*/
public class EndOfStreamMessage extends ControlMessage{
/**
* 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;
}
/**
* @return the iflag
*/
public boolean isIflag(){
return(iflag);
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "Message[ c message: end of stream ]";
}
}
@@ -0,0 +1,28 @@
/**
*
* 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.messages;
/**
* Message that can be put to the data queue
* @author ebner
*
*/
public abstract class Message {
}
@@ -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.messages;
/**
* Message that is send at the end of the action loop inside an ActionLoop implementation
* @author ebner
*
*/
public class StreamDelimiterMessage extends ControlMessage{
/**
* 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;
/**
* Constructor
* @param number Number of the dimension this delimiter belongs to
*/
public StreamDelimiterMessage(int number){
this(number, false);
}
/**
* Constructor
* @param number
* @param iflag Flag to indicate that data is grouped
*/
public StreamDelimiterMessage(int number, boolean iflag){
this.number = number;
this.iflag = iflag;
}
/**
* @return the number
*/
public int getNumber() {
return number;
}
/**
* @return the iflag
*/
public boolean isIflag(){
return iflag;
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
// TODO Auto-generated method stub
return "Message [ c message: delimiter dimension "+number+" ]";
}
}
@@ -0,0 +1,55 @@
/**
*
* 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;
/**
* @author ebner
*
*/
public class JythonGlobalVariable {
private String name;
private double value;
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the value
*/
public double getValue() {
return value;
}
/**
* @param value the value to set
*/
public void setValue(double value) {
this.value = value;
}
}
@@ -0,0 +1,75 @@
/**
*
* 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 java.util.HashMap;
/**
* Singleton dictionary class
*
* @author ebner
*
*/
public class JythonGlobalVariableDictionary {
private static JythonGlobalVariableDictionary instance = new JythonGlobalVariableDictionary();
private HashMap<String, JythonGlobalVariable> dictionary = new HashMap<String, JythonGlobalVariable>();
/**
* Private constructor
*/
private JythonGlobalVariableDictionary(){
}
/**
* Get singleton instance of the dictionary
* @return
*/
public static JythonGlobalVariableDictionary getInstance(){
return instance;
}
/**
* Get variable from dictionary. If the variable does not exist it will be created.
* @param name
* @return
*/
public JythonGlobalVariable getVariable(String name){
JythonGlobalVariable variable;
if(dictionary.containsKey(name)){
variable = dictionary.get(name);
}
else{
variable = new JythonGlobalVariable();
variable.setName(name);
dictionary.put(name, variable);
}
return variable;
}
/**
* Clear variable dictionary
*/
public void clear(){
dictionary.clear();
}
}
@@ -0,0 +1,55 @@
/**
*
* 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
* @author ebner
*
*/
public abstract class JythonParameterMapping {
/**
* Variable name inside the script
*/
private String variable;
/**
* Constructor
* @param variable
*/
public JythonParameterMapping(String variable){
this.variable = variable;
}
/**
* @return the variable
*/
public String getVariable() {
return variable;
}
/**
* @param variable the variable to set
*/
public void setVariable(String variable) {
this.variable = variable;
}
}
@@ -0,0 +1,75 @@
/**
*
* 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 channel bean.
* @author ebner
*
*/
public class JythonParameterMappingChannel extends JythonParameterMapping {
/**
* Id of the component to map to this variable
*/
private String channel;
private Class<?> type;
/**
* Constructor accepting varible/id pair
* @param variable
* @param channel
* @param type
*/
public JythonParameterMappingChannel(String variable, String channel, Class<?> type){
super(variable);
this.channel = channel;
this.type = type;
}
/**
* @return the channel
*/
public String getChannel() {
return channel;
}
/**
* @param channel
*/
public void setChannel(String channel) {
this.channel = channel;
}
/**
* @return the type
*/
public Class<?> getType() {
return type;
}
/**
* @param type the type to set
*/
public void setType(Class<?> type) {
this.type = type;
}
}
@@ -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.
* @author ebner
*
*/
public class JythonParameterMappingGlobalVariable extends JythonParameterMapping {
private JythonGlobalVariable globalVariable;
/**
* Constuctor
* @param variable
* @param globalVariable
*/
public JythonParameterMappingGlobalVariable(String variable, JythonGlobalVariable globalVariable){
super(variable);
this.globalVariable = globalVariable;
}
/**
* @return the globalVariable
*/
public JythonGlobalVariable getGlobalVariable() {
return globalVariable;
}
/**
* @param globalVariable the globalVariable to set
*/
public void setGlobalVariable(JythonGlobalVariable globalVariable) {
this.globalVariable = globalVariable;
}
}
@@ -0,0 +1,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.core.scripting;
/**
* Mapping of a script parameter to a component via the Id.
* @author ebner
*
*/
public class JythonParameterMappingID extends JythonParameterMapping {
/**
* Id of the component to map to this variable
*/
private String refid;
/**
* Constructor accepting varible/id pair
* @param variable
* @param refid
*/
public JythonParameterMappingID(String variable, String refid){
super(variable);
this.refid = refid;
}
/**
* @return the refid
*/
public String getRefid() {
return refid;
}
/**
* @param refid the refid to set
*/
public void setRefid(String refid) {
this.refid = refid;
}
}
@@ -0,0 +1,144 @@
/**
*
* 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.logging.Logger;
import gov.aps.jca.CAException;
import ch.psi.fda.core.EngineConfiguration;
import ch.psi.fda.core.Sensor;
import ch.psi.jcae.ChannelBeanFactory;
import ch.psi.jcae.ChannelBean;
/**
* Scalar sensor that reads a double form a Channel Access channel
* @author ebner
*
*/
public class ChannelAccessDoubleArraySensor implements Sensor {
// Get Logger
private static Logger logger = Logger.getLogger(ChannelAccessDoubleArraySensor.class.getName());
/**
* Channel Access channel of this sensor
*/
private final ChannelBean<double[]> channel;
/**
* Number of elements to read from the waveform
*/
private final int numberOfElements;
/**
* Global id of the sensor
*/
private final String id;
/**
* Constructor
* @param id Global id of the sensor
* @param channelName Name of the Channel Access channel of this sensor
* @param numberOfElements Number of elements to read out of the waveform
*/
public ChannelAccessDoubleArraySensor(String id, String channelName, int numberOfElements){
try {
this.channel = ChannelBeanFactory.getFactory().createChannelBean(double[].class, channelName, false);
this.numberOfElements = numberOfElements;
this.id = id;
} catch (CAException e) {
// Convert Exception into unchecked RuntimeException
throw new IllegalArgumentException("Unable to initialize sensor channel [name:"+channelName+"]",e);
} catch (InterruptedException e) {
throw new RuntimeException("Unable to initialize sensor channel [name:"+channelName+"]",e);
}
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Sensor#readout()
*/
@Override
public Object read() throws InterruptedException {
logger.finest("Read sensor "+channel.getName());
double[] v;
try {
v = channel.getValue(numberOfElements);
} catch (IllegalStateException e) {
// Only fail during data acquisition if fail on error sensor flag is on true. Otherwise
// return NaN (Not a Number)
if(EngineConfiguration.getInstance().isFailOnSensorError()){
throw e;
}
v = new double[numberOfElements];
for(int i =0;i<v.length;i++){
v[i] = Double.NaN;
}
} catch (CAException e) {
// Only fail during data acquisition if fail on error sensor flag is on true. Otherwise
// return NaN (Not a Number)
if(EngineConfiguration.getInstance().isFailOnSensorError()){
// Convert Exception into unchecked RuntimeException
throw new RuntimeException("Unable to get value from channel [name:"+channel.getName()+"]",e);
}
v = new double[numberOfElements];
for(int i =0;i<v.length;i++){
v[i] = Double.NaN;
}
// } catch (InterruptedException e) {
// // Only fail during data acquisition if fail on error sensor flag is on true. Otherwise
// // return NaN (Not a Number)
// if(EngineConfiguration.getInstance().isFailOnSensorError()){
// throw new RuntimeException("Unable to get value from channel [name:"+channel.getName()+"]",e);
// }
//
// v = new double[numberOfElements];
// for(int i =0;i<v.length;i++){
// v[i] = Double.NaN;
// }
}
return(v);
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Sensor#getId()
*/
@Override
public String getId() {
return id;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Sensor#destroy()
*/
@Override
public void destroy() {
// Destroy channel
try {
logger.finest("Destroy sensor channel: "+channel.getName());
channel.destroy();
} catch (CAException e) {
throw new RuntimeException("Unable to destroy channel ["+channel.getName()+"]",e);
}
}
}
@@ -0,0 +1,132 @@
/**
*
* 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.logging.Logger;
import gov.aps.jca.CAException;
import ch.psi.fda.core.EngineConfiguration;
import ch.psi.fda.core.Sensor;
import ch.psi.jcae.ChannelBeanFactory;
import ch.psi.jcae.ChannelBean;
/**
* Scalar sensor that reads a double form a Channel Access channel
* @author ebner
*
*/
public class ChannelAccessDoubleSensor implements Sensor {
// Get Logger
private static Logger logger = Logger.getLogger(ChannelAccessDoubleSensor.class.getName());
/**
* Channel Access channel of this sensor
*/
private ChannelBean<Double> channel;
/**
* Global id of the sensor
*/
private final String id;
/**
* Constructor
* @param id Global id of the sensor
* @param channelName Name of the Channel Access channel of this sensor
*/
public ChannelAccessDoubleSensor(String id, String channelName){
try {
channel = ChannelBeanFactory.getFactory().createChannelBean(Double.class, channelName, false);
this.id = id;
} catch (CAException e) {
// Convert Exception into unchecked RuntimeException
throw new IllegalArgumentException("Unable to initialize sensor channel [name:"+channelName+"]",e);
} catch (InterruptedException e) {
throw new RuntimeException("Unable to initialize sensor channel [name:"+channelName+"]",e);
}
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Sensor#readout()
*/
@Override
public Object read() throws InterruptedException {
logger.finest("Read sensor "+channel.getName());
Double v;
try {
v = channel.getValue();
} catch (IllegalStateException e) {
// Only fail during data acquisition if fail on error sensor flag is on true. Otherwise
// return NaN (Not a Number)
if(EngineConfiguration.getInstance().isFailOnSensorError()){
throw e;
}
v = Double.NaN;
} catch (CAException e) {
// Only fail during data acquisition if fail on error sensor flag is on true. Otherwise
// return NaN (Not a Number)
if(EngineConfiguration.getInstance().isFailOnSensorError()){
// Convert Exception into unchecked RuntimeException
throw new RuntimeException("Unable to get value from channel [name:"+channel.getName()+"]",e);
}
v = Double.NaN;
// } catch (InterruptedException e) {
// if(EngineConfiguration.getInstance().isFailOnSensorError()){
// throw new RuntimeException("Unable to get value from channel [name:"+channel.getName()+"]",e);
// }
// v = Double.NaN;
}
return(v);
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Sensor#getId()
*/
@Override
public String getId() {
return id;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Sensor#destroy()
*/
@Override
public void destroy() {
// Destroy channel
try {
logger.finest("Destroy sensor channel: "+channel.getName());
channel.destroy();
} catch (CAException e) {
throw new RuntimeException("Unable to destroy channel ["+channel.getName()+"]",e);
}
}
/**
* Get channel object of sensor
* @return
*/
public ChannelBean<Double> getChannel(){
return channel;
}
}
@@ -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.sensors;
import java.util.logging.Logger;
import gov.aps.jca.CAException;
import ch.psi.fda.core.EngineConfiguration;
import ch.psi.fda.core.Sensor;
import ch.psi.jcae.ChannelBeanFactory;
import ch.psi.jcae.ChannelBean;
/**
* Scalar sensor that reads a double form a Channel Access channel
* @author ebner
*
*/
public class ChannelAccessStringSensor implements Sensor {
// Get Logger
private static Logger logger = Logger.getLogger(ChannelAccessStringSensor.class.getName());
/**
* Channel Access channel of this sensor
*/
private ChannelBean<String> channel;
/**
* Global id of the sensor
*/
private final String id;
/**
* Constructor
* @param id Global id of the sensor
* @param channelName Name of the Channel Access channel of this sensor
*/
public ChannelAccessStringSensor(String id, String channelName){
try {
channel = ChannelBeanFactory.getFactory().createChannelBean(String.class, channelName, false);
this.id = id;
} catch (CAException e) {
// Convert Exception into unchecked RuntimeException
throw new IllegalArgumentException("Unable to initialize sensor channel [name:"+channelName+"]",e);
} catch (InterruptedException e) {
throw new RuntimeException("Unable to initialize sensor channel [name:"+channelName+"]",e);
}
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Sensor#readout()
*/
@Override
public Object read() throws InterruptedException {
logger.finest("Read sensor "+channel.getName());
String v;
try {
v = channel.getValue();
} catch (IllegalStateException e) {
// Only fail during data acquisition if fail on error sensor flag is on true. Otherwise
// return NaN (Not a Number)
if(EngineConfiguration.getInstance().isFailOnSensorError()){
throw e;
}
v = null;
} catch (CAException e) {
// Only fail during data acquisition if fail on error sensor flag is on true. Otherwise
// return NaN (Not a Number)
if(EngineConfiguration.getInstance().isFailOnSensorError()){
// Convert Exception into unchecked RuntimeException
throw new RuntimeException("Unable to get value from channel [name:"+channel.getName()+"]",e);
}
v = null;
// } catch (InterruptedException e) {
// if(EngineConfiguration.getInstance().isFailOnSensorError()){
// throw new RuntimeException("Unable to get value from channel [name:"+channel.getName()+"]",e);
// }
// v = null;
}
return(v);
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Sensor#getId()
*/
@Override
public String getId() {
return id;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Sensor#destroy()
*/
@Override
public void destroy() {
// Destroy channel
try {
logger.finest("Destroy sensor channel: "+channel.getName());
channel.destroy();
} catch (CAException e) {
throw new RuntimeException("Unable to destroy channel ["+channel.getName()+"]",e);
}
}
}
@@ -0,0 +1,136 @@
/**
*
* 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.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.
*
* @author ebner
*
*/
public class ComplexSensor implements Sensor {
// Get Logger
private static Logger logger = Logger.getLogger(ComplexSensor.class.getName());
/**
* Id of the sensor
*/
private String id;
/**
* Sensor to complement
*/
private final Sensor sensor;
/**
* 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;
/**
* Constructor
*/
public ComplexSensor(String id, Sensor sensor){
this.id = id;
this.sensor = sensor;
this.preActions = new ArrayList<Action>();
this.postActions = new ArrayList<Action>();
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Sensor#read()
*/
@Override
public Object read() throws InterruptedException {
// Execute pre actions
logger.finest("Execute pre actions");
for(Action action: preActions){
action.execute();
}
// Readout sensor
Object value = sensor.read();
// Execute post actions
logger.finest("Execute post actions");
for(Action action: postActions){
action.execute();
}
return value;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Sensor#getId()
*/
@Override
public String getId() {
return id;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Sensor#destroy()
*/
@Override
public void destroy() {
// Destroy preActions
for(Action a: preActions){
a.destroy();
}
sensor.destroy();
// Destroy postActions
for(Action a: postActions){
a.destroy();
}
}
/**
* @return the preActions
*/
public List<Action> getPreActions() {
return preActions;
}
/**
* @return the postActions
*/
public List<Action> getPostActions() {
return postActions;
}
}
@@ -0,0 +1,69 @@
/**
*
* 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;
/**
* Get the current time in milliseconds
* @author ebner
*
*/
public class MillisecondTimestampSensor implements Sensor {
/**
* Global id of the sensor
*/
private final String id;
/**
* Constructor
* @param id Global id of the sensor
*/
public MillisecondTimestampSensor(String id){
this.id = id;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Sensor#read()
*/
@Override
public Object read() {
// Return current time in milliseconds
return new Double(System.currentTimeMillis());
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Sensor#getId()
*/
@Override
public String getId() {
return id;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Sensor#destroy()
*/
@Override
public void destroy() {
// Nothing to be done
}
}
@@ -0,0 +1,84 @@
/**
*
* 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 out a named (Epics) channel. This sensor can only be used within the
* OTFLoop. If it is used in other loops, the read value will always be 0.
* @author ebner
*
*/
public class OTFNamedChannelSensor implements Sensor {
/**
* Name of the channel
*/
private final String name;
/**
* Global id of the sensor
*/
private final String id;
/**
* Constructor
* @param id Global id of the sensor
* @param name Name of the (Epics) channel
*/
public OTFNamedChannelSensor(String id, String name){
this.id = id;
this.name = name;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Sensor#read()
*/
@Override
public Object read() {
// Always return 0 if read() method is called.
return 0d;
}
/**
* @return the name of the channel
*/
public String getName() {
return name;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Sensor#getId()
*/
@Override
public String getId() {
return id;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Sensor#destroy()
*/
@Override
public void destroy() {
// Nothing to be done
}
}
@@ -0,0 +1,70 @@
/**
*
* 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 actuator readback. This sensor must only be used within the
* OTFLoop. If it is used in other loops, the read value will always be 0.
* @author ebner
*
*/
public class OTFReadbackSensor implements Sensor {
/**
* Global id of the sensor
*/
private final String id;
/**
* Constructor
* @param id Global id of the sensor
*/
public OTFReadbackSensor(String id){
this.id = id;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Sensor#read()
*/
@Override
public Object read() {
// Always return 0 if read() method is called.
return 0d;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Sensor#getId()
*/
@Override
public String getId() {
return id;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Sensor#destroy()
*/
@Override
public void destroy() {
// Nothing to be done
}
}
@@ -0,0 +1,84 @@
/**
*
* 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 out a scaler channel. This sensor can only be used within the
* OTFLoop. If it is used in other loops, the read value will always be 0.
* @author ebner
*
*/
public class OTFScalerChannelSensor implements Sensor {
/**
* Index of the scaler channel. The index starts at 0;
*/
private final int index;
/**
* Global id of the sensor
*/
private final String id;
/**
* Constructor
* @param id Global id of the sensor
* @param index Index of the scaler channel. Index starts at 0.
*/
public OTFScalerChannelSensor(String id, int index){
this.id = id;
this.index = index;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Sensor#read()
*/
@Override
public Object read() {
// Always return 0 if read() method is called.
return 0d;
}
/**
* @return the index of the scaler channel
*/
public int getIndex() {
return index;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Sensor#getId()
*/
@Override
public String getId() {
return id;
}
/* (non-Javadoc)
* @see ch.psi.fda.core.Sensor#destroy()
*/
@Override
public void destroy() {
// Nothing to be done
}
}
@@ -0,0 +1,36 @@
/**
*
* 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.deserializer;
import ch.psi.fda.core.messages.DataQueue;
/**
* Data deserializer
* @author ebner
*
*/
public interface DataDeserializer extends Runnable {
/**
* Get data queue of deserializer
* @return data queue of deserializer
*/
public DataQueue getQueue();
}
@@ -0,0 +1,603 @@
package ch.psi.fda.deserializer;
import hep.io.xdr.XDRInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.Logger;
import ch.psi.fda.core.messages.ComponentMetadata;
import ch.psi.fda.core.messages.DataMessage;
import ch.psi.fda.core.messages.DataMessageMetadata;
import ch.psi.fda.core.messages.DataQueue;
import ch.psi.fda.core.messages.EndOfStreamMessage;
import ch.psi.fda.core.messages.Message;
import ch.psi.fda.core.messages.StreamDelimiterMessage;
public class DataDeserializerMDA implements DataDeserializer {
private static Logger logger = Logger.getLogger(DataDeserializerMDA.class.getName());
private DataQueue queue;
public DataDeserializerMDA(File file){
try {
RecursiveReturnContainer c = read(new FileInputStream(file));
this.queue = new DataQueue(new LinkedBlockingQueue<Message>(), c.getMetadata());
// Add data to queue
for(Message m: c.getMessage()){
queue.getQueue().put(m);
}
queue.getQueue().put(new EndOfStreamMessage());
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
public RecursiveReturnContainer read(InputStream in) throws IOException {
logger.fine("Read MDA input stream");
XDRInputStream x = new XDRInputStream(in);
/**
* Read file header
*/
float version = x.readFloat();
logger.fine("MDA - version:: "+version);
int scanNumber = x.readInt();
logger.fine("MDA - scan number:: "+scanNumber);
int rank = x.readInt();
logger.fine("MDA - rank:: "+rank);
for(int i=rank;i>0; i--){
// Read dimension size
int dimension = x.readInt();
logger.fine("MDA - dimension["+i+"] :: "+dimension);
}
int isRegular = x.readInt(); //(true=1, false=0)
logger.fine("MDA - isRegular:: "+isRegular);
int pExtra = x.readInt(); // Number of extra pvs
logger.fine("MDA - pExtra:: "+pExtra);
/**
* Read data
*/
RecursiveReturnContainer container = readData(x);
/**
* Read extra PVs
*/
if(pExtra > 0){
int length;
logger.fine("Extra PVs");
int numExtra = x.readInt();
logger.fine("MDA - number of extra pvs:: "+numExtra);
for(int i = 0;i<numExtra; i++){
length = x.readInt();
if(length > 0){
String pvName = x.readString();
logger.fine("MDA - pv name:: "+pvName);
}
length = x.readInt();
if(length > 0){
String pvDescription = x.readString(); // description
logger.fine("MDA - pv desciption:: "+pvDescription);
}
int pvType = x.readInt();
logger.fine("MDA - pv type:: "+pvType);
int pvCount = 0;
String pvUnit;
if(pvType != 0){ // if pv type != DBR_STRING
pvCount = x.readInt();
logger.fine("MDA - pv count:: "+pvCount);
length = x.readInt();
if(length > 0){
pvUnit = x.readString();
logger.fine("MDA - pv unit:: "+ pvUnit);
}
}
if(pvType == 0){ // pvType == DBR_STRING
length = x.readInt();
if(length > 0){
String pvValue = x.readString();
logger.fine("MDA - pv value:: "+pvValue);
}
}
else if(pvType == 32){ // pvType == DBR_CTRL_CHAR
for(int u=0;u<pvCount; u++){
char pvValue = x.readChar();
logger.fine("MDA - pv value:: "+pvValue);
}
}
else if(pvType == 29){ // pvType == DBR_CTRL_SHORT
for(int u=0;u<pvCount; u++){
int pvValue = x.readInt();
logger.fine("MDA - pv value:: "+pvValue);
}
}
else if(pvType == 33){ // pvType == DBR_CTRL_LONG
for(int u=0;u<pvCount; u++){
int pvValue = x.readInt();
logger.fine("MDA - pv value:: "+pvValue);
}
}
else if(pvType == 30){ // pvType == DBR_CTRL_FLOAT
for(int u=0;u<pvCount; u++){
float pvValue = x.readFloat();
logger.fine("MDA - pv value:: "+pvValue);
}
}
else if(pvType == 34){ // pvType == DBR_CTRL_DOUBLE
for(int u=0;u<pvCount; u++){
double pvValue = x.readDouble();
logger.fine("MDA - pv value:: "+pvValue);
}
}
}
}
logger.fine("Reading and conversion done ...");
return container;
}
private RecursiveReturnContainer readData(XDRInputStream x) throws IOException {
int length;
logger.fine("Read scan");
/**
* Read scan header
*/
int scanRank = x.readInt();
logger.fine("MDA - scan rank (this):: " + scanRank);
int npts = x.readInt();
logger.fine("MDA - number of requested points (npts):: " + npts);
int currentPoint = x.readInt();
logger.fine("MDA - current point:: " + currentPoint);
// Read pointers to lower scans
if(scanRank > 1){
// For 1D scans this block is never reached
for(int i=0;i<npts;i++){
int pointerLowerScans = x.readInt(); // pointer to lower scans
logger.fine("MDA - pointer lower scans:: "+pointerLowerScans);
}
}
/**
* Read scan info
*/
length = x.readInt();
if(length > 0){
String scanName = x.readString();
logger.fine("MDA - scanName:: "+scanName);
}
length = x.readInt();
if(length > 0){
String scanTime = x.readString(); // timestamp
logger.fine("MDA - scanTime:: "+ scanTime);
}
int numberOfPositioners = x.readInt();
logger.fine("MDA - number of positioners:: "+numberOfPositioners);
int numberOfDetectors = x.readInt();
logger.fine("MDA - number of detectors:: "+ numberOfDetectors);
int numberOfTriggers = x.readInt();
logger.fine("MDA - number of triggers:: "+ numberOfTriggers);
List<ComponentMetadata> componentMetadataList = new ArrayList<ComponentMetadata>();
/**
* Read positioners metadata
*/
for(int i = 0; i<numberOfPositioners; i++){
logger.fine("Read positioner metadata");
int positionerNumber = x.readInt();
logger.fine("MDA - positioner number:: "+positionerNumber);
length = x.readInt();
if(length>0){
String positionerName = x.readString();
logger.fine("MDA - positioner name:: "+positionerName);
// MDA starts at dimension number 1 we start at 0
componentMetadataList.add(new ComponentMetadata(positionerName,scanRank-1));
}
length = x.readInt();
if(length > 0){
String positionerDescription = x.readString();
logger.fine("MDA - positioner description:: "+positionerDescription);
}
length = x.readInt();
if(length > 0){
String positionerStepMode = x.readString();
logger.fine("MDA - positioner step mode:: "+positionerStepMode);
}
length = x.readInt();
if(length > 0){
String positionerUnit = x.readString();
logger.fine("MDA - positioner unit:: "+positionerUnit);
}
length = x.readInt();
if(length > 0){
String readbackName = x.readString();
logger.fine("MDA - readback name:: "+readbackName);
}
length = x.readInt();
if(length > 0){
String readbackDescription = x.readString();
logger.fine("MDA - readback description:: "+readbackDescription);
}
length = x.readInt();
if(length > 0){
String readbackUnit = x.readString();
logger.fine("MDA - readback unit:: "+readbackUnit);
}
}
/**
* Read detector metadata
*/
for(int i=0; i<numberOfDetectors; i++){
logger.fine("Read detector metadata");
int detectorNumber = x.readInt();
logger.fine("MDA - detector number:: "+ detectorNumber);
length = x.readInt();
if(length > 0){
String detectorName = x.readString();
logger.fine("MDA - detector name:: "+detectorName);
// MDA starts at dimension number 1 we start at 0
componentMetadataList.add(new ComponentMetadata(detectorName, scanRank-1));
}
length = x.readInt();
if(length > 0){
String detectorDescription = x.readString();
logger.fine("MDA - detector description:: "+detectorDescription);
}
length = x.readInt();
if(length > 0){
String detectorUnit = x.readString();
logger.fine("MDA - detector unit:: "+detectorUnit);
}
}
/**
* Read trigger metadata
*/
for(int i=0; i<numberOfTriggers; i++){
logger.fine("Read trigger metadata");
int triggerNumber = x.readInt();
logger.fine("MDA - trigger number:: "+triggerNumber);
length = x.readInt();
if(length > 0){
String triggerName = x.readString();
logger.fine("MDA - trigger name:: "+triggerName);
}
float triggerCommand = x.readFloat();
logger.fine("MDA - trigger command:: "+triggerCommand);
}
ArrayList<Double[]> data = new ArrayList<Double[]>();
/**
* Read positioner data (readback)
*/
for(int i = 0; i<numberOfPositioners; i++){
logger.fine("Read positioner data");
Double[] pdata = new Double[npts];
StringBuffer b = new StringBuffer();
b.append("[ ");
for(int t=0; t<npts; t++){
pdata[t]=x.readDouble();
b.append(pdata[t]+" ");
}
b.append("]");
logger.fine("MDA - positioner "+b.toString());
data.add(pdata);
}
/**
* Read detector data
*/
for(int i=0; i<numberOfDetectors; i++){
logger.fine("Read detector data");
Double[] pdata = new Double[npts];
StringBuffer b = new StringBuffer();
b.append("[ ");
for(int t=0; t<npts; t++){
pdata[t] = new Double(x.readFloat());
b.append(pdata[t]+" ");
}
b.append("]");
logger.fine("MDA - detector "+b.toString());
data.add(pdata);
}
logger.fine("Read scan done ...");
RecursiveReturnContainer cont = new RecursiveReturnContainer();
// Update component metadata
cont.getMetadata().getComponents().addAll(componentMetadataList);
if(scanRank > 1){
/**
* Conversion logic if scan rank is > 1
*/
/**
* Read all scans recursively
*/
for(int i=0;i<npts;i++){
// Recursive call
RecursiveReturnContainer container = readData(x);
if(i==0){
// For the first scan of each dimension component data is read and stored
cont.getMetadata().getComponents().addAll(container.getMetadata().getComponents());
}
logger.fine("Convert data structure [rank="+scanRank+"] ...");
for(Message m: container.getMessage()){
if(m instanceof DataMessage){
// Add own data to message and pass it to container
DataMessage mm = new DataMessage();
for(Double[] d: data){
mm.getData().add(d[i]);
}
mm.getData().addAll(((DataMessage) m).getData());
cont.getMessage().add(mm);
}
else if(m instanceof StreamDelimiterMessage){
// Just pass message to own container
cont.getMessage().add(m);
}
}
logger.fine("Conversion done [rank="+scanRank+"]");
}
// Add dimension delimiter message
StreamDelimiterMessage m = new StreamDelimiterMessage(scanRank);
cont.getMessage().add(m);
}
else{
/**
* Conversion logic if scan rank is 1
*/
logger.fine("Convert data structure [rank="+scanRank+"]...");
for(int t = 0; t<npts ; t++){
DataMessage m = new DataMessage();
for(Double[] d: data){
m.getData().add(d[t]);
}
cont.getMessage().add(m);
}
// Add dimension delimiter message
StreamDelimiterMessage m = new StreamDelimiterMessage(scanRank);
cont.getMessage().add(m);
logger.fine("Conversion done [rank="+scanRank+"]");
}
return(cont);
}
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
// TODO Auto-generated method stub
}
/* (non-Javadoc)
* @see ch.psi.fda.deserializer.DataDeserializer#getQueue()
*/
@Override
public DataQueue getQueue() {
return queue;
}
}
/**
* Helper class
* @author ebner
*
*/
class RecursiveReturnContainer{
private List<Message> message = new ArrayList<Message>();
private DataMessageMetadata metadata = new DataMessageMetadata();
public List<Message> getMessage() {
return message;
}
public DataMessageMetadata getMetadata() {
return metadata;
}
}
/**
*
* http://www.aps.anl.gov/bcda/synApps/sscan/saveData_fileFormat.txt
*
* scan file format
* FILE HEADER
* xdr_float: VERSION (1.3)
* xdr_long: scan number
* xdr_short data's rank
* xdr_vector(rank, xdr_int) dims;
* xdr_int isRegular (true=1, false=0)
* xdr_long: pointer to the extra pvs
*
*
* SCAN
* HEADER:
* xdr_short: this scan's rank
* xdr_long: number of requested points (NPTS)
* xdr_long: current point (CPT)
* if the scan rank is > 1
* xdr_vector(NPTS, xdr_long) pointer to the lower scans
*
* INFO:
* xdr_counted_string: scan name
* xdr_counted_string: time stamp
*
*
* xdr_int: number of positioners
* xdr_int: number of detectors
* xdr_int: number of triggers
*
* for each positioner
* xdr_int: positioner number
* xdr_counted_string: positioner name
* xdr_counted_string: positioner desc
* xdr_counted_string: positioner step mode
* xdr_counted_string: positioner unit
* xdr_counted_string: readback name
* xdr_counted_string: readback description
* xdr_counted_string: readback unit
*
* for each detector
* xdr_int: detector number
* xdr_counted_string: detector name
* xdr_counted_string: detector desc
* xdr_counted_string: detector unit
*
* for each trigger
* xdr_int: trigger number
* xdr_counted_string: trigger name
* xdr_float: trigger command
*
* DATA:
* for each positioner
* xdr_vector(NPTS, xdr_double): readback array
*
* for each detector
* xdr_vector(NPTS, xdr_float): detector array
*
* [SCAN]
* ...
* ...
* ...
* [SCAN]
*
* EXTRA PVs
* xdr_int: number of extra pvs
*
* for each pv
* xdr_counted_string: name
* xdr_counted_string: desc
* xdr_int: type
* if type != DBR_STRING
* xdr_long: count
* xdr_counted_string: unit
*
* depending on the type:
* DBR_STRING:
* xdr_counted_string: value
* DBR_CTRL_CHAR:
* xdr_vector(count, xdr_char): value
* DBR_CTRL_SHORT:
* xdr_vector(count, xdr_short): value
* DBR_CTRL_LONG:
* xdr_vector(count, xdr_long): value
* DBR_CTRL_FLOAT:
* xdr_vector(count, xdr_float): value
* DBR_CTRL_DOUBLE:
* xdr_vector(count, xdr_double): value
* -----------------------------------------------------------------------
*
* A 1D scan looks like this:
*
* header
* extra PV's
*
* A 2D scan looks like this
*
* header
* scan2
* scan1
* scan1
* ...
* extra PV's
*
* A 3D scan looks like this
*
* header
* scan3
* scan2
* scan1
* scan1
* ...
* scan2
* scan1
* scan1
* ...
* ...
* extra PV's
*
*/
@@ -0,0 +1,233 @@
/**
*
* 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.deserializer;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.Logger;
import ch.psi.fda.core.messages.ComponentMetadata;
import ch.psi.fda.core.messages.DataMessage;
import ch.psi.fda.core.messages.DataMessageMetadata;
import ch.psi.fda.core.messages.DataQueue;
import ch.psi.fda.core.messages.StreamDelimiterMessage;
import ch.psi.fda.core.messages.EndOfStreamMessage;
import ch.psi.fda.core.messages.Message;
/**
* Deserialize file data and put it into the DataQueue
* @author ebner
*
*/
public class DataDeserializerTXT implements DataDeserializer {
// Get Logger
private static Logger logger = Logger.getLogger(DataDeserializerTXT.class.getName());
private DataQueue queue;
private File file;
private List<Integer> dindex;
private List<Integer> iindex;
/**
* Default Constructor
* @param file
*/
public DataDeserializerTXT(File file){
this.file = file;
this.dindex = new ArrayList<Integer>();
this.iindex = new ArrayList<Integer>();
DataMessageMetadata metadata;
try{
// Read metadata
// Open file
BufferedReader reader = new BufferedReader(new FileReader(file));
// Read file
String line;
// First line is id
line = reader.readLine();
line = line.replaceAll("^ *# *", "");
String[] ids = line.split("\t");
// Second line dimension
line = reader.readLine();
line = line.replaceAll("^ *# *", "");
String[] dimensions = line.split("\t");
// Create data message metadata
metadata = new DataMessageMetadata();
Integer d = -1;
for(int i=0;i<ids.length;i++){
Integer dimension = Integer.parseInt(dimensions[i]);
metadata.getComponents().add(new ComponentMetadata(ids[i], dimension));
// Store the first index of the first component
// in each dimension ...
if(!d.equals(dimension)){
logger.finest("Add component index: "+i);
dindex.add(dimension);
iindex.add(i);
d=dimension;
}
}
// Close file
reader.close();
}
catch(Exception e){
throw new RuntimeException("Unable to read file metadata and initialize data queue",e);
}
this.queue = new DataQueue(new LinkedBlockingQueue<Message>(10000000), metadata);
}
/* (non-Javadoc)
* @see ch.psi.fda.deserializer.DataDeserializer#getQueue()
*/
@Override
public DataQueue getQueue(){
return(queue);
}
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
try{
List<Double> checklist = new ArrayList<Double>(dindex.size());
for(int i=0;i<dindex.size();i++){
checklist.add(null);
}
// Open file
BufferedReader reader = new BufferedReader(new FileReader(file));
// Read file
String line;
while((line=reader.readLine())!=null){
// Ignore empty lines
if(line.matches("^ *$")){
continue;
}
// Ignore comment lines
if(line.matches("^ *# *.*")){
continue;
}
// Create and populate new data message
String[] data = line.split("\t");
DataMessage message = new DataMessage();
for (String d : data) {
// Remove spaces at the end and beginning of the value
d = d.trim();
// If the String does not contains spaces we assume that it is a double
if (! d.contains(" ")) {
// Scalar value
Object value;
try {
value = new Double(d);
} catch (NumberFormatException e) {
// We treat it as a String
// TODO Need to find a way to treat other data formats
value = d;
}
// Add data to message
message.getData().add(value);
} else {
try {
// If the string contains spaces we treat it as an array
String[] values = d.split(" ");
double[] dv = new double[values.length];
for (int i = 0; i < values.length; i++) {
dv[i] = new Double(values[i]);
}
// Add data to message
message.getData().add(dv);
} catch (NumberFormatException e) {
// Workaround
// TODO Need to find a way to treat the array if it is not a double array
message.getData().add(new Object());
}
}
}
// Check whether to issue a end of dimension control message
for(int t=0;t<iindex.size();t++){
Integer i = iindex.get(t);
if(dindex.get(t)>0){
Double d = (Double) message.getData().get(i);
// if(checklist.get(i)==null){
// checklist.set(i, d);
// }
if(checklist.get(i)!=null &&!checklist.get(i).equals(d)){
// If value changes issue a dimension delimiter message
queue.getQueue().put(new StreamDelimiterMessage(dindex.get(t)-1));
}
checklist.set(i, d);
}
}
// Put message to queue
queue.getQueue().put(message);
// TODO Need to detect dimension boundaries
}
// Add delimiter for all the dimensions
for(int i=dindex.size()-1;i>=0;i--){
queue.getQueue().put(new StreamDelimiterMessage(dindex.get(i)));
}
// queue.getQueue().put(new DimensionDelimiterMessage(dindex.get(0)-1));
// queue.getQueue().put(new DimensionDelimiterMessage(dindex.get(0)));
// Place end of stream message
queue.getQueue().put(new EndOfStreamMessage());
// Close file
reader.close();
} catch (InterruptedException e) {
throw new RuntimeException("Data deserializer was interrupted while reading the datafile",e);
} catch (IOException e) {
throw new RuntimeException("Data deserializer had a problem reading the specified datafile",e);
}
}
}
@@ -0,0 +1,78 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
/*
* ScanProgressPanel.java
*
* Created on Dec 1, 2009, 2:20:26 PM
*/
package ch.psi.fda.gui;
import java.awt.event.ActionListener;
public class ProgressPanel extends javax.swing.JPanel {
private static final long serialVersionUID = 1L;
/** Creates new form ScanProgressPanel */
public ProgressPanel() {
initComponents();
}
/** This method is called from within the constructor to
* initialize the form.
*/
private void initComponents() {
progressBar = new javax.swing.JProgressBar();
abortButton = new javax.swing.JButton();
progressBar.setIndeterminate(true);
progressBar.setPreferredSize(new java.awt.Dimension(100, 24));
abortButton.setText("Abort");
abortButton.setFocusable(false);
abortButton.setPreferredSize(new java.awt.Dimension(70, 25));
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(progressBar, javax.swing.GroupLayout.DEFAULT_SIZE, 152, Short.MAX_VALUE)
.addComponent(abortButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(progressBar, javax.swing.GroupLayout.PREFERRED_SIZE, 18, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(abortButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
}
public void addActionListener(ActionListener l){
abortButton.addActionListener(l);
}
public void done(){
progressBar.setIndeterminate(false);
progressBar.setValue(100);
abortButton.setEnabled(false);
}
// Variables declaration - do not modify
private javax.swing.JButton abortButton;
private javax.swing.JProgressBar progressBar;
// End of variables declaration
}
@@ -0,0 +1,57 @@
package ch.psi.fda.gui;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Rectangle;
import javax.swing.JPanel;
import javax.swing.Scrollable;
import javax.swing.SwingConstants;
public class ScrollableFlowPanel extends JPanel implements Scrollable {
private static final long serialVersionUID = 1L;
public void setBounds(int x, int y, int width, int height) {
super.setBounds(x, y, getParent().getWidth(), height);
}
public Dimension getPreferredSize() {
return new Dimension(getWidth(), getPreferredHeight());
}
public Dimension getPreferredScrollableViewportSize() {
return super.getPreferredSize();
}
public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
int hundredth = (orientation == SwingConstants.VERTICAL ? getParent().getHeight() : getParent().getWidth()) / 100;
return (hundredth == 0 ? 1 : hundredth);
}
public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
return orientation == SwingConstants.VERTICAL ? getParent().getHeight() : getParent().getWidth();
}
public boolean getScrollableTracksViewportWidth() {
return true;
}
public boolean getScrollableTracksViewportHeight() {
return false;
}
private int getPreferredHeight() {
int rv = 0;
for (int k = 0, count = getComponentCount(); k < count; k++) {
Component comp = getComponent(k);
Rectangle r = comp.getBounds();
int height = r.y + r.height;
if (height > rv)
rv = height;
}
rv += ((FlowLayout) getLayout()).getVgap();
return rv;
}
}
@@ -0,0 +1,337 @@
/**
*
* 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.install;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.URISyntaxException;
import java.util.Enumeration;
import java.util.List;
import java.util.logging.Logger;
/**
* @author ebner
*
*/
public class ApplicationConfigurator {
// Get Logger
private static final Logger logger = Logger.getLogger(ApplicationConfigurator.class.getName());
public final static String FDA_HOME_ARGUMENT = "ch.psi.fda.home";
public final static String FDA_CONFIG_FILE_ARGUMENT = "ch.psi.fda.config.file";
private final File home;
private final File configdir;
private final File datadir;
private final File logdir;
private final File scandir;
private final File jcaeProperties;
private final File fdaProperties;
private final File loggingProperties;
/**
* Constructor: The constructor will determine the home directory based on the
* System property fda.home passed via the VM option -Dfda.home=dir or, if not present,
* via the source code (jar) location (this will fail if the source code is not loaded from a file source).
*/
public ApplicationConfigurator(){
this.home = determineHome();
this.configdir = new File(home, "config");
this.datadir = new File(home, "data");
this.logdir = new File(home, "logs");
this.scandir = new File(home, "scans");
this.jcaeProperties = new File(configdir, "jcae.properties");
this.fdaProperties = new File(configdir, "fda.properties");
this.loggingProperties = new File(configdir, "logging.properties");
}
public void initializeApplication(){
// Overwrite system property
System.setProperty(FDA_HOME_ARGUMENT, this.home.getAbsolutePath());
createApplicationHome();
createDefaultConfigurationFiles();
// Configure used packages / e.g. via setting system/environment parameters, etc.
// Set logging configuration -Djava.util.logging=...
String property = System.getProperty("java.util.logging.config.file");
if(property==null){
System.setProperty("java.util.logging.config.file", loggingProperties.getAbsolutePath());
}
// Set FDA configuration argument -Dch.psi.fda.config.file=...
property = System.getProperty(FDA_CONFIG_FILE_ARGUMENT);
if(property==null){
System.setProperty(FDA_CONFIG_FILE_ARGUMENT, fdaProperties.getAbsolutePath());
}
// Set jcae.properties file
property = System.getProperty("ch.psi.jcae.config.file");
if(property==null){
System.setProperty("ch.psi.jcae.config.file", jcaeProperties.getAbsolutePath());
}
}
/**
* Function to determine home directory. This function assumes that the application directories are
* ordered as follows:
* HOME
* |-<version>
* | |-lib
* | | |-<jars>
* | |-bin
* |-config
* |-logs
* |-data
* |-scans
* | |-templates
* | |-users
* @return
*/
public File determineHome(){
File home = null;
// Check whether fda.home System property is set
String s = System.getProperty(FDA_HOME_ARGUMENT);
if(s!=null){
// A system property is specified
home = new File(s);
}
else{
// No system property is specified
// Try to determine home location based on the location of the sourcecode
try {
// Note: Obviously, this will do odd things if you class was loaded from a non-file location.
// http://stackoverflow.com/questions/320542/how-to-get-the-path-of-a-running-jar-file
// Get path where the jar is located
File f = new File(ApplicationConfigurator.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath());
// Check whether class was loaded from a jar file
if(f.isDirectory()){
// Loaded from a directory
home = f.getParentFile().getParentFile();
}
else{
// Jar file
// Assume that the jar file is located in a <version>/lib directory
home = f.getParentFile().getParentFile().getParentFile();
}
} catch (URISyntaxException e) {;
}
}
return(home);
}
/**
* Initialize fda home directory
* Will create the home directory directory structure
*
* @param home Home directory
*/
public void createApplicationHome(){
// Create directories
if(!home.exists()){
logger.info("Create home directory: "+home.getAbsolutePath());
home.mkdirs();
}
// Configuration directory
if(!configdir.exists()){
logger.info("Create configuration directory: "+configdir.getAbsolutePath());
configdir.mkdir();
}
// Data directory
if(!datadir.exists()){
logger.info("Create data directory: "+datadir.getAbsolutePath());
datadir.mkdir();
}
// Log directory
if(!logdir.exists()){
logger.info("Create log directory: "+logdir.getAbsolutePath());
logdir.mkdir();
}
// Scan directories
if(!scandir.exists()){
logger.info("Create scan directory: "+scandir.getAbsolutePath());
scandir.mkdir();
}
File sdir = new File(scandir, "templates");
if(!sdir.exists()){
logger.info("Create template scan directory: "+sdir.getAbsolutePath());
sdir.mkdir();
}
sdir = new File(scandir, "users");
if(!sdir.exists()){
logger.info("Create user scan directory: "+sdir.getAbsolutePath());
sdir.mkdir();
}
}
/**
* Create required configuration files
*/
public void createDefaultConfigurationFiles() {
try{
if(!jcaeProperties.exists()){
logger.info("Create jcae.properties file: "+jcaeProperties.getAbsolutePath());
// jca.properties
String addresses = getLocalBroadcastAddresses();
BufferedWriter writer = new BufferedWriter(new FileWriter(jcaeProperties));
PrintWriter w = new PrintWriter(writer);
// Set address list
w.println("ch.psi.jcae.ContextFactory.addressList="+addresses);
// Timeout for creating channels
w.println("ch.psi.jcae.ChannelFactory.timeout=2000");
w.println("ch.psi.jcae.ChannelFactory.retries=4");
// Get/set timeout of a ChannelBean
w.println("ch.psi.jcae.ChannelBeanFactory.timeout=10000");
// Set default wait time for wait operations of a ChannelBean (30 minutes)
w.println("ch.psi.jcae.ChannelBeanFactory.waitTimeout=1800000");
// Set wait retry period for wait operations of a ChannelBean (1 minutes)
w.println("ch.psi.jcae.ChannelBeanFactory.waitRetryPeriod=60000");
// Set retries for operations on a ChannelBean
w.println("ch.psi.jcae.ChannelBeanFactory.retries=4");
writer.close();
}
if(!fdaProperties.exists()){
logger.info("Create fda.properties file: "+fdaProperties.getAbsolutePath());
// fda.properties
BufferedWriter writer = new BufferedWriter(new FileWriter(fdaProperties));
PrintWriter w = new PrintWriter(writer);
// w.println("ch.psi.fda.aq.data.baseDirectory="+datadir.getAbsolutePath());
w.println("ch.psi.fda.aq.data.filePrefix=${yyyy_MM}/${yyyyMMdd}/${yyyyMMddHHmmss}_${name}/${yyyyMMddHHmm}_");
w.println();
w.println("# Global Error Notification (uncomment if needed)");
w.println("#ch.psi.fda.aq.notification.recipients=mail.address@psi.ch smsnumber");
w.println();
w.println("# OTFSCAN Configuration (uncomment if needed)");
w.println("#ch.psi.fda.aq.otf.channelPrefix=X-X-OTFX");
w.println("#ch.psi.fda.aq.otf.nfsServer=beamlineFileServer.psi.ch");
w.println("#ch.psi.fda.aq.otf.nfsShare=/usr/local/nfsshare");
w.println("#ch.psi.fda.aq.otf.smbShare=smb://:@beamlineFileServer.psi.ch/otftemp/");
writer.close();
}
if(!loggingProperties.exists()){
logger.info("Create logging.properties file: "+loggingProperties.getAbsolutePath());
// logging.properties
BufferedWriter writer = new BufferedWriter(new FileWriter(loggingProperties));
PrintWriter w = new PrintWriter(writer);
w.println("# Specify the handlers to create in the root logger");
w.println("handlers = java.util.logging.ConsoleHandler, java.util.logging.FileHandler");
w.println("");
w.println("# Set the default logging level for the root logger");
w.println(".level=INFO");
w.println("");
w.println("# Set the default logging level for new ConsoleHandler instances");
w.println("java.util.logging.ConsoleHandler.level=ALL");
w.println("");
w.println("# Set the default formatter for new ConsoleHandler instances");
w.println("java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter");
w.println("");
w.println("# Set the default logging level for new FileHandler instances");
w.println("java.util.logging.FileHandler.level=ALL");
w.println("");
w.println("# Set the default formatter for new ConsoleHandler instances");
w.println("java.util.logging.FileHandler.formatter=java.util.logging.SimpleFormatter");
w.println("");
w.println("# Naming of the output file:");
w.println("java.util.logging.FileHandler.pattern="+logdir.getAbsolutePath()+"/fda-%u.%g.log");
w.println("");
w.println("# Limiting size of output file in bytes (10000kb):");
w.println("java.util.logging.FileHandler.limit=10000000");
w.println("");
w.println("# Number of output files to cycle through, by appending an");
w.println("# integer to the base file name:");
w.println("java.util.logging.FileHandler.count=10");
w.println("");
w.println("# Set the default logging level for the logger named com.mycompany");
w.println("ch.psi.fda.level=ALL");
writer.close();
}
}
catch(IOException e){
throw new RuntimeException("Cannot create default configuration files",e);
}
}
/**
* Determine local broadcast addresses of the available network interfaces
* @return
*/
private String getLocalBroadcastAddresses(){
StringBuffer bAddressList = new StringBuffer();
// Loop host interfaces
Enumeration<NetworkInterface> networkInterfaces;
try {
networkInterfaces = NetworkInterface.getNetworkInterfaces();
} catch (SocketException e) {
// Something went wrong while determining the network interfaces of the current machine.
// As there is nothing a user can do, turn this exception into a unchecked RuntimeException.
throw new RuntimeException("Unable to determine network interfaces of the machine", e);
}
while (networkInterfaces.hasMoreElements()) {
NetworkInterface networkInterface = networkInterfaces.nextElement();
// Loop IP addresses of interface
List<InterfaceAddress> interfaceAddresses = networkInterface.getInterfaceAddresses();
for(InterfaceAddress interfaceAddress: interfaceAddresses){
InetAddress broadcastAddress = interfaceAddress.getBroadcast();
if (broadcastAddress != null) {
if(bAddressList.length()>0){
bAddressList.append(" ");
}
// Use of .substring(1) to remove leading "/" character
bAddressList.append(broadcastAddress.toString().substring(1));
}
}
}
return(bAddressList.toString());
}
}
@@ -0,0 +1,178 @@
/**
*
* 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.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
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.parsers.ParserConfigurationException;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.stream.StreamResult;
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 model
* @author ebner
*
*/
public class ModelManager {
/**
* Deserialize an instance of a data model of the type Configuration
* Before creating the object model variables will be replaced
*
* @param file Scan file
* @param variables Scan variables
* @throws JAXBException Something went wrong while unmarshalling
* @throws SAXException Cannot read model schema file
* @throws ParserConfigurationException
* @throws IOException
* @throws TransformerFactoryConfigurationError
* @throws TransformerException
*/
public static Configuration unmarshall(File file, HashMap<String, String> variables) throws JAXBException, SAXException, IOException,
ParserConfigurationException, TransformerFactoryConfigurationError, TransformerException {
// Load template file
Transformer xformer = TransformerFactory.newInstance().newTransformer(new StreamSource(file));
// Overwrite parameters
for(String key: variables.keySet()){
xformer.setParameter(key, variables.get(key));
}
// Workaround for complex parameters (parameter including xml fragments)
// How the workaround works:
// Because we want to set complex parameters we first have to apply the xslt transformation to the input
// stream and create an other output stream. In the template file the complex parameter must be referenced as follows
// <xsl:value-of select="$var.regions" disable-output-escaping="yes" />
// If it is not done this way the < and > of the tags will be replaced by &lt; and &gt;
// Also if we directly transform to a DOMResult the dom tree would not include the
// nodes added by the parameter but just a text object. Therefor we do the workaround via the StreamResult ...
ByteArrayOutputStream bstream = new ByteArrayOutputStream();
StreamResult sresult = new StreamResult(bstream);
// Perform transformation (using template file also as input source)
xformer.transform(new StreamSource(file), sresult );
// Workaround for complex parameters
ByteArrayInputStream bistream = new ByteArrayInputStream(bstream.toByteArray());
StreamSource bsource = new StreamSource(bistream);
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(bsource, 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;
}
}
/**
* Deserialize an instance of a data model of the type Configuration
* @param file
* @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 a data model of the type Configuration
* @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);
}
}
@@ -0,0 +1,112 @@
/*
* 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.notification;
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;
/**
* This is a copy of the NotificationAgent class that should go into the foundation classes
* This actually should be extracted to an own project
* @author ebner
*/
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();
// Setup mail server
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;
}
}
@@ -0,0 +1,28 @@
/**
*
* 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;
/**
* Data Serializer
* @author ebner
*
*/
public interface DataSerializer extends Runnable {
}
@@ -0,0 +1,179 @@
/**
*
* 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.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import com.jmatio.io.MatFileWriter;
import com.jmatio.types.MLArray;
import com.jmatio.types.MLDouble;
import ch.psi.fda.core.messages.ComponentMetadata;
import ch.psi.fda.core.messages.DataMessage;
import ch.psi.fda.core.messages.DataQueue;
import ch.psi.fda.core.messages.EndOfStreamMessage;
import ch.psi.fda.core.messages.Message;
/**
* Serialize data received by a DataQueue into a Matlab file
* @author ebner
*
*/
public class DataSerializerMAT implements DataSerializer{
private DataQueue queue;
private File file;
private boolean appendSuffix = false;
/**
* Construtor
* @param queue Data queue holding the data to serialize
* @param file Name of the Matlab file to serialize the data to
*/
public DataSerializerMAT(DataQueue queue, File file){
this.queue = queue;
this.file = file;
}
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
try{
// WORKAROUND BEGIN
File outfile;
if(appendSuffix){
// Append a count suffix to the file. If there is already a file with
// this suffix increase the counter for the suffix
int cnt = 0;
String fname = this.file.getAbsolutePath(); // Determine file name
String extension = fname.replaceAll("^.*\\.", ""); // Determine extension
fname = fname.replaceAll("\\."+extension+"$", "");
outfile = new File(String.format("%s_%04d.%s", fname, cnt, extension));
while(outfile.exists()){
cnt++;
outfile = new File(String.format("%s_%04d.%s", fname, cnt, extension));
}
}
else{
outfile = this.file;
}
// WORKAROUND END
// Transposed data list
List<List<Object>> dlist = new ArrayList<List<Object>>();
List<Class<?>> clist = new ArrayList<Class<?>>();
boolean firstF = true;
// Write data
// Read Message
Message message = queue.getQueue().take();
while(!(message instanceof EndOfStreamMessage)){
if(message instanceof DataMessage){
DataMessage m = (DataMessage) message;
// Initialize list
if(firstF){
for(Object o: m.getData()){
dlist.add(new ArrayList<Object>());
clist.add(o.getClass());
}
firstF=false;
}
// Put data into data list
for(int i=0;i< m.getData().size();i++){
Object object = m.getData().get(i);
dlist.get(i).add(object);
}
}
// Read next message
message = queue.getQueue().take();
}
// Create Matlab vectors
ArrayList<MLArray> matlablist = new ArrayList<MLArray>();
for(int t=0; t<dlist.size(); t++ ){
// Get component metadata
ComponentMetadata c = queue.getDataMessageMetadata().getComponents().get(t);
c.getId();
List<Object> list = dlist.get(t);
if(clist.get(t).isArray()){
// Array Handling
}
else if(clist.get(t).equals(Double.class)){
// Data is of type Double
MLDouble darray = new MLDouble(escapeString(c.getId()),(Double[])list.toArray(new Double[list.size()]),1);
matlablist.add(darray);
}
}
// Write Matlab file
MatFileWriter writerr = new MatFileWriter();
writerr.write(outfile, matlablist);
} catch (InterruptedException e) {
// TODO Stop loop and exit logic instead of throwing an Exception
throw new RuntimeException("Data serializer was interrupted while writing data to file",e);
} catch (IOException e) {
throw new RuntimeException("Data serializer had a problem writing to the specified file",e);
}
}
/**
* Escape string to be Matlab key conform
* @param value
* @return Escaped string value
*/
private String escapeString(String value){
String evalue = value.replaceAll("-", "_");
evalue = evalue.replaceAll(":", "_");
evalue = evalue.replaceAll("\\.", "_");
evalue = evalue.replaceAll(" ", "_");
evalue = evalue.replaceAll("\\(", "_");
evalue = evalue.replaceAll("\\)", "_");
evalue = evalue.replaceAll("\\[", "_");
evalue = evalue.replaceAll("\\]", "_");
return(evalue);
}
}
@@ -0,0 +1,254 @@
/**
*
* 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.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import com.jmatio.io.MatFileWriter;
import com.jmatio.types.MLArray;
import com.jmatio.types.MLDouble;
import ch.psi.fda.core.messages.ComponentMetadata;
import ch.psi.fda.core.messages.DataMessage;
import ch.psi.fda.core.messages.DataQueue;
import ch.psi.fda.core.messages.StreamDelimiterMessage;
import ch.psi.fda.core.messages.EndOfStreamMessage;
import ch.psi.fda.core.messages.Message;
/**
* Serialize data received by a DataQueue into a Matlab file
* @author ebner
*
*/
public class DataSerializerMAT2D implements DataSerializer{
// Get Logger
private static final Logger logger = Logger.getLogger(DataSerializerMAT2D.class.getName());
private DataQueue queue;
private File file;
private boolean appendSuffix = false;
/**
* Construtor
* @param queue Data queue holding the data to serialize
* @param file Name of the Matlab file to serialize the data to
*/
public DataSerializerMAT2D(DataQueue queue, File file){
this.queue = queue;
this.file = file;
// Check if input queue does only hold 2D data
int maxdim=0;
for(ComponentMetadata m: queue.getDataMessageMetadata().getComponents()){
if(m.getDimension()>maxdim){
maxdim=m.getDimension();
}
if(m.getDimension()>1){
throw new RuntimeException("Serializer does only support 2D data (XD data found)");
}
}
if(maxdim<1){
throw new RuntimeException("Serializer does only support 2D data ("+maxdim+"D data found)");
}
}
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
try{
// WORKAROUND BEGIN
File outfile;
if(appendSuffix){
// Append a count suffix to the file. If there is already a file with
// this suffix increase the counter for the suffix
int cnt = 0;
String fname = this.file.getAbsolutePath(); // Determine file name
String extension = fname.replaceAll("^.*\\.", ""); // Determine extension
fname = fname.replaceAll("\\."+extension+"$", "");
outfile = new File(String.format("%s_%04d.%s", fname, cnt, extension));
while(outfile.exists()){
cnt++;
outfile = new File(String.format("%s_%04d.%s", fname, cnt, extension));
}
}
else{
outfile = this.file;
}
// WORKAROUND END
// Transposed data list
List<List<List<Object>>> dlist = new ArrayList<List<List<Object>>>();
List<Class<?>> clist = new ArrayList<Class<?>>();
int dsize = 0; // Size of the dimension
int dcount = 0;
Integer mindsize = null;
boolean firstF = true;
// Write data
// Read Message
Message message = queue.getQueue().take();
while(!(message instanceof EndOfStreamMessage)){
if(message instanceof DataMessage){
DataMessage m = (DataMessage) message;
// Initialize list
if(firstF){
for(Object o: m.getData()){
// TODO Create list of lists (for each line one list - to
// be able to fill up empty data points if there are some)
List<List<Object>> l = new ArrayList<List<Object>>();
l.add(new ArrayList<Object>());
dlist.add(l);
clist.add(o.getClass());
}
firstF=false;
}
// Put data into data list
for(int i=0;i< m.getData().size();i++){
Object object = m.getData().get(i);
List<List<Object>> lo = dlist.get(i);
lo.get(lo.size()-1).add(object); // add data to latest list
}
dcount++;
}
else if(message instanceof StreamDelimiterMessage){
StreamDelimiterMessage m = (StreamDelimiterMessage) message;
if(m.getNumber()==0){
// Determine minimum dimension size
if(dsize<dcount){
dsize=dcount;
}
// Determine maximum dimension size
if(mindsize == null){
mindsize=dcount;
}
if(mindsize>dcount){
mindsize=dcount;
}
// Add a new list for all component to the dlist
for(List<List<Object>> lo:dlist){
lo.add(new ArrayList<Object>());
}
dcount=0;
}
}
// Read next message
message = queue.getQueue().take();
}
logger.info("dsize: "+dsize + " mindsize:"+mindsize);
// Create Matlab vectors
ArrayList<MLArray> matlablist = new ArrayList<MLArray>();
logger.info("dlist size: "+dlist.size());
for(int t=0; t<dlist.size(); t++ ){
// Get component metadata
ComponentMetadata c = queue.getDataMessageMetadata().getComponents().get(t);
// Combine all lists to one big list (pad if there are data points missing)
List<Object> list = new ArrayList<Object>();
List<List<Object>> ol = dlist.get(t);
// Remove last array list as it is empty
ol.remove(ol.size()-1);
for(List<Object> li: ol){
list.addAll(li);
// Pad list if there are missing data points for some lines
for(int i=li.size();i<dsize;i++){
logger.info("Pad data point: "+i);
list.add(Double.NaN);
}
}
// List<Object> list = dlist.get(t);
logger.info("List: "+list.size());
if(clist.get(t).isArray()){
// Array Handling
}
else if(clist.get(t).equals(Double.class)){
// Data is of type Double
MLDouble darray = new MLDouble(escapeString(c.getId()),(Double[])list.toArray(new Double[list.size()]), dsize);
matlablist.add(darray);
}
}
// Write Matlab file
MatFileWriter writerr = new MatFileWriter();
writerr.write(outfile, matlablist);
} catch (InterruptedException e) {
// TODO Stop loop and exit logic instead of throwing an Exception
throw new RuntimeException("Data serializer was interrupted while writing data to file",e);
} catch (IOException e) {
throw new RuntimeException("Data serializer had a problem writing to the specified file",e);
}
}
/**
* Escape string to be Matlab key conform
* @param value
* @return Escaped string value
*/
private String escapeString(String value){
String evalue = value.replaceAll("-", "_");
evalue = evalue.replaceAll(":", "_");
evalue = evalue.replaceAll("\\.", "_");
evalue = evalue.replaceAll(" ", "_");
evalue = evalue.replaceAll("\\(", "_");
evalue = evalue.replaceAll("\\)", "_");
evalue = evalue.replaceAll("\\[", "_");
evalue = evalue.replaceAll("\\]", "_");
return(evalue);
}
}
@@ -0,0 +1,235 @@
/**
*
* 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.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;
import com.jmatio.io.MatFileWriter;
import com.jmatio.types.MLArray;
import com.jmatio.types.MLDouble;
import ch.psi.fda.core.messages.ComponentMetadata;
import ch.psi.fda.core.messages.DataMessage;
import ch.psi.fda.core.messages.DataQueue;
import ch.psi.fda.core.messages.StreamDelimiterMessage;
import ch.psi.fda.core.messages.EndOfStreamMessage;
import ch.psi.fda.core.messages.Message;
/**
* Serialize data received by a DataQueue into a Matlab file
* @author ebner
*
*/
public class DataSerializerMAT2DZigZag implements DataSerializer{
// Get Logger
private static final Logger logger = Logger.getLogger(DataSerializerMAT2DZigZag.class.getName());
private DataQueue queue;
private File file;
private boolean appendSuffix = false;
/**
* Construtor
* @param queue Data queue holding the data to serialize
* @param file Name of the Matlab file to serialize the data to
*/
public DataSerializerMAT2DZigZag(DataQueue queue, File file){
this.queue = queue;
this.file = file;
// Check if input queue does only hold 2D data
int maxdim=0;
for(ComponentMetadata m: queue.getDataMessageMetadata().getComponents()){
if(m.getDimension()>maxdim){
maxdim=m.getDimension();
}
if(m.getDimension()>1){
throw new RuntimeException("Serializer does only support 2D data (XD data found)");
}
}
if(maxdim<1){
throw new RuntimeException("Serializer does only support 2D data ("+maxdim+"D data found)");
}
}
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
try{
// WORKAROUND BEGIN
File outfile;
if(appendSuffix){
// Append a count suffix to the file. If there is already a file with
// this suffix increase the counter for the suffix
int cnt = 0;
String fname = this.file.getAbsolutePath(); // Determine file name
String extension = fname.replaceAll("^.*\\.", ""); // Determine extension
fname = fname.replaceAll("\\."+extension+"$", "");
outfile = new File(String.format("%s_%04d.%s", fname, cnt, extension));
while(outfile.exists()){
cnt++;
outfile = new File(String.format("%s_%04d.%s", fname, cnt, extension));
}
}
else{
outfile = this.file;
}
// WORKAROUND END
// Transposed data list
List<List<Object>> dlist = new ArrayList<List<Object>>();
List<List<Object>> dlistTmp = new ArrayList<List<Object>>();
List<Class<?>> clist = new ArrayList<Class<?>>();
int dsize = 0; // Size of the dimension
int dcount = 0;
int delimiterCount = 0;
boolean firstF = true;
boolean firstC = true;
// Write data
// Read Message
Message message = queue.getQueue().take();
while(!(message instanceof EndOfStreamMessage)){
if(message instanceof DataMessage){
DataMessage m = (DataMessage) message;
if(firstC){
for(Object o: m.getData()){
dlist.add(new ArrayList<Object>());
clist.add(o.getClass());
firstC=false;
}
}
// Initialize list
if(firstF){
for(int i=0;i<m.getData().size();i++){
dlistTmp.add(new ArrayList<Object>());
}
firstF=false;
}
// Put data into data list
for(int i=0;i< m.getData().size();i++){
Object object = m.getData().get(i);
dlistTmp.get(i).add(object);
}
dcount++;
}
else if(message instanceof StreamDelimiterMessage){
StreamDelimiterMessage m = (StreamDelimiterMessage) message;
if(m.getNumber()==0){
if(dsize<dcount){
dsize=dcount;
}
dcount=0;
// Add temporary list to final list
for(int i=0;i<dlist.size();i++){
if(delimiterCount%2==1){
Collections.reverse(dlistTmp.get(i));
}
dlist.get(i).addAll(dlistTmp.get(i));
}
dlistTmp.clear();
firstF=true;
delimiterCount++;
}
}
// Read next message
message = queue.getQueue().take();
}
// Create Matlab vectors
ArrayList<MLArray> matlablist = new ArrayList<MLArray>();
logger.info("dlist size: "+dlist.size());
for(int t=0; t<dlist.size(); t++ ){
// Get component metadata
ComponentMetadata c = queue.getDataMessageMetadata().getComponents().get(t);
List<Object> list = dlist.get(t);
if(clist.get(t).isArray()){
// Array Handling
}
else if(clist.get(t).equals(Double.class)){
// Data is of type Double
MLDouble darray = new MLDouble(escapeString(c.getId()),(Double[])list.toArray(new Double[list.size()]), dsize);
matlablist.add(darray);
}
}
// Write Matlab file
MatFileWriter writerr = new MatFileWriter();
writerr.write(outfile, matlablist);
} catch (InterruptedException e) {
// TODO Stop loop and exit logic instead of throwing an Exception
throw new RuntimeException("Data serializer was interrupted while writing data to file",e);
} catch (IOException e) {
throw new RuntimeException("Data serializer had a problem writing to the specified file",e);
}
}
/**
* Escape string to be Matlab key conform
* @param value
* @return Escaped string value
*/
private String escapeString(String value){
String evalue = value.replaceAll("-", "_");
evalue = evalue.replaceAll(":", "_");
evalue = evalue.replaceAll("\\.", "_");
evalue = evalue.replaceAll(" ", "_");
evalue = evalue.replaceAll("\\(", "_");
evalue = evalue.replaceAll("\\)", "_");
evalue = evalue.replaceAll("\\[", "_");
evalue = evalue.replaceAll("\\]", "_");
return(evalue);
}
}
@@ -0,0 +1,392 @@
/**
*
* 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 hep.io.xdr.XDRRandomAccessFile;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.logging.Logger;
import ch.psi.fda.core.messages.ComponentMetadata;
import ch.psi.fda.core.messages.DataMessage;
import ch.psi.fda.core.messages.DataQueue;
import ch.psi.fda.core.messages.EndOfStreamMessage;
import ch.psi.fda.core.messages.Message;
import ch.psi.fda.core.messages.StreamDelimiterMessage;
/**
* Serialize data received by a DataQueue
*
* http://www.aps.anl.gov/bcda/synApps/sscan/saveData_fileFormat.txt
*
* @author ebner
*
*/
public class DataSerializerMDA implements DataSerializer{
// Get Logger
private static final Logger logger = Logger.getLogger(DataSerializerMDA.class.getName());
private DataQueue queue;
private File file;
public DataSerializerMDA(DataQueue queue, File file){
this.queue = queue;
this.file = file;
}
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
try{
// Analyze header
// Map holding all indexes for a given dimension
HashMap<Integer,List<Integer>> dMap = new HashMap<Integer, List<Integer>>();
// Map holding all ids for a given dimension
HashMap<Integer,List<String>> idMap = new HashMap<Integer, List<String>>();
List<ComponentMetadata> mlist = queue.getDataMessageMetadata().getComponents();
for(int index=0;index<mlist.size();index++){
ComponentMetadata m = mlist.get(index);
if(!dMap.containsKey(m.getDimension())){
dMap.put(m.getDimension(), new ArrayList<Integer>());
}
if(!idMap.containsKey(m.getDimension())){
idMap.put(m.getDimension(), new ArrayList<String>());
}
dMap.get(m.getDimension()).add(index);
idMap.get(m.getDimension()).add(m.getId());
}
//dimensions/dimension/dimensioncomponents/component/componentvalue
List<List<List<List<Double>>>> dimensionList = new ArrayList<List<List<List<Double>>>>();
int numberOfDimensions = dMap.size();
logger.info("Number of dimensions: "+numberOfDimensions);
for(int i=0;i<numberOfDimensions; i++){
// For each dimension add an list
dimensionList.add(new ArrayList<List<List<Double>>>());
}
// // Transposed data list
// List<List<Double>> dlist = new ArrayList<List<Double>>();
//// List<Class<?>> clist = new ArrayList<Class<?>>();
// int dsize = 0; // Size of the dimension
// int dcount = 0;
// boolean firstF = true;
List<Boolean> firstL = new ArrayList<Boolean>();
List<Boolean> takeData = new ArrayList<Boolean>(); // Flag whether to take data for this dimension
List<Integer> dcountL = new ArrayList<Integer>(); // How many times this dimension is there
for(int i=0;i<numberOfDimensions;i++){
firstL.add(true);
takeData.add(true);
dcountL.add(0);
}
// Write data
// Read Message
Message message = queue.getQueue().take();
while(!(message instanceof EndOfStreamMessage)){
if(message instanceof DataMessage){
DataMessage m = (DataMessage) message;
for(Integer dimensionNumberKey : dMap.keySet()){
if(firstL.get(dimensionNumberKey)){
List<List<Double>> l = new ArrayList<List<Double>>();
dimensionList.get(dimensionNumberKey).add(l);
for(int y=0;y<dMap.get(dimensionNumberKey).size(); y++){
l.add(new ArrayList<Double>());
}
// Set first flag to false
firstL.set(dimensionNumberKey, false);
}
// Read data from data message
if(takeData.get(dimensionNumberKey) || dimensionNumberKey == 0){
for(int y=0;y<dMap.get(dimensionNumberKey).size(); y++){
int ind = dMap.get(dimensionNumberKey).get(y);
// Get data of component at index
Object value = m.getData().get(ind);
if(value instanceof Double){
dimensionList.get(dimensionNumberKey).get(dcountL.get(dimensionNumberKey)).get(y).add((Double)value);
}
}
takeData.set(dimensionNumberKey,false);
}
}
}
else if(message instanceof StreamDelimiterMessage){
StreamDelimiterMessage m = (StreamDelimiterMessage) message;
logger.fine("----Delimiter "+m.getNumber()+"----");
// Increase dimension count
dcountL.set(m.getNumber(), (dcountL.get(m.getNumber())+1));
// Set flag that next message in the stream will be the first again of the dimension
firstL.set(m.getNumber(), true);
// Every time a delimiter comes set take data of the dimension above to true
int c = m.getNumber()+1;
if(c<takeData.size()){
takeData.set(c, true);
}
}
// Read next message
message = queue.getQueue().take();
}
// Write MDA file
logger.info("Write MDA data file");
try {
if(file.exists()){
file.delete();
}
// XDROutputStream x = new XDROutputStream(new FileOutputStream(file));
XDRRandomAccessFile x = new XDRRandomAccessFile(file, "rw");
/**
* Read file header
*/
x.writeFloat(0f); // Version
x.writeInt(0); // Scan number
x.writeInt(numberOfDimensions); // Rank/Number of dimensions
for(int i=numberOfDimensions-1;i>=0; i--){
int s = dimensionList.get(i).get(0).get(0).size();
x.writeInt(s); // Dimension size
logger.info("Size: "+i+" - "+s+" ");
}
x.writeInt(1); // Is Regular (true=1, false=0)
x.writeInt(0); // Number of extra pvs
// Write data
HashMap<Integer,Integer> indexCount = new HashMap<Integer,Integer>();
for(int i=0;i<dimensionList.size();i++){
indexCount.put(i, 0);
}
// The highest dimension only consist of one data list
int dnum = dimensionList.size()-1;
writeDimension(x, dimensionList, indexCount, idMap, dnum);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
} catch (InterruptedException e) {
// TODO Stop loop and exit logic instead of throwing an Exception
throw new RuntimeException("Data serializer was interrupted while writing data to file",e);
// } catch (IOException e) {
// throw new RuntimeException("Data serializer had a problem writing to the specified file",e);
}
}
private long writeDimension(XDRRandomAccessFile x, List<List<List<List<Double>>>> dimensionList, HashMap<Integer,Integer> icount, HashMap<Integer,List<String>> idMap, int dnum) throws IOException{
int ic = icount.get(dnum);
List<List<Double>> l = dimensionList.get(dnum).get(ic);
// Increment count for dimension
icount.put(dnum,(ic+1));
logger.info("Write data: "+dnum+"["+ic+"]");
// Write data to file
// Address of this dimension (block)
long daddress = x.getFilePointer();
List<Long> dpaddressL = writeData(x, l, idMap.get(dnum), dnum);
if(dnum>0){
for(int i=0;i<l.get(0).size();i++){
// long caddress = x.getFilePointer();
// Write lower level dimensions to file
long p = writeDimension(x, dimensionList, icount, idMap, dnum-1);
// Write pointer to pdaddress+i*long
x.seek(dpaddressL.get(i));
x.writeInt((int)p);
// Got to caddress again
// x.seek(caddress);
x.seek(x.length());
}
}
return daddress;
}
/**
* Write data of a dimension
* @param x
* @param list
* @param dimension
* @throws IOException
*
* @return Address where the pointers lower level dimensions are stored
*/
private List<Long> writeData(XDRRandomAccessFile x, List<List<Double>> list, List<String> ids, int dimension) throws IOException{
int npoints = list.get(0).size();
logger.info("Dimension rank: "+(dimension+1));
x.writeInt(dimension+1); // Scan rank
x.writeInt(npoints); // Number of points
x.writeInt(npoints); // Current Point
// Address of the place where the pointers to the lower level dimensions
// are stored.
List<Long> dpaddressList = new ArrayList<Long>();
// long dpaddress = x.getFilePointer();
// Read pointers to lower scans
if(dimension > 0){
// For 1D scans this block is never reached
for(int i=0;i<npoints;i++){
dpaddressList.add(x.getFilePointer());
x.writeInt(0); // pointer to lower scans (data is not correct)
}
}
/**
* Read scan info
*/
String sname = "dimension: "+dimension;
x.writeInt(sname.length()); // Length
x.writeString(sname); // Scan name
Date d = new Date();
// String tstamp = "Oct 04, 2008 19:17:43.633180";
String tstamp = d.toString(); // Take current time as timestamp
x.writeInt(tstamp.length()); // Length
x.writeString(tstamp); // timestamp
int nPosDet = list.size();
int nPositioner = 1; // Assume that first in list is always the positioner
int nDetector = nPosDet-1; // Assume that the rest are detectors
x.writeInt(nPositioner); // Number of positioners
x.writeInt(nDetector); // Number of detectors
x.writeInt(0); // Number of triggers
int cnt = 0;
// Positioners metadata
for(int i = 0; i<nPositioner; i++){
String pos = ids.get(cnt);
cnt++;
x.writeInt(i); // Positioner number
x.writeInt(pos.length());
x.writeString(pos); // Positioner name
x.writeInt(0);
// x.writeString(); // Description
x.writeInt(0);
// x.writeString(); // Step mode
x.writeInt(0);
// x.writeString("mm"); // Unit
x.writeInt(pos.length());
x.writeString(pos); // Readback name
x.writeInt(0);
// x.writeString(); // Readback description
x.writeInt(0);
// x.writeString(); // Readback unit
}
// Detector metadata
for(int i=0; i<nDetector; i++){
String det = ids.get(cnt);
cnt++;
x.writeInt(i); // Detector number
x.writeInt(det.length());
x.writeString(det); // Detector name
x.writeInt(0);
// x.writeString(); // Description
x.writeInt(0);
// x.writeString(); // Unit
}
// No trigger metadata (as there are no triggers)
// Positioner data
for(int i = 0; i<nPositioner; i++){
for(int t=0; t<npoints; t++){
x.writeDouble(list.get(i).get(t));
}
}
// Detector data
for(int i=0; i<nDetector; i++){
for(int t=0; t<npoints; t++){
x.writeFloat(list.get(i+nPositioner).get(t).floatValue());
}
}
return dpaddressList;
}
}
@@ -0,0 +1,235 @@
/**
*
* 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.Logger;
import ch.psi.fda.core.messages.ComponentMetadata;
import ch.psi.fda.core.messages.DataMessage;
import ch.psi.fda.core.messages.DataQueue;
import ch.psi.fda.core.messages.EndOfStreamMessage;
import ch.psi.fda.core.messages.Message;
import ch.psi.fda.core.messages.StreamDelimiterMessage;
/**
* Serialize data received by a DataQueue
* @author ebner
*
*/
public class DataSerializerTXT implements DataSerializer{
// Get Logger
private static final Logger logger = Logger.getLogger(DataSerializerTXT.class.getName());
private DataQueue queue;
private File file;
private boolean appendSuffix = true;
/**
*
* @param queue
* @param file
* @param appendSuffix Flag whether to append a _0000 suffix after the original file name
*/
public DataSerializerTXT(DataQueue queue, File file, boolean appendSuffix){
this.queue = queue;
this.file = file;
this.appendSuffix = appendSuffix;
}
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
try{
// WORKAROUND BEGIN
File outfile;
// if(appendSuffix){
// // Append a count suffix to the file. If there is already a file with
// // this suffix increase the counter for the suffix
// int cnt = 0;
// String fname = this.file.getAbsolutePath(); // Determine file name
// String extension = fname.replaceAll("^.*\\.", ""); // Determine extension
// fname = fname.replaceAll("\\."+extension+"$", "");
//
// outfile = new File(String.format("%s_%04d.%s", fname, cnt, extension));
//
// while(outfile.exists()){
// cnt++;
// outfile = new File(String.format("%s_%04d.%s", fname, cnt, extension));
// }
// }
// else{
// outfile = this.file;
// }
// WORKAROUND END
// Write header
StringBuffer b = new StringBuffer();
StringBuffer b1 = new StringBuffer();
b.append("#");
b1.append("#");
for(ComponentMetadata c: queue.getDataMessageMetadata().getComponents()){
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');
int icount = 0;
boolean newfile = true;
boolean dataInBetween = false;
BufferedWriter writer = null;
// Get basename of the file
String basename = this.file.getAbsolutePath(); // Determine file name
String extension = basename.replaceAll("^.*\\.", ""); // Determine extension
basename = basename.replaceAll("\\."+extension+"$", "");
// Write data
// Read Message
Message message = queue.getQueue().take();
while(!(message instanceof EndOfStreamMessage)){
if(message instanceof DataMessage){
dataInBetween = true;
if(newfile){
// 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());
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();
}
}
// Read next message
message = queue.getQueue().take();
}
if(writer!=null){
// Close file
writer.close(); //If the stream was closed previously this has no effect
}
// Writer can be null if a scan is defined without a dimension
} catch (InterruptedException e) {
// TODO Stop loop and exit logic instead of throwing an Exception
throw new RuntimeException("Data serializer was interrupted while writing data to file",e);
} catch (IOException e) {
throw new RuntimeException("Data serializer had a problem writing to the specified file",e);
}
}
// /**
// * Enable/disable the generation of the _0000 suffix in front of the extension of the out file
// * @param appendSuffix the appendSuffix to set
// */
// public void setAppendSuffix(boolean appendSuffix) {
// this.appendSuffix = appendSuffix;
// }
}
@@ -0,0 +1,205 @@
/**
*
* 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.ArrayList;
import java.util.List;
import ch.psi.fda.core.messages.ComponentMetadata;
import ch.psi.fda.core.messages.DataMessage;
import ch.psi.fda.core.messages.DataQueue;
import ch.psi.fda.core.messages.StreamDelimiterMessage;
import ch.psi.fda.core.messages.EndOfStreamMessage;
import ch.psi.fda.core.messages.Message;
/**
* Serialize data received by a DataQueue into a Matlab file
* @author ebner
*
*/
public class DataSerializerTXT2D implements DataSerializer{
private DataQueue queue;
private File file;
private boolean appendSuffix = false;
/**
* Construtor
* @param queue Data queue holding the data to serialize
* @param file Name of the Matlab file to serialize the data to
*/
public DataSerializerTXT2D(DataQueue queue, File file){
this.queue = queue;
this.file = file;
// Check if input queue does only hold 2D data
int maxdim=0;
for(ComponentMetadata m: queue.getDataMessageMetadata().getComponents()){
if(m.getDimension()>maxdim){
maxdim=m.getDimension();
}
if(m.getDimension()>1){
throw new RuntimeException("Serializer does only support 2D data (XD data found)");
}
}
if(maxdim<1){
throw new RuntimeException("Serializer does only support 2D data ("+maxdim+"D data found)");
}
}
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
try{
// WORKAROUND BEGIN
File outfile;
if(appendSuffix){
// Append a count suffix to the file. If there is already a file with
// this suffix increase the counter for the suffix
int cnt = 0;
String fname = this.file.getAbsolutePath(); // Determine file name
String extension = fname.replaceAll("^.*\\.", ""); // Determine extension
// fname = fname.replaceAll("(_[0-9]+)?\\."+extension+"$", "");
fname = fname.replaceAll("\\."+extension+"$", "");
outfile = new File(String.format("%s_%04d.%s", fname, cnt, extension));
while(outfile.exists()){
cnt++;
outfile = new File(String.format("%s_%04d.%s", fname, cnt, extension));
}
}
else{
outfile = this.file;
}
// WORKAROUND END
// Transposed data list
List<List<Object>> dlist = new ArrayList<List<Object>>();
List<Class<?>> clist = new ArrayList<Class<?>>();
int dsize = 0; // Size of the dimension
int dcount = 0;
boolean firstF = true;
// Write data
// Read Message
Message message = queue.getQueue().take();
while(!(message instanceof EndOfStreamMessage)){
if(message instanceof DataMessage){
DataMessage m = (DataMessage) message;
// Initialize list
if(firstF){
for(Object o: m.getData()){
dlist.add(new ArrayList<Object>());
clist.add(o.getClass());
}
firstF=false;
}
// Put data into data list
for(int i=0;i< m.getData().size();i++){
Object object = m.getData().get(i);
dlist.get(i).add(object);
}
dcount++;
}
else if(message instanceof StreamDelimiterMessage){
StreamDelimiterMessage m = (StreamDelimiterMessage) message;
if(m.getNumber()==0){
if(dsize<dcount){
dsize=dcount;
}
dcount=0;
}
}
// Read next message
message = queue.getQueue().take();
}
// Open file
BufferedWriter writer = new BufferedWriter(new FileWriter(outfile));
// Create text images
for(int t=0; t<dlist.size(); t++ ){
// Get component metadata
ComponentMetadata c = queue.getDataMessageMetadata().getComponents().get(t);
writer.write(c.getId()+"\n");
List<Object> list = dlist.get(t);
if(clist.get(t).isArray()){
// Array Handling
}
else if(clist.get(t).equals(Double.class)){
// Data is of type Double
StringBuffer b = new StringBuffer();
int counter = 0;
for(Object o: list){
b.append(o);
counter++;
if(counter==dsize){
b.append("\n");
counter=0;
}
else{
b.append(" ");
}
}
writer.write(b.toString());
}
writer.write("\n");
}
// Close file
writer.close();
} catch (InterruptedException e) {
// TODO Stop loop and exit logic instead of throwing an Exception
throw new RuntimeException("Data serializer was interrupted while writing data to file",e);
} catch (IOException e) {
throw new RuntimeException("Data serializer had a problem writing to the specified file",e);
}
}
}
@@ -0,0 +1,240 @@
/**
*
* 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.ArrayList;
import java.util.List;
import ch.psi.fda.core.messages.ComponentMetadata;
import ch.psi.fda.core.messages.DataMessage;
import ch.psi.fda.core.messages.DataQueue;
import ch.psi.fda.core.messages.StreamDelimiterMessage;
import ch.psi.fda.core.messages.EndOfStreamMessage;
import ch.psi.fda.core.messages.Message;
/**
* Serialize data received by a DataQueue
* @author ebner
*
*/
public class DataSerializerTXTSplit implements DataSerializer{
private DataQueue queue;
private File file;
private int maxdim = 0;
// private boolean appendSuffix = false;
public DataSerializerTXTSplit(DataQueue queue, File file){
this.queue = queue;
this.file = file;
// Determine maximum dimension
for(ComponentMetadata m: queue.getDataMessageMetadata().getComponents()){
if(m.getDimension()>maxdim){
maxdim=m.getDimension();
}
}
if(maxdim<1){
throw new RuntimeException("Split serializer only supports data > 1 dimension");
}
}
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
@Override
public void run() {
try{
// WORKAROUND BEGIN
// File outfile;
// if(appendSuffix){
// // Append a count suffix to the file. If there is already a file with
// // this suffix increase the counter for the suffix
// int cnt = 0;
// String fname = this.file.getAbsolutePath(); // Determine file name
// String extension = fname.replaceAll("^.*\\.", ""); // Determine extension
// fname = fname.replaceAll("\\."+extension+"$", "");
//
// outfile = new File(String.format("%s_%04d.%s", fname, cnt, extension));
//
// while(outfile.exists()){
// cnt++;
// outfile = new File(String.format("%s_%04d.%s", fname, cnt, extension));
// }
// }
// else{
// outfile = this.file;
// }
// WORKAROUND END
List<String> header = new ArrayList<String>();
// Write header
StringBuffer b = new StringBuffer();
StringBuffer b1 = new StringBuffer();
b.append("#");
b1.append("#");
for(ComponentMetadata c: queue.getDataMessageMetadata().getComponents()){
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');
header.add(b.toString());
header.add(b1.toString());
List<String> data = new ArrayList<String>();
// Write data
// Read Message
Message message = queue.getQueue().take();
while(!(message instanceof EndOfStreamMessage)){
if(message instanceof DataMessage){
// 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");
}
}
buffer.deleteCharAt(buffer.length()-1); // Remove last character (i.e. \t)
buffer.append("\n"); // Append newline
data.add(buffer.toString());
}
else if(message instanceof StreamDelimiterMessage){
StreamDelimiterMessage m = (StreamDelimiterMessage) message;
if(m.getNumber()==maxdim-1){
writeData(header, data);
// Clear data
data.clear();
}
}
// Read next message
message = queue.getQueue().take();
}
// // Open file
// BufferedWriter writer = new BufferedWriter(new FileWriter(outfile));
//
// // Close file
// writer.close();
} catch (InterruptedException e) {
// TODO Stop loop and exit logic instead of throwing an Exception
throw new RuntimeException("Data serializer was interrupted while writing data to file",e);
} catch (IOException e) {
throw new RuntimeException("Data serializer had a problem writing to the specified file",e);
}
}
private void writeData(List<String> header, List<String> data) throws IOException{
// WORKAROUND BEGIN
File outfile;
// if(appendSuffix){
// Append a count suffix to the file. If there is already a file with
// this suffix increase the counter for the suffix
int cnt = 0;
String fname = this.file.getAbsolutePath(); // Determine file name
String extension = fname.replaceAll("^.*\\.", ""); // Determine extension
fname = fname.replaceAll("\\."+extension+"$", "");
outfile = new File(String.format("%s_%04d.%s", fname, cnt, extension));
while(outfile.exists()){
cnt++;
outfile = new File(String.format("%s_%04d.%s", fname, cnt, extension));
}
// }
// else{
// outfile = this.file;
// }
// WORKAROUND END
// Open file
BufferedWriter writer = new BufferedWriter(new FileWriter(outfile));
// Write header
for(String s: header){
writer.write(s);
}
// Write data
for(String s: data){
writer.write(s);
}
// Close file
writer.close();
}
/**
* Enable/disable the generation of the _0000 suffix in front of the extension of the out file
* @param appendSuffix the appendSuffix to set
*/
public void setAppendSuffix(boolean appendSuffix) {
// this.appendSuffix = appendSuffix;
}
}
@@ -0,0 +1,229 @@
/**
*
* 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.vis;
import java.awt.FlowLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.PrintWriter;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.ScrollPaneLayout;
import javax.xml.bind.JAXBException;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.OptionBuilder;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.xml.sax.SAXException;
import ch.psi.fda.deserializer.DataDeserializer;
import ch.psi.fda.deserializer.DataDeserializerTXT;
import ch.psi.fda.gui.ScrollableFlowPanel;
import ch.psi.fda.model.ModelManager;
import ch.psi.fda.model.v1.Configuration;
import ch.psi.fda.visualizer.Visualizer;
/**
* Visualize data according to the scan description
*
* @author ebner
*
*/
public class VisualizationEngine {
// Get Logger
private static Logger logger = Logger.getLogger(VisualizationEngine.class.getName());
/**
* Default constructor
*/
public VisualizationEngine(){
}
/**
* Visualize data based on the configuration file
* @param configuration
* @param data
* @throws InterruptedException
* @throws SAXException
* @throws JAXBException
*/
public void visualize(File configuration, File data) throws InterruptedException{
if(configuration==null){
throw new IllegalArgumentException("Configuration file is null");
}
else if(!configuration.exists()){
throw new IllegalArgumentException("Configuration file ["+configuration.getAbsolutePath()+"] does not exist");
}
// Unmarshall configuration file
Configuration c;
try {
c = ModelManager.unmarshall(configuration);
} catch (Exception e) {
throw new RuntimeException("Unable to load configuration file "+configuration.getAbsolutePath(), e);
}
// Visualize data
visualize(c, data);
}
/**
* Visualize data
* @param configuration
* @param data
* @throws InterruptedException
*/
public void visualize(Configuration configuration, File data) throws InterruptedException{
if(data==null){
throw new IllegalArgumentException("Data file is null");
}
else if(!data.exists()){
throw new IllegalArgumentException("Data file ["+data.getAbsolutePath()+"] does not exist");
}
// Create deserializer
DataDeserializer deserializer = new DataDeserializerTXT(data);
// Create Visualizer
Visualizer visualizer = new Visualizer(deserializer.getQueue(), configuration.getVisualization());
visualizer.setTerminateAtEOS(true);
// Adapt default visualizer behavior to optimize performance for visualization
visualizer.setUpdateAtStreamElement(false);
visualizer.setUpdateAtStreamDelimiter(false);
visualizer.setUpdateAtEndOfStream(true);
JPanel opanel = new ScrollableFlowPanel();
opanel.setLayout(new FlowLayout());
JScrollPane spane = new JScrollPane(opanel, ScrollPaneLayout.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneLayout.HORIZONTAL_SCROLLBAR_NEVER);
JTabbedPane tpane = new JTabbedPane();
tpane.addTab("Overview", spane);
for (JPanel p : visualizer.getPlotPanels()) {
opanel.add(p);
}
final JFrame frame = new JFrame();
frame.setSize(1200,800);
frame.add(tpane);
// frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.addWindowListener(new WindowAdapter(){
@Override
public void windowClosing(WindowEvent we){
System.exit(0);
}
});
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);//.DO_NOTHING_ON_CLOSE);
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
frame.setVisible(true);
}
});
// Start deserializer and visualizer
Thread td = new Thread(deserializer);
td.start();
visualizer.startVisualization();
td.join();
logger.info("Deserializer finished");
// visualizer.stopVisualization();
}
/**
* This method accepts a data file and an option (-file) specifying the scan configuration file
* used for creating the data file. If no option is specified the configuration file is derived by the
* data file name.
*
* @param args Command line arguments
*/
public static void main(String[] args) {
String scriptname = "fda_vis";
File dfile = null;
File cfile = null;
OptionBuilder.hasArg();
OptionBuilder.withArgName("file");
OptionBuilder.withDescription("Scan configuration file");
OptionBuilder.withType(new String());
Option o_cfile = OptionBuilder.create( "file");
Options options = new Options();
options.addOption(o_cfile);
CommandLineParser parser = new GnuParser();
// Parse the command line arguments
try {
CommandLine line = parser.parse( options, args );
// Check whether exactly one file is specified
if(line.getArgs().length!=1){
throw new ParseException("Only up to one argument is supported");
}
dfile= new File(line.getArgs()[0]);
if( line.hasOption(o_cfile.getOpt() )){
cfile = new File(line.getOptionValue(o_cfile.getOpt()));
}
else{
// Try to determine configuration file from data file name
File dir = dfile.getParentFile();
String name = dfile.getName();
name = name.replaceAll("_[0-9]*.txt$", "");
cfile = new File(dir, name+".xml");
}
} catch (ParseException e) {
System.err.println(e.getMessage());
HelpFormatter formatter = new HelpFormatter();
formatter.printUsage(new PrintWriter(System.out, true), HelpFormatter.DEFAULT_WIDTH, scriptname, options);
System.exit(-1);
}
VisualizationEngine e = new VisualizationEngine();
try{
e.visualize(cfile, dfile);
}
catch(Exception ee){
System.out.println("Visualization failed due to: "+ee.getMessage());
logger.log(Level.WARNING, "Visualization failed due to Exception:", ee);
System.exit(-1);
}
}
}
@@ -0,0 +1,55 @@
/**
*
* 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.visualizer;
import java.util.ArrayList;
import java.util.List;
import ch.psi.plot.Plot;
/**
* @author ebner
*
*/
public class DataVisualization {
private final Plot plot;
private final List<XYSeriesDataFilter> series;
public DataVisualization(Plot plot){
this.plot = plot;
this.series = new ArrayList<XYSeriesDataFilter>();
}
/**
* @return the plot
*/
public Plot getPlot() {
return plot;
}
/**
* @return the series
*/
public List<XYSeriesDataFilter> getSeries() {
return series;
}
}
@@ -0,0 +1,74 @@
/**
*
* 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.visualizer;
import java.util.ArrayList;
import java.util.List;
import ch.psi.fda.core.messages.DataQueue;
/**
* @author ebner
*
*/
public class FilterSet {
private DataQueue queue;
private List<SeriesDataFilter> filters = new ArrayList<SeriesDataFilter>();
private String name;
public FilterSet(DataQueue queue){
this("", queue);
}
public FilterSet(String name, DataQueue queue){
this.queue = queue;
}
/**
* @return the queue
*/
public DataQueue getQueue() {
return queue;
}
/**
* @return the filters
*/
public List<SeriesDataFilter> getFilters() {
return filters;
}
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
}
@@ -0,0 +1,28 @@
/**
*
* 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.visualizer;
/**
* @author ebner
*
*/
public interface SeriesDataFilter {
}

Some files were not shown because too many files have changed in this diff Show More