merged xscan into project

This commit is contained in:
2016-03-17 08:17:05 +01:00
parent b014dd6afd
commit e1ff42d464
88 changed files with 13880 additions and 0 deletions
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,187 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.aq;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Properties;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class AcquisitionConfiguration {
private static final Logger logger = Logger.getLogger(AcquisitionConfiguration.class.getName());
public final static String FDA_CONFIG_FILE = "ch.psi.fda.xscan.config.file";
private String crlogicPrefix;
private String crlogicIoc;
/**
* Base directory for data. The directory may contain date macros. The string may contain any @see java.text.SimpleDateFormat
* patterns within ${ } brackets. The macros are resolved with the actual time while the get method
* of this property is called.
*/
private String dataBaseDirectory = System.getProperty("user.home");
/**
* Prefix of the data file. The prefix may contain date macros. The string may contain any @see java.text.SimpleDateFormat
* patterns within ${ } brackets. The macros are resolved with the actual time while the get method
* of this property is called.
*/
private String dataFilePrefix = "";
/**
* Maximum time for a actor move
*/
private Long actorMoveTimeout = 600000l; // 10 Minutes maximum move time
private String smptServer;
/**
* Default Constructor
* The constructor will read the configuration from the /fda.properties file (resource) located in the classpath.
*/
public AcquisitionConfiguration(){
loadConfiguration(System.getProperty(FDA_CONFIG_FILE));
}
/**
* Load configuration from properties file
*/
public void loadConfiguration(String file) {
Properties properties = new Properties();
File cfile = null;
// Only read in the property file if a file is specified
if(file != null){
cfile = new File(file);
try {
properties.load(new FileReader(cfile));
} catch (FileNotFoundException e) {
throw new RuntimeException("Configuration file "+file+" not found", e);
} catch (IOException e) {
throw new RuntimeException("Cannot read configuration file "+file, e);
}
}
else{
logger.warning("No configfile specified !");
}
// The defaults are set here
crlogicPrefix = properties.getProperty(AcquisitionConfiguration.class.getPackage().getName()+".crlogic.prefix", "");
crlogicIoc= properties.getProperty(AcquisitionConfiguration.class.getPackage().getName()+".crlogic.ioc", "");
dataBaseDirectory = properties.getProperty(AcquisitionConfiguration.class.getPackage().getName()+".data.dir",".");
if(cfile!=null && dataBaseDirectory.matches("^\\.\\.?/.*")){ // if basedir starts with . or .. we assume the data directory to be relative to the directory of the configuration file
dataBaseDirectory = cfile.getParentFile().getAbsolutePath()+"/"+dataBaseDirectory;
}
dataFilePrefix = properties.getProperty(AcquisitionConfiguration.class.getPackage().getName()+".data.filePrefix","");
actorMoveTimeout = new Long(properties.getProperty(AcquisitionConfiguration.class.getPackage().getName()+".actorMoveTimeout","600000"));
smptServer= properties.getProperty(AcquisitionConfiguration.class.getPackage().getName()+".notification.host","mail.psi.ch");
}
/**
* Replace ${name} and ${date} macro given string
* @param string
* @param date
* @param name
* @return
*/
public String replaceMacros(String string, Date date, String name){
String newString = string;
// Replace scan name macros
newString = newString.replaceAll("\\$\\{name\\}", name);
// Replace date macros
Pattern pattern = Pattern.compile("\\$\\{[a-z,A-Z,-,_,:]*\\}");
Matcher matcher = pattern.matcher(newString);
while(matcher.find()){
String datePattern = matcher.group();
datePattern = datePattern.replaceAll("\\$\\{", "");
datePattern = datePattern.replaceAll("\\}", "");
SimpleDateFormat datef = new SimpleDateFormat(datePattern);
newString = matcher.replaceFirst(datef.format(date));
matcher = pattern.matcher(newString);
}
return newString;
}
public String getCrlogicPrefix() {
return crlogicPrefix;
}
public void setCrlogicPrefix(String crlogicPrefix) {
this.crlogicPrefix = crlogicPrefix;
}
public void setCrlogicIoc(String crlogicIoc) {
this.crlogicIoc = crlogicIoc;
}
public String getCrlogicIoc() {
return crlogicIoc;
}
public String getDataBaseDirectory() {
return dataBaseDirectory;
}
public void setDataBaseDirectory(String dataBaseDirectory) {
this.dataBaseDirectory = dataBaseDirectory;
}
public String getDataFilePrefix() {
return dataFilePrefix;
}
public void setDataFilePrefix(String dataFilePrefix) {
this.dataFilePrefix = dataFilePrefix;
}
public Long getActorMoveTimeout() {
return actorMoveTimeout;
}
public void setActorMoveTimeout(Long actorMoveTimeout) {
this.actorMoveTimeout = actorMoveTimeout;
}
public String getSmptServer() {
return smptServer;
}
public void setSmptServer(String smptServer) {
this.smptServer = smptServer;
}
}
+102
View File
@@ -0,0 +1,102 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.aq;
import java.util.ArrayList;
import java.util.List;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import ch.psi.fda.messages.DataMessage;
import ch.psi.fda.messages.EndOfStreamMessage;
import ch.psi.fda.messages.Message;
import ch.psi.fda.messages.Metadata;
import ch.psi.fda.messages.StreamDelimiterMessage;
/**
* Collector class that is collecting and merging data from different Queues.
*/
public class Collector {
private EventBus bus;
private List<MessageListener> listeners = new ArrayList<>();
private List<Metadata> metadata;
private boolean first = true;
public Collector(EventBus b){
this.bus = b;
}
public void addEventBus(EventBus b){
MessageListener l = new MessageListener();
listeners.add(l);
b.register(l);
}
private class MessageListener{
private DataMessage message;
@Subscribe
public void onMessage(Message message){
int level = listeners.indexOf(this);
if(message instanceof DataMessage){
this.message = (DataMessage) message;
if(level==0){
if(first){
metadata = new ArrayList<>();
for(int i=listeners.size()-1;i>=0;i--){
// Correct/Add dimension information
for(Metadata m: listeners.get(i).getMessage().getMetadata()){
m.setDimension(i);
}
metadata.addAll(listeners.get(i).getMessage().getMetadata());
}
}
DataMessage m = new DataMessage(metadata);
for(int i=listeners.size()-1;i>=0;i--){
m.getData().addAll(listeners.get(i).getMessage().getData());
}
bus.post(m);
}
}
if(message instanceof EndOfStreamMessage){
StreamDelimiterMessage ddm = new StreamDelimiterMessage(level, ((EndOfStreamMessage)message).isIflag());
bus.post(ddm);
if(level==(listeners.size()-1)){ // if highest dimension then send end of stream
bus.post(new EndOfStreamMessage());
}
}
}
public DataMessage getMessage(){
return message;
}
}
}
@@ -0,0 +1,79 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.aq;
import java.util.ArrayList;
import java.util.List;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import ch.psi.fda.core.Manipulation;
import ch.psi.fda.messages.DataMessage;
import ch.psi.fda.messages.Message;
import ch.psi.fda.messages.Metadata;
/**
* Applies manipulations to the data stream
*/
public class Manipulator {
private EventBus bus;
private final List<Manipulation> manipulations;
private boolean first = true;
private List<Metadata> metadata = new ArrayList<>();
public Manipulator(EventBus b, List<Manipulation> manipulations){
this.bus = b;
this.manipulations = manipulations;
}
@Subscribe
public void onMessage(Message message){
if(message instanceof DataMessage){
if(first){
first=false;
metadata.addAll(((DataMessage) message).getMetadata());
for(Manipulation manipulation: this.manipulations){
manipulation.initialize(this.metadata);
// Add manipulation id to metadata
this.metadata.add(new Metadata(manipulation.getId(),0)); // Calculated component always belongs to lowes dimension
}
}
DataMessage dm = (DataMessage) message;
// message = new DataMessage(metadata);
for(Manipulation manipulation: manipulations){
// ((DataMessage)message).getData().add(manipulation.execute(dm));
dm.getData().add(manipulation.execute(dm));
// Need to update the metadata of the message
dm.setMetadata(this.metadata);
}
}
bus.post(message);
//System.out.println(Thread.currentThread());
}
}
@@ -0,0 +1,109 @@
/*
* Copyright (C) 2011 Paul Scherrer Institute
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package ch.psi.fda.aq;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import ch.psi.fda.model.v1.Recipient;
/**
* Agent to send out notifications to specified recipients.
*/
public class NotificationAgent {
private final static String smsPostfix = "@sms.switch.ch";
private Properties properties;
private String fromAddress;
private final List<Recipient> recipients = new ArrayList<Recipient>();
/**
* Constructor
* @param host SMTP server to send notifications to
* @param recipients List of recipients
* @param from Address string that will show up in the from field. For example: fda@psi.ch. This argument must not contain white spaces
*/
public NotificationAgent(String host, String from){
fromAddress = from;
properties = new Properties();
properties.put("mail.smtp.host", host);
}
public void sendNotification(String aSubject, String aBody, boolean error, boolean success) {
for(Recipient recipient: recipients){
if((error && recipient.isError()) || (success && recipient.isSuccess())){
String receiver;
// Verify mail recipients
if(recipient.getValue().matches("[0-9,\\\\.,-,a-z,A-Z]*@[0-9,\\\\.,a-z,A-Z]*")){
receiver = recipient.getValue();
}
else if(recipient.getValue().matches("[0-9]+")){
// Assume that it is a SMS number
receiver = recipient.getValue() + smsPostfix;
}
else{
Logger.getLogger(NotificationAgent.class.getName()).log(Level.WARNING, "Invalid email address");
continue;
}
Logger.getLogger(NotificationAgent.class.getName()).log(Level.INFO, "Send notification to " + receiver);
//Here, no Authenticator argument is used (it is null).
//Authenticators are used to prompt the user for user
//name and password.
Session session = Session.getDefaultInstance(properties, null);
MimeMessage message = new MimeMessage(session);
try {
//The "from" address may be set in code, or set in the
//config file under "mail.from" ; here, the latter style is used
message.setFrom( new InternetAddress(fromAddress) );
message.addRecipient(Message.RecipientType.TO, new InternetAddress(receiver));
message.setSubject(aSubject);
message.setText(aBody);
Transport.send(message);
} catch (MessagingException ex) {
Logger.getLogger(NotificationAgent.class.getName()).log(Level.WARNING, "Failed to send notification to " + receiver, ex);
}
}
}
}
public List<Recipient> getRecipients() {
return recipients;
}
}
@@ -0,0 +1,50 @@
package ch.psi.fda.aq;
import com.google.common.eventbus.EventBus;
import ch.psi.fda.EContainer;
import ch.psi.fda.model.v1.Configuration;
import ch.psi.jcae.ChannelService;
public class XScanContainer implements EContainer {
private final Acquisition acquisition;
private EventBus bus;
private Configuration xscanConfiguration;
public XScanContainer(ChannelService cservice, AcquisitionConfiguration config, EventBus bus, Configuration xscanConfiguration){
acquisition = new Acquisition(cservice, config);
this.bus = bus;
this.xscanConfiguration = xscanConfiguration;
}
@Override
public void initialize() {
acquisition.initalize(bus, xscanConfiguration);
}
@Override
public void execute() {
try {
acquisition.execute();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
@Override
public void abort() {
acquisition.abort();
}
@Override
public void destroy() {
acquisition.destroy();
}
@Override
public boolean isActive() {
return acquisition.isActive();
}
}
@@ -0,0 +1,29 @@
package ch.psi.fda.aq;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
import ch.psi.fda.edescriptor.EDescriptor;
import ch.psi.fda.model.v1.Configuration;
@XmlRootElement(name="edescriptor")
@XmlType(name="edescriptor")
public class XScanDescriptor implements EDescriptor {
private Configuration configuration;
public XScanDescriptor(){
}
public XScanDescriptor(Configuration configuration){
this.configuration = configuration;
}
public Configuration getConfiguration() {
return configuration;
}
public void setConfiguration(Configuration configuration) {
this.configuration = configuration;
}
}
@@ -0,0 +1,339 @@
package ch.psi.fda.aq;
import java.io.File;
import java.util.List;
import java.util.logging.Logger;
import ch.psi.fda.DescriptorProvider;
import ch.psi.fda.edescriptor.EDescriptor;
import ch.psi.fda.model.ModelManager;
import ch.psi.fda.model.v1.ArrayDetector;
import ch.psi.fda.model.v1.Configuration;
import ch.psi.fda.model.v1.ContinuousPositioner;
import ch.psi.fda.model.v1.Data;
import ch.psi.fda.model.v1.Detector;
import ch.psi.fda.model.v1.LinearPositioner;
import ch.psi.fda.model.v1.Positioner;
import ch.psi.fda.model.v1.PseudoPositioner;
import ch.psi.fda.model.v1.Visualization;
import ch.psi.fda.vdescriptor.LinePlot;
import ch.psi.fda.vdescriptor.VDescriptor;
import ch.psi.fda.vdescriptor.XYSeries;
import ch.psi.fda.vdescriptor.XYZSeries;
import ch.psi.fda.vdescriptor.YSeries;
import ch.psi.fda.vdescriptor.YZSeries;
public class XScanDescriptorProvider implements DescriptorProvider {
private static final Logger logger = Logger.getLogger(XScanDescriptorProvider.class.getName());
private EDescriptor edescriptor;
private VDescriptor vdescriptor;
@Override
public void load(File... files) {
if(files.length<1 || files[0]==null){
throw new IllegalArgumentException("There need to be at lease one file specified");
}
File file = files[0];
if(!file.exists()){
throw new IllegalArgumentException("File "+file.getAbsolutePath()+" does not exist");
}
Configuration c;
try {
c = ModelManager.unmarshall(file);
} catch (Exception e) {
throw new UnsupportedOperationException("Unable to deserialize configuration: "+e.getMessage(), e);
}
// Set data file name
// Determine name used for the data file
String name = file.getName();
name = name.replaceAll("\\.xml$", "");
if(c.getData()!=null){
Data data = c.getData();
// Only update filename if no name is specified in xml file
if(data.getFileName()==null){
data.setFileName(name);
}
}
else{
Data data = new Data();
data.setFileName(name);
c.setData(data);
}
// // Override number of executions
// if(iterations != null){
// c.setNumberOfExecution(iterations);
// }
// Fix configuration if iterations is specified with 0 and no iterations option is specified
if(c.getNumberOfExecution()==0){
c.setNumberOfExecution(1);
}
this.edescriptor = new XScanDescriptor(c);
this.vdescriptor = mapVisualizations(c.getVisualization());
}
/**
* Create a vdescriptor out of the scan description
* @param vl
* @return
*/
public static VDescriptor mapVisualizations(List<Visualization> vl){
VDescriptor vd = new VDescriptor();
for(Visualization v: vl){
if(v instanceof ch.psi.fda.model.v1.LinePlot){
ch.psi.fda.model.v1.LinePlot lp = (ch.psi.fda.model.v1.LinePlot) v;
String x = getId(lp.getX());
LinePlot lineplot = new LinePlot(lp.getTitle());
List<Object> l = lp.getY();
for(Object o: l){
String y = getId(o);
lineplot.getData().add(new XYSeries(x, y));
}
vd.getPlots().add(lineplot);
}
else if(v instanceof ch.psi.fda.model.v1.LinePlotArray){
// Array visualization
ch.psi.fda.model.v1.LinePlotArray lp = (ch.psi.fda.model.v1.LinePlotArray) v;
LinePlot lineplot = new LinePlot(lp.getTitle());
// Create data filter for visualization
List<Object> l = lp.getY();
for(Object o: l){
String idY = getId(o);
// TODO Need to actually check if minX of
lineplot.setMinX(new Double(lp.getOffset()));
lineplot.setMaxX(new Double(lp.getOffset()+lp.getSize()));
lineplot.setMaxSeries(lp.getMaxSeries());
lineplot.getData().add(new YSeries(idY));
}
vd.getPlots().add(lineplot);
}
else if(v instanceof ch.psi.fda.model.v1.MatrixPlot){
// MatrixPlot does currently not support RegionPositioners because of the
// plotting problems this would cause. If regions of the positioner have different
// step sizes it is not easily possible (without (specialized) rasterization) to plot the data.
ch.psi.fda.model.v1.MatrixPlot mp = (ch.psi.fda.model.v1.MatrixPlot) v;
double minX, maxX;
int nX;
double minY, maxY;
int nY;
String idX, idY, idZ;
// X Axis
if(mp.getX() instanceof LinearPositioner){
LinearPositioner linp = ((LinearPositioner)mp.getX());
idX = linp.getId();
minX = (Math.min(linp.getStart(), linp.getEnd()));
maxX = (Math.max(linp.getStart(), linp.getEnd()));
nX = ((int) Math.floor((Math.abs(maxX-minX))/linp.getStepSize()) + 1);
}
else if(mp.getX() instanceof PseudoPositioner){
PseudoPositioner pp = ((PseudoPositioner)mp.getX());
idX = pp.getId();
minX = (1); // Count starts at 1
maxX = (pp.getCounts());
nX = (pp.getCounts());
}
else if(mp.getX() instanceof ContinuousPositioner){
ContinuousPositioner conp = ((ContinuousPositioner)mp.getX());
idX = conp.getId();
minX = (Math.min(conp.getStart(), conp.getEnd()));
maxX = (Math.max(conp.getStart(), conp.getEnd()));
nX = ((int) Math.floor((Math.abs(maxX-minX))/conp.getStepSize()) + 1);
}
else{
// Fail as we cannot determine the min, max and number of steps
throw new RuntimeException(mp.getX().getClass().getName()+" is not supported as x-axis of a MatrixPlot");
}
// Y Axis
if(mp.getY() instanceof LinearPositioner){
LinearPositioner linp = ((LinearPositioner)mp.getY());
idY = linp.getId();
minY = (Math.min(linp.getStart(), linp.getEnd()));
maxY = (Math.max(linp.getStart(), linp.getEnd()));
nY = ((int) Math.floor((Math.abs(maxY-minY))/linp.getStepSize()) + 1);
}
else if(mp.getY() instanceof PseudoPositioner){
PseudoPositioner pp = ((PseudoPositioner)mp.getY());
idY = pp.getId();
minY = (1); // Count starts at 1
maxY = (pp.getCounts());
nY = (pp.getCounts());
}
else{
// Fail as we cannot determine the min, max and number of steps
throw new RuntimeException(mp.getY().getClass().getName()+" is not supported as y-axis of a MatrixPlot");
}
// Z Dimension
idZ = getId(mp.getZ());
ch.psi.fda.vdescriptor.MatrixPlot matrixplot = new ch.psi.fda.vdescriptor.MatrixPlot(mp.getTitle());
matrixplot.setMinX(minX);
matrixplot.setMaxX(maxX);
matrixplot.setnX(nX);
matrixplot.setMinY(minY);
matrixplot.setMaxY(maxY);
matrixplot.setnY(nY);
matrixplot.setType(mp.getType());
matrixplot.getData().add(new XYZSeries(idX, idY, idZ));
vd.getPlots().add(matrixplot);
}
else if(v instanceof ch.psi.fda.model.v1.MatrixPlotArray){
// Support for 2D waveform plots
ch.psi.fda.model.v1.MatrixPlotArray mp = (ch.psi.fda.model.v1.MatrixPlotArray) v;
// Get size of the array detector
int arraySize = 0;
Object o = mp.getZ();
if(o instanceof ArrayDetector){
ArrayDetector ad = (ArrayDetector) o;
arraySize = ad.getArraySize();
}
else{
// Workaround
arraySize = mp.getSize(); // of array is from a manipulation the size is not known. Then the size will indicate the size of the array to display
}
int offset = mp.getOffset();
// Determine size for array
int size = mp.getSize();
if(size>0 && offset+size<arraySize){
size = mp.getSize();
}
else{
size=arraySize-offset;
}
double minY, maxY;
int nY;
double minX = offset;
double maxX = offset+size-1;
int nX = size;
String idY, idZ;
// Y Axis
if(mp.getY() instanceof LinearPositioner){
LinearPositioner linp = ((LinearPositioner)mp.getY());
idY = linp.getId();
minY = (Math.min(linp.getStart(), linp.getEnd()));
maxY = (Math.max(linp.getStart(), linp.getEnd()));
nY = ((int) Math.floor((Math.abs(maxY-minY))/linp.getStepSize()) + 1);
}
else if(mp.getY() instanceof PseudoPositioner){
PseudoPositioner pp = ((PseudoPositioner)mp.getY());
idY = pp.getId();
minY = (1); // Count starts at 1
maxY = (pp.getCounts());
nY = (pp.getCounts());
}
else if(mp.getY() instanceof ContinuousPositioner){
ContinuousPositioner conp = ((ContinuousPositioner)mp.getY());
idY = conp.getId();
minY = (Math.min(conp.getStart(), conp.getEnd()));
maxY = (Math.max(conp.getStart(), conp.getEnd()));
nY = ((int) Math.floor((Math.abs(maxY-minY))/conp.getStepSize()) + 1);
}
else{
// Fail as we cannot determine the min, max and number of steps
throw new RuntimeException(mp.getY().getClass().getName()+" is not supported as x-axis of a MatrixPlot");
}
// Z Dimension
idZ = getId(mp.getZ());
ch.psi.fda.vdescriptor.MatrixPlot matrixplot = new ch.psi.fda.vdescriptor.MatrixPlot(mp.getTitle());
matrixplot.setMinX(minX);
matrixplot.setMaxX(maxX);
matrixplot.setnX(nX);
matrixplot.setMinY(minY);
matrixplot.setMaxY(maxY);
matrixplot.setnY(nY);
matrixplot.setType(mp.getType());
matrixplot.getData().add(new YZSeries(idY, idZ));
vd.getPlots().add(matrixplot);
}
else{
logger.warning(v.getClass().getName()+" is not supported as visualization type");
}
}
return vd;
}
/**
* Retrieve id string of the passed object
* @param object
* @return Id string of object
*/
private static String getId(Object object){
String id;
if(object instanceof Positioner){
id = ((Positioner)object).getId();
}
else if (object instanceof Detector){
id = ((Detector)object).getId();
}
else if (object instanceof ch.psi.fda.model.v1.Manipulation){
id = ((ch.psi.fda.model.v1.Manipulation)object).getId();
}
// For testing purposes
else if(object instanceof String){
id = (String) object;
}
else{
throw new RuntimeException("Unable to identify id of object reference "+object);
}
return id;
}
@Override
public EDescriptor getEDescriptor() {
return edescriptor;
}
@Override
public VDescriptor getVDescriptor() {
return vdescriptor;
}
@Override
public Class<?> getEDescriptorClass() {
return XScanDescriptor.class;
}
}
@@ -0,0 +1,36 @@
package ch.psi.fda.aq;
import javax.inject.Inject;
import com.google.common.eventbus.EventBus;
import ch.psi.fda.EContainer;
import ch.psi.fda.EContainerFactory;
import ch.psi.fda.edescriptor.EDescriptor;
import ch.psi.jcae.ChannelService;
public class XScanFactory implements EContainerFactory {
@Inject
private ChannelService cservice;
private AcquisitionConfiguration config = new AcquisitionConfiguration();
@Override
public boolean supportsEDescriptor(EDescriptor descriptor) {
return (descriptor instanceof XScanDescriptor);
}
@Override
public EContainer getEContainer(EDescriptor descriptor, EventBus bus) {
if(! (descriptor instanceof XScanDescriptor)){
throw new IllegalArgumentException("Descriptor of type "+descriptor.getClass().getName()+" is not supported - descriptor need to be of type "+XScanDescriptor.class);
}
XScanDescriptor xdescriptor = (XScanDescriptor) descriptor;
return new XScanContainer(cservice, config, bus, xdescriptor.getConfiguration());
}
}
+33
View File
@@ -0,0 +1,33 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core;
public interface Action {
/**
* Execute logic of the action
*/
public void execute() throws InterruptedException;
/**
* Abort the execution logic of the action
*/
public void abort();
}
@@ -0,0 +1,66 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core;
import java.util.List;
import com.google.common.eventbus.EventBus;
/**
* Loop of actions to accomplish a task. Depending on the loop
* actions may be executed in a different way.
*/
public interface ActionLoop extends Action {
/**
* Prepare ActionLoop for execution.
*/
public void prepare();
/**
* Cleanup resources used by this ActionLoop while it was executed.
*/
public void cleanup();
/**
* Get the pre actions of the Loop
* @return pre actions
*/
public List<Action> getPreActions();
/**
* Get the post actions of the loop
* @return post actions
*/
public List<Action> getPostActions();
/**
* @return is a datagroup
*/
public boolean isDataGroup();
/**
* Set whether data of the loop belongs to a own data group
* @param dataGroup
*/
public void setDataGroup(boolean dataGroup);
public EventBus getEventBus();
}
+49
View File
@@ -0,0 +1,49 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core;
public interface Actor {
/**
* Set actor value
*/
public void set() throws InterruptedException;
/**
* Function to check whether the actor has a next set value
* @return Returns true if there is an actor value for the next iteration.
* False if there is no actor value (i.e. this will be the last iteration in the ActionLoop)
*/
public boolean hasNext();
/**
* Initialize the actor to the start
*/
public void init();
/**
* Reverse the set values of the actor
*/
public void reverse();
/**
* Reset the actuator to its initial configuration
*/
public void reset();
}
@@ -0,0 +1,40 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core;
import java.util.concurrent.Callable;
/**
* Callable used for parallel execution of the set method of an actor
*/
public class ActorSetCallable implements Callable<Object> {
private Actor actor;
public ActorSetCallable(Actor actor){
this.actor = actor;
}
@Override
public Object call() throws Exception {
actor.set();
return null;
}
}
+41
View File
@@ -0,0 +1,41 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core;
/**
* Guard to protect specific activities. A guard can be used to check for environment changes while a
* certain activity was executed.
*
* Example:
* An Guard can be used to check whether an injection happened while the the detector were read.
*/
public interface Guard {
/**
* Initialize guard object and its internal state.
*/
public void init();
/**
* Check the status of the guard.
* @return Returns true if the guard condition was not constrainted since the last init call. False otherwise.
*/
public boolean check();
}
@@ -0,0 +1,47 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core;
import java.util.List;
import ch.psi.fda.messages.DataMessage;
import ch.psi.fda.messages.Metadata;
public interface Manipulation {
/**
* Get the id of the manipulation
* @return id of manipulation
*/
public String getId();
/**
* Initialize the manipulation
* @param metadata Metadata of the incomming data message
*/
public void initialize(List<Metadata> metadata);
/**
* Execute the manipulation on the passed data message
* @param message Message to manipulate
* @return Result of the manipulation
*/
public Object execute(DataMessage message);
}
+39
View File
@@ -0,0 +1,39 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core;
/**
* The sensor interface describes an entity that can be read out like a
* simple channel or (image) detector. Depending on the sensor type the
* returned data is of a certain type.
*/
public interface Sensor {
/**
* Readout sensor.
* @return Sensor value. The type of the returned value depends on the sensor type.
*/
public Object read() throws InterruptedException;
/**
* Get the global id of the sensor
* @return id of sensor
*/
public String getId();
}
@@ -0,0 +1,87 @@
/**
*
* Copyright 2012 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This code is distributed in the hope that it will be useful, but without any
* warranty; without even the implied warranty of merchantability or fitness for
* a particular purpose. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core;
public class TestConfiguration {
private static final TestConfiguration instance = new TestConfiguration();
private final String otfPrefix = "MTEST-HW3-OTFX";
private final String crlogicPrefix = "MTEST-HW3-CRL";
private final String prefixScaler = "MTEST-HW3:JS";
private final String server = "MTEST-VME-HW3";
private final String motor1 = "MTEST-HW3:MOT1";
private final String analogIn1 = "MTEST-HW3-AI1:AI_01";
private final String ioc = "MTEST-VME-HW3.psi.ch";
private TestConfiguration(){
}
public static TestConfiguration getInstance(){
return instance;
}
/**
* @return the prefix
*/
public String getCrlogicPrefix() {
return crlogicPrefix;
}
/**
* @return the prefixScaler
*/
public String getPrefixScaler() {
return prefixScaler;
}
/**
* @return the server
*/
public String getServer() {
return server;
}
/**
* @return the motor1
*/
public String getMotor1() {
return motor1;
}
/**
* @return the analogIn1
*/
public String getAnalogIn1() {
return analogIn1;
}
/**
* @return the otfPrefix
*/
public String getOtfPrefix() {
return otfPrefix;
}
public String getIoc() {
return ioc;
}
}
@@ -0,0 +1,124 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core.actions;
import java.util.Comparator;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import java.util.logging.Logger;
import ch.psi.fda.core.Action;
import ch.psi.jcae.Channel;
import ch.psi.jcae.ChannelException;
/**
* Perform a put on the specified Channel Access channel. The put can be done synchronous or
* asynchronously.
*/
public class ChannelAccessCondition<E> implements Action {
private static Logger logger = Logger.getLogger(ChannelAccessCondition.class.getName());
private final Channel<E> channel;
private final E expectedValue;
private final Comparator<E> comparator;
private final Long timeout;
private volatile boolean abort = false;
private volatile Thread waitT = null;
/**
* @param channel Channel to wait value for
* @param expectedValue Value to wait for
* @param timeout Timeout of the condition in milliseconds (null accepted - will take default wait timeout for channels ch.psi.jcae.ChannelBeanFactory.waitTimeout)
*
* @throws IllegalArgumentException Timeout specified is not >=0
*/
public ChannelAccessCondition(Channel<E> channel, E expectedValue, Long timeout){
if(timeout != null && timeout<=0){
throw new IllegalArgumentException("Timeout must be > 0");
}
this.channel = channel;
this.expectedValue = expectedValue;
this.comparator = null;
this.timeout = timeout;
}
public ChannelAccessCondition(Channel<E> channel, E expectedValue, Comparator<E> comparator, Long timeout){
if(timeout != null && timeout<=0){
throw new IllegalArgumentException("Timeout must be > 0");
}
this.channel = channel;
this.expectedValue = expectedValue;
this.comparator = comparator;
this.timeout = timeout;
}
/**
* @throws RuntimeException Channel value did not reach expected value (within the specified timeout period)
*/
@Override
public void execute() throws InterruptedException {
abort=false;
logger.finest("Checking channel "+channel.getName()+" for value "+expectedValue+" [timeout: "+timeout+"]" );
try{
waitT = Thread.currentThread();
try {
if(comparator==null){
if(timeout == null){
channel.waitForValue(expectedValue);
}
else{
channel.waitForValue(expectedValue, timeout);
}
}
else{
if(timeout == null){
channel.waitForValue(expectedValue, comparator);
}
else{
channel.waitForValue(expectedValue, comparator, timeout);
}
}
} catch (ExecutionException | ChannelException | TimeoutException | InterruptedException e) {
if(abort && e instanceof InterruptedException){
return;
}
throw new RuntimeException("Channel [name:"+channel.getName()+"] did not reach expected value "+expectedValue+" ", e);
}
}
finally{
waitT=null;
}
}
@Override
public void abort() {
abort=true;
if(waitT!=null){
waitT.interrupt();
}
}
}
@@ -0,0 +1,94 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core.actions;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.logging.Logger;
import ch.psi.fda.core.Action;
import ch.psi.jcae.Channel;
import ch.psi.jcae.ChannelException;
/**
* Perform a put on the specified Channel Access channel. The put can be done synchronous or
* asynchronously.
*/
public class ChannelAccessPut<E> implements Action {
private static Logger logger = Logger.getLogger(ChannelAccessPut.class.getName());
private final Channel<E> channel;
private final E value;
private final boolean asynchronous;
private final Long timeout;
/**
* @param channel
* @param value Value to set
* @param asynchronous Flag whether to set the value synchronous (wait for response) or asynchronously (fire and forget)
* @param timeout Timeout used for set operation (time that set need to come back)
*/
public ChannelAccessPut(Channel<E> channel, E value, boolean asynchronous, Long timeout){
this.channel = channel;
this.value = value;
this.asynchronous = asynchronous;
this.timeout = timeout;
}
/**
* Additional constructor for convenience. This constructor defaults the operation type to synchronous put.
* @param channel
* @param value Value to set
*/
public ChannelAccessPut(Channel<E> channel, E value){
this(channel, value, false, null);
}
/**
* @throws RuntimeException Cannot set value on channel
*/
@Override
public void execute() throws InterruptedException {
logger.finest("Put to channel: "+channel.getName()+ " asynchronous: "+asynchronous);
try{
if(asynchronous){
channel.setValueNoWait(value);
}
else{
if(timeout==null){
channel.setValue(value);
}
else{
channel.setValueAsync(value).get(timeout, TimeUnit.MILLISECONDS);
}
}
} catch (ExecutionException | TimeoutException | ChannelException e) {
throw new RuntimeException("Unable to set channel [name:"+channel.getName()+"] to value "+value, e);
}
}
@Override
public void abort() {
}
}
@@ -0,0 +1,52 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core.actions;
import ch.psi.fda.core.Action;
/**
* Wait a specific time until executing the next action ...
*/
public class Delay implements Action {
private final long time;
/**
* @param time Time to wait in milliseconds
*/
public Delay(long time){
// Check if delay time is positive and >0
if(time<=0){
throw new IllegalArgumentException("Wait time must be >0");
}
this.time = time;
}
@Override
public void execute() throws InterruptedException {
Thread.sleep(time);
}
@Override
public void abort() {
}
}
@@ -0,0 +1,142 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core.actions;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import ch.psi.fda.core.Action;
/**
* Executes a python script inside a Jython interpreter
*/
public class JythonAction implements Action {
private static Logger logger = Logger.getLogger(JythonAction.class.getName());
/**
* Pattern of the entry function of the jython script
*/
private static final String entryFunction = "process";
private static final String entryFunctionPattern = "def "+entryFunction+"\\((.*)\\):";
private ScriptEngine engine;
private String jythonCall; // entry call to script - including all parameters, etc.
private final Map<String,Object> globalObjects;
public JythonAction(String script, Map<String, ?> mapping){
this(script, mapping, new HashMap<String,Object>());
}
public JythonAction(String script, Map<String, ?> mapping, Map<String,Object> globalObjects){
this.globalObjects = globalObjects;
// Workaround for Jython memory leak
// http://blog.hillbrecht.de/2009/07/11/jython-memory-leakout-of-memory-problem/
System.setProperty("python.options.internalTablesImpl","weak");
// Create new script engine
this.engine = new ScriptEngineManager().getEngineByName("python");
// Determine script entry function and the function parameters
Pattern pattern = Pattern.compile(entryFunctionPattern);
Matcher matcher = pattern.matcher(script);
String[] functionParameters = null;
if(matcher.find() && matcher.groupCount()==1){
logger.finest("Entry function '"+entryFunctionPattern+"' found - Identified parameters: "+matcher.group(1));
jythonCall = entryFunction+"("+matcher.group(1)+")";
if(matcher.group(1).matches(" *")){
functionParameters = new String[0];
}
else{
functionParameters = matcher.group(1).split(" *, *");
}
}
else{
throw new IllegalArgumentException("Cannot determine entry function: "+entryFunctionPattern);
}
// Check whether all function parameters have a mapping
for(int i=0;i<functionParameters.length; i++){
String p = functionParameters[i];
p = p.trim();
boolean found = false;
for(String pm: mapping.keySet()){
if(pm.equals(p)){
found=true;
break;
}
}
if(!found){
throw new IllegalArgumentException("No mapping compontent found for parameter "+p);
}
}
// Check whether more mappings are specified than function parameters
if(functionParameters.length<mapping.size()){
throw new IllegalArgumentException("More mappings than function parameters are specified");
}
// Load manipulation script
try {
engine.eval(script);
} catch (ScriptException e) {
throw new RuntimeException("Unable to load manipulation script", e);
}
for(String b: mapping.keySet()){
// Assign channel bean to variable
engine.put(b, mapping.get(b));
}
}
@Override
public void execute() {
// Set global objects
// This block is not in initialization as we want to assure that all invocations
// of this manipulation will get the same value (i.e. to prevent inconsistent behavior
// if variable was changed during an execution of the manipulation)
for(String k: globalObjects.keySet()){
engine.put(k, globalObjects.get(k));
}
try {
engine.eval(jythonCall+"\n");
} catch (ScriptException e) {
throw new RuntimeException("Action failed while executing the Jython script",e);
}
}
@Override
public void abort() {
}
}
@@ -0,0 +1,133 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core.actions;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.logging.Level;
import java.util.logging.Logger;
import ch.psi.fda.core.Action;
/**
* Action that executes a specified command when it is executed.
*/
public class ShellAction implements Action{
private static Logger logger = Logger.getLogger(ShellAction.class.getName());
private volatile Process process;
private volatile boolean abort = false;
private boolean checkExitValue = true;
private int exitValue = 0;
/**
* Name (full path if it is not in the system path) of the script to execute when
* the execute() function of this action is invoked.
*/
private final String script;
/**
* @param script Name of the command to execute when this action is invoked
*
* @throws IllegalArgumentException Specified script does not exist
*/
public ShellAction(String script){
String[] scri = script.split("[ ,\t]");
File s = new File(scri[0]);
if(!s.exists()){
throw new IllegalArgumentException("Script "+script+" does not exist.");
}
this.script = script;
}
@Override
public void execute() throws InterruptedException {
try{
abort = false;
logger.fine("Execute script "+script);
process = Runtime.getRuntime().exec(new String[]{"/bin/bash","-c",script});
int exitVal = process.waitFor();
// Log output of the shell script if loglevel is finest
if(logger.isLoggable(Level.FINEST)){
logger.finest("STDOUT [BEGIN]");
// Ideally the readout of the stream should be in parallel to the processing of the script! I.e. the output appears in the log as it is generated by the script!
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line = null;
while((line=reader.readLine()) != null){
logger.finest(line);
}
logger.finest("STDOUT [END]");
logger.finest("STDERR [BEGIN]");
// Ideally the readout of the stream should be in parallel to the processing of the script! I.e. the output appears in the log as it is generated by the script!
reader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
line = null;
while((line=reader.readLine()) != null){
logger.finest(line);
}
logger.finest("STDERR [END]");
}
logger.fine("Script ["+script+"] return value: "+exitVal);
if(abort){
throw new RuntimeException("Script ["+script+"] was aborted");
}
else{
// Check script exit value to 0 if != 0 then throw an runtime exception
if(checkExitValue && exitVal != exitValue){
throw new RuntimeException("Script ["+script+"] returned with an exit value not equal to 0");
}
}
process = null;
}
catch(IOException e){
throw new RuntimeException("Unable to execute script: "+script,e);
}
}
@Override
public void abort() {
abort=true;
if(process!=null){
process.destroy();
}
}
public boolean isCheckExitValue() {
return checkExitValue;
}
public void setCheckExitValue(boolean checkExitValue) {
this.checkExitValue = checkExitValue;
}
public int getExitValue() {
return exitValue;
}
public void setExitValue(int exitValue) {
this.exitValue = exitValue;
}
}
@@ -0,0 +1,253 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core.actors;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.logging.Logger;
import ch.psi.fda.core.Actor;
import ch.psi.jcae.Channel;
import ch.psi.jcae.ChannelException;
public class ChannelAccessFunctionActuator<T> implements Actor {
private static Logger logger = Logger.getLogger(ChannelAccessFunctionActuator.class.getName());
private boolean asynchronous = false;
private double start;
private double end;
private double stepSize;
private int direction; // Move direction (start&lt;end = 1, start&gt;end = -1)
/**
* Execution count of actuator. This variable is used to minimize the floating point
* rounding errors for calculating the next step.
*/
private int count;
/**
* Flag that indicates whether there is a next set value for the Actor
*/
private boolean next;
/**
* Value to set at next @see ch.psi.fda.engine.Actor#set() call
*/
private double value;
/**
* Level of accuracy the positioner need to have (e.g. if a positioner is set to 1 the readback set value
* of the positioner need to have at lease 1+/-accuracy)
* Default is stepSize/2
*/
private double accuracy;
/**
* Move timeout
*/
private Long timeout;
private final T doneValue;
private final long doneDelay;
private final double originalStart;
private final double originalEnd;
private final int originalDirection;
private Channel<Double> channel;
private Channel<T> doneChannel = null;
private final Function function;
private boolean checkActorSet = true;
/**
* Constructor - Initialize actor
* @param channelName
* @param start
* @param end
* @param stepSize
* @param timeout Maximum move time (in milliseconds)
*/
public ChannelAccessFunctionActuator(Channel<Double> channel, Function function, double start, double end, double stepSize, Long timeout){
this(channel, null, null, 0, function, start, end, stepSize, timeout);
}
/**
* Constructor
* @param channel
* @param doneChannel If null actor will not wait (for this channel) to continue
* @param doneValue
* @param doneDelay Delay in seconds before checking the done channel
* @param start
* @param end
* @param stepSize
* @param timeout Maximum move time (in milliseconds)
*/
public ChannelAccessFunctionActuator(Channel<Double> channel, Channel<T> doneChannel, T doneValue, double doneDelay, Function function, double start, double end, double stepSize, Long timeout){
this.doneValue = doneValue;
this.doneDelay = (long) Math.floor((doneDelay*1000));
this.start = start;
this.end = end;
if(stepSize <= 0){
throw new IllegalArgumentException("Step size ["+stepSize+"] must be > 0");
}
this.stepSize = stepSize;
this.accuracy = stepSize/2;
// Validate and save timeout parameter
if(timeout!=null && timeout<=0){
throw new IllegalArgumentException("Timeout must be >0 or null");
}
else{
this.timeout = timeout;
}
if(function==null){
throw new IllegalArgumentException("Function must not be null");
}
this.function = function;
init();
// Save original settings
this.originalStart = start;
this.originalEnd = end;
this.originalDirection = direction;
this.channel = channel;
this.doneChannel = doneChannel;
}
@Override
public void set() throws InterruptedException {
// Throw an IllegalStateException in the case that set is called although there is no next step.
if(!next){
throw new IllegalStateException("The actuator does not have any next step.");
}
// Set actuator channel
logger.finest("Set actuator channel "+channel.getName()+" to value: "+value);
try {
double fvalue = function.calculate(value);
if(!asynchronous){
if(timeout==null){
channel.setValue(fvalue);
}
else{
channel.setValueAsync(fvalue).get(timeout, TimeUnit.MILLISECONDS);
}
}
else{
channel.setValueNoWait(fvalue);
}
if(doneChannel != null){
Thread.sleep(doneDelay);
doneChannel.waitForValue(doneValue);
}
// Check whether the set value is really on the value that was set before.
if(checkActorSet){
double c = channel.getValue(true);
double a = Math.abs( c - fvalue );
if ( a > accuracy ){
throw new RuntimeException("Actor could not be set to the value "+fvalue+" The readback of the set value does not match the value that was set [value: "+c+" delta: "+a+" accuracy: "+accuracy+"]");
}
}
} catch (ExecutionException | TimeoutException | ChannelException e) {
throw new RuntimeException("Unable to move actuator [channel: "+channel.getName()+"] to value "+value,e);
}
count++;
double nextValue = start+(count*stepSize*direction); // Done like this to keep floating point rounding errors minimal
if((direction==1&&nextValue<=end)||(direction==-1&nextValue>=end)){
// Apply function
// nextValue = function.calculate(nextValue);
logger.fine("Next actor value: "+nextValue);
value=nextValue;
this.next = true;
}
else{
// There is no next set value
this.next = false;
}
}
@Override
public boolean hasNext() {
return next;
}
@Override
public void init() {
this.count = 0;
// Determine move direction
this.direction = 1;
if(start>end){
direction=-1; // Move in negative direction
}
// Set first set value to the start value
this.value = start;
this.next = true;
}
@Override
public synchronized void reverse() {
double oldStart = start;
this.start = this.end;
this.end = oldStart;
// Determine move direction
this.direction = 1;
if(this.start>this.end){
direction=-1; // Move in negative direction
}
}
@Override
public void reset() {
this.start = this.originalStart;
this.end = this.originalEnd;
this.direction = this.originalDirection;
}
public boolean isAsynchronous() {
return asynchronous;
}
public void setAsynchronous(boolean asynchronous) {
this.asynchronous = asynchronous;
}
}
@@ -0,0 +1,234 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core.actors;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.logging.Logger;
import ch.psi.fda.core.Actor;
import ch.psi.jcae.Channel;
import ch.psi.jcae.ChannelException;
/**
* This actuator sets an Channel Access channel from a start to an end value by doing discrete steps.
*/
public class ChannelAccessLinearActuator<T> implements Actor {
private static Logger logger = Logger.getLogger(ChannelAccessLinearActuator.class.getName());
private boolean asynchronous = false;
private double start;
private double end;
private double stepSize;
private int direction;
/**
* Execution count of actuator. This variable is used to minimize the floating point
* rounding errors for calculating the next step.
*/
private int count;
/**
* Flag that indicates whether there is a next set value for the Actor
*/
private boolean next;
/**
* Value to set at next @see ch.psi.fda.engine.Actor#set() call
*/
private double value;
/**
* Level of accuracy the positioner need to have (e.g. if a positioner is set to 1 the readback set value
* of the positioner need to have at lease 1+/-accuracy)
* Default is stepSize/2
*/
private double accuracy;
private final T doneValue;
private final long doneDelay;
private final double originalStart;
private final double originalEnd;
private final int originalDirection;
private Long timeout; // Set timeout
private final Channel<Double> channel;
private final Channel<T> doneChannel;
private boolean checkActorSet = true;
/**
* Constructor
* @param channel
* @param doneChannel If null actor will not wait (for this channel) to continue
* @param doneValue
* @param doneDelay Delay in seconds before checking the done channel
* @param start
* @param end
* @param stepSize
* @param timeout Maximum move time (in milliseconds)
*/
public ChannelAccessLinearActuator(Channel<Double> channel, Channel<T> doneChannel, T doneValue, double doneDelay, double start, double end, double stepSize, Long timeout){
this.doneValue = doneValue;
this.doneDelay = (long) Math.floor((doneDelay*1000));
this.start = start;
this.end = end;
if(stepSize <= 0){
throw new IllegalArgumentException("Step size ["+stepSize+"] must be > 0");
}
this.stepSize = stepSize;
this.accuracy = stepSize/2;
// Validate and save timeout parameter
if(timeout!=null && timeout<=0){
throw new IllegalArgumentException("Timeout must be >0 or null");
}
else{
this.timeout = timeout;
}
init();
// Save original settings
this.originalStart = start;
this.originalEnd = end;
this.originalDirection = direction;
this.channel = channel;
this.doneChannel = doneChannel;
}
@Override
public void set() throws InterruptedException {
// Throw an IllegalStateException in the case that set is called although there is no next step.
if(!next){
throw new IllegalStateException("The actuator does not have any next step.");
}
// Set actuator channel
logger.finest("Set actuator channel "+channel.getName()+" to value: "+value);
try {
if(!asynchronous){
if(timeout==null){
channel.setValue(value);
}
else{
channel.setValueAsync(value).get(timeout, TimeUnit.MILLISECONDS);
}
}
else{
channel.setValueNoWait(value);
}
if(doneChannel != null){
Thread.sleep(doneDelay);
doneChannel.waitForValue(doneValue);
}
// Check whether the set value is really on the value that was set before.
if(checkActorSet){
double c = channel.getValue(true);
double a = Math.abs( c - value );
if ( a > accuracy ){
throw new RuntimeException("Actor could not be set to the value "+value+" The readback of the set value does not match the value that was set [value: "+c+" delta: "+a+" accuracy: "+accuracy+"]");
}
}
} catch (ExecutionException | TimeoutException | ChannelException e) {
throw new RuntimeException("Unable to move actuator [channel: "+channel.getName()+"] to value "+value,e);
}
count++;
double nextValue = start+(count*stepSize*direction); // Done like this to keep floating point rounding errors minimal
if((direction==1&&nextValue<=end)||(direction==-1&nextValue>=end)){
logger.fine("Next actor value: "+nextValue);
value=nextValue;
this.next = true;
}
else{
// There is no next set value
this.next = false;
}
}
@Override
public boolean hasNext() {
return next;
}
@Override
public void init() {
this.count = 0;
// Determine move direction
this.direction = 1;
if(start>end){
direction=-1; // Move in negative direction
}
// Set first set value to the start value
this.value = start;
this.next = true;
}
@Override
public synchronized void reverse() {
double oldStart = start;
this.start = this.end;
this.end = oldStart;
// Determine move direction
this.direction = 1;
if(this.start>this.end){
direction=-1; // Move in negative direction
}
}
@Override
public void reset() {
this.start = this.originalStart;
this.end = this.originalEnd;
this.direction = this.originalDirection;
}
public boolean isAsynchronous() {
return asynchronous;
}
public void setAsynchronous(boolean asynchronous) {
this.asynchronous = asynchronous;
}
}
@@ -0,0 +1,236 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core.actors;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.logging.Logger;
import ch.psi.fda.core.Actor;
import ch.psi.jcae.Channel;
import ch.psi.jcae.ChannelException;
/**
* This actuator sets an Channel Access channel by using the positions from the given table.
*/
public class ChannelAccessTableActuator<T> implements Actor {
private static Logger logger = Logger.getLogger(ChannelAccessTableActuator.class.getName());
private boolean asynchronous = false;
/**
* Position table
*/
private final double[] table;
/**
* Level of accuracy the positioner need to have (e.g. if a positioner is set to 1 the readback set value
* of the positioner need to have at lease 1+/-accuracy)
*/
private double accuracy = 0.1;
/**
* Execution count of actuator. This variable is used to minimize the floating point
* rounding errors for calculating the next step.
*/
private int count;
/**
* Flag that indicates whether there is a next set value for the Actor
*/
private boolean next;
private Channel<Double> channel;
private Channel<T> doneChannel = null;
private final T doneValue;
private final long doneDelay;
/**
* Flag that indicates whether the actor moves in the positive direction
*/
private boolean positiveDirection = true;
private final boolean originalPositiveDirection;
/**
* Maximum move time (in milliseconds)
*/
private Long timeout = null;
private boolean checkActorSet = true;
/**
* Constructor - Initialize actor
* @param channelName Name of the channel to set
* @param table Position table with the explicit positions for each step
* @param timeout Maximum move time (in milliseconds)
*/
public ChannelAccessTableActuator(Channel<Double> channel, double[] table, Long timeout){
this(channel, null, null, 0, table, timeout);
}
/**
* Constructor
* @param channel
* @param doneChannel
* @param doneValue
* @param doneDelay
* @param table
* @param timeout Maximum move time (in milliseconds)
*/
public ChannelAccessTableActuator(Channel<Double> channel, Channel<T> doneChannel, T doneValue, double doneDelay, double[] table, Long timeout){
this.doneValue = doneValue;
this.doneDelay = (long) Math.floor((doneDelay*1000));
if(table==null){
throw new IllegalArgumentException("Null table is not accepted");
}
if(table.length==0){
throw new IllegalArgumentException("Position table need to have at least one position");
}
this.table = table;
// Validate and save timeout parameter
if(timeout!=null && timeout<=0){
throw new IllegalArgumentException("Timeout must be >0 or null");
}
else{
this.timeout = timeout;
}
init();
// Save the initial direction
this.originalPositiveDirection = positiveDirection;
this.channel = channel;
this.doneChannel = doneChannel;
}
@Override
public void set() throws InterruptedException {
// Throw an IllegalStateException in the case that set is called although there is no next step.
if(!next){
throw new IllegalStateException("The actuator does not have any next step.");
}
// Set actuator channel
logger.finest("Set actuator channel "+channel.getName()+" to value: "+table[count]);
try {
if(!asynchronous){
if(timeout==null){
channel.setValue(table[count]);
}
else{
channel.setValueAsync(table[count]).get(timeout, TimeUnit.MILLISECONDS);
}
}
else{
channel.setValueNoWait(table[count]);
}
if(doneChannel != null){
Thread.sleep(doneDelay);
doneChannel.waitForValue(doneValue);
}
// Check whether the set value is really on the value that was set before.
if(doneChannel==null && !asynchronous && checkActorSet){
if ( Math.abs( channel.getValue() - table[count] ) > accuracy ){
throw new RuntimeException("Actor could not be set to the value "+table[count]+" The readback of the set value does not match the value that was set");
}
}
} catch (ExecutionException | ChannelException | TimeoutException e) {
throw new RuntimeException("Move actuator [channel: "+channel.getName()+"] to value "+table[count],e);
}
if(positiveDirection){
count++;
if(count<table.length){
this.next = true;
}
else{
// There is no next set value
this.next = false;
}
}
else{
count--;
if(count>=0){
this.next = true;
}
else{
// There is no next set value
this.next = false;
}
}
}
@Override
public boolean hasNext() {
return next;
}
@Override
public void init() {
// Set first set value to the start value
if(positiveDirection){
this.count = 0; // Set count to the first element
}
else{
this.count = table.length-1; // Set count to the last element
}
if(table.length>0){
this.next = true;
}
}
@Override
public void reverse() {
if(positiveDirection){
positiveDirection=false;
}
else{
positiveDirection=true;
}
}
@Override
public void reset() {
this.positiveDirection = this.originalPositiveDirection;
}
public boolean isAsynchronous() {
return asynchronous;
}
public void setAsynchronous(boolean asynchronous) {
this.asynchronous = asynchronous;
}
}
@@ -0,0 +1,201 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core.actors;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import ch.psi.fda.core.Action;
import ch.psi.fda.core.Actor;
/**
* Complex actuator that consists of a list of other actuators. The steps of this actor are composed out of the
* steps of the actors that this actuator consists of.
* First all the steps of the first actor are used, after that the steps of the next actor are done.
* Before the first step of an actor pre actions are available and after the last step of an actor a post action is available.
*/
public class ComplexActuator implements Actor {
private static Logger logger = Logger.getLogger(ComplexActuator.class.getName());
/**
* List of actors this actor is made of
*/
private final List<Actor> actors;
/**
* Actions that are executed directly before the first step of this actor
*/
private final List<Action> preActions;
/**
* Actions that are executed directly after the last step of this actor
*/
private final List<Action> postActions;
/**
* Flag that indicates whether there is a next set value for the Actor
*/
private boolean next;
/**
* Index of the actor currently used
*/
private int actualActorCount;
/**
* Actor currently used to perform steps
*/
private Actor actualActor;
/**
* Flag to indicate the first set() execution of this actor.
* This flag is used to decide whether to execute the pre actions.
*/
private boolean firstrun;
public ComplexActuator(){
this.actors = new ArrayList<Actor>();
this.preActions = new ArrayList<Action>();
this.postActions = new ArrayList<Action>();
this.next = false;
this.actualActorCount = 0;
this.firstrun = true;
}
@Override
public void set() throws InterruptedException {
if(!next){
throw new IllegalStateException("The actuator does not have any next step.");
}
// PRE actions
if(firstrun){
this.firstrun = false;
// Execute pre actions
logger.finest("Execute pre actions");
for(Action action: preActions){
action.execute();
}
}
actualActor.set(); // If the actor has no next step then something is wrong in the init/next step logic
// If the last point of the actual actuator is set take the next actuator in the list if there is one available
while(!actualActor.hasNext()&&actualActorCount<actors.size()){
actualActorCount++;
if(actualActorCount<actors.size()){
actualActor = actors.get(actualActorCount);
}
else{
break;
}
}
if(actualActor.hasNext()){
this.next = true;
}
else{
this.next = false;
// Execute post actions
logger.finest("Execute post actions");
for(Action action: postActions){
action.execute();
}
}
}
@Override
public boolean hasNext() {
return next;
}
@Override
public void init() {
// Initialize all actors this actor consist of
for(Actor a: actors){
a.init();
}
// Set the actual actor to the first actor in the list
actualActorCount=0;
if(actualActorCount<actors.size()){
actualActor = actors.get(actualActorCount);
// Check whether the actor has a next step, if not increase to the next one
// This is needed because the first actor might not have any step ... (very unlikely but bad things happens some time)
while(!actualActor.hasNext()&&actualActorCount<actors.size()){
actualActorCount++;
if(actualActorCount<actors.size()){
actualActor = actors.get(actualActorCount);
}
else{
break;
}
}
if(actualActor.hasNext()){
this.next = true;
}
else{
this.next = false;
}
}
else{
actualActor=null;
this.next=false;
}
this.firstrun = true;
}
@Override
public void reverse() {
// Reverse all actors this actor consist of
for(Actor a: actors){
a.reverse();
}
}
@Override
public void reset() {
// Reset all actors this actor consist of
for(Actor a: actors){
a.reset();
}
}
public List<Action> getPreActions() {
return preActions;
}
public List<Actor> getActors() {
return actors;
}
public List<Action> getPostActions() {
return postActions;
}
}
@@ -0,0 +1,24 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core.actors;
public interface Function {
public double calculate(double parameter);
}
@@ -0,0 +1,106 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core.actors;
import java.util.Map;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import ch.psi.fda.core.scripting.JythonGlobalVariable;
public class JythonFunction implements Function {
private static final Logger logger = Logger.getLogger(JythonFunction.class.getName());
public static final String ENTRY_FUNCTION_NAME = "calculate";
private static final String ENTRY_FUNCTION_PATTERN = "def "+ENTRY_FUNCTION_NAME+"\\((.*)\\):";
private ScriptEngine engine;
private String additionalParameter = "";
public JythonFunction(String script, Map<String, JythonGlobalVariable> map){
// Workaround for Jython memory leak
// http://blog.hillbrecht.de/2009/07/11/jython-memory-leakout-of-memory-problem/
System.setProperty("python.options.internalTablesImpl","weak");
this.engine = new ScriptEngineManager().getEngineByName("python");
// Determine script entry function and the function parameters
String[] parameter;
Pattern pattern = Pattern.compile(ENTRY_FUNCTION_PATTERN);
Matcher matcher = pattern.matcher(script);
if(matcher.find() && matcher.groupCount()==1){
if(!matcher.group(1).trim().equals("")){
logger.finest("Entry function '"+ENTRY_FUNCTION_PATTERN+"' found - Identified parameters: "+matcher.group(1));
parameter = matcher.group(1).split(" *, *");
}
else{
parameter = new String[0];
}
}
else{
throw new IllegalArgumentException("Cannot determine entry function: "+ENTRY_FUNCTION_PATTERN);
}
// Check whether all parameters are mapped
StringBuilder b = new StringBuilder();
for(int i=1;i<parameter.length; i++){ // Starting at 1 because first argument is implicit.
if(!map.containsKey(parameter[i])){
throw new IllegalArgumentException("Function parameter "+parameter[i]+" is not mapped");
}
b.append(",");
b.append(parameter[i]);
}
additionalParameter = b.toString();
// Set variables in Jython engine
for(String k: map.keySet()){
engine.put(k, map.get(k));
}
// Load manipulation script
try {
engine.eval(script);
} catch (ScriptException e) {
throw new RuntimeException("Unable to load manipulation script", e);
}
}
@Override
public double calculate(double parameter) {
logger.fine("Function called");
try {
logger.info("calculate( "+parameter+""+additionalParameter+" )");
return ((Double) engine.eval("calculate( "+parameter+""+additionalParameter+" )"));
} catch (ScriptException e) {
throw new RuntimeException("Calculating actuator step failed while executing the Jython script",e);
}
}
}
@@ -0,0 +1,93 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core.actors;
import ch.psi.fda.core.Actor;
import ch.psi.fda.core.Sensor;
/**
* Pseudo actor that is literally doing nothing for n times
*/
public class PseudoActuatorSensor implements Actor, Sensor {
/**
* Execution count of actuator.
*/
private int count;
/**
* Number of counts for this actuator
*/
private final int counts;
private final String id;
/**
* @param counts
* @param id Id of the Actor/Sensor
*/
public PseudoActuatorSensor(String id, int counts){
if(counts < 1){
throw new IllegalArgumentException("Count ["+counts+"] must be > 0");
}
this.id = id;
this.counts = counts;
init();
}
@Override
public void set() {
if(!hasNext()){
throw new IllegalStateException("The actuator does not have any next step.");
}
count++;
}
@Override
public boolean hasNext() {
return (count<counts);
}
@Override
public void init() {
count=0;
}
@Override
public void reverse() {
}
@Override
public void reset() {
}
@Override
public Object read() {
return new Double(count); // Return actual count
}
@Override
public String getId() {
return this.id;
}
}
@@ -0,0 +1,89 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core.guard;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;
import ch.psi.fda.core.Guard;
import ch.psi.jcae.ChannelException;
/**
* Guard checking channels to meet a certain condition
*/
public class ChannelAccessGuard implements Guard {
private static Logger logger = Logger.getLogger(ChannelAccessGuard.class.getName());
/**
* Flag to indicate whether a guard condition failed since the last init call
* true: all conditions met, false: at least one condition failed
*/
private volatile boolean check = true;
private final List<ChannelAccessGuardCondition<?>> conditions;
public ChannelAccessGuard(List<ChannelAccessGuardCondition<?>> conditions){
this.conditions = conditions;
for(final ChannelAccessGuardCondition<?> condition: conditions){
condition.getChannel().addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if(! evt.getNewValue().equals(condition.getValue())){
check=false;
}
}
});
}
}
@Override
public void init() {
check = true;
// Check one time if all conditions are met
for(ChannelAccessGuardCondition<?> condition: conditions){
try{
if(! (condition.getChannel().getValue(true)).equals(condition.getValue()) ){
check=false;
break;
}
}
catch (InterruptedException e) {
throw new RuntimeException("Guard interrupted ",e);
} catch (TimeoutException | ChannelException | ExecutionException e) {
logger.log(Level.WARNING, "Unable ", e);
check=false;
}
}
}
@Override
public boolean check() {
return check;
}
}
@@ -0,0 +1,41 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core.guard;
import ch.psi.jcae.Channel;
public class ChannelAccessGuardCondition<T> {
private final Channel<T> channel;
private final T value; // Value of the channel to meet condition
public ChannelAccessGuardCondition(Channel<T> channel, T value){
this.channel = channel;
this.value = value;
}
public Channel<T> getChannel() {
return channel;
}
public T getValue() {
return value;
}
}
@@ -0,0 +1,436 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core.loops;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.logging.Logger;
import com.google.common.eventbus.EventBus;
import ch.psi.fda.core.Action;
import ch.psi.fda.core.ActionLoop;
import ch.psi.fda.core.Actor;
import ch.psi.fda.core.ActorSetCallable;
import ch.psi.fda.core.Guard;
import ch.psi.fda.core.Sensor;
import ch.psi.fda.messages.DataMessage;
import ch.psi.fda.messages.EndOfStreamMessage;
import ch.psi.fda.messages.Metadata;
/**
* Loop of actions to accomplish a task or test.
*/
public class ActorSensorLoop implements ActionLoop {
private static Logger logger = Logger.getLogger(ActorSensorLoop.class.getName());
/**
* Flag to indicate whether the data of this loop will be grouped
* According to this flag the dataGroup flag in EndOfStream will be set.
*/
private boolean dataGroup = false;
/**
* List of additional action loops that are executed at the end of each
* iteration of the loop
*/
private List<ActionLoop> actionLoops;
/**
* List of actions that are executed at the beginning of the loop.
*/
private List<Action> preActions;
/**
* List of actions that are executed at the end of the loop.
*/
private List<Action> postActions;
/**
* List of actions that are executed before the actors are set
*/
private List<Action> preActorActions;
/**
* List of actions that are executed after all actors have been set
*/
private List<Action> postActorActions;
/**
* List of actions that are executed before the sensors are read out
*/
private List<Action> preSensorActions;
/**
* List of actions that are executed after all sensors have been read out
*/
private List<Action> postSensorActions;
/**
* List of actors of this loop
*/
private List<Actor> actors;
/**
* List of sensors of this loop
*/
private List<Sensor> sensors;
/**
* Guard used to check whether the environment was ok while reading out the sensors
*/
private Guard guard = null;
private volatile boolean loop = false;
private volatile boolean abort = false;
private final boolean zigZag;
private List<ActorSetCallable> pactors;
private EventBus eventBus = new EventBus();
public ActorSensorLoop(){
this(false);
}
public ActorSensorLoop(boolean zigZag){
this.zigZag = zigZag;
this.actionLoops = new ArrayList<ActionLoop>();
this.preActions = new ArrayList<Action>();
this.postActions = new ArrayList<Action>();
this.preActorActions = new ArrayList<Action>();
this.postActorActions = new ArrayList<Action>();
this.preSensorActions = new ArrayList<Action>();
this.postSensorActions = new ArrayList<Action>();
this.actors = new ArrayList<Actor>();
this.pactors = new ArrayList<ActorSetCallable>();
this.sensors = new ArrayList<Sensor>();
}
/**
* Executes the actor sensor loop. The actor sensor loop is build up as follows:
* preActions
* loop{
* check actors - abort if there are no new steps
* pre actor actions
* set actors
* post actor actions
* pre sensor actions
* read sensors
* post sensor actions
* execute additional registered action loops
* }
* postActions
* @throws InterruptedException
*/
@Override
public void execute() throws InterruptedException {
abort = false;
List<Metadata> metadata = new ArrayList<>();
// Build up data message metadata based on the sensors currently registered.
for(Sensor s: sensors){
metadata.add(new Metadata(s.getId()));
}
/**
* Thread pool for parallel execution of tasks
*/
ExecutorService executorService = Executors.newCachedThreadPool();
loop = true;
// Execute pre actions
for(Action action: preActions){
action.execute();
}
// Initialize actors of Loop
for(Actor actor: actors){
actor.init();
}
// Variable to store the last guard status
boolean guardOK=true;
try{
// Execute loop logic
while(loop){
if(guardOK){
// If actors are defined for the loop check whether all of them
// have a next step defined if there is no actor defined only run this loop once
if(actors.size()>0){
// Check whether the actors of this loop have a next step. If not
// abort the loop
boolean hasNext = true;
for(Actor actor: actors){
if(!actor.hasNext()){
hasNext=false;
break; // Stop actor check loop
}
}
// If not all actors have a next step abort the loop
if(!hasNext){
break; // Stop action loop
}
}
else{
// No actors defined, only run loop once
loop = false;
}
// Execute pre actor actions
for(Action action: preActorActions){
action.execute();
}
if(abort){ // End loop if abort was issued
break;
}
// Set actors
// for(Actor actor: actors){
// actor.set();
// }
// Parallel set of the actors
try {
for (Future<Object> f : executorService.invokeAll(pactors)) {
f.get(); //Blocks until the async set() is finished
}
} catch (ExecutionException e) {
throw new RuntimeException("Setting the actors failed",e);
}
// Execute post actor actions
for(Action action: postActorActions){
action.execute();
}
if(abort){ // End loop if abort was issued
break;
}
}
if(guard!=null){
// Initialize guard
guard.init();
guardOK=guard.check();
// Wait until guard is ok
while(!guardOK){
logger.info("Waiting for guard condition(s) to be met");
// Sleep 100 milliseconds before next check
Thread.sleep(1000);
// Check whether the loop is not aborted, if it is aborted
// break the wait loop (afterwards also the loop loop is aborted)
if(!loop){
break;
}
guard.init();
guardOK=guard.check();
}
// If loop is aborted proceed to next iteration an abort loop
if(!loop){
continue;
}
}
// Execute pre sensor actions
for(Action action: preSensorActions){
action.execute();
}
if(abort){ // End loop if abort was issued
break;
}
// Read sensors
DataMessage message = new DataMessage(metadata);
for(Sensor sensor: sensors){
// Readout sensor
Object o = sensor.read();
// Add sensor data item to message
message.getData().add(o);
}
// Execute post sensor actions
for(Action action: postSensorActions){
action.execute();
}
if(abort){ // End loop if abort was issued
break;
}
// Check guard if one is registered
if(guard!=null){
guardOK=guard.check();
}
if(guardOK){
// Post a message with the sensor data
eventBus.post(message);
// Loop all configured ActionLoop objects
for(ActionLoop actionLoop: actionLoops){
actionLoop.execute();
}
}
}
// Execute post actions
for(Action action: postActions){
action.execute();
}
}
finally{
// Ensure that data stream is terminated ...
// Issue end of loop control message
// Set iflag of the EndOfStreamMessage according to dataGroup flag of this loop
eventBus.post(new EndOfStreamMessage(dataGroup));
}
if(zigZag){
// Reverse actors for the next run
for(Actor actor: actors){
actor.reverse();
}
}
executorService.shutdownNow();
}
@Override
public void abort(){
abort = true;
loop = false;
// To abort all wait actions interrupt this thread
for(Action a: preSensorActions){
a.abort();
}
// Recursively abort all registered action loops
for(ActionLoop actionLoop: actionLoops){
actionLoop.abort();
}
}
@Override
public void prepare() {
// Create callable for all actors
pactors.clear();
for(final Actor a: actors){
pactors.add(new ActorSetCallable(a));
}
// Reset all actuators
for(Actor a: actors){
a.reset();
}
// Recursively call prepare() method of all registered action loops
for(ActionLoop actionLoop: actionLoops){
actionLoop.prepare();
}
}
@Override
public void cleanup() {
// Recursively call cleanup() method of all registered action loops
for(ActionLoop actionLoop: actionLoops){
actionLoop.cleanup();
}
}
@Override
public List<Action> getPreActions() {
return preActions;
}
@Override
public List<Action> getPostActions() {
return postActions;
}
@Override
public EventBus getEventBus(){
return eventBus;
}
public List<ActionLoop> getActionLoops() {
return actionLoops;
}
public List<Action> getPreActorActions() {
return preActorActions;
}
public List<Action> getPostActorActions() {
return postActorActions;
}
public List<Action> getPreSensorActions() {
return preSensorActions;
}
public List<Action> getPostSensorActions() {
return postSensorActions;
}
public List<Actor> getActors() {
return actors;
}
public List<Sensor> getSensors() {
return sensors;
}
public Guard getGuard() {
return guard;
}
public void setGuard(Guard guard) {
this.guard = guard;
}
public boolean isDataGroup() {
return dataGroup;
}
public void setDataGroup(boolean dataGroup) {
this.dataGroup = dataGroup;
}
}
@@ -0,0 +1,46 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core.loops.cr;
/**
* Filter calculating the delta of two subsequent values
*/
public class CrlogicDeltaDataFilter {
private Double lastValue = null;
public void reset(){
lastValue = null;
}
public Double delta(Double value){
Double lvalue = lastValue;
lastValue = value;
if(lvalue==null){
return Double.NaN;
}
else{
// Return delta
return(value-lvalue);
}
}
}
@@ -0,0 +1,723 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core.loops.cr;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.logging.Logger;
import org.zeromq.ZMQ;
import org.zeromq.ZMQ.Context;
import org.zeromq.ZMQ.Socket;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.eventbus.EventBus;
import ch.psi.fda.core.Action;
import ch.psi.fda.core.ActionLoop;
import ch.psi.fda.messages.DataMessage;
import ch.psi.fda.messages.EndOfStreamMessage;
import ch.psi.fda.messages.Metadata;
import ch.psi.jcae.ChannelException;
import ch.psi.jcae.ChannelService;
/**
* While using Crlogic the IOC system clock rate should/must be set to 1000 (default 60)
*
* sysClkRateSet 1000
*
*/
public class CrlogicLoopStream implements ActionLoop {
private static final Logger logger = Logger.getLogger(CrlogicLoopStream.class.getName());
public static final int HIGH_WATER_MARK = 100;
private Context context;
private Socket socket;
private ObjectMapper mapper = new ObjectMapper();
/**
* Flag to indicate whether the data of this loop will be grouped
* According to this flag the dataGroup flag in EndOfStream will be set.
*/
private boolean dataGroup = false;
// Default timeout (in milliseconds) for wait operations
private long startStopTimeout = 8000;
private String ioc;
/**
* Flag whether the actor of this loop should move in zig zag mode
*/
private final boolean zigZag;
private boolean useReadback;
private boolean useEncoder;
private List<Action> preActions;
private List<Action> postActions;
private List<CrlogicResource> sensors;
// Prefix for the CRLOGIC channels
private final String prefix;
private TemplateCrlogic template;
private TemplateMotor motortemplate;
private List<String> readoutResources;
private Map<Integer, CrlogicDeltaDataFilter> scalerIndices;
private CrlogicRangeDataFilter crlogicDataFilter;
private boolean abort = false;
private final ChannelService cservice;
private String id;
private String name; // name of the motor channel
private String readback; // name of the encoder channel
private double start;
private double end;
private double stepSize;
private double integrationTime;
private double additionalBacklash;
private final EventBus eventbus;
private List<Metadata> metadata;
/**
* Constructor
* @param cservice Channel Access context
* @param prefix Prefix of the IOC channels
* @param ioc Name of the IOC running CRLOGIC
* @param zigZag Do zigZag scan
*/
public CrlogicLoopStream(ChannelService cservice, String prefix, String ioc, boolean zigZag){
eventbus = new EventBus();
this.cservice = cservice;
this.prefix = prefix;
this.ioc = ioc;
this.zigZag = zigZag;
// Initialize lists used by the loop
this.preActions = new ArrayList<Action>();
this.postActions = new ArrayList<Action>();
this.sensors = new ArrayList<>();
this.readoutResources = new ArrayList<String>();
this.scalerIndices = new HashMap<Integer, CrlogicDeltaDataFilter>();
this.crlogicDataFilter = new CrlogicRangeDataFilter();
}
public void setActuator(String id, String name, String readback, double start, double end, double stepSize, double integrationTime, double additionalBacklash){
this.id = id;
this.name = name;
this.readback = readback;
this.start = start;
this.end = end;
this.stepSize = stepSize;
this.integrationTime = integrationTime;
this.additionalBacklash = additionalBacklash;
}
/**
* Receive a ZMQ message
*/
private void receive() {
MainHeader mainHeader;
try {
byte[] bytes = socket.recv(ZMQ.NOBLOCK);
if(bytes == null){
// There is no message hanging
return;
}
mainHeader = mapper.readValue(bytes, MainHeader.class);
} catch (IOException e) {
throw new RuntimeException("Unable to decode main header", e);
}
if(!socket.hasReceiveMore()){
throw new RuntimeException("There is no data submessage");
}
byte[] bytes = socket.recv();
ByteBuffer byteBuffer = ByteBuffer.wrap(bytes);
DataMessage message = new DataMessage(metadata);
boolean use = true;
for(int i=0; i<mainHeader.getElements(); i++){
Double raw = byteBuffer.getDouble();
Double val;
if(i==0){
if(useEncoder){
val = crlogicDataFilter.calculatePositionMotorUseEncoder(raw);
}
else if(useReadback){
val = crlogicDataFilter.calculatePositionMotorUseReadback(raw);
}
else{
val = crlogicDataFilter.calculatePositionMotor(raw);
}
// Check whether data is within the configured range - otherwise drop data
use = crlogicDataFilter.filter(val);
}
else if(scalerIndices.containsKey(i)){
CrlogicDeltaDataFilter f = scalerIndices.get(i);
val = f.delta(raw);
}
else{
val = raw;
}
message.getData().add(val);
}
// Drain remaining messages
int n = drainHangingSubmessages();
if(n>1){
throw new RuntimeException("More than 1 message drained from stream: "+n);
}
if(use){
eventbus.post(message);
}
}
@Override
public void execute() throws InterruptedException {
try{
// Set values for the datafilter
crlogicDataFilter.setStart(start);
crlogicDataFilter.setEnd(end);
// Reset data filter
for(Integer k: scalerIndices.keySet()){
scalerIndices.get(k).reset();
}
// Set abort state to false
abort = false;
Long timeout = 600000l; // 10 minutes move timeout
// Check if logic is inactive, otherwise return early
if(!template.getStatus().getValue().equals(TemplateCrlogic.Status.INACTIVE.toString())){
logger.info("CRLOGIC is not inactive!");
// TODO Decide what to do in this situation
if(template.getStatus().getValue().equals(TemplateCrlogic.Status.FAULT.toString())){
// If in fault show message and recover
logger.info("CRLOGIC in FAULT state");
logger.info("Error message: "+template.getMessage().getValue());
logger.info("Recover logic and set it to INACTIVE");
template.getStatus().setValue(TemplateCrlogic.Status.INACTIVE.toString());
}
else if(template.getStatus().getValue().equals(TemplateCrlogic.Status.ACTIVE.toString())){
template.getStatus().setValue(TemplateCrlogic.Status.STOP.toString());
template.getStatus().waitForValue(TemplateCrlogic.Status.INACTIVE.toString(), startStopTimeout);
}
else{
throw new RuntimeException("CRLOGIC is not inactive");
}
}
logger.info("Set parameters");
template.getNfsServer().setValue("");
template.getNfsShare().setValue("");
template.getDataFile().setValue("");
int tps = template.getTicksPerSecond().getValue();
logger.info("Ticks per second: "+tps);
logger.info("Set readout resources");
template.getReadoutResources().setValue(readoutResources.toArray(new String[readoutResources.size()]));
// Set ticks between interrupt to integration time
int ticks = (int)(tps*integrationTime);
template.getTicksBetweenInterrupts().setValue(ticks);
// Prepare motor
double totalTimeSeconds = Math.abs((end-start)/stepSize*integrationTime);
int hours = (int) Math.floor(totalTimeSeconds/60/60);
int minutes = (int) Math.floor(totalTimeSeconds/60-hours*60);
int seconds = (int) Math.floor(totalTimeSeconds-hours*60*60-minutes*60);
logger.info("Estimated time: "+hours+":"+minutes+":"+seconds);
int direction = 1;
if(end-start<0){
direction = -1;
}
double motorBaseSpeed = motortemplate.getBaseSpeed().getValue();
double motorHighLimit = motortemplate.getHighLimit().getValue();
double motorLowLimit = motortemplate.getLowLimit().getValue();
double motorBacklash = motortemplate.getBacklashDistance().getValue();
boolean respectMotorMinSpeed = false; // if false set min speed to 0
double motorMinSpeed = 0;
if(respectMotorMinSpeed){
motorMinSpeed = motorBaseSpeed;
}
// Check user parameters
// TODO start and end values must be between the motor high and low value - otherwise fail
if(start>motorHighLimit || start<motorLowLimit){
// Start value is outside motor high and/or low value
logger.info("Start value is outside motor high and/or low value");
throw new IllegalArgumentException("Start value is outside motor high and/or low value");
}
if(end>motorHighLimit || end<motorLowLimit){
// End value is outside motor high and/or low value
logger.info("End value is outside motor high and/or low value");
throw new IllegalArgumentException("End value is outside motor high and/or low value");
}
// TODO Check minimum step size
int minimumTicks = 10;
double minStepSize = motorMinSpeed*(minimumTicks/tps);
if(stepSize<minStepSize){
// Step size is too small
logger.info("Step size is too small");
throw new IllegalArgumentException("Step size is too small");
}
// TODO Check integration time
if(motorMinSpeed>0){
double maxIntegrationTime = stepSize/motorMinSpeed;
if(integrationTime>maxIntegrationTime){
logger.info("Integration time is too big");
// Integration time is too big
throw new IllegalArgumentException("Integration time is too big");
}
}
double motorMaxSpeed = motortemplate.getVelocity().getValue();
double minIntegrationTime = Math.min( (stepSize/motorMaxSpeed), ((double)minimumTicks/(double)tps) );
if(integrationTime<minIntegrationTime){
// Integration time is too small
logger.info("Integration time is too small [min integration time: "+minIntegrationTime+"]");
throw new IllegalArgumentException("Integration time is too small [min integration time: "+minIntegrationTime+"]");
}
// TODO Calculate and set motor speed, backlash, etc.
double motorSpeed = stepSize/integrationTime;
double backlash = (0.5*motorSpeed*motortemplate.getAccelerationTime().getValue())+motorBacklash+additionalBacklash;
double realEnd = end+(backlash*direction);
double realStart = start-(backlash*direction);
// Move to start
logger.info("Move motor to start ["+realStart+"]");
motortemplate.getSetValue().setValueAsync(realStart).get(timeout, TimeUnit.MILLISECONDS); // Will block until move is done
// Set motor paramters
// Backup settings
logger.info("Backup motor settings");
double backupSpeed = motortemplate.getVelocity().getValue();
double backupBacklash = motorBacklash;
double backupMinSpeed = motorBaseSpeed;
try{
// Set motor settings
logger.info("Update motor settings");
// if(!respectMotorMinSpeed){
// motortemplate.getBaseSpeed().setValue(0d);
// }
// Set base speed as fast as possible but not faster than the original base speed.
double base = motorBaseSpeed;
if(motorSpeed<base){
base = motorSpeed;
}
motortemplate.getBaseSpeed().setValue(base);
motortemplate.getVelocity().setValue(motorSpeed);
motortemplate.getBacklashDistance().setValue(0d);
// Execute pre actions
for(Action action: preActions){
action.execute();
}
// Start crlogic logic
logger.info("Start CRLOGIC");
template.getStatus().setValue(TemplateCrlogic.Status.INITIALIZE.toString());
try{
template.getStatus().waitForValue(TemplateCrlogic.Status.ACTIVE.toString(), startStopTimeout);
}
catch(ChannelException | ExecutionException | TimeoutException e){
logger.info( "Failed to start CRLOGIC. Logic in status: "+template.getStatus().getValue() );
if(template.getStatus().getValue().equals(TemplateCrlogic.Status.FAULT.toString())){
logger.info("Error message: "+template.getMessage().getValue());
}
// Recover to inactive
template.getStatus().setValue(TemplateCrlogic.Status.INACTIVE.toString());
// TODO Improve error handling
throw new RuntimeException("Failed to start CRLOGIC. Logic in status: "+template.getStatus().getValue()+ " Error message: "+template.getMessage().getValue(), e);
}
// Connect ZMQ stream
connect(ioc);
// drainHangingSubmessages();
// Move motor(s) to end / wait until motor is stopped
logger.info("Move motor to end ["+realEnd+"]");
try{
// This is the continuous motor move
Future<Double> future = motortemplate.getSetValue().setValueAsync(realEnd);
long timeoutTime = System.currentTimeMillis()+timeout;
// This loop will keep spinning until the motor reached the final position
while(!future.isDone()){
if(System.currentTimeMillis()>timeoutTime){
throw new TimeoutException("Motion timed out");
}
receive(); // TODO Problem still blocking
if(abort){
// Abort motor move
motortemplate.getCommand().setValue(TemplateMotor.Commands.Stop.ordinal());
motortemplate.getCommand().setValue(TemplateMotor.Commands.Go.ordinal());
break;
}
}
}
finally {
receive();
// Send end of stream message
logger.info("Sending - End of Line - Data Group: "+ dataGroup);
eventbus.post(new EndOfStreamMessage(dataGroup));
// Close ZMQ stream
close();
}
logger.info("Motor reached end position");
// Stop crlogic logic
logger.info("Stop CRLOGIC");
template.getStatus().setValue(TemplateCrlogic.Status.STOP.toString());
// Wait until stopped
logger.info("Wait until stopped");
try{
template.getStatus().waitForValue(TemplateCrlogic.Status.INACTIVE.toString(), startStopTimeout);
}
catch(ChannelException | ExecutionException | TimeoutException e){
logger.info( "Failed to stop CRLOGIC. Logic in status: "+template.getStatus().getValue() );
// TODO Improve error handling
throw new RuntimeException("Failed to stop CRLOGIC. Logic in status: "+template.getStatus().getValue(), e);
}
logger.info("CRLOGIC is now stopped");
// Execute post actions
for(Action action: postActions){
action.execute();
}
}
finally{
logger.info("Restore motor settings");
motortemplate.getBaseSpeed().setValue(backupMinSpeed);
motortemplate.getVelocity().setValue(backupSpeed);
motortemplate.getBacklashDistance().setValue(backupBacklash);
}
if(zigZag){
// reverse start/end
double aend = end;
end=start;
start=aend;
}
}
catch(ChannelException | ExecutionException | TimeoutException e){
throw new RuntimeException("Unable to execute crloop", e);
}
}
/**
* Abort execution
*/
@Override
public void abort() {
abort = true;
}
/**
* Prepare this loop for execution
*/
@Override
public void prepare() {
metadata = new ArrayList<>();
// Build up metadata
metadata.add(new Metadata(this.id));
for(CrlogicResource s: sensors){
metadata.add(new Metadata(s.getId()));
}
try{
// Connect crlogic channels
template = new TemplateCrlogic();
logger.info("Connect channels");
Map<String,String> map = new HashMap<>();
map.put("PREFIX", prefix);
cservice.createAnnotatedChannels(template, map);
// Connect motor channels
motortemplate = new TemplateMotor();
map = new HashMap<>();
map.put("PREFIX", this.name);
cservice.createAnnotatedChannels(motortemplate, map);
useReadback = motortemplate.getUseReadback().getValue();
useEncoder = motortemplate.getUseEncoder().getValue();
logger.info("Motor type: "+ TemplateMotor.Type.values()[motortemplate.getType().getValue()]);
logger.info("Motor use readback: "+useReadback);
logger.info("Motor use encoder: "+useEncoder);
// TODO build up list of readout resources (based on sensors)
readoutResources.clear();
// first sensor is the actuator
// Determine mode of motor
if((!useReadback) && (!useEncoder)){
// Open loop
if(this.readback!=null){
throw new IllegalArgumentException("Readback not supported if motor is configured in open loop");
}
else{
readoutResources.add(this.name);
}
}
else if(useReadback && (!useEncoder)){
String readback;
// use readback link
if(this.readback!=null){
// Use specified readback
readback = this.readback;
}
else{
// Set resouce to readback link
readback = (motortemplate.getReadbackLink().getValue());
readback = readback.replaceAll(" +.*", ""); // remove NPP etc at the end
}
readoutResources.add(readback);
// Fill readback encoder settings
// Connect to encoder
TemplateEncoder encodertemplate = new TemplateEncoder();
map = new HashMap<>();
map.put("PREFIX", readback);
cservice.createAnnotatedChannels(encodertemplate, map);
// Read encoder settings
if(encodertemplate.getDirection().getValue()==TemplateEncoder.Direction.Positive.ordinal()){
crlogicDataFilter.setEncoderDirection(1);
}
else{
crlogicDataFilter.setEncoderDirection(-1);
}
crlogicDataFilter.setEncoderOffset(encodertemplate.getOffset().getValue());
crlogicDataFilter.setEncoderResolution(encodertemplate.getResolution().getValue());
// Disconnect from encoder
cservice.destroyAnnotatedChannels(encodertemplate);
}
else if (useEncoder && (!useReadback)){
// use readback link
if(this.readback!=null){
throw new IllegalArgumentException("Readback not supported if motor is configured to use encoder");
}
else{
// Set resouce to readback link
readoutResources.add(this.name+"_ENC");
}
}
else{
throw new IllegalArgumentException("Motor configuration not supportet: use readback - "+useReadback+" use encoder - "+useEncoder);
}
// Fill Motor specific settings
if(motortemplate.getDirection().getValue()==TemplateMotor.Direction.Positive.ordinal()){
crlogicDataFilter.setMotorDirection(1);
}
else{
crlogicDataFilter.setMotorDirection(-1);
}
crlogicDataFilter.setMotorEncoderResolution(motortemplate.getEncoderResolution().getValue());
crlogicDataFilter.setMotorOffset(motortemplate.getOffset().getValue());
crlogicDataFilter.setMotorReadbackResolution(motortemplate.getReadbackResolution().getValue());
crlogicDataFilter.setMotorResolution(motortemplate.getMotorResolution().getValue());
// Clear all indices
scalerIndices.clear();
int c = 1; // We start at 1 because the actuator right now is an implicit sensor
for(CrlogicResource s: sensors){
readoutResources.add(s.getKey());
if(s.isDelta()){
scalerIndices.put(c, new CrlogicDeltaDataFilter());
}
c++;
}
// Workaround - somehow one has to add an empty thing to the value otherwise the c logic
// does not pick up the end
readoutResources.add("");
}
catch(Exception e){
throw new RuntimeException("Unable to prepare crloop: ",e);
}
}
/**
* Connect ZMQ stream
* @param address ZMQ endpoint address
*/
private void connect(String address) {
// Clear interrupted state
Thread.interrupted();
logger.info("Connecting with IOC"+address);
context = ZMQ.context(1);
socket = context.socket(ZMQ.PULL);
socket.setRcvHWM(HIGH_WATER_MARK);
socket.connect("tcp://"+address+":9999");
}
/**
* Close ZMQ stream
*/
private void close() {
logger.info("Closing stream from IOC "+ioc);
socket.close();
context.close();
socket = null;
context = null;
}
/**
* Drain sub-messages
* @return Number of sub-messages drained
*/
private int drainHangingSubmessages() {
int count = 0;
while (socket.hasReceiveMore()) {
// is there a way to avoid copying data to user space here?
socket.recv();
count++;
}
if(count>0){
logger.info("Drain hanging submessages");
}
return count;
}
/**
* Cleanup loop
*/
@Override
public void cleanup() {
logger.info("Cleanup");
try {
cservice.destroyAnnotatedChannels(template);
cservice.destroyAnnotatedChannels(motortemplate);
template = null;
motortemplate = null;
} catch (Exception e) {
throw new RuntimeException("Unable to destroy Channel Access channels", e);
}
}
@Override
public List<Action> getPreActions() {
return preActions;
}
@Override
public List<Action> getPostActions() {
return postActions;
}
public List<CrlogicResource> getSensors() {
return sensors;
}
@Override
public boolean isDataGroup() {
return dataGroup;
}
@Override
public void setDataGroup(boolean dataGroup) {
this.dataGroup = dataGroup;
}
public EventBus getEventBus(){
return eventbus;
}
}
@@ -0,0 +1,270 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core.loops.cr;
public class CrlogicRangeDataFilter {
private double motorResolution = 1;
private int motorDirection = 1;
private double motorOffset = 0;
private double motorReadbackResolution = 1;
private double motorEncoderResolution = 1;
private double encoderOffset = 0;
private double encoderResolution = 1;
private int encoderDirection = 1;
private double start = 0;
private double end = 0;
private boolean positive = true;
/**
* Marker whether the value was equal
*/
private boolean wasEqualBefore = false;
/**
* @return the motorResolution
*/
public double getMotorResolution() {
return motorResolution;
}
/**
* @param motorResolution the motorResolution to set
*/
public void setMotorResolution(double motorResolution) {
this.motorResolution = motorResolution;
}
/**
* @return the motorDirection
*/
public int getMotorDirection() {
return motorDirection;
}
/**
* @param motorDirection the motorDirection to set
*/
public void setMotorDirection(int motorDirection) {
this.motorDirection = motorDirection;
}
/**
* @return the motorOffset
*/
public double getMotorOffset() {
return motorOffset;
}
/**
* @param motorOffset the motorOffset to set
*/
public void setMotorOffset(double motorOffset) {
this.motorOffset = motorOffset;
}
/**
* @return the motorReadbackResolution
*/
public double getMotorReadbackResolution() {
return motorReadbackResolution;
}
/**
* @param motorReadbackResolution the motorReadbackResolution to set
*/
public void setMotorReadbackResolution(double motorReadbackResolution) {
this.motorReadbackResolution = motorReadbackResolution;
}
/**
* @return the motorEncoderResolution
*/
public double getMotorEncoderResolution() {
return motorEncoderResolution;
}
/**
* @param motorEncoderResolution the motorEncoderResolution to set
*/
public void setMotorEncoderResolution(double motorEncoderResolution) {
this.motorEncoderResolution = motorEncoderResolution;
}
/**
* @return the encoderOffset
*/
public double getEncoderOffset() {
return encoderOffset;
}
/**
* @param encoderOffset the encoderOffset to set
*/
public void setEncoderOffset(double encoderOffset) {
this.encoderOffset = encoderOffset;
}
/**
* @return the encoderResolution
*/
public double getEncoderResolution() {
return encoderResolution;
}
/**
* @param encoderResolution the encoderResolution to set
*/
public void setEncoderResolution(double encoderResolution) {
this.encoderResolution = encoderResolution;
}
/**
* @return the encoderDirection
*/
public int getEncoderDirection() {
return encoderDirection;
}
/**
* @param encoderDirection the encoderDirection to set
*/
public void setEncoderDirection(int encoderDirection) {
this.encoderDirection = encoderDirection;
}
/**
* @return the start
*/
public double getStart() {
return start;
}
/**
* @param start the start to set
*/
public void setStart(double start) {
this.start = start;
if(start<=end){
positive=true;
}
else{
positive=false;
}
}
/**
* @return the end
*/
public double getEnd() {
return end;
}
/**
* @param end the end to set
*/
public void setEnd(double end) {
this.end = end;
if(end>=start){
positive=true;
}
else{
positive=false;
}
}
/**
* Calculate real position
* @param raw
* @return
*/
public double calculatePositionMotor(double raw){
return(((raw * motorResolution * motorReadbackResolution)/motorDirection)+motorOffset);
}
/**
* Calculate real motor position using the readback link
* @param raw
* @return
*/
public double calculatePositionMotorUseReadback(double raw){
return((((raw - encoderOffset) * encoderResolution * encoderDirection * motorReadbackResolution) / motorDirection) + motorOffset);
}
/**
* Calculate real motor position using encoder
* @param raw
* @return
*/
public double calculatePositionMotorUseEncoder(double raw){
return(raw * motorEncoderResolution * motorReadbackResolution/motorDirection + motorOffset);
}
/**
* Filter whether value is with the range
* @param value
* @return
*/
public boolean filter(Double value){
if(positive){
if(start<=value && value<=end){
// If motor is very accurete and user backlash==null it might be that value is exactly
// the end value. To prevent that unnecessary data is captured execute this check
if(wasEqualBefore){
wasEqualBefore=false; // Reset flag
return false;
}
// Check whether was equal
if(value==end){
wasEqualBefore=true;
}
return true;
}
else{
return false;
}
}
else{
if(end<=value && value <=start){
if(wasEqualBefore){
wasEqualBefore=false; // Reset flag
return false;
}
// Check whether was equal
if(value==start){
wasEqualBefore=true;
}
return true;
}
else{
return false;
}
}
}
}
@@ -0,0 +1,71 @@
/**
*
* Copyright 2013 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This code is distributed in the hope that it will be useful, but without any
* warranty; without even the implied warranty of merchantability or fitness for
* a particular purpose. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core.loops.cr;
/**
* Readout resource of crlogic
*/
public class CrlogicResource {
/**
* Id of the data
*/
private String id;
/**
* Id of the crlogic resource to be read out. As configured in the CRLOGIC part of the IOC startup script
*/
private String key;
/**
* Flag whether to read back the delta of the values of this resource. (delta is done in software)
*/
private boolean delta = false;
public CrlogicResource(){
}
public CrlogicResource(String id, String key){
this.id = id;
this.key = key;
}
public CrlogicResource(String id, String key, boolean delta){
this.id = id;
this.key = key;
this.delta = delta;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public boolean isDelta() {
return delta;
}
public void setDelta(boolean delta) {
this.delta = delta;
}
}
@@ -0,0 +1,23 @@
package ch.psi.fda.core.loops.cr;
/**
* Header of the ZMQ message
*/
public class MainHeader {
private String htype;
private int elements;
public void setElements(int elements) {
this.elements = elements;
}
public int getElements() {
return elements;
}
public void setHtype(String htype) {
this.htype = htype;
}
public String getHtype() {
return htype;
}
}
@@ -0,0 +1,209 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core.loops.cr;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import ch.psi.fda.messages.DataMessage;
import ch.psi.fda.messages.EndOfStreamMessage;
import ch.psi.fda.messages.Message;
import ch.psi.fda.messages.Metadata;
/**
* Class to merge two data streams into one. The secondary queues data is added to the primary queues data.
* The resulting queue therefor will hold the same amount of elements as the primary queue does.
* The datagroup flag set in the end of stream message will be set according to the flag set in the primary queue.
*/
public class MergeLogic {
private static final Logger logger = Logger.getLogger(MergeLogic.class.getName());
private final EventBus primaryEventBus;
private final EventBus secondaryEventBus;
private final EventBus outputEventBus;
private List<Metadata> metadata;
// Handler of the secondary event bus. We need to keep the reference on this
// to be able to unregister ...
private Object handler;
boolean firstPrimary = true;
boolean firstSecondary = true;
private BlockingQueue<DataMessage> queue = new LinkedBlockingQueue<>();
private List<Object> currentData = null;
public MergeLogic(EventBus primaryEventBus, EventBus secondaryEventBus, EventBus outputEventBus){
this.primaryEventBus = primaryEventBus;
this.secondaryEventBus = secondaryEventBus;
this.outputEventBus = outputEventBus;
}
// Enables the merge logic
public void enable(){
firstPrimary = true;
firstSecondary = true;
queue.clear();
handler = new Object(){
@Subscribe
public void onMessage(DataMessage m){
// Only queue data messages
queue.add(m);
}
};
primaryEventBus.register(this);
secondaryEventBus.register(handler);
}
// Disables the merge logic
public void disable(){
primaryEventBus.unregister(this);
secondaryEventBus.unregister(handler);
queue.clear();
}
/**
* This is the master merging logic
* @param m Data message from primary event bus
*/
@Subscribe
public void onMessage(Message rawMessage){
if(rawMessage instanceof EndOfStreamMessage){
outputEventBus.post(rawMessage);
// Clear queue of secondary event bus
queue.clear();
return;
}
else if(!(rawMessage instanceof DataMessage)){
logger.warning("Message type not supported - ignore");
return;
}
DataMessage message = (DataMessage) rawMessage;
// Hack to remove the synchronization timestamp from the primary event bus
if(firstPrimary){
firstPrimary = false;
metadata = new ArrayList<>();
List<Metadata> pqm = message.getMetadata();
metadata.add(pqm.get(0)); // add first component (this is the actuator)
// Skip the next component as this is the timestamp used to merge the data
for(int i=2;i<pqm.size();i++){
metadata.add(pqm.get(i));
}
}
// Merge logic
// Get and remove merge timestamp from the data of the message
Double timestamp = (Double) message.getData().remove(1);
long milliseconds = (long) (timestamp*1000);
long nanoOffset = (long)((timestamp*1000-milliseconds)*1000000);
int count = 10;
while(true){ // TODO Wait only for a limited amount of time ...
// Assumption: the secondary Queue holds at least the data up to the timestamp of the message handled by this function
DataMessage msCheck = queue.peek();
// No data is available need to wait for some time
// If still no data is available, throw an exception
if(msCheck == null){
if(count==0){
throw new RuntimeException("No data comming in from SCR channels");
}
if(currentData == null){
// Need to wait until data comes in ... (this case is for the beginning only)
logger.info("No data available");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
logger.log(Level.INFO, "", e);
}
count--;
continue;
}
else{
break;
}
}
// If we handle the first message of the secondary event bus, we copy its metadata
if(firstSecondary){
firstSecondary = false;
List<Metadata> sqm = msCheck.getMetadata();
// Skip first two components of the message as this is the timestamp
for(int i=2;i<sqm.size();i++){
metadata.add(sqm.get(i));
}
}
// Check whether timestamp of the next message is bigger than the timestamp of the
// message from the primary queue - if the timestamp is bigger do not take message out of the queue
long currMilliCheck = ((Double) msCheck.getData().get(0)).longValue();
long currNanoCheck = ((Double) msCheck.getData().get(1)).longValue();
if(currMilliCheck>milliseconds || (currMilliCheck==milliseconds && currNanoCheck>nanoOffset)){
break;
}
try {
DataMessage currentDataMessage = queue.take();
currentData = currentDataMessage.getData();
// Remove timestamps used for synchronization
currentData.remove(0);
currentData.remove(0);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
if(currentData == null){
throw new RuntimeException("No SCR data arrived");
}
// Add data to primary data queue message and put it into the out queue
message.getData().addAll(currentData);
message.setMetadata(metadata);
outputEventBus.post(message);
}
}
@@ -0,0 +1,211 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core.loops.cr;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.google.common.eventbus.EventBus;
import ch.psi.fda.core.Action;
import ch.psi.fda.core.ActionLoop;
public class ParallelCrlogic implements ActionLoop {
private static final Logger logger = Logger.getLogger(ParallelCrlogic.class.getName());
/**
* Flag to indicate whether the data of this loop will be grouped
* According to this flag the dataGroup flag in EndOfStream will be set.
*/
private boolean dataGroup = false;
private CrlogicLoopStream crlogic;
private ScrlogicLoop scrlogic;
/**
* List of actions that are executed at the beginning of the loop.
*/
private List<Action> preActions;
/**
* List of actions that are executed at the end of the loop.
*/
private List<Action> postActions;
private MergeLogic mergeLogic;
private final EventBus eventbus;
public ParallelCrlogic(CrlogicLoopStream crlogic, ScrlogicLoop scrlogic){
if(crlogic==null){
throw new IllegalArgumentException("No Crloop specified");
}
if(scrlogic==null){
throw new IllegalArgumentException("No Scrloop specified");
}
this.eventbus = new EventBus();
this.crlogic = crlogic;
// Add timestamp to sensor at the beginning of the sensor list as this is required for merging the data
// Timestamp will be at the second position of a message in the queue!
this.crlogic.getSensors().add(0, new CrlogicResource("tmp_timestamp","TIMESTAMP"));
this.scrlogic = scrlogic;
// Initialize lists used by the loop
this.preActions = new ArrayList<Action>();
this.postActions = new ArrayList<Action>();
this.mergeLogic = new MergeLogic(crlogic.getEventBus(), scrlogic.getEventBus(), eventbus);
}
@Override
public void prepare() {
}
@Override
public void execute() throws InterruptedException {
mergeLogic.enable();
// Execute pre actions
for(Action action: preActions){
action.execute();
}
ExecutorService service = Executors.newFixedThreadPool(3); // 2 for the parallel data acquisition and 1 for the merger thread
final CyclicBarrier b = new CyclicBarrier(2);
List<Future<Boolean>> list = new ArrayList<Future<Boolean>>();
// Start a thread for each logic
logger.info("Submit logic");
Future<Boolean> f = service.submit(new Callable<Boolean>(){
@Override
public Boolean call() throws Exception {
// Prepare logic
crlogic.prepare();
// Ensure that really all parallel logics start in parallel
b.await();
// Execute the logic of this path
crlogic.execute();
// crlogic.cleanup();
// Need to stop the scrlogic logic (otherwise it would keep going to take data)
scrlogic.abort();
return true;
}});
list.add(f);
//Start a thread for the scrlogic
logger.info("Submit logic");
f = service.submit(new Callable<Boolean>(){
@Override
public Boolean call() throws Exception {
// Prepare logic
scrlogic.prepare();
// Ensure that really all parallel logics start in parallel
b.await();
// Execute the logic of this path
scrlogic.execute(); // This actually just starts the collection ...
// scrlogic.cleanup();
return true;
}});
list.add(f);
for(Future<Boolean> bf: list){
// Wait for completion of the thread
try {
bf.get();
} catch (ExecutionException e) {
logger.log(Level.WARNING, "Something went wrong while waiting for crthreads to finish");
throw new RuntimeException(e);
}
}
// Wait until all threads have finished
service.shutdown();
service.awaitTermination(1, TimeUnit.MINUTES);
// Execute post actions
for(Action action: postActions){
action.execute();
}
mergeLogic.disable();
}
@Override
public void abort() {
}
@Override
public void cleanup() {
}
@Override
public List<Action> getPreActions() {
return preActions;
}
@Override
public List<Action> getPostActions() {
return postActions;
}
@Override
public boolean isDataGroup() {
return dataGroup;
}
@Override
public void setDataGroup(boolean dataGroup) {
this.dataGroup = dataGroup;
}
@Override
public EventBus getEventBus(){
return eventbus;
}
}
@@ -0,0 +1,224 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This code is distributed in the hope that it will be useful, but without any
* warranty; without even the implied warranty of merchantability or fitness for
* a particular purpose. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core.loops.cr;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import java.util.logging.Logger;
import com.google.common.eventbus.EventBus;
import ch.psi.fda.core.Action;
import ch.psi.fda.core.ActionLoop;
import ch.psi.fda.messages.DataMessage;
import ch.psi.fda.messages.EndOfStreamMessage;
import ch.psi.fda.messages.Metadata;
import ch.psi.jcae.Channel;
import ch.psi.jcae.ChannelException;
import ch.psi.jcae.impl.type.DoubleTimestamp;
/**
* Assumptions: - The delay between the monitor writing the value to the monitor
* queue and the readout of all the queues is sufficient to prevent the
* situation that some monitors of events close to each other on different IOC's
* have not arrived yet. - The sequence of monitors fired for one channel is
* according to the sequence of the causes. No monitor package is overtaking an
* other package on the network.
*
* - No monitor events are lost on the network (while using monitors you cannot
* guarantee this)
*
* The data queue returned by this logic includes two items for the timestamp
* and nanoseconds offset. These two items are the first two items of a message
* The id's are: crTimestampMilliseconds crTimestampOffsetNanoseconds
*/
public class ScrlogicLoop implements ActionLoop {
private static String ID_TIMESTAMP_MILLISECONDS = "crTimestampMilliseconds";
private static String ID_TIMESTAMP_OFFSET_NANOSECONDS = "crTimestampOffsetNanoseconds";
private static final Logger logger = Logger.getLogger(ScrlogicLoop.class.getName());
private boolean dataGroup = false;
private final List<Action> preActions = new ArrayList<Action>();
private final List<Action> postActions = new ArrayList<Action>();
private final List<Channel<DoubleTimestamp>> sensors;
private final List<String> sensorIds;
/**
* List of monitors that were attached to the sensor channels (i.e
* workaround)
*/
private final List<PropertyChangeListener> monitors = new ArrayList<>();
private List<Object> currentValues;
private CountDownLatch latch;
private final EventBus eventbus;
private List<Metadata> metadata;
public ScrlogicLoop(List<String> sensorIds, List<Channel<DoubleTimestamp>> sensors) {
this.eventbus = new EventBus();
this.sensors = sensors;
this.sensorIds = sensorIds;
}
@Override
public void execute() throws InterruptedException {
latch = new CountDownLatch(1);
// Initialize current values
currentValues = new ArrayList<Object>(sensors.size() + 2);
// Initialize values
currentValues.add(0.0);
currentValues.add(0.0);
for (Channel<DoubleTimestamp> sensor : sensors) {
try {
DoubleTimestamp value = sensor.getValue();
double timestamp = value.getTimestamp().getTime();
double noffset = value.getNanosecondOffset();
if (timestamp > ((Double) currentValues.get(0))) {
// We don't care about the nanoseconds offset so far
currentValues.set(0, timestamp);
currentValues.set(1, noffset);
}
currentValues.add(value.getValue());
} catch (InterruptedException | TimeoutException | ChannelException | ExecutionException e) {
throw new RuntimeException("Unable to retrieve initial value");
}
// Initialize current value with NAN
}
// Create metadata
metadata = new ArrayList<>(sensors.size());
// Build up data message metadata based on the channels registered.
metadata.add(new Metadata(ID_TIMESTAMP_MILLISECONDS));
metadata.add(new Metadata(ID_TIMESTAMP_OFFSET_NANOSECONDS));
for (String id : sensorIds) {
metadata.add(new Metadata(id));
}
// Send a first data message to be sure that there is a message inside
// the queue
DataMessage message = new DataMessage(metadata);
message.getData().addAll(currentValues);
eventbus.post(message);
// Attach monitors to the channels (this is actually a workaround)
int counter = 2;
for (Channel<DoubleTimestamp> sensor : sensors) {
final int currentCount = counter;
PropertyChangeListener listener = new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals("value")) {
// Merging values this way it is not 100% accurate as
// theoretically values does not need to come in
// in the correct time sequence. However we tradeoff
// this accuracy as we are not precise anyway
DoubleTimestamp v = (DoubleTimestamp) evt.getNewValue();
double timestamp = v.getTimestamp().getTime();
double noffset = v.getNanosecondOffset();
currentValues.set(0, timestamp);
currentValues.set(1, noffset);
currentValues.set(currentCount, v.getValue());
DataMessage message = new DataMessage(metadata);
message.getData().addAll(currentValues);
// logger.info("update"+v.getValue());
eventbus.post(message);
}
}
};
sensor.addPropertyChangeListener(listener);
monitors.add(listener);
counter++;
}
logger.info("Start data acquisition");
latch.await();
// Remove monitors
logger.info("Remove monitors");
for (int i = 0; i < sensors.size(); i++) {
Channel<DoubleTimestamp> sensor = sensors.get(i);
sensor.removePropertyChangeListener(monitors.get(i));
}
// Put end of stream to the queue
eventbus.post(new EndOfStreamMessage(dataGroup));
}
@Override
public void abort() {
latch.countDown();
}
@Override
public void prepare() {
}
@Override
public void cleanup() {
}
@Override
public List<Action> getPreActions() {
return preActions;
}
@Override
public List<Action> getPostActions() {
return postActions;
}
@Override
public boolean isDataGroup() {
return dataGroup;
}
@Override
public void setDataGroup(boolean dataGroup) {
this.dataGroup = dataGroup;
}
@Override
public EventBus getEventBus() {
return eventbus;
}
}
@@ -0,0 +1,114 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core.loops.cr;
import ch.psi.jcae.Channel;
import ch.psi.jcae.annotation.CaChannel;
public class TemplateCrlogic {
/**
* Ticks per second - IOC setting
* ATTENTION - This field must only be set bu the IOC - ATTENTION
*/
@CaChannel(type=Integer.class, name ="${PREFIX}:TPS")
private Channel<Integer> ticksPerSecond;
/**
* Status of the OTFSCAN IOC logic
*/
public enum Status { SETUP, INACTIVE, INITIALIZE, ACTIVE, STOP, FAULT, ERROR };
@CaChannel(type=String.class, name ="${PREFIX}:STATUS")
private Channel<String> status;
/**
* Message from the OTFSCAN IOC logic
*/
@CaChannel(type=String.class, name ="${PREFIX}:MSG")
private Channel<String> message;
/**
* IOC ticks between data acquisition interrupts
*/
@CaChannel(type=Integer.class, name ="${PREFIX}:TBINT")
private Channel<Integer> ticksBetweenInterrupts;
/**
* Name or ip address of the NFS server to save the data to
* (depending on the IOC setup)
*/
@CaChannel(type=String.class, name ="${PREFIX}:NFSSE")
private Channel<String> nfsServer;
/**
* Name of the NFS share on the NFS server
*/
@CaChannel(type=String.class, name ="${PREFIX}:NFSSH")
private Channel<String> nfsShare;
/**
* Name of the data file
*/
@CaChannel(type=String.class, name ="${PREFIX}:DFNAM")
private Channel<String> dataFile;
/**
* Flag to identify whether the data file should be appended
*/
@CaChannel(type=Boolean.class, name ="${PREFIX}:FAPPE")
private Channel<Boolean> appendFile;
/**
* Readout resources
*/
@CaChannel(type=String[].class, name ="${PREFIX}:RRES")
private Channel<String[]> readoutResources;
public Channel<Integer> getTicksPerSecond() {
return ticksPerSecond;
}
public Channel<String> getStatus() {
return status;
}
public Channel<String> getMessage() {
return message;
}
public Channel<Integer> getTicksBetweenInterrupts() {
return ticksBetweenInterrupts;
}
public Channel<String> getNfsServer() {
return nfsServer;
}
public Channel<String> getNfsShare() {
return nfsShare;
}
public Channel<String> getDataFile() {
return dataFile;
}
public Channel<Boolean> getAppendFile() {
return appendFile;
}
public Channel<String[]> getReadoutResources() {
return readoutResources;
}
}
@@ -0,0 +1,59 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core.loops.cr;
import ch.psi.jcae.Channel;
import ch.psi.jcae.annotation.CaChannel;
public class TemplateEncoder {
/**
* Resolution - $(P)$(E)_SCL
*/
@CaChannel(type=Double.class, name="${PREFIX}_SCL")
private Channel<Double> resolution;
/**
* Offset - $(P)$(E)_OFF
*/
@CaChannel(type=Double.class, name ="${PREFIX}_OFF")
private Channel<Double> offset;
/**
* Direction - $(P)$(E)_DIR
*/
public enum Direction {Negative, Positive};
@CaChannel(type=Integer.class, name ="${PREFIX}_DIR")
private Channel<Integer> direction;
public Channel<Double> getResolution() {
return resolution;
}
public Channel<Double> getOffset() {
return offset;
}
public Channel<Integer> getDirection() {
return direction;
}
}
@@ -0,0 +1,471 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core.loops.cr;
import ch.psi.jcae.Channel;
import ch.psi.jcae.annotation.CaChannel;
public class TemplateMotor {
public enum Type {SOFT_CHANNEL, MOTOR_SIMULATION, OMS_VME58, OMS_MAXv};
/**
* ## Drive section ##
* # User coordinates #
*/
/**
* .HLM High limit - double
*/
@CaChannel(type=Double.class, name ="${PREFIX}.HLM")
private Channel<Double> highLimit;
/**
* .LLM Low limit - double
*/
@CaChannel(type=Double.class, name ="${PREFIX}.LLM")
private Channel<Double> lowLimit;
/**
* .RBV Readback value - double
*/
@CaChannel(type=Double.class, name ="${PREFIX}.RBV", monitor=true)
private Channel<Double> readbackValue;
/**
* .VAL Set value - double
*/
@CaChannel(type=Double.class, name ="${PREFIX}.VAL", monitor=true)
private Channel<Double> setValue;
/**
* .RLV Relative move value - double
*/
@CaChannel(type=Double.class, name ="${PREFIX}.RLV")
private Channel<Double> relativeMoveValue;
/**
* .TWV Teak value - double
*/
@CaChannel(type=Double.class, name ="${PREFIX}.TWV")
private Channel<Double> tweakValue;
/**
* .TWR Tweak reverse - move left - int
*/
@CaChannel(type=Integer.class, name ="${PREFIX}.TWR")
private Channel<Integer> tweakReverse;
/**
* .TWF Tweak forward - move right - int
*/
@CaChannel(type=Integer.class, name ="${PREFIX}.TWF")
private Channel<Integer> tweakForward;
/**
* .JOGR Jog reverse - int
*/
@CaChannel(type=Integer.class, name ="${PREFIX}.JOGR")
private Channel<Integer> jogReverse;
/**
* .JOGF Jog forward - int
*/
@CaChannel(type=Integer.class, name ="${PREFIX}.JOGF")
private Channel<Integer> jogForward;
/**
* .HOMR Home reverse - int
*/
@CaChannel(type=Integer.class, name ="${PREFIX}.HOMR")
private Channel<Integer> homeReverse;
/**
* .HOMF Home forward - int
*/
@CaChannel(type=Integer.class, name ="${PREFIX}.HOMF")
private Channel<Integer> homeForward;
/**
* .EGU Engineering unit - String
*/
@CaChannel(type=String.class, name ="${PREFIX}.EGU")
private Channel<String> engineeringUnit;
/**
* .DTYP Type - String (e.g. "OMS MAXv") see enum Type
*/
@CaChannel(type=Integer.class, name ="${PREFIX}.DTYP")
private Channel<Integer> type;
/**
* .DESC Description - String
*/
@CaChannel(type=String.class, name ="${PREFIX}.DESC")
private Channel<String> description;
/**
* # Dial coordinates #
*/
/**
* .DHLM Dial high limit - double
*/
@CaChannel(type=Double.class, name ="${PREFIX}.DHLM")
private Channel<Double> dialHighLimit;
/**
* .DLLM Dial low limit - double
*/
@CaChannel(type=Double.class, name ="${PREFIX}.DLLM")
private Channel<Double> dialLowLimit;
/**
* .DRBV Dial readback value - double
*/
@CaChannel(type=Double.class, name ="${PREFIX}.DRBV", monitor=true)
private Channel<Double> dialReadbackValue;
/**
* .DVAL Dial set value - double
*/
@CaChannel(type=Double.class, name ="${PREFIX}.DVAL", monitor=true)
private Channel<Double> dialSetValue;
/**
* .RVAL Raw value - int
*/
@CaChannel(type=Integer.class, name ="${PREFIX}.RVAL", monitor=true)
private Channel<Integer> rawValue;
/**
* .RRBV Raw readback value - int
*/
@CaChannel(type=Integer.class, name ="${PREFIX}.RRBV", monitor=true)
private Channel<Integer> rawReadbackValue;
/**
* .SPMG Stop/Pause/Move/Go - (0:"Stop", 1:"Pause", 2:"Move", 3:"Go") - 3
*/
public enum Commands { Stop, Pause, Move, Go };
@CaChannel(type=Integer.class, name ="${PREFIX}.SPMG")
private Channel<Integer> command;
/**
* ## Calibration section ##
*/
/**
* .SET Set/Use Switch - (0:"Use", 1:"Set") - 0
*/
public enum Calibration {Use, Set};
@CaChannel(type=Integer.class, name ="${PREFIX}.SET")
private Channel<Integer> calibration;
/**
* .OFF User offset (EGU) - double
*/
@CaChannel(type=Double.class, name ="${PREFIX}.OFF")
private Channel<Double> offset;
/**
* .FOFF Offset-Freeze Switch - (0:"Variable", 1:"Frozen") - 1
*/
public enum OffsetMode {Variable, Frozen};
@CaChannel(type=Integer.class, name ="${PREFIX}.FOFF")
private Channel<Integer> offsetMode;
/**
* .DIR User direction - (0:"Pos", 1:"Neg")
*/
public enum Direction {Positive, Negative};
@CaChannel(type=Integer.class, name ="${PREFIX}.DIR")
private Channel<Integer> direction;
/**
* ## Dynamics ##
*/
/**
* .VELO Velocity (EGU/s) - double
*/
@CaChannel(type=Double.class, name ="${PREFIX}.VELO")
private Channel<Double> velocity;
/**
* .BVEL Backlash velocity (EGU/s) - double
*/
@CaChannel(type=Double.class, name ="${PREFIX}.BVEL")
private Channel<Double> backlashVelocity;
/**
* .VBAS Base speed (EGU/s) - double
*/
@CaChannel(type=Double.class, name ="${PREFIX}.VBAS")
private Channel<Double> baseSpeed;
/**
* .ACCL Acceleration time / seconds to velocity - double
*/
@CaChannel(type=Double.class, name ="${PREFIX}.ACCL")
private Channel<Double> accelerationTime;
/**
* .BACC Backlash acceleration time / seconds to velocity - double
*/
@CaChannel(type=Double.class, name ="${PREFIX}.BACC")
private Channel<Double> backlashAccelerationTime;
/**
* .BDST Backlash distance (EGU) - double
*/
@CaChannel(type=Double.class, name ="${PREFIX}.BDST")
private Channel<Double> backlashDistance;
/**
* .FRAC Move fraction - double
*/
@CaChannel(type=Double.class, name ="${PREFIX}.FRAC")
private Channel<Double> moveFracion;
/**
* ## Resolution ##
*/
/**
* .MRES Motor resolution - double
*/
@CaChannel(type=Double.class, name ="${PREFIX}.MRES")
private Channel<Double> motorResolution;
/**
* .ERES Encoder resolution - double
*/
@CaChannel(type=Double.class, name ="${PREFIX}.ERES")
private Channel<Double> encoderResolution;
/**
* .RRES Readback resolution - double
*/
@CaChannel(type=Double.class, name ="${PREFIX}.RRES")
private Channel<Double> readbackResolution;
/**
* .RDBD Retry deadband (EGU) - double
*/
@CaChannel(type=Double.class, name ="${PREFIX}.RDBD")
private Channel<Double> retryDeadband;
/**
* .RTRY Max retry count - int
*/
@CaChannel(type=Integer.class, name ="${PREFIX}.RTRY")
private Channel<Integer> maxRetryCount;
/**
* .RCNT Retry count - int
*/
@CaChannel(type=Integer.class, name ="${PREFIX}.RCNT", monitor=true)
private Channel<Integer> retryCount;
/**
* .UEIP Use encoder (if present) - (0:"No", 1:"Yes")
*/
@CaChannel(type=Boolean.class, name ="${PREFIX}.UEIP")
private Channel<Boolean> useEncoder;
/**
* .URIP Use readback link (if present) - (0:"No", 1:"Yes")
*/
@CaChannel(type=Boolean.class, name ="${PREFIX}.URIP")
private Channel<Boolean> useReadback;
/**
* .DLY Readback delay (s) - double
*/
@CaChannel(type=Double.class, name ="${PREFIX}.DLY")
private Channel<Double> readbackDelay;
/**
* .RDBL Readback link - String
*/
@CaChannel(type=String.class, name ="${PREFIX}.RDBL")
private Channel<String> readbackLink;
/**
* .OMSL Output mode select - (0:"supervisory", 1:"closed_loop")
*/
public enum OutputMode {Supervisory, Closed_Loop};
@CaChannel(type=Integer.class, name ="${PREFIX}.OMSL")
private Channel<Integer> outputMode;
/**
* ## Status ##
*/
/**
* .DMOV Done move - int
*/
@CaChannel(type=Boolean.class, name ="${PREFIX}.DMOV", monitor=true)
private Channel<Boolean> moveDone;
public Channel<Double> getHighLimit() {
return highLimit;
}
public Channel<Double> getLowLimit() {
return lowLimit;
}
public Channel<Double> getReadbackValue() {
return readbackValue;
}
public Channel<Double> getSetValue() {
return setValue;
}
public Channel<Double> getRelativeMoveValue() {
return relativeMoveValue;
}
public Channel<Double> getTweakValue() {
return tweakValue;
}
public Channel<Integer> getTweakReverse() {
return tweakReverse;
}
public Channel<Integer> getTweakForward() {
return tweakForward;
}
public Channel<Integer> getJogReverse() {
return jogReverse;
}
public Channel<Integer> getJogForward() {
return jogForward;
}
public Channel<Integer> getHomeReverse() {
return homeReverse;
}
public Channel<Integer> getHomeForward() {
return homeForward;
}
public Channel<String> getEngineeringUnit() {
return engineeringUnit;
}
public Channel<Integer> getType() {
return type;
}
public Channel<String> getDescription() {
return description;
}
public Channel<Double> getDialHighLimit() {
return dialHighLimit;
}
public Channel<Double> getDialLowLimit() {
return dialLowLimit;
}
public Channel<Double> getDialReadbackValue() {
return dialReadbackValue;
}
public Channel<Double> getDialSetValue() {
return dialSetValue;
}
public Channel<Integer> getRawValue() {
return rawValue;
}
public Channel<Integer> getRawReadbackValue() {
return rawReadbackValue;
}
public Channel<Integer> getCommand() {
return command;
}
public Channel<Integer> getCalibration() {
return calibration;
}
public Channel<Double> getOffset() {
return offset;
}
public Channel<Integer> getOffsetMode() {
return offsetMode;
}
public Channel<Integer> getDirection() {
return direction;
}
public Channel<Double> getVelocity() {
return velocity;
}
public Channel<Double> getBacklashVelocity() {
return backlashVelocity;
}
public Channel<Double> getBaseSpeed() {
return baseSpeed;
}
public Channel<Double> getAccelerationTime() {
return accelerationTime;
}
public Channel<Double> getBacklashAccelerationTime() {
return backlashAccelerationTime;
}
public Channel<Double> getBacklashDistance() {
return backlashDistance;
}
public Channel<Double> getMoveFracion() {
return moveFracion;
}
public Channel<Double> getMotorResolution() {
return motorResolution;
}
public Channel<Double> getEncoderResolution() {
return encoderResolution;
}
public Channel<Double> getReadbackResolution() {
return readbackResolution;
}
public Channel<Double> getRetryDeadband() {
return retryDeadband;
}
public Channel<Integer> getMaxRetryCount() {
return maxRetryCount;
}
public Channel<Integer> getRetryCount() {
return retryCount;
}
public Channel<Boolean> getUseEncoder() {
return useEncoder;
}
public Channel<Boolean> getUseReadback() {
return useReadback;
}
public Channel<Double> getReadbackDelay() {
return readbackDelay;
}
public Channel<String> getReadbackLink() {
return readbackLink;
}
public Channel<Integer> getOutputMode() {
return outputMode;
}
public Channel<Boolean> getMoveDone() {
return moveDone;
}
}
@@ -0,0 +1,80 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core.loops.cr;
import java.util.List;
import ch.psi.jcae.Channel;
import ch.psi.jcae.annotation.CaChannel;
public class TemplateVSC16Scaler {
/**
* Command
*/
public enum Command {Done, Count};
@CaChannel(type=Integer.class, name ="${PREFIX}.CNT")
private Channel<Integer> command;
public enum Mode {OneShot, AutoCount};
/**
* Count mode
*/
@CaChannel(type=Integer.class, name ="${PREFIX}.CONT")
private Channel<Integer> mode;
/**
* Channel description
*/
@CaChannel(type=Boolean.class, name={"${PREFIX}.NM1", "${PREFIX}.NM2", "${PREFIX}.NM3", "${PREFIX}.NM4", "${PREFIX}.NM5", "${PREFIX}.NM6", "${PREFIX}.NM7", "${PREFIX}.NM8", "${PREFIX}.NM9", "${PREFIX}.NM10", "${PREFIX}.NM11", "${PREFIX}.NM12", "${PREFIX}.NM13", "${PREFIX}.NM14", "${PREFIX}.NM15", "${PREFIX}.NM16"})
private List<Channel<Boolean>> channelDescription;
/**
* Channel gate
*/
@CaChannel(type=Boolean.class, name={"${PREFIX}.G1", "${PREFIX}.G2", "${PREFIX}.G3", "${PREFIX}.G4", "${PREFIX}.G5", "${PREFIX}.G6", "${PREFIX}.G7", "${PREFIX}.G8", "${PREFIX}.G9", "${PREFIX}.G10", "${PREFIX}.G11", "${PREFIX}.G12", "${PREFIX}.G13", "${PREFIX}.G14", "${PREFIX}.G15", "${PREFIX}.G16"})
private List<Channel<Boolean>> channelGate;
/**
* Channel preset count
* If gate is on scaler will only count until this value
*/
@CaChannel(type=Integer.class, name={"${PREFIX}.PR1", "${PREFIX}.PR2", "${PREFIX}.PR3", "${PREFIX}.PR4", "${PREFIX}.PR5", "${PREFIX}.PR6", "${PREFIX}.PR7", "${PREFIX}.PR8", "${PREFIX}.PR9", "${PREFIX}.PR10", "${PREFIX}.PR11", "${PREFIX}.PR12", "${PREFIX}.PR13", "${PREFIX}.PR14", "${PREFIX}.PR15", "${PREFIX}.PR16"})
private List<Channel<Integer>> channelPresetCount;
public Channel<Integer> getCommand() {
return command;
}
public Channel<Integer> getMode() {
return mode;
}
public List<Channel<Boolean>> getChannelDescription() {
return channelDescription;
}
public List<Channel<Boolean>> getChannelGate() {
return channelGate;
}
public List<Channel<Integer>> getChannelPresetCount() {
return channelPresetCount;
}
}
@@ -0,0 +1,43 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core.loops.cr;
public class TimestampedValue {
private final Double value;
private final long timestamp;
private final long nanosecondsOffset;
public TimestampedValue(Double value, long timestamp, long nanosecondsOffset){
this.value = value;
this.timestamp = timestamp;
this.nanosecondsOffset = nanosecondsOffset;
}
public Double getValue() {
return value;
}
public long getTimestamp() {
return timestamp;
}
public long getNanosecondsOffset() {
return nanosecondsOffset;
}
}
@@ -0,0 +1,44 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core.loops.cr;
import java.util.Comparator;
/**
* Comparator for comparint 2 timestamped values
*/
public class TimestampedValueComparator implements Comparator<TimestampedValue> {
@Override
public int compare(TimestampedValue o1, TimestampedValue o2) {
if (o1.getTimestamp() < o2.getTimestamp()) {
return -1;
} else if (o1.getTimestamp() > o2.getTimestamp()) {
return 1;
} else if (o1.getTimestamp() == o2.getTimestamp()) {
if (o1.getNanosecondsOffset() < o2.getNanosecondsOffset()) {
return -1;
} else if (o1.getNanosecondsOffset() > o2.getNanosecondsOffset()) {
return 1;
}
}
return 0;
}
}
@@ -0,0 +1,283 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core.manipulator;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import ch.psi.fda.core.Manipulation;
import ch.psi.fda.core.scripting.JythonParameterMapping;
import ch.psi.fda.core.scripting.JythonParameterMappingChannel;
import ch.psi.fda.core.scripting.JythonParameterMappingGlobalVariable;
import ch.psi.fda.core.scripting.JythonParameterMappingID;
import ch.psi.fda.messages.DataMessage;
import ch.psi.fda.messages.Metadata;
public class JythonManipulation implements Manipulation{
private static Logger logger = Logger.getLogger(JythonManipulation.class.getName());
/**
* Pattern of the entry function of the jython script
*/
private static final String entryFunction = "process";
private static final String entryFunctionPattern = "def "+entryFunction+"\\((.*)\\):";
/**
* Code of the manipulation. The script need to implement a function of the type:
* <code>
* def process():
* # ... your code
* return value
* </code>
*
* The number of parameters of the script are not limited. So <code>process(a)</code> is valid
* as well as <code>process(a,b,c,d,e,f)</code>.
* However for each parameter there need to be a mapping inside the mapping list! If there is no
* mapping inside the Manipulator evaluating this Manipulation will throw an Exception.
*/
private final String script;
/**
* Id of the resulting component
*/
private final String id;
/**
* Mapping of components to manipulation code variables
*/
private final List<JythonParameterMapping> mapping;
private final boolean returnArray;
/**
* Script engine of the manipulator
*/
private ScriptEngine engine;
/**
* Component index of the script parameter. The sequence of the indexes in this array correspond to the script
* parameter position, i.e. the first index corresponds to the first parameter.
*/
// private Integer[] parameterIndex;
private List<String> parameterIds = new ArrayList<>();
/**
* Parameter array of the entry function
*/
private String[] parameter;
/**
* Jython entry call
*/
private String jythonCall;
private Map<String,Object> gvariables = new HashMap<String,Object>();
public JythonManipulation(String id, String script, List<JythonParameterMapping> mapping){
this(id, script, mapping, false);
}
/**
* Constructor
* @param id
* @param script
* @param mapping
*/
public JythonManipulation(String id, String script, List<JythonParameterMapping> mapping, boolean returnArray){
this.id = id;
this.script = script;
this.mapping = mapping;
this.returnArray = returnArray;
}
/**
* @return the script
*/
public String getScript() {
return script;
}
/**
* @return the mapping
*/
public List<JythonParameterMapping> getMapping() {
return mapping;
}
@Override
public String getId() {
return id;
}
@Override
public void initialize(List<Metadata> metadata){
// Workaround for Jython memory leak
// http://blog.hillbrecht.de/2009/07/11/jython-memory-leakout-of-memory-problem/
System.setProperty("python.options.internalTablesImpl","weak");
// Create new script engine
this.engine = new ScriptEngineManager().getEngineByName("python");
// Determine script entry function and the function parameters
Pattern pattern = Pattern.compile(entryFunctionPattern);
Matcher matcher = pattern.matcher(this.script);
if(matcher.find() && matcher.groupCount()==1){
if(!matcher.group(1).trim().equals("")){
logger.finest("Entry function '"+entryFunctionPattern+"' found - Identified parameters: "+matcher.group(1));
parameter = matcher.group(1).split(" *, *");
}
else{
parameter = new String[0];
}
}
else{
throw new IllegalArgumentException("Cannot determine entry function: "+entryFunctionPattern);
}
// Load manipulation script
try {
engine.eval(this.script);
} catch (ScriptException e) {
throw new RuntimeException("Unable to load manipulation script", e);
}
// Determine component index of the needed parameters
// If the component index of the parameter cannot be determined an IllegalArgumentException is thrown
// parameterIndex = new Integer[parameter.length];
parameterIds = new ArrayList<>();
for(int i=0;i<parameter.length; i++){
String p = parameter[i];
p = p.trim();
boolean found = false;
for(JythonParameterMapping jpm: this.mapping){
if(jpm.getVariable().equals(p)){
if(jpm instanceof JythonParameterMappingID){
JythonParameterMappingID pm = (JythonParameterMappingID)jpm;
// Mapping for parameter found, determine index of the corresponding component
// parameterIndex[i] = metadata.getIndex(pm.getRefid());
parameterIds.add(pm.getRefid());
}
else if (jpm instanceof JythonParameterMappingChannel){
JythonParameterMappingChannel<?> pm = (JythonParameterMappingChannel<?>) jpm;
// parameterIndex[i] = null;
parameterIds.add(null);
engine.put(pm.getVariable(), pm.getChannel());
}
else if (jpm instanceof JythonParameterMappingGlobalVariable){
JythonParameterMappingGlobalVariable pm = (JythonParameterMappingGlobalVariable)jpm;
parameterIds.add(null);
// parameterIndex[i] = null;
engine.put(pm.getVariable(), pm.getGlobalVariable());
}
found=true;
break;
}
}
// If there is no mapping nothing can be found
// If there are mappings everything need to be found
if(!found){
throw new IllegalArgumentException("No mapping compontent found for parameter "+p);
}
}
StringBuffer buffer = new StringBuffer();
buffer.append(entryFunction);
buffer.append("(");
for(String p: parameter){
buffer.append(p);
buffer.append(",");
}
buffer.setCharAt(buffer.length()-1, ')');
jythonCall = buffer.toString();
}
@Override
public Object execute(DataMessage message){
// Set global variables - WORKAROUND gvariables
// This block is not in initialization as we want to assure that all invocations
// of this manipulation will get the same value (i.e. to prevent inconsistent behaviour
// if variable was changed during an execution of the manipulation)
for(String k: gvariables.keySet()){
engine.put(k, gvariables.get(k));
}
// Manipulate data
for(int i=0;i<parameterIds.size();i++){
if(parameterIds.get(i) != null){
engine.put(parameter[i], message.getData(parameterIds.get(i)));
}
}
try {
if(returnArray){
return((double[]) engine.eval(jythonCall));
}
else{
// Due to the typeless nature of Python an Integer of Float/Double might be returned from the
// Python code (e.g. depending on the factors in a calculation)
// Always return the return value to a double. If this cannot be done return NaN
Object r = engine.eval(jythonCall);
if(r instanceof Double){
return((Double) r);
}
else if( r instanceof Integer){
return(((Integer)r).doubleValue());
}
else{
// return Double.NaN;
return r;
}
}
} catch (ScriptException e) {
// throw new RuntimeException("Data manipulaton [id: "+id+"] failed while executing the manipulation script",e);
logger.log(Level.WARNING, "Data manipulaton [id: "+id+"] failed while executing the manipulation script");
return Double.NaN;
}
}
/**
* Workaround to put variables into the jython engine.
* @param name
* @param value
*/
public void setVariable(String name, Object value){
gvariables.put(name, value);
}
}
@@ -0,0 +1,51 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core.scripting;
public class JythonGlobalVariable {
private String name;
private double value;
/**
* @return the name
*/
public String getName() {
return name;
}
/**
* @param name the name to set
*/
public void setName(String name) {
this.name = name;
}
/**
* @return the value
*/
public double getValue() {
return value;
}
/**
* @param value the value to set
*/
public void setValue(double value) {
this.value = value;
}
}
@@ -0,0 +1,53 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core.scripting;
/**
* Mapping of a parameter to something
*/
public abstract class JythonParameterMapping {
/**
* Variable name inside the script
*/
private String variable;
/**
* Constructor
* @param variable
*/
public JythonParameterMapping(String variable){
this.variable = variable;
}
/**
* @return the variable
*/
public String getVariable() {
return variable;
}
/**
* @param variable the variable to set
*/
public void setVariable(String variable) {
this.variable = variable;
}
}
@@ -0,0 +1,40 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core.scripting;
import ch.psi.jcae.Channel;
/**
* Mapping of a script parameter to a channel bean.
*/
public class JythonParameterMappingChannel<T> extends JythonParameterMapping {
private Channel<T> channel;
public JythonParameterMappingChannel(String variable, Channel<T> channel){
super(variable);
this.channel = channel;
}
public Channel<T> getChannel() {
return channel;
}
}
@@ -0,0 +1,54 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core.scripting;
/**
* Mapping of a script parameter to a component via the Id.
*/
public class JythonParameterMappingGlobalVariable extends JythonParameterMapping {
private JythonGlobalVariable globalVariable;
/**
* Constuctor
* @param variable
* @param globalVariable
*/
public JythonParameterMappingGlobalVariable(String variable, JythonGlobalVariable globalVariable){
super(variable);
this.globalVariable = globalVariable;
}
/**
* @return the globalVariable
*/
public JythonGlobalVariable getGlobalVariable() {
return globalVariable;
}
/**
* @param globalVariable the globalVariable to set
*/
public void setGlobalVariable(JythonGlobalVariable globalVariable) {
this.globalVariable = globalVariable;
}
}
@@ -0,0 +1,56 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core.scripting;
/**
* Mapping of a script parameter to a component via the Id.
*/
public class JythonParameterMappingID extends JythonParameterMapping {
/**
* Id of the component to map to this variable
*/
private String refid;
/**
* Constructor accepting varible/id pair
* @param variable
* @param refid
*/
public JythonParameterMappingID(String variable, String refid){
super(variable);
this.refid = refid;
}
/**
* @return the refid
*/
public String getRefid() {
return refid;
}
/**
* @param refid the refid to set
*/
public void setRefid(String refid) {
this.refid = refid;
}
}
@@ -0,0 +1,72 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core.sensors;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import java.util.logging.Logger;
import ch.psi.fda.core.Sensor;
import ch.psi.jcae.Channel;
import ch.psi.jcae.ChannelException;
/**
* Channel Access sensor capable of reading a channel access channel
*/
public class ChannelAccessSensor<T> implements Sensor {
private static Logger logger = Logger.getLogger(ChannelAccessSensor.class.getName());
private Channel<T> channel;
private final String id;
private boolean failOnSensorError = true;
public ChannelAccessSensor(String id, Channel<T> channel){
this.id = id;
this.channel = channel;
}
public ChannelAccessSensor(String id, Channel<T> channel, boolean failOnSensorError){
this.id = id;
this.channel = channel;
this.failOnSensorError = failOnSensorError;
}
@Override
public Object read() throws InterruptedException {
logger.finest("Read sensor "+channel.getName());
T v;
try {
v = channel.getValue();
} catch (TimeoutException | ChannelException | ExecutionException e) {
if(failOnSensorError){
throw new RuntimeException("Unable to get value from channel [name:"+channel.getName()+"]",e);
}
v = null;
}
return(v);
}
@Override
public String getId() {
return id;
}
}
@@ -0,0 +1,56 @@
package ch.psi.fda.core.sensors;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import ch.psi.fda.core.Action;
import ch.psi.fda.core.Sensor;
/**
* Complex sensor that complements an other sensor with pre and post actions.
* Before reading out the complemented sensor the pre actions are executed. After the
* readout the post actions.
*/
public class ComplexSensor implements Sensor {
private static Logger logger = Logger.getLogger(ComplexSensor.class.getName());
private String id;
private final Sensor sensor;
private final List<Action> preActions;
private final List<Action> postActions;
public ComplexSensor(String id, Sensor sensor){
this.id = id;
this.sensor = sensor;
this.preActions = new ArrayList<Action>();
this.postActions = new ArrayList<Action>();
}
@Override
public Object read() throws InterruptedException {
logger.finest("Execute pre actions");
for(Action action: preActions){
action.execute();
}
Object value = sensor.read();
logger.finest("Execute post actions");
for(Action action: postActions){
action.execute();
}
return value;
}
@Override
public String getId() {
return id;
}
public List<Action> getPreActions() {
return preActions;
}
public List<Action> getPostActions() {
return postActions;
}
}
@@ -0,0 +1,46 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core.sensors;
import ch.psi.fda.core.Sensor;
/**
* Sensor to read the current time in milliseconds
*/
public class TimestampSensor implements Sensor {
private final String id; // Global id of the sensor
public TimestampSensor(String id){
this.id = id;
}
@Override
public Object read() {
// Return current time in milliseconds
return new Double(System.currentTimeMillis());
}
@Override
public String getId() {
return id;
}
}
@@ -0,0 +1 @@
/v1/
@@ -0,0 +1,100 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.model;
import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.UnmarshalException;
import javax.xml.bind.Unmarshaller;
import javax.xml.namespace.QName;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import ch.psi.fda.model.v1.Configuration;
/**
* Manage the serialization and deserialization of the FDA data model
*/
public class ModelManager {
/**
* De-serialize an instance of the FDA data model
*
* @param file File to deserialize
* @throws JAXBException Something went wrong while unmarshalling
* @throws SAXException Cannot read model schema file
*/
public static Configuration unmarshall(File file) throws JAXBException, SAXException {
JAXBContext context = JAXBContext.newInstance(Configuration.class);
Unmarshaller u = context.createUnmarshaller();
// Validation
SchemaFactory sf = SchemaFactory.newInstance(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI);
Source s = new StreamSource(Configuration.class.getResourceAsStream("/model-v1.xsd"));
Schema schema = sf.newSchema(new Source[]{s}); // Use schema reference provided in XML
u.setSchema(schema);
try{
Configuration model = (Configuration) u.unmarshal(new StreamSource(file), Configuration.class).getValue();
return (model);
}
catch(UnmarshalException e){
// Check
if(e.getLinkedException() instanceof SAXParseException){
throw new RuntimeException("Configuration file does not comply to required model specification\nCause: "+e.getLinkedException().getMessage(), e);
}
throw e;
}
}
/**
* Serialize an instance of the FDA data model
*
* @param model Model datastructure
* @param file File to write the model data into
* @throws JAXBException Something went wrong while marshalling model
* @throws SAXException Cannot read model schema files
*/
public static void marshall(Configuration model, File file) throws JAXBException, SAXException{
QName qname = new QName("http://www.psi.ch/~ebner/models/scan/1.0", "configuration");
JAXBContext context = JAXBContext.newInstance(Configuration.class);
Marshaller m = context.createMarshaller();
m.setProperty("jaxb.formatted.output", true);
// Validation
SchemaFactory sf = SchemaFactory.newInstance(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI);
Source s = new StreamSource(Configuration.class.getResourceAsStream("/model-v1.xsd"));
Schema schema = sf.newSchema(new Source[]{s}); // Use schema reference provided in XML
m.setSchema(schema);
m.marshal( new JAXBElement<Configuration>(qname, Configuration.class, model ), file);
}
}
@@ -1,2 +1,3 @@
ch.psi.fda.cdump.CdumpEDescriptorProvider
ch.psi.fda.fdaq.FdaqEDescriptorProvider
ch.psi.fda.aq.XScanDescriptorProvider
@@ -1,2 +1,3 @@
ch.psi.fda.cdump.CdumpEContainerFactory
ch.psi.fda.fdaq.FdaqEContainerFactory
ch.psi.fda.aq.XScanFactory
+548
View File
@@ -0,0 +1,548 @@
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema targetNamespace="http://www.psi.ch/~ebner/models/scan/1.0" version="1.0" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.psi.ch/~ebner/models/scan/1.0" elementFormDefault="qualified">
<xsd:complexType name="Visualization" abstract="true">
<xsd:attribute name="title" type="xsd:string" use="optional" default=" "></xsd:attribute>
</xsd:complexType>
<xsd:complexType name="LinePlot">
<xsd:complexContent>
<xsd:extension base="Visualization">
<xsd:attribute name="x" type="xsd:IDREF" use="required"></xsd:attribute>
<xsd:attribute name="y" type="xsd:IDREFS" use="required"></xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="MatrixPlot">
<xsd:complexContent>
<xsd:extension base="Visualization">
<xsd:attribute name="x" type="xsd:IDREF" use="required"></xsd:attribute>
<xsd:attribute name="y" type="xsd:IDREF" use="required"></xsd:attribute>
<xsd:attribute name="z" type="xsd:IDREF" use="required"></xsd:attribute>
<xsd:attribute name="type" default="2D"
use="optional">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="2D"></xsd:enumeration>
<xsd:enumeration value="3D"></xsd:enumeration>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="Action" abstract="true"></xsd:complexType>
<xsd:complexType name="ChannelAction">
<xsd:complexContent>
<xsd:extension base="Action">
<xsd:attribute name="channel" type="xsd:string"
use="required">
</xsd:attribute>
<xsd:attribute name="value" type="xsd:string"
use="required">
</xsd:attribute>
<xsd:attribute name="operation" default="put"
use="optional">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="put"></xsd:enumeration>
<xsd:enumeration value="putq"></xsd:enumeration>
<xsd:enumeration value="wait"></xsd:enumeration>
<xsd:enumeration value="waitOR"></xsd:enumeration>
<xsd:enumeration value="waitAND"></xsd:enumeration>
<xsd:enumeration value="waitREGEX"></xsd:enumeration>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="type" default="String"
use="optional">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="String"></xsd:enumeration>
<xsd:enumeration value="Integer"></xsd:enumeration>
<xsd:enumeration value="Double"></xsd:enumeration>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="timeout" type="xsd:double" use="optional"></xsd:attribute>
<xsd:attribute name="delay" type="xsd:double"
use="optional">
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="Positioner" abstract="true">
<xsd:attribute name="id" type="xsd:ID" use="required"></xsd:attribute>
</xsd:complexType>
<xsd:complexType name="ContinuousPositioner">
<xsd:complexContent>
<xsd:extension base="Positioner">
<xsd:sequence>
<xsd:element name="start" type="xsd:double"
maxOccurs="1" minOccurs="1">
</xsd:element>
<xsd:element name="end" type="xsd:double"
maxOccurs="1" minOccurs="1">
</xsd:element>
<xsd:element name="stepSize" type="xsd:double"
maxOccurs="1" minOccurs="1">
</xsd:element>
<xsd:element name="integrationTime"
type="xsd:double" maxOccurs="1" minOccurs="1">
</xsd:element>
<xsd:element name="additionalBacklash"
type="xsd:double" maxOccurs="1" minOccurs="0">
</xsd:element>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string"
use="required">
</xsd:attribute>
<xsd:attribute name="readback" type="xsd:string" use="optional"></xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="DiscreteStepPositioner" abstract="true">
<xsd:complexContent>
<xsd:extension base="Positioner">
<xsd:attribute name="name" type="xsd:string" use="optional"></xsd:attribute>
<xsd:attribute name="readback" type="xsd:string" use="optional"></xsd:attribute>
<xsd:attribute name="settlingTime" default="0" use="optional">
<xsd:simpleType>
<xsd:restriction base="xsd:double">
<xsd:minInclusive value="0"></xsd:minInclusive>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="done" type="xsd:string" use="optional"></xsd:attribute>
<xsd:attribute name="doneValue" type="xsd:string" use="optional" default="1"></xsd:attribute>
<xsd:attribute name="type" default="Integer" use="optional">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="String"></xsd:enumeration>
<xsd:enumeration value="Integer"></xsd:enumeration>
<xsd:enumeration value="Double"></xsd:enumeration>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="doneDelay" default="0">
<xsd:simpleType>
<xsd:restriction base="xsd:double">
<xsd:minInclusive value="0"></xsd:minInclusive>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="asynchronous" type="xsd:boolean" default="false"></xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="LinearPositioner">
<xsd:complexContent>
<xsd:extension base="DiscreteStepPositioner">
<xsd:sequence>
<xsd:element name="start" type="xsd:double"></xsd:element>
<xsd:element name="end" type="xsd:double"></xsd:element>
<xsd:element name="stepSize" type="xsd:double"></xsd:element>
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="ArrayPositioner">
<xsd:complexContent>
<xsd:extension base="DiscreteStepPositioner">
<xsd:sequence>
<xsd:element name="positions" type="xsd:string"></xsd:element>
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="RegionPositioner">
<xsd:complexContent>
<xsd:extension base="DiscreteStepPositioner">
<xsd:sequence>
<xsd:element name="region" type="Region" maxOccurs="unbounded" minOccurs="1"></xsd:element>
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="Region">
<xsd:sequence>
<xsd:element name="preAction" type="Action" maxOccurs="unbounded" minOccurs="0"></xsd:element>
<xsd:element name="start" type="xsd:double" maxOccurs="1" minOccurs="1"></xsd:element>
<xsd:element name="end" type="xsd:double" maxOccurs="1" minOccurs="1"></xsd:element>
<xsd:element name="stepSize" type="xsd:double" maxOccurs="1" minOccurs="1"></xsd:element>
<xsd:element name="function" type="Function" minOccurs="0" maxOccurs="1"></xsd:element>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="Data">
<xsd:attribute name="format" use="optional" default="txt">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="txt"></xsd:enumeration>
<xsd:enumeration value="mat"></xsd:enumeration>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
<xsd:attribute name="fileName" type="xsd:string" use="optional"></xsd:attribute>
</xsd:complexType>
<xsd:complexType name="Dimension" abstract="true">
<xsd:attribute name="zigzag" type="xsd:boolean" use="optional" default="false"></xsd:attribute>
<xsd:attribute name="dataGroup" type="xsd:boolean" use="optional" default="false"></xsd:attribute>
</xsd:complexType>
<xsd:complexType name="ShellAction">
<xsd:complexContent>
<xsd:extension base="Action">
<xsd:attribute name="command" type="xsd:string"
use="required">
</xsd:attribute>
<xsd:attribute name="exitValue" type="xsd:int" default="0"></xsd:attribute>
<xsd:attribute name="checkExitValue"
type="xsd:boolean" default="true">
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="Detector" abstract="true">
<xsd:attribute name="id" type="xsd:ID" use="required"></xsd:attribute>
</xsd:complexType>
<xsd:complexType name="SimpleDetector" abstract="true">
<xsd:complexContent>
<xsd:extension base="Detector"></xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="ComplexDetector">
<xsd:complexContent>
<xsd:extension base="Detector">
<xsd:sequence>
<xsd:element name="preAction" type="Action" maxOccurs="unbounded" minOccurs="0"></xsd:element>
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required"></xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="ArrayDetector">
<xsd:complexContent>
<xsd:extension base="ComplexDetector">
<xsd:attribute name="arraySize" type="xsd:int" use="required"></xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="ScalarDetector">
<xsd:complexContent>
<xsd:extension base="ComplexDetector">
<xsd:attribute name="type" use="optional" default="Double">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="Double"></xsd:enumeration>
<xsd:enumeration value="String"></xsd:enumeration>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="Timestamp">
<xsd:complexContent>
<xsd:extension base="SimpleDetector"></xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="SimpleScalarDetector">
<xsd:complexContent>
<xsd:extension base="SimpleDetector">
<xsd:attribute name="name" type="xsd:string" use="required"></xsd:attribute>
<xsd:attribute name="scr" type="xsd:boolean" default="false" use="optional"></xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="ScalerChannel">
<xsd:complexContent>
<xsd:extension base="SimpleDetector">
<xsd:attribute name="channel" use="required">
<xsd:simpleType>
<xsd:restriction base="xsd:int">
<xsd:maxInclusive value="15"></xsd:maxInclusive>
<xsd:minInclusive value="0"></xsd:minInclusive>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="DiscreteStepDimension">
<xsd:complexContent>
<xsd:extension base="Dimension">
<xsd:sequence>
<xsd:element name="preAction" type="Action" minOccurs="0" maxOccurs="unbounded"></xsd:element>
<xsd:element name="positioner" type="DiscreteStepPositioner" maxOccurs="unbounded" minOccurs="0"></xsd:element>
<xsd:element name="action" type="Action" minOccurs="0" maxOccurs="unbounded"></xsd:element>
<xsd:element name="guard" type="Guard" maxOccurs="1" minOccurs="0"></xsd:element>
<xsd:element name="detector" type="Detector" maxOccurs="unbounded" minOccurs="0"></xsd:element>
<xsd:element name="postAction" type="Action" minOccurs="0" maxOccurs="unbounded"></xsd:element>
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="ContinuousDimension">
<xsd:complexContent>
<xsd:extension base="Dimension">
<xsd:sequence>
<xsd:element name="preAction" type="Action" minOccurs="0" maxOccurs="unbounded"></xsd:element>
<xsd:element name="positioner" type="ContinuousPositioner" maxOccurs="1" minOccurs="1"></xsd:element>
<xsd:element name="detector" type="SimpleScalarDetector" maxOccurs="unbounded" minOccurs="0"></xsd:element>
<xsd:element name="scaler" type="ScalerChannel" maxOccurs="unbounded" minOccurs="0"></xsd:element>
<xsd:element name="timestamp" type="Timestamp" maxOccurs="1" minOccurs="0"></xsd:element>
<xsd:element name="postAction" type="Action" minOccurs="0" maxOccurs="unbounded"></xsd:element>
</xsd:sequence>
<xsd:attribute name="rasterize" type="xsd:boolean" use="optional" default="false"></xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="Scan">
<xsd:sequence>
<xsd:element name="preAction" type="Action"
maxOccurs="unbounded" minOccurs="0">
</xsd:element>
<xsd:element name="cdimension" type="ContinuousDimension"
maxOccurs="1" minOccurs="0">
</xsd:element>
<xsd:element name="dimension" type="DiscreteStepDimension"
maxOccurs="unbounded" minOccurs="0">
</xsd:element>
<xsd:element name="postAction" type="Action"
maxOccurs="unbounded" minOccurs="0">
</xsd:element>
<xsd:element name="manipulation" type="Manipulation" maxOccurs="unbounded" minOccurs="0"></xsd:element>
</xsd:sequence>
<xsd:attribute name="id" type="xsd:string" use="optional"></xsd:attribute>
</xsd:complexType>
<xsd:complexType name="Configuration">
<xsd:sequence>
<xsd:element name="notification" type="Notification" minOccurs="0" maxOccurs="1"></xsd:element>
<xsd:element name="data" type="Data" maxOccurs="1" minOccurs="0"></xsd:element>
<xsd:element name="description" type="xsd:string" maxOccurs="1" minOccurs="0"></xsd:element>
<xsd:element name="variable" type="Variable" maxOccurs="unbounded" minOccurs="0"></xsd:element>
<xsd:element name="scan" type="Scan" maxOccurs="1" minOccurs="1"></xsd:element>
<xsd:element name="visualization" type="Visualization" maxOccurs="unbounded" minOccurs="0"></xsd:element>
</xsd:sequence>
<xsd:attribute name="numberOfExecution" type="xsd:int" use="optional" default="1"></xsd:attribute>
<xsd:attribute name="failOnSensorError" type="xsd:boolean" default="true" use="optional"></xsd:attribute>
</xsd:complexType>
<xsd:element name="configuration" type="Configuration"></xsd:element>
<xsd:complexType name="DetectorOfDetectors">
<xsd:complexContent>
<xsd:extension base="ComplexDetector">
<xsd:sequence>
<xsd:element name="detector" type="ScalarDetector" maxOccurs="unbounded" minOccurs="0"></xsd:element>
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="ParameterMapping">
<xsd:attribute name="variable" type="xsd:string"
use="required">
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="Manipulation" abstract="true">
<xsd:attribute name="id" type="xsd:ID" use="required"></xsd:attribute>
</xsd:complexType>
<xsd:complexType name="ScriptManipulation">
<xsd:complexContent>
<xsd:extension base="Manipulation">
<xsd:sequence>
<xsd:element name="mapping" type="ParameterMapping" maxOccurs="unbounded" minOccurs="0"></xsd:element>
<xsd:element name="script" type="xsd:string" maxOccurs="1" minOccurs="1"></xsd:element>
</xsd:sequence>
<xsd:attribute name="returnArray" type="xsd:boolean" default="false"></xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="PseudoPositioner">
<xsd:complexContent>
<xsd:extension base="DiscreteStepPositioner">
<xsd:sequence>
<xsd:element name="counts" type="xsd:int" maxOccurs="1" minOccurs="1"></xsd:element>
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="IDParameterMapping">
<xsd:complexContent>
<xsd:extension base="ParameterMapping">
<xsd:attribute name="refid" type="xsd:IDREF"></xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="ChannelParameterMapping">
<xsd:complexContent>
<xsd:extension base="ParameterMapping">
<xsd:attribute name="channel" type="xsd:string"></xsd:attribute>
<xsd:attribute name="type" default="Double" use="optional">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="Double"></xsd:enumeration>
<xsd:enumeration value="Integer"></xsd:enumeration>
<xsd:enumeration value="String"></xsd:enumeration>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="ScriptAction">
<xsd:complexContent>
<xsd:extension base="Action">
<xsd:sequence>
<xsd:element name="mapping" type="ChannelParameterMapping" maxOccurs="unbounded" minOccurs="0"></xsd:element>
<xsd:element name="script" type="xsd:string"
maxOccurs="1" minOccurs="1">
</xsd:element>
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="GuardCondition">
<xsd:attribute name="channel" type="xsd:string"
use="required">
</xsd:attribute>
<xsd:attribute name="value" type="xsd:string" use="required"></xsd:attribute>
<xsd:attribute name="type" default="Integer">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="Double"></xsd:enumeration>
<xsd:enumeration value="Integer"></xsd:enumeration>
<xsd:enumeration value="String"></xsd:enumeration>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
</xsd:complexType>
<xsd:complexType name="Guard">
<xsd:sequence>
<xsd:element name="condition" type="GuardCondition" maxOccurs="unbounded" minOccurs="1"></xsd:element>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="Notification">
<xsd:sequence>
<xsd:element name="recipient" type="Recipient" minOccurs="0" maxOccurs="unbounded"></xsd:element>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="Recipient">
<xsd:simpleContent>
<xsd:extension base="xsd:string">
<xsd:attribute name="error" type="xsd:boolean" default="true"></xsd:attribute>
<xsd:attribute name="success" type="xsd:boolean" default="false" use="optional"></xsd:attribute>
</xsd:extension>
</xsd:simpleContent>
</xsd:complexType>
<xsd:complexType name="FunctionPositioner">
<xsd:complexContent>
<xsd:extension base="DiscreteStepPositioner">
<xsd:sequence>
<xsd:element name="start" type="xsd:double" maxOccurs="1" minOccurs="1"></xsd:element>
<xsd:element name="end" type="xsd:double" maxOccurs="1" minOccurs="1"></xsd:element>
<xsd:element name="stepSize" type="xsd:double" maxOccurs="1" minOccurs="1"></xsd:element>
<xsd:element name="function" type="Function" maxOccurs="1" minOccurs="1"></xsd:element>
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="LinePlotArray">
<xsd:complexContent>
<xsd:extension base="Visualization">
<xsd:attribute name="y" type="xsd:IDREFS" use="required"></xsd:attribute>
<xsd:attribute name="maxSeries" type="xsd:int" default="1" use="optional"></xsd:attribute>
<xsd:attribute name="offset" type="xsd:int" use="optional" default="0"></xsd:attribute>
<xsd:attribute name="size" type="xsd:int" use="optional" default="0"></xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="MatrixPlotArray">
<xsd:complexContent>
<xsd:extension base="Visualization">
<xsd:attribute name="y" type="xsd:IDREF" use="required"></xsd:attribute>
<xsd:attribute name="z" type="xsd:IDREF" use="required"></xsd:attribute>
<xsd:attribute name="offset" type="xsd:int" use="optional" default="0"></xsd:attribute>
<xsd:attribute name="size" type="xsd:int" use="optional" default="0"></xsd:attribute>
<xsd:attribute name="type" default="2D"
use="optional">
<xsd:simpleType>
<xsd:restriction base="xsd:string">
<xsd:enumeration value="2D"></xsd:enumeration>
<xsd:enumeration value="3D"></xsd:enumeration>
</xsd:restriction>
</xsd:simpleType>
</xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
<xsd:complexType name="Function">
<xsd:sequence>
<xsd:element name="mapping" type="ParameterMapping" minOccurs="0" maxOccurs="unbounded"></xsd:element>
<xsd:element name="script" type="xsd:string" minOccurs="1" maxOccurs="1"></xsd:element>
</xsd:sequence>
</xsd:complexType>
<xsd:complexType name="Variable">
<xsd:attribute name="name" type="xsd:ID" use="required"></xsd:attribute>
<xsd:attribute name="value" type="xsd:double" use="required"></xsd:attribute>
<xsd:attribute name="description" type="xsd:string" use="optional"></xsd:attribute>
</xsd:complexType>
<xsd:complexType name="VariableParameterMapping">
<xsd:complexContent>
<xsd:extension base="ParameterMapping">
<xsd:attribute name="name" type="xsd:IDREF" use="required"></xsd:attribute>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
</xsd:schema>
@@ -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.aq;
import java.io.File;
import java.net.URI;
import java.net.URL;
import java.util.logging.Logger;
import junit.framework.Assert;
import org.junit.Before;
import org.junit.Test;
import ch.psi.fda.aq.AcquisitionConfiguration;
public class AcquisitionConfigurationTest {
private static Logger logger = Logger.getLogger(AcquisitionConfigurationTest.class.getName());
@Before
public void setUp() throws Exception {
URL url = this.getClass().getClassLoader().getResource("home/config/fda.properties");
System.setProperty("ch.psi.fda.config.file", new File(new URI(url.toString())).getAbsolutePath());
}
/**
* Test method for {@link ch.psi.fda.aq.AcquisitionConfiguration#getDataFilePrefix()}.
* Test whether the supported date macros in the file prefix are resolved
*/
@Test
public void testGetConfiguration() {
AcquisitionConfiguration configuration = new AcquisitionConfiguration();
String s = configuration.getDataFilePrefix();
if(s==null){
Assert.fail("No configuration returned for data file prefix");
}
logger.info("Data file prefix: "+s);
}
}
@@ -0,0 +1,232 @@
/**
*
* 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 static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import ch.psi.fda.aq.Collector;
import ch.psi.fda.messages.ControlMessage;
import ch.psi.fda.messages.DataMessage;
import ch.psi.fda.messages.EndOfStreamMessage;
import ch.psi.fda.messages.Message;
import ch.psi.fda.messages.Metadata;
public class CollectorTest {
private static Logger logger = Logger.getLogger(CollectorTest.class.getName());
@Before
public void setUp() throws Exception {
}
@After
public void tearDown() throws Exception {
}
/**
* Test method for {@link ch.psi.fda.aq.Collector#run()}.
*
* @throws InterruptedException
*/
@Test
public void testRun() throws InterruptedException {
EventBus bus = new EventBus();
// Metadata
List<Metadata> m0;
List<Metadata> m1;
List<Metadata> m2;
m0 = new ArrayList<>();
m0.add(new Metadata("id0-0", 0));
m1 = new ArrayList<>();
m1.add(new Metadata("id1-0", 1));
m2 = new ArrayList<>();
m2.add(new Metadata("id2-0", 2));
EventBus b0 = new EventBus();
EventBus b1 = new EventBus();
EventBus b2 = new EventBus();
Collector collector = new Collector(bus);
collector.addEventBus(b0);
collector.addEventBus(b1);
collector.addEventBus(b2);
// Check component metadata of output queue
// check wether messages arrive
bus.register(new Object() {
private boolean first = true;
@Subscribe
public void onMessage(Message m) {
if (m instanceof DataMessage) {
DataMessage x = (DataMessage) m;
if (first) {
first = false;
// Check metadata
int c = 2;
for (Metadata cm : x.getMetadata()) {
logger.info(String.format("id: %s dimension: %s", cm.getId(),cm.getDimension()));
if (cm.getDimension() != c) {
fail("Dimension number does not match required dimension number");
}
if (!cm.getId().equals("id" + c + "-0")) {
fail("Id does not match required id");
}
c--;
}
}
logger.info(x.toString());
} else if (m instanceof ControlMessage) {
logger.info("---- " + m.toString() + " ----");
}
}
});
// dimension 2
DataMessage m = new DataMessage(m2);
m.getData().add(1.0);
b2.post(m);
// dimension 1
m = new DataMessage(m1);
m.getData().add(0.1);
b1.post(m);
// dimension 0
m = new DataMessage(m0);
m.getData().add(0.01);
b0.post(m);
m = new DataMessage(m0);
m.getData().add(0.02);
b0.post(m);
b0.post(new EndOfStreamMessage());
// dimension 1
m = new DataMessage(m1);
m.getData().add(0.2);
b1.post(m);
// dimension 0
m = new DataMessage(m0);
m.getData().add(0.01);
b0.post(m);
m = new DataMessage(m0);
m.getData().add(0.02);
b0.post(m);
b0.post(new EndOfStreamMessage());
// dimension 1
b1.post(new EndOfStreamMessage());
// dimension 2
m = new DataMessage(m2);
m.getData().add(2.0);
b2.post(m);
// dimension 1
m = new DataMessage(m1);
m.getData().add(0.1);
b1.post(m);
// dimension 0
m = new DataMessage(m0);
m.getData().add(0.01);
b0.post(m);
m = new DataMessage(m0);
m.getData().add(0.02);
b0.post(m);
b0.post(new EndOfStreamMessage());
// dimension 1
m = new DataMessage(m1);
m.getData().add(0.2);
b1.post(m);
// dimension 0
m = new DataMessage(m0);
m.getData().add(0.01);
b0.post(m);
m = new DataMessage(m0);
m.getData().add(0.02);
b0.post(m);
b0.post(new EndOfStreamMessage());
// dimension 1
b1.post(new EndOfStreamMessage());
// dimension 2
m = new DataMessage(m2);
m.getData().add(3.0);
b2.post(m);
// dimension 1
m = new DataMessage(m1);
m.getData().add(0.1);
b1.post(m);
// dimension 0
m = new DataMessage(m0);
m.getData().add(0.01);
b0.post(m);
m = new DataMessage(m0);
m.getData().add(0.02);
b0.post(m);
b0.post(new EndOfStreamMessage());
// dimension 1
m = new DataMessage(m1);
m.getData().add(0.2);
b1.post(m);
// dimension 0
m = new DataMessage(m0);
m.getData().add(0.01);
b0.post(m);
m = new DataMessage(m0);
m.getData().add(0.02);
b0.post(m);
b0.post(new EndOfStreamMessage());
// dimension 1
b1.post(new EndOfStreamMessage());
// dimension 2
b2.post(new EndOfStreamMessage());
}
}
@@ -0,0 +1,490 @@
/**
*
* 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 static org.junit.Assert.*;
import gov.aps.jca.CAException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import java.util.logging.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import ch.psi.fda.core.TestChannels;
import ch.psi.fda.aq.Manipulator;
import ch.psi.fda.core.Manipulation;
import ch.psi.fda.core.manipulator.JythonManipulation;
import ch.psi.fda.core.scripting.JythonParameterMapping;
import ch.psi.fda.core.scripting.JythonParameterMappingChannel;
import ch.psi.fda.core.scripting.JythonParameterMappingID;
import ch.psi.fda.messages.DataMessage;
import ch.psi.fda.messages.EndOfStreamMessage;
import ch.psi.fda.messages.Message;
import ch.psi.fda.messages.Metadata;
import ch.psi.jcae.Channel;
import ch.psi.jcae.ChannelDescriptor;
import ch.psi.jcae.ChannelException;
import ch.psi.jcae.ChannelService;
import ch.psi.jcae.impl.DefaultChannelService;
public class ManipulatorTest {
private static Logger logger = Logger.getLogger(ManipulatorTest.class.getName());
private EventBus bus;
private ChannelService cservice;
@Before
public void setUp() throws Exception {
bus = new EventBus();
cservice = new DefaultChannelService();
}
@After
public void tearDown() throws Exception {
cservice.destroy();
}
@Test
public void testConstructorNoMappingNoParam() {
String id="cid";
String script = "import math\ndef process():\n return 0.0";
List<JythonParameterMapping> mapping = new ArrayList<JythonParameterMapping>();
mapping.add(new JythonParameterMappingID("o", "myid"));
JythonManipulation manipulation = new JythonManipulation(id, script, mapping);
List<Manipulation> manipulations = new ArrayList<Manipulation>();
manipulations.add(manipulation);
new Manipulator(bus, manipulations);
// Expect IllegalArgument Exception as there is no mapping for the parameter c
}
@Test
public void testConstructorMappingNoParam() {
List<Metadata> dmm = new ArrayList<>();
dmm.add(new Metadata("myid"));
dmm.add(new Metadata("myid2"));
String id="cid";
String script = "import math\ndef process():\n return 0.0";
List<JythonParameterMapping> mapping = new ArrayList<JythonParameterMapping>();
mapping.add(new JythonParameterMappingID("o", "myid"));
JythonManipulation manipulation = new JythonManipulation(id, script, mapping);
List<Manipulation> manipulations = new ArrayList<Manipulation>();
manipulations.add(manipulation);
new Manipulator(bus, manipulations).onMessage(new DataMessage(dmm));
}
/**
* Test method for {@link ch.psi.fda.aq.Manipulator#Manipulator()}.
*/
@Test(expected=IllegalArgumentException.class)
public void testConstructorNoMapping() {
List<Metadata> dmm = new ArrayList<>();
dmm.add(new Metadata("myid"));
dmm.add(new Metadata("myid2"));
String id="cid";
String script = "import math\ndef process(o ,c):\n return math.cos(c) + math.sin(o)";
List<JythonParameterMapping> mapping = new ArrayList<JythonParameterMapping>();
mapping.add(new JythonParameterMappingID("o", "myid"));
JythonManipulation manipulation = new JythonManipulation(id, script, mapping);
List<Manipulation> manipulations = new ArrayList<Manipulation>();
manipulations.add(manipulation);
new Manipulator(bus, manipulations).onMessage(new DataMessage(dmm));;
// Expect IllegalArgument Exception as there is no mapping for the parameter c
}
/**
* Test method for {@link ch.psi.fda.aq.Manipulator#run()}.
* @throws InterruptedException
*/
@Test
public void testRun() throws InterruptedException {
List<Metadata> dmm = new ArrayList<>();
dmm.add(new Metadata("myid"));
String id="cid";
String script = "import math\ndef process(o):\n return math.cos(10.0) + math.sin(o)";
List<JythonParameterMapping> mapping = new ArrayList<JythonParameterMapping>();
mapping.add(new JythonParameterMappingID("o", "myid"));
JythonManipulation manipulation = new JythonManipulation(id, script, mapping);
List<Manipulation> manipulations = new ArrayList<Manipulation>();
manipulations.add(manipulation);
Manipulator manipulator = new Manipulator(bus, manipulations);
bus.register(new Object(){
boolean first = true;
@Subscribe
public void onMessage(Message message){
logger.info(message.toString());
if(message instanceof DataMessage){
DataMessage dm = (DataMessage) message;
if(first){
first=false;
// Check whether output queue message structur complies to expected one
// Test whether only the expected components are within the outgoing queue
if(dm.getMetadata().size()!=2){
fail("There are more than the expected components in the outgoing message");
}
// Test whether the id of the first component matches the expected id
if(!dm.getMetadata().get(0).getId().equals("myid")){
fail("Id of the first component does not match the expected id 'myid'");
}
// Test whether the id of the second component (which was computed) matches the expected id
if(!dm.getMetadata().get(1).getId().equals("cid")){
fail("Id of the second component does not match the expected id 'cid'");
}
}
dm.getData().get(0);
double res = ((Double)dm.getData().get(1)) - (Math.cos(10.0)+Math.sin(((Double)dm.getData().get(0))));
if( Math.abs(res) > 0.000000001){
fail("Calculation failed");
}
}
}
});
EventBus b = new EventBus();
b.register(manipulator);
DataMessage m = new DataMessage(dmm);
m.getData().add(10d);
b.post(m);
b.post(new EndOfStreamMessage());
logger.info(""+(Math.cos(10.0)+Math.sin(10)));
}
/**
* Test run that returns an integer
* @throws InterruptedException
*/
@Test
public void testRunIntegerReturn() throws InterruptedException {
List<Metadata> dmm = new ArrayList<>();
dmm.add(new Metadata("myid"));
String id="cid";
String script = "import math\ndef process(o):\n return 1";
List<JythonParameterMapping> mapping = new ArrayList<JythonParameterMapping>();
mapping.add(new JythonParameterMappingID("o", "myid"));
JythonManipulation manipulation = new JythonManipulation(id, script, mapping);
List<Manipulation> manipulations = new ArrayList<Manipulation>();
manipulations.add(manipulation);
Manipulator manipulator = new Manipulator(bus, manipulations);
bus.register(new Object(){
boolean first = true;
@Subscribe
public void onMessage(Message message){
logger.info(message.toString());
if(message instanceof DataMessage){
DataMessage dm = (DataMessage) message;
if(first){
first=false;
// Check whether output queue message structur complies to expected one
// Test whether only the expected components are within the outgoing queue
if(dm.getMetadata().size()!=2){
fail("There are more than the expected components in the outgoing message");
}
// Test whether the id of the first component matches the expected id
if(!dm.getMetadata().get(0).getId().equals("myid")){
fail("Id of the first component does not match the expected id 'myid'");
}
// Test whether the id of the second component (which was computed) matches the expected id
if(!dm.getMetadata().get(1).getId().equals("cid")){
fail("Id of the second component does not match the expected id 'cid'");
}
}
dm.getData().get(0);
}
}
});
EventBus b = new EventBus();
b.register(manipulator);
DataMessage m = new DataMessage(dmm);
m.getData().add(10d);
b.post(m);
b.post(new EndOfStreamMessage());
}
/**
* Test method for {@link ch.psi.fda.aq.Manipulator#run()}.
* @throws InterruptedException
*/
@Ignore
@Test
public void testRunLongTimeTest() throws InterruptedException {
List<Metadata> dmm = new ArrayList<>();
dmm.add(new Metadata("myid"));
String id="cid";
String script = "import math\ndef process(o):\n return math.cos(10.0) + math.sin(o)";
List<JythonParameterMapping> mapping = new ArrayList<JythonParameterMapping>();
mapping.add(new JythonParameterMappingID("o", "myid"));
JythonManipulation manipulation = new JythonManipulation(id, script, mapping);
List<Manipulation> manipulations = new ArrayList<Manipulation>();
manipulations.add(manipulation);
Manipulator manipulator = new Manipulator(bus, manipulations);
bus.register(new Object(){
int count=0;
@Subscribe
public void onMessage(Message message){
if(!(message instanceof EndOfStreamMessage)){
logger.info(count+" - "+message.toString());
count++;
}
}
});
EventBus b = new EventBus();
b.register(manipulator);
for(Double i=0d;i<1000000;i++){
DataMessage m = new DataMessage(dmm);
m.getData().add(i);
b.post(m);
}
b.post(new EndOfStreamMessage());
}
/**
* Test method for {@link ch.psi.fda.aq.Manipulator#run()}.
* @throws InterruptedException
*/
@Test
public void testRunMultipleParameter() throws InterruptedException {
List<Metadata> dmm = new ArrayList<>();
dmm.add(new Metadata("myid"));
dmm.add(new Metadata("myid2"));
String id="cid";
String script = "import math\ndef process(o ,c):\n return math.cos(c) + math.sin(o)";
List<JythonParameterMapping> mapping = new ArrayList<JythonParameterMapping>();
mapping.add(new JythonParameterMappingID("o", "myid"));
mapping.add(new JythonParameterMappingID("c", "myid2"));
JythonManipulation manipulation = new JythonManipulation(id, script, mapping);
List<Manipulation> manipulations = new ArrayList<Manipulation>();
manipulations.add(manipulation);
Manipulator manipulator = new Manipulator(bus, manipulations);
bus.register(new Object(){
boolean first = true;
@Subscribe
public void onMessage(Message message){
logger.info(message.toString());
if(message instanceof DataMessage){
DataMessage dm = (DataMessage) message;
if(first){
first=false;
// Check whether output queue message structur complies to expected one
// Test whether only the expected components are within the outgoing queue
if(dm.getMetadata().size()!=3){
fail("There are more than the expected components in the outgoing message");
}
// Test whether the id of the first component matches the expected id
if(!dm.getMetadata().get(0).getId().equals("myid")){
fail("Id of the first component does not match the expected id 'myid'");
}
if(!dm.getMetadata().get(1).getId().equals("myid2")){
fail("Id of the first component does not match the expected id 'myid'");
}
// Test whether the id of the second component (which was computed) matches the expected id
if(!dm.getMetadata().get(2).getId().equals("cid")){
fail("Id of the second component does not match the expected id 'cid'");
}
}
dm.getData().get(0);
double res = ((Double)dm.getData().get(2)) - (Math.cos(((Double)dm.getData().get(1)))+Math.sin(((Double)dm.getData().get(0))));
if( Math.abs(res) > 0.000000001){
fail("Calculation failed");
}
}
}
});
EventBus b = new EventBus();
b.register(manipulator);
DataMessage m = new DataMessage(dmm);
m.getData().add(10d);
m.getData().add(0.2d);
b.post(m);
b.post(new EndOfStreamMessage());
logger.info(""+(Math.cos(0.2)+Math.sin(10)));
}
/**
* Test method for {@link ch.psi.fda.aq.Manipulator#run()}.
* @throws InterruptedException
* @throws CAException
* @throws TimeoutException
* @throws ChannelException
* @throws ExecutionException
*/
@Test
public void testRunMultipleParameterAndChannel() throws InterruptedException, CAException, ChannelException, TimeoutException, ExecutionException {
Double setValue = 12.22;
Channel<Double> channel = cservice.createChannel(new ChannelDescriptor<>(Double.class, TestChannels.ANALOG_OUT));
List<Metadata> dmm = new ArrayList<>();
dmm.add(new Metadata("myid"));
dmm.add(new Metadata("myid2"));
String id="cid";
String script = "import math\ndef process(o ,c,d):\n d.setValue("+setValue+")\n return math.cos(c) + math.sin(o)";
List<JythonParameterMapping> mapping = new ArrayList<JythonParameterMapping>();
mapping.add(new JythonParameterMappingID("o", "myid"));
mapping.add(new JythonParameterMappingID("c", "myid2"));
mapping.add(new JythonParameterMappingChannel<Double>("d", channel));
JythonManipulation manipulation = new JythonManipulation(id, script, mapping);
List<Manipulation> manipulations = new ArrayList<Manipulation>();
manipulations.add(manipulation);
Manipulator manipulator = new Manipulator(bus, manipulations);
// Change something different on the channel than the value that will be set in the manipulator script
channel.setValue(setValue+1);
bus.register(new Object(){
boolean first = true;
@Subscribe
public void onMessage(Message message){
logger.info(message.toString());
if(message instanceof DataMessage){
DataMessage dm = (DataMessage) message;
if(first){
first=false;
// Check whether output queue message structur complies to expected one
// Test whether only the expected components are within the outgoing queue
if(dm.getMetadata().size()!=3){
fail("There are more than the expected components in the outgoing message");
}
// Test whether the id of the first component matches the expected id
if(!dm.getMetadata().get(0).getId().equals("myid")){
fail("Id of the first component does not match the expected id 'myid'");
}
if(!dm.getMetadata().get(1).getId().equals("myid2")){
fail("Id of the first component does not match the expected id 'myid'");
}
// Test whether the id of the second component (which was computed) matches the expected id
if(!dm.getMetadata().get(2).getId().equals("cid")){
fail("Id of the second component does not match the expected id 'cid'");
}
}
dm.getData().get(0);
double res = ((Double)dm.getData().get(2)) - (Math.cos(((Double)dm.getData().get(1)))+Math.sin(((Double)dm.getData().get(0))));
if( Math.abs(res) > 0.000000001){
fail("Calculation failed");
}
}
}
});
EventBus b = new EventBus();
b.register(manipulator);
DataMessage m = new DataMessage(dmm);
m.getData().add(10d);
m.getData().add(0.2d);
b.post(m);
b.post(new EndOfStreamMessage());
logger.info(""+(Math.cos(0.2)+Math.sin(10)));
// Check whether the channel was set correctly by the manipulator script
if(Math.abs(channel.getValue()-setValue)>0.00000001){
fail("Channel was not set correctly in the manipulator script");
}
}
}
@@ -0,0 +1,39 @@
/**
*
* Copyright 2013 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This code is distributed in the hope that it will be useful, but without any
* warranty; without even the implied warranty of merchantability or fitness for
* a particular purpose. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.aq;
import org.junit.Ignore;
import org.junit.Test;
import ch.psi.fda.aq.NotificationAgent;
import ch.psi.fda.model.v1.Recipient;
public class NotificationAgentTest {
@Ignore
@Test
public void testSendNotification() {
NotificationAgent a = new NotificationAgent("mail.psi.ch", "supertoll@psi.ch");
Recipient r = new Recipient();
r.setValue("simon.ebner@psi.ch");
a.getRecipients().add(r);
a.sendNotification("super","hello worl this is a test", true, true);
}
}
@@ -0,0 +1,31 @@
package ch.psi.fda.aq;
import java.util.ServiceLoader;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import ch.psi.fda.EContainerFactory;
public class XScanDescriptorProviderTest {
@Before
public void setUp() throws Exception {
}
@After
public void tearDown() throws Exception {
}
@Test
public void test() {
ServiceLoader<EContainerFactory> factories = ServiceLoader.load(EContainerFactory.class);
for (EContainerFactory factory : factories) {
System.out.println(factory.getClass().getName());
}
System.out.println("done");
}
}
@@ -0,0 +1,34 @@
package ch.psi.fda.core;
/**
*
* Copyright 2011 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/>.
*
*/
public class TestChannels {
// TODO Need to create softio with test channels !!!!
public static final String BINARY_OUT = "MTEST-PC-FDA:BO";
public static final String BINARY_OUT_TWO = "MTEST-PC-FDA:BO2";
public static final String ANALOG_OUT = "MTEST-PC-FDA:AO";
public static final String STRING_OUT = "MTEST-PC-FDA:SOUT";
public static final String DOUBLE_WAVEFORM = "MTEST-PC-FDA:DWAVE";
public static final String STRING_OUT_NOT_EXIST = "MTEST-PC-FDA:SOUT-NOT-EXIST";
public static final String MOTOR_ONE = "MTEST-HW3:MOT1";
}
@@ -0,0 +1,232 @@
/**
*
* 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 static org.junit.Assert.fail;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import java.util.logging.Logger;
import gov.aps.jca.CAException;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import ch.psi.fda.core.TestChannels;
import ch.psi.fda.core.actions.ChannelAccessCondition;
import ch.psi.jcae.Channel;
import ch.psi.jcae.ChannelDescriptor;
import ch.psi.jcae.ChannelException;
import ch.psi.jcae.ChannelService;
import ch.psi.jcae.impl.DefaultChannelService;
import ch.psi.jcae.util.ComparatorREGEX;
public class ChannelAccessConditionTest {
private static Logger logger = Logger.getLogger(ChannelAccessConditionTest.class.getName());
private ChannelService cservice;
@Before
public void setUp() throws Exception {
cservice = new DefaultChannelService();
}
@After
public void tearDown() throws Exception {
cservice.destroy();
}
/**
* Test method for {@link ch.psi.fda.core.actions.ChannelAccessStringCondition#ChannelAccessStringCondition(java.lang.String, java.lang.String, long)}.
* @throws InterruptedException
* @throws CAException
* @throws TimeoutException
* @throws ChannelException
* @throws ExecutionException
*/
@Test
public void testChannelAccessStringCondition() throws InterruptedException, CAException, ChannelException, TimeoutException, ExecutionException {
final Channel<String> channel = cservice.createChannel(new ChannelDescriptor<>(String.class, TestChannels.STRING_OUT));
channel.setValue("SomeValue");
ChannelAccessCondition<String> c = new ChannelAccessCondition<String>(channel, "SomeValue", 1000l);
c.execute();
}
@Test
public void testChannelAccessStringConditionRegex() throws InterruptedException, CAException, ExecutionException, ChannelException, TimeoutException {
final Channel<String> channel = cservice.createChannel(new ChannelDescriptor<>(String.class, TestChannels.STRING_OUT));
channel.setValue("SomeValue");
ChannelAccessCondition<String> c = new ChannelAccessCondition<>(channel, "Some.*",new ComparatorREGEX(), 1000l);
c.execute();
}
/**
* Test method for {@link ch.psi.fda.core.actions.ChannelAccessStringCondition#execute()}.
* @throws CAException
* @throws InterruptedException
* @throws TimeoutException
* @throws ChannelException
* @throws ExecutionException
*/
@Test
public void testExecute() throws CAException, InterruptedException, ChannelException, TimeoutException, ExecutionException {
String value = "SomeValue";
final Channel<String> channel = cservice.createChannel(new ChannelDescriptor<>(String.class, TestChannels.STRING_OUT));
final ChannelAccessCondition<String> action = new ChannelAccessCondition<String>(channel, value, 10000l);
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
action.execute();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
// Set the value to something else than the expected value
channel.setValue(value+"hello");
// Start new thread that is executing the actions execute() method
logger.fine("Execute action");
t.start();
// Wait some time and set the channel to the expected value
Thread.sleep(1000);
logger.fine("Set channel value to "+value);
channel.setValue(value);
// Wait thread to join and check whether the thread has terminated
t.join(100);
if(t.isAlive()){
fail("Action did not return although the value was reached");
}
}
@Test(expected=RuntimeException.class)
public void testExecuteFail() throws CAException, InterruptedException, ExecutionException, ChannelException, TimeoutException {
String value = "SomeValue";
final Channel<String> channel = cservice.createChannel(new ChannelDescriptor<>(String.class, TestChannels.STRING_OUT));
final ChannelAccessCondition<String> action = new ChannelAccessCondition<String>(channel, value, 9000l);
// Set the value to something else than the expected value
channel.setValue(value+"hello");
action.execute();
}
/**
* Test method for {@link ch.psi.fda.core.actions.ChannelAccessStringCondition#execute()}.
* @throws CAException
* @throws InterruptedException
* @throws TimeoutException
* @throws ChannelException
* @throws ExecutionException
*/
@Test
public void testExecuteInteger() throws CAException, InterruptedException, ChannelException, TimeoutException, ExecutionException {
Integer value = 12;
final Channel<Integer> channel = cservice.createChannel(new ChannelDescriptor<>(Integer.class, TestChannels.STRING_OUT));
final ChannelAccessCondition<Integer> action = new ChannelAccessCondition<Integer>(channel, value, 10000l);
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
action.execute();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
// Set the value to something else than the expected value
channel.setValue(value+1);
// Start new thread that is executing the actions execute() method
logger.fine("Execute action");
t.start();
// Wait some time and set the channel to the expected value
Thread.sleep(1000);
logger.fine("Set channel value to "+value);
channel.setValue(value);
// Wait thread to join and check whether the thread has terminated
t.join(100);
if(t.isAlive()){
fail("Action did not return although the value was reached");
}
}
/**
* Test to ensure that the abort function is working correctly
* @throws CAException
* @throws InterruptedException
* @throws TimeoutException
* @throws ChannelException
* @throws ExecutionException
*/
@Test
public void testWaitAbort() throws CAException, InterruptedException, ChannelException, TimeoutException, ExecutionException {
final Channel<Integer> channel = cservice.createChannel(new ChannelDescriptor<>(Integer.class, TestChannels.BINARY_OUT));
final ChannelAccessCondition<Integer> action = new ChannelAccessCondition<Integer>(channel, 1, 10000l);
Thread t = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
logger.info("Send abort ...");
action.abort();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
// Set wait channel to 0
channel.setValue(0);
t.start();
action.execute(); // timeouts after 10 seconds
}
}
@@ -0,0 +1,163 @@
/**
*
* 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 static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import ch.psi.fda.core.TestChannels;
import ch.psi.fda.core.actions.ChannelAccessPut;
import ch.psi.jcae.Channel;
import ch.psi.jcae.ChannelDescriptor;
import ch.psi.jcae.ChannelException;
import ch.psi.jcae.ChannelService;
import ch.psi.jcae.impl.DefaultChannelService;
public class ChannelAccessPutTest {
private ChannelService cservice;
@Before
public void setUp() throws Exception {
cservice = new DefaultChannelService();
}
@After
public void tearDown() throws Exception {
cservice.destroy();
}
/**
* Test method for {@link ch.psi.fda.core.actions.ChannelAccessStringPut#ChannelAccessStringPut(java.lang.String, java.lang.String, boolean)}.
* Test correct initialization
* @throws TimeoutException
* @throws InterruptedException
* @throws ChannelException
*/
@Test
public void testChannelAccessStringPutStringStringBoolean() throws ChannelException, InterruptedException, TimeoutException {
new ChannelAccessPut<String>(cservice.createChannel(new ChannelDescriptor<>(String.class,TestChannels.STRING_OUT)), "SomeValue", true, null);
}
/**
* Test method for {@link ch.psi.fda.core.actions.ChannelAccessStringPut#ChannelAccessStringPut(java.lang.String, java.lang.String)}.
* Test correct initialization
* @throws TimeoutException
* @throws InterruptedException
* @throws ChannelException
*/
@Test
public void testChannelAccessStringPutStringString() throws ChannelException, InterruptedException, TimeoutException {
new ChannelAccessPut<String>(cservice.createChannel(new ChannelDescriptor<>(String.class,TestChannels.STRING_OUT)), "SomeValue");
}
/**
* Test method for {@link ch.psi.fda.core.actions.ChannelAccessStringPut#ChannelAccessStringPut(java.lang.String, java.lang.String)}.
* Test whether the correct Exception is thrown if a non existing channel is specified at creation time
* @throws TimeoutException
* @throws InterruptedException
* @throws ChannelException
*/
@Test(expected=ChannelException.class)
public void testChannelAccessStringPutStringStringNotExist() throws ChannelException, InterruptedException, TimeoutException {
new ChannelAccessPut<String>(cservice.createChannel(new ChannelDescriptor<>(String.class, TestChannels.STRING_OUT_NOT_EXIST)), "SomeValue");
}
/**
* Test method for {@link ch.psi.fda.core.actions.ChannelAccessStringPut#execute()}.
* @throws CAException
* @throws InterruptedException
* @throws TimeoutException
* @throws ChannelException
* @throws ExecutionException
*/
@Test
public void testExecute() throws InterruptedException, ChannelException, TimeoutException, ExecutionException {
String setValue = "MyTestString";
Channel<String> channel = cservice.createChannel(new ChannelDescriptor<>(String.class, TestChannels.STRING_OUT));
ChannelAccessPut<String> action = new ChannelAccessPut<String>(channel, setValue);
action.execute();
String value = channel.getValue();
if(!value.equals(setValue)){
fail("Test string was not set correctly");
}
}
/**
* Test method for {@link ch.psi.fda.core.actions.ChannelAccessStringPut#execute()}.
* @throws CAException
* @throws TimeoutException
* @throws ChannelException
* @throws ExecutionException
*/
@Test
public void testExecuteString() throws InterruptedException, ChannelException, TimeoutException, ExecutionException {
String setValue = "MyTestString";
Channel<String> channel = cservice.createChannel(new ChannelDescriptor<>(String.class, TestChannels.STRING_OUT));
// Set channel to something else
channel.setValue(setValue+"test");
ChannelAccessPut<String> action = new ChannelAccessPut<String>(channel, setValue);
action.execute();
String value = channel.getValue();
if(!value.equals(setValue)){
fail("Test string was not set correctly");
}
}
/**
* Test method for {@link ch.psi.fda.core.actions.ChannelAccessStringPut#execute()}.
* @throws CAException
* @throws InterruptedException
* @throws TimeoutException
* @throws ChannelException
*/
@Test
public void testExecuteMulti() throws InterruptedException, ChannelException, TimeoutException {
List<ChannelAccessPut<String>> actions = new ArrayList<ChannelAccessPut<String>>();
for(int i=0;i<20;i++){
actions.add(new ChannelAccessPut<String>(cservice.createChannel(new ChannelDescriptor<>(String.class, TestChannels.STRING_OUT)), i+""));
}
for(ChannelAccessPut<String> action: actions){
action.execute();
}
}
}
@@ -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.actions;
import static org.junit.Assert.*;
import java.util.logging.Logger;
import org.junit.Test;
import ch.psi.fda.core.actions.Delay;
public class DelayTest {
private static Logger logger = Logger.getLogger(DelayTest.class.getName());
/**
* Test method for {@link ch.psi.fda.core.actions.Delay#Delay(long)}.
* Test correct initialization
*/
@Test
public void testDelay() {
new Delay(1000);
new Delay(1);
}
/**
* Test method for {@link ch.psi.fda.core.actions.Delay#Delay(long)}.
* Test initialization with time = 0
*/
@Test(expected=IllegalArgumentException.class)
public void testDelayZeroWait() {
new Delay(0);
}
/**
* Test method for {@link ch.psi.fda.core.actions.Delay#Delay(long)}.
* Test initialization with time < 0
*/
@Test(expected=IllegalArgumentException.class)
public void testDelayNegativeWait() {
new Delay(-10000);
}
/**
* Test method for {@link ch.psi.fda.core.actions.Delay#execute()}.
* @throws InterruptedException
*/
@Test
public void testExecute() throws InterruptedException {
long wait = 1000;
Delay action = new Delay(wait);
long start = System.currentTimeMillis();
action.execute();
long end = System.currentTimeMillis();
// Check whether wait was of the correct length
// Accept 10% "jitter" because of the delays of the function calls, etc.
long elapsed = end-start;
logger.fine("Elapsed wait time: "+elapsed+" - Specified wait time: "+wait);
if(elapsed>(wait*1.1)){
fail("Wait was not of the correct length");
}
}
}
@@ -0,0 +1,111 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core.actions;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import ch.psi.fda.core.TestChannels;
import ch.psi.fda.core.actions.JythonAction;
import ch.psi.jcae.ChannelDescriptor;
import ch.psi.jcae.ChannelException;
import ch.psi.jcae.ChannelService;
import ch.psi.jcae.impl.DefaultChannelService;
public class JythonActionTest {
private static String SCRIPT = "import math\ndef process(o):\n print o.getValue()\n o.setValue(-1.0)\n print o.getValue()\n val=math.cos(10.0) + math.sin(o.getValue())\n print val\n o.setValue(val)";
private ChannelService cservice;
@Before
public void setUp() throws Exception {
cservice = new DefaultChannelService();
}
@After
public void tearDown() throws Exception {
cservice.destroy();
}
@Test
public void testExecute() throws ChannelException, InterruptedException, TimeoutException {
try{
Map<String,Object> mapping = new HashMap<>();
mapping.put("o", cservice.createChannel(new ChannelDescriptor<>(Double.class, TestChannels.ANALOG_OUT)));
JythonAction action = new JythonAction(SCRIPT, mapping);
action.execute();
}
catch(Exception e){
e.printStackTrace();
throw e;
}
}
@Test(expected=IllegalArgumentException.class)
public void testWrongMapping1() throws ChannelException, InterruptedException, TimeoutException {
Map<String,Object> mapping = new HashMap<>();
mapping.put("o", cservice.createChannel(new ChannelDescriptor<>(Double.class, TestChannels.ANALOG_OUT)));
mapping.put("b", cservice.createChannel(new ChannelDescriptor<>(Double.class, TestChannels.ANALOG_OUT)));
String script = "import math\ndef process(o):\n print o.getValue()\n o.setValue(-1.0)\n print o.getValue()\n val=math.cos(10.0) + math.sin(o.getValue())\n print val\n o.setValue(val)";
JythonAction action = new JythonAction(script, mapping);
action.execute();
}
@Test(expected=IllegalArgumentException.class)
public void testWrongMapping2() {
Map<String,Object> mapping = new HashMap<>();
mapping.put("ooo", new String());
String script = "import math\ndef process(o):\n print o.getValue()\n o.setValue(-1.0)\n print o.getValue()\n val=math.cos(10.0) + math.sin(o.getValue())\n print val\n o.setValue(val)";
JythonAction action = new JythonAction(script, mapping);
action.execute();
}
@Test
public void testExecuteGlobalObjects() throws ChannelException, InterruptedException, TimeoutException {
try{
Map<String,Object> mapping = new HashMap<>();
mapping.put("o", cservice.createChannel(new ChannelDescriptor<>(Double.class, TestChannels.ANALOG_OUT)));
Map<String,Object> gobjects = new HashMap<>();
gobjects.put("TESTVAR", "salli!");
String script = SCRIPT = "def process(o):\n print TESTVAR";
JythonAction action = new JythonAction(script, mapping, gobjects);
action.execute();
}
catch(Exception e){
e.printStackTrace();
throw 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.actions;
import java.io.File;
import java.net.URI;
import java.net.URL;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import ch.psi.fda.core.actions.ShellAction;
public class ShellActionTest {
/**
* Test Shell Script that returns with an exit code of 0
*/
private String[] testscripts;
/**
* Test Shell Script that returns with an exit code of 1
*/
private String testscriptError;
/**
* Test Shell Script that does not exist
*/
private String testscriptNotExist;
/**
* @throws java.lang.Exception
*/
@Before
public void setUp() throws Exception {
URL url = this.getClass().getClassLoader().getResource("testscripts");
String file = new File(new URI(url.toString())).getAbsolutePath()+"/../../../src/test/resources/testscripts";
// need to perform this hack as the copied scripts in the target directory do not have the execute permissions
// test scripts also need to be able to accept options !!!!
testscripts = new String[]{file+"/testscript1.sh", file+"/testscript1.sh -option opt a b "};
testscriptError = file+"/testscript2-error.sh";
testscriptNotExist = file+"/testscriptNotExist.sh";
}
/**
* @throws java.lang.Exception
*/
@After
public void tearDown() throws Exception {
}
/**
* Test method for {@link ch.psi.fda.core.actions.ShellAction#ScriptAction(java.lang.String)}.
* Test the creation of a ScriptAction object with an existing script.
*/
@Test
public void testScriptAction() {
for(String testscript: testscripts){
new ShellAction(testscript);
}
}
/**
* Test method for {@link ch.psi.fda.core.actions.ShellAction#ScriptAction(java.lang.String)}.
* Test the creation of a ScriptAction object with a non existing script. IllegalArgumentException expected ...
*/
@Test(expected=IllegalArgumentException.class)
public void testScriptActionScriptNotExit() {
new ShellAction(testscriptNotExist);
}
/**
* Test method for {@link ch.psi.fda.core.actions.ShellAction#execute()}.
* @throws InterruptedException
*/
@Test
public void testExecute() throws InterruptedException {
for(String testscript: testscripts){
File f = new File(testscript);
f.setExecutable(true); // Make file executable
ShellAction action = new ShellAction(testscript);
action.execute();
}
}
/**
* Test method for {@link ch.psi.fda.core.actions.ShellAction#execute()}.
* @throws InterruptedException
*/
@Test(expected=RuntimeException.class)
public void testExecuteError() throws InterruptedException {
ShellAction action = new ShellAction(testscriptError);
action.execute();
}
/**
* Test method for {@link ch.psi.fda.core.actions.ShellAction#execute()}.
* @throws InterruptedException
*/
@Test
public void testExecuteErrorDisabledCheck() throws InterruptedException {
ShellAction action = new ShellAction(testscriptError);
action.setCheckExitValue(false);
action.execute();
}
/**
* Test method for {@link ch.psi.fda.core.actions.ShellAction#abort()}.
*/
@Test
public void testAbort() {
// Abort function in ScriptAction is not implemented - therefore there is nothing to test.
}
}
@@ -0,0 +1,778 @@
/**
*
* 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 static org.junit.Assert.fail;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import java.util.logging.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import ch.psi.fda.core.TestChannels;
import ch.psi.fda.core.actors.ChannelAccessFunctionActuator;
import ch.psi.jcae.Channel;
import ch.psi.jcae.ChannelDescriptor;
import ch.psi.jcae.ChannelException;
import ch.psi.jcae.ChannelService;
import ch.psi.jcae.impl.DefaultChannelService;
/**
* Test class specific for the FunctionActuatorChannelAccess implementation.
*/
public class ChannelAccessFunctionActuatorTest {
private static Logger logger = Logger.getLogger(ChannelAccessFunctionActuatorTest.class.getName());
private static final String channelName = TestChannels.ANALOG_OUT;
private static final String channelNameDone = TestChannels.BINARY_OUT;
private static final int doneValue = 1;
private static final double doneDelay = 0;
private static final Long timeout = 1800000l; // 30 minutes
private ChannelService cservice;
private Channel<Double> channel;
private Channel<Integer> doneChannel;
private Function function;
@Before
public void setUp() throws Exception {
cservice = new DefaultChannelService();
channel = cservice.createChannel(new ChannelDescriptor<>(Double.class, channelName));
doneChannel = cservice.createChannel(new ChannelDescriptor<>(Integer.class, channelNameDone));
function = new Function() {
@Override
public double calculate(double parameter) {
return parameter+2; // Translation by 2
}
};
}
@After
public void tearDown() throws Exception {
cservice.destroy();
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessFunctionActuator#FunctionActuatorChannelAccess(String, double, double, double)}.
* Check whether Exception is thrown if a negative step size is specified.
*/
@Test(expected=IllegalArgumentException.class)
public void testChannelAccessFunctionActuatorNegativeStepSize() throws SocketException, Exception {
// Need to throw exception because of negative step size
new ChannelAccessFunctionActuator<Object>(channel, function,0, 1, -0.1, timeout);
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessFunctionActuator#FunctionActuatorChannelAccess(String, double, double, double)}.
*/
@Test
public void testChannelAccessFunctionActuatorTimeout() throws SocketException, Exception {
// Negative timeout
boolean flag = false;
try{
// Need to return IllegalArgumentException due to negative Timeout
new ChannelAccessFunctionActuator<Object>(channel, function, 0, 1, 0.1, -1l);
}
catch(IllegalArgumentException e){
flag=true;
}
if(!flag){
fail("Negative timeout is not handeled correctly");
}
// 0 timeout
flag=false;
try{
// Need to return IllegalArgumentException
new ChannelAccessFunctionActuator<Object>(channel, function, 0, 1, 0.1, -0l);
}
catch(IllegalArgumentException e){
flag=true;
}
if(!flag){
fail("0 timeout is not handled correctly");
}
// Accept null timeout
new ChannelAccessFunctionActuator<Object>(channel, function, 0, 1, 0.1, null);
// Accept positive timeout
new ChannelAccessFunctionActuator<Object>(channel, function, 0, 1, 0.1, 1l);
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessFunctionActuator#FunctionActuatorChannelAccess(String, double, double, double)}.
* Check whether Exception is thrown if a zero step size is specified.
*/
@Test(expected=IllegalArgumentException.class)
public void testChannelAccessFunctionActuatorZeroStepSize() throws SocketException, Exception {
// Zero step size need to cause an exception
new ChannelAccessFunctionActuator<Object>(channel, function, 0, 1, 0, timeout);
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessFunctionActuator#FunctionActuatorChannelAccess(String, double, double, double)}.
* Check correct initialization
*/
public void testChannelAccessFunctionActuator() throws SocketException, Exception {
// Zero step size need to cause an exception
new ChannelAccessFunctionActuator<Object>(channel, function, 0, 10, 1, timeout);
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessFunctionActuator#set()}.
* @throws InterruptedException
*/
@Test
public void testSet() throws InterruptedException {
ChannelAccessFunctionActuator<Object> actuator = new ChannelAccessFunctionActuator<Object>(channel, function, 0, 0.09999, 0.1, timeout);
actuator.set();
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessFunctionActuator#hasNext()}.
* Check whether the actuator throws an Exception if there is no next point but set() is called
* @throws InterruptedException
*/
@Test(expected=IllegalStateException.class)
public void testSetNoNext() throws InterruptedException {
ChannelAccessFunctionActuator<Object> actuator = new ChannelAccessFunctionActuator<Object>(channel, function, 0, 0.09999, 0.1, timeout);
actuator.set();
actuator.set();
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessFunctionActuator#hasNext()}.
* @throws InterruptedException
*/
@Test
public void testHasNextOneStep() throws InterruptedException {
ChannelAccessFunctionActuator<Object> actuator = new ChannelAccessFunctionActuator<Object>(channel, function, 0, 0.09999, 0.1, timeout);
// Execute first set (because there is always a first move)
actuator.set();
// Check whether actuator returns that there is no next point
boolean next = actuator.hasNext();
if(next){
fail("There must be no next step");
}
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessFunctionActuator#hasNext()}.
* @throws InterruptedException
*/
@Test
public void testHasNext() throws InterruptedException {
ChannelAccessFunctionActuator<Object> actuator = new ChannelAccessFunctionActuator<Object>(channel, function, 0, 10, 0.1, timeout);
int count = 0;
int steps = (int) ((10-0)/0.1)+1;
while(actuator.hasNext()){
actuator.set();
count++;
}
logger.fine("Actual steps: "+count+" - Needed steps: "+steps);
if(count != steps){
fail("Actuator set more steps than specified");
}
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessFunctionActuator#set()}.
* Test actuator move start<end and start>end ...
* @throws InterruptedException
*/
@Test
public void testSetLoop() throws InterruptedException {
List<double[]> settings = new ArrayList<double[]>();
// start end stepsize
settings.add(new double[] {0, 10, 0.1});
settings.add(new double[] {0, -1, 0.1});
for(double[] svalue: settings){
ChannelAccessFunctionActuator<Object> actuator = new ChannelAccessFunctionActuator<Object>(channel, function, svalue[0], svalue[1], svalue[2], timeout);
int count =0;
while(actuator.hasNext()){
actuator.set();
count++;
}
int cnt = (int) Math.floor(Math.abs(svalue[0]-svalue[1])/svalue[2])+1;
if(count != cnt){
fail("Actuator did not move required steps [actual count: "+count+" needed count: "+cnt+" ]");
}
}
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessFunctionActuator#set()}.
* Test actuator move start<end and start>end ...
* @throws ExecutionException
* @throws ChannelException
* @throws TimeoutException
* @throws CAException
*/
@Test
public void testReverse() throws InterruptedException, TimeoutException, ChannelException, ExecutionException {
List<double[]> settings = new ArrayList<double[]>();
// start end stepsize
settings.add(new double[] {0, 10, 0.1});
settings.add(new double[] {0, -1, 0.1});
for(double[] svalue: settings){
ChannelAccessFunctionActuator<Object> actuator = new ChannelAccessFunctionActuator<Object>(channel, function, svalue[0], svalue[1], svalue[2], timeout);
actuator.init();
int count =0;
while(actuator.hasNext()){
actuator.set();
// Check set value
double val = channel.getValue();
logger.info("Value: "+val);
if(svalue[0]<svalue[1]){
double sval = function.calculate(svalue[0]+(count*svalue[2]));
if(Math.abs(val-sval) > 0.001){ // 0.001 is precision
fail("Set value ["+sval+"] does not match actual value ["+val+"]");
}
}
else{
double sval = function.calculate(svalue[0]-(count*svalue[2]));
if(Math.abs(val-sval) > 0.001){ // 0.001 is precision
fail("Set value ["+sval+"] does not match actual value ["+val+"]");
}
}
count++;
}
int cnt = (int) Math.floor(Math.abs(svalue[0]-svalue[1])/svalue[2])+1;
if(count != cnt){
fail("Actuator did not move required steps [actual count: "+count+" needed count: "+cnt+" ]");
}
actuator.reverse();
actuator.init();
count =0;
while(actuator.hasNext()){
actuator.set();
// Check set value
double val = channel.getValue();
logger.info("Value: "+val);
if(svalue[1]<svalue[0]){
double sval = function.calculate(svalue[1]+(count*svalue[2]));
if(Math.abs(val-sval) > 0.001){ // 0.001 is precision
fail("Set value ["+sval+"] does not match actual value ["+val+"]");
}
}
else{
double sval = function.calculate(svalue[1]-(count*svalue[2]));
if(Math.abs(val-sval) > 0.001){ // 0.001 is precision
fail("Set value ["+sval+"] does not match actual value ["+val+"]");
}
}
count++;
}
cnt = (int) Math.floor(Math.abs(svalue[0]-svalue[1])/svalue[2])+1;
if(count != cnt){
fail("Actuator did not move required steps [actual count: "+count+" needed count: "+cnt+" ]");
}
}
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessFunctionActuator#set()}.
* Test actuator move start<end and start>end ...
* @throws ExecutionException
* @throws ChannelException
* @throws TimeoutException
* @throws CAException
*/
@Test
public void testReverseReset() throws InterruptedException, TimeoutException, ChannelException, ExecutionException {
List<double[]> settings = new ArrayList<double[]>();
// start end stepsize
settings.add(new double[] {0, 10, 0.1});
settings.add(new double[] {0, -1, 0.1});
for(double[] svalue: settings){
ChannelAccessFunctionActuator<Object> actuator = new ChannelAccessFunctionActuator<Object>(channel, function, svalue[0], svalue[1], svalue[2], timeout);
actuator.init();
int count =0;
while(actuator.hasNext()){
actuator.set();
// Check set value
double val = channel.getValue();
logger.info("Value: "+val);
if(svalue[0]<svalue[1]){
double sval = function.calculate(svalue[0]+(count*svalue[2]));
if(Math.abs(val-sval) > 0.001){ // 0.001 is precision
fail("Set value ["+sval+"] does not match actual value ["+val+"]");
}
}
else{
double sval = function.calculate(svalue[0]-(count*svalue[2]));
if(Math.abs(val-sval) > 0.001){ // 0.001 is precision
fail("Set value ["+sval+"] does not match actual value ["+val+"]");
}
}
count++;
}
int cnt = (int) Math.floor(Math.abs(svalue[0]-svalue[1])/svalue[2])+1;
if(count != cnt){
fail("Actuator did not move required steps [actual count: "+count+" needed count: "+cnt+" ]");
}
actuator.reverse();
actuator.reset();
actuator.init();
count =0;
while(actuator.hasNext()){
actuator.set();
// Check set value
double val = channel.getValue();
logger.info("Value: "+val);
if(svalue[0]<svalue[1]){
double sval = function.calculate(svalue[0]+(count*svalue[2]));
if(Math.abs(val-sval) > 0.001){ // 0.001 is precision
fail("Set value ["+sval+"] does not match actual value ["+val+"]");
}
}
else{
double sval = function.calculate(svalue[0]-(count*svalue[2]));
if(Math.abs(val-sval) > 0.001){ // 0.001 is precision
fail("Set value ["+sval+"] does not match actual value ["+val+"]");
}
}
count++;
}
cnt = (int) Math.floor(Math.abs(svalue[0]-svalue[1])/svalue[2])+1;
if(count != cnt){
fail("Actuator did not move required steps [actual count: "+count+" needed count: "+cnt+" ]");
}
}
}
// TODO Check if actuator makes the required number of steps
// TODO Check if actuator makes the steps in the right direction
// TODO Check if actuator really blocks until motor/actuator is moved.
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessGPFunctionActuator#FunctionActuatorChannelAccess(String, double, double, double)}.
* Check whether Exception is thrown if a negative step size is specified.
*/
@Test(expected=IllegalArgumentException.class)
public void testChannelAccessGPFunctionActuatorNegativeStepSize() throws SocketException, Exception {
// Need to throw exception because of negative step size
new ChannelAccessFunctionActuator<>(channel, doneChannel, doneValue, doneDelay, function, 0, 1, -0.1, timeout);
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessGPFunctionActuator#FunctionActuatorChannelAccess(String, double, double, double)}.
* Check whether Exception is thrown if a zero step size is specified.
*/
@Test(expected=IllegalArgumentException.class)
public void testChannelAccessGPFunctionActuatorZeroStepSize() throws SocketException, Exception {
// Zero step size need to cause an exception
new ChannelAccessFunctionActuator<>(channel, doneChannel, doneValue, doneDelay, function, 0, 1, 0, timeout);
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessGPFunctionActuator#FunctionActuatorChannelAccess(String, double, double, double)}.
* Check correct initialization
*/
public void testChannelAccessGPFunctionActuator() throws SocketException, Exception {
// Zero step size need to cause an exception
new ChannelAccessFunctionActuator<>(channel, doneChannel, doneValue, doneDelay, function, 0, 10, 1, timeout);
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessGPFunctionActuator#set()}.
* @throws ChannelException
* @throws ExecutionException
* @throws CAException
*/
@Test
public void testDoneSet() throws InterruptedException, ExecutionException, ChannelException {
ChannelAccessFunctionActuator<Integer> actuator = new ChannelAccessFunctionActuator<>(channel, doneChannel, doneValue, doneDelay, function, 0, 0.09999, 0.1, timeout);
// Simulate done channel
doneChannel.setValue(0);
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
doneChannel.setValue(1);
} catch (Exception e) {
}
}
}).start();
actuator.set();
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessGPFunctionActuator#set()}.
* @throws ChannelException
* @throws ExecutionException
* @throws CAException
*/
@Test
public void testDoneDelay() throws InterruptedException, ExecutionException, ChannelException {
ChannelAccessFunctionActuator<Integer> actuator = new ChannelAccessFunctionActuator<>(channel, doneChannel, doneValue, 1.5, function, 0, 1, 0.1, timeout);
// Simulate done channel
doneChannel.setValue(1);
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
doneChannel.setValue(0);
Thread.sleep(4000);
doneChannel.setValue(1);
} catch (Exception e) {
}
}
}).start();
long start = System.currentTimeMillis();
actuator.set();
long end = System.currentTimeMillis();
logger.fine("Elapsed time: "+(end-start));
if((end-start)<4000){ // Check whether all the moves took less than 6 seconds (thats the delay the done is set to 1)
fail("Done delay does not work");
}
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessGPFunctionActuator#hasNext()}.
* Check whether the actuator throws an Exception if there is no next point but set() is called
* @throws ChannelException
* @throws ExecutionException
* @throws CAException
*/
@Test(expected=IllegalStateException.class)
public void testDoneSetNoNext() throws InterruptedException, ExecutionException, ChannelException {
ChannelAccessFunctionActuator<Integer> actuator = new ChannelAccessFunctionActuator<>(channel, doneChannel, doneValue, doneDelay, function,0, 0.09999, 0.1, timeout);
// Simulate done channel
doneChannel.setValue(0);
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
doneChannel.setValue(1);
} catch (Exception e) {
}
}
}).start();
actuator.set();
// Simulate done channel
doneChannel.setValue(0);
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
doneChannel.setValue(1);
} catch (Exception e) {
}
}
}).start();
actuator.set();
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessGPFunctionActuator#hasNext()}.
* @throws ChannelException
* @throws ExecutionException
* @throws CAException
*/
@Test
public void testDoneHasNextOneStep() throws InterruptedException, ExecutionException, ChannelException {
ChannelAccessFunctionActuator<Integer> actuator = new ChannelAccessFunctionActuator<>(channel, doneChannel, doneValue, doneDelay, function, 0, 0.09999, 0.1, timeout);
// Simulate done channel
doneChannel.setValue(0);
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
doneChannel.setValue(1);
} catch (Exception e) {
}
}
}).start();
// Execute first set (because there is always a first move)
actuator.set();
// Check whether actuator returns that there is no next point
boolean next = actuator.hasNext();
if(next){
fail("There must be no next step");
}
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessGPFunctionActuator#hasNext()}.
* @throws ChannelException
* @throws ExecutionException
* @throws CAException
*/
@Test
public void testDoneHasNext() throws InterruptedException, ExecutionException, ChannelException {
ChannelAccessFunctionActuator<Integer> actuator = new ChannelAccessFunctionActuator<>(channel, doneChannel, doneValue, doneDelay, function,0, 10, 0.1, timeout);
int count = 0;
int steps = (int) ((10-0)/0.1)+1;
while(actuator.hasNext()){
// Simulate done channel
doneChannel.setValue(0);
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(10);
doneChannel.setValue(1);
} catch (Exception e) {
}
}
}).start();
actuator.set();
count++;
}
logger.fine("Actual steps: "+count+" - Needed steps: "+steps);
if(count != steps){
fail("Actuator set more steps than specified");
}
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessGPFunctionActuator#set()}.
* Test actuator move start<end and start>end ...
* @throws ChannelException
* @throws ExecutionException
* @throws CAException
*/
@Test
public void testDoneSetLoop() throws InterruptedException, ExecutionException, ChannelException {
List<double[]> settings = new ArrayList<double[]>();
// start end stepsize
settings.add(new double[] {0, 10, 0.1});
settings.add(new double[] {0, -1, 0.1});
for(double[] svalue: settings){
ChannelAccessFunctionActuator<Integer> actuator = new ChannelAccessFunctionActuator<>(channel, doneChannel, doneValue, doneDelay, function, svalue[0], svalue[1], svalue[2], timeout);
int count =0;
while(actuator.hasNext()){
// Simulate done channel
doneChannel.setValue(0);
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(10);
doneChannel.setValue(1);
} catch (Exception e) {
}
}
}).start();
actuator.set();
count++;
}
int cnt = (int) Math.floor(Math.abs(svalue[0]-svalue[1])/svalue[2])+1;
if(count != cnt){
fail("Actuator did not move required steps [actual count: "+count+" needed count: "+cnt+" ]");
}
}
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessGPFunctionActuator#set()}.
* Test whether the actuator returns if the actuator is already on the position it should be before the move ...
* (see issue XASEC-278)
* @throws ExecutionException
* @throws ChannelException
* @throws TimeoutException
* @throws CAException
*/
@Test
public void testMoveToActualPosition() throws InterruptedException, TimeoutException, ChannelException, ExecutionException {
double start = 0;
double end = 2;
double stepSize = 1;
channel.setValue(start);
Thread.sleep(1000);
logger.info("Current channel value: "+channel.getValue());
ChannelAccessFunctionActuator<Integer> actuator = new ChannelAccessFunctionActuator<>(channel, doneChannel, doneValue, doneDelay, function, start, end, stepSize, timeout);
while(actuator.hasNext()){
// Simulate done channel
doneChannel.setValue(1);
// new Thread(new Runnable() {
//
// @Override
// public void run() {
//
// try {
// Thread.sleep(10);
//// doneChannel.setValue(1);
// } catch (Exception e) {
// }
//
// }
// }).start();
actuator.set();
}
}
@Test
public void testSetLoop2() throws InterruptedException {
Function function = new Function() {
@Override
public double calculate(double parameter) {
return parameter+2;
}
};
List<double[]> settings = new ArrayList<double[]>();
// start end stepsize
settings.add(new double[] {0, 10, 0.1});
settings.add(new double[] {0, -1, 0.1});
for(double[] svalue: settings){
ChannelAccessFunctionActuator<Object> actuator = new ChannelAccessFunctionActuator<>(channel, function, svalue[0], svalue[1], svalue[2], timeout);
int count =0;
while(actuator.hasNext()){
actuator.set();
count++;
}
int cnt = (int) Math.floor(Math.abs(svalue[0]-svalue[1])/svalue[2])+1;
if(count != cnt){
fail("Actuator did not move required steps [actual count: "+count+" needed count: "+cnt+" ]");
}
}
}
}
@@ -0,0 +1,729 @@
/**
*
* 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 static org.junit.Assert.fail;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import java.util.logging.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import ch.psi.fda.core.TestChannels;
import ch.psi.fda.core.actors.ChannelAccessLinearActuator;
import ch.psi.jcae.Channel;
import ch.psi.jcae.ChannelDescriptor;
import ch.psi.jcae.ChannelException;
import ch.psi.jcae.ChannelService;
import ch.psi.jcae.impl.DefaultChannelService;
/**
* Test class specific for the LinearActuatorChannelAccess implementation.
*/
public class ChannelAccessLinearActuatorTest {
// Get Logger
private static Logger logger = Logger.getLogger(ChannelAccessLinearActuatorTest.class.getName());
private static final String channelName = TestChannels.ANALOG_OUT;
private static final String channelNameDone = TestChannels.BINARY_OUT;
private static final int doneValue = 1;
private static final double doneDelay = 0;
private static final Long timeout = 1800000l; // 30 minutes
private ChannelService cservice;
private Channel<Double> channel;
private Channel<Integer> doneChannel;
@Before
public void setUp() throws Exception {
cservice = new DefaultChannelService();
channel = cservice.createChannel(new ChannelDescriptor<>(Double.class, channelName));
doneChannel = cservice.createChannel(new ChannelDescriptor<>(Integer.class, channelNameDone));
}
@After
public void tearDown() throws Exception {
cservice.destroy();
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessLinearActuator#LinearActuatorChannelAccess(String, double, double, double)}.
* Check whether Exception is thrown if a negative step size is specified.
*/
@Test(expected=IllegalArgumentException.class)
public void testChannelAccessLinearActuatorNegativeStepSize() throws SocketException, Exception {
// Need to throw exception because of negative step size
new ChannelAccessLinearActuator<Object>(channel, null, 1,0, 0, 1, -0.1, timeout);
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessLinearActuator#LinearActuatorChannelAccess(String, double, double, double)}.
*/
@Test
public void testChannelAccessLinearActuatorTimeout() throws SocketException, Exception {
// Negative timeout
boolean flag = false;
try{
// Need to return IllegalArgumentException due to negative Timeout
new ChannelAccessLinearActuator<Object>(channel, null, 1,0, 0, 1, 0.1, -1l);
}
catch(IllegalArgumentException e){
flag=true;
}
if(!flag){
fail("Negative timeout is not handeled correctly");
}
// 0 timeout
flag=false;
try{
// Need to return IllegalArgumentException
new ChannelAccessLinearActuator<Object>(channel, null, 1,0, 0, 1, 0.1, -0l);
}
catch(IllegalArgumentException e){
flag=true;
}
if(!flag){
fail("0 timeout is not handled correctly");
}
// Accept null timeout
new ChannelAccessLinearActuator<Object>(channel, null, 1,0, 0, 1, 0.1, null);
// Accept positive timeout
new ChannelAccessLinearActuator<Object>(channel, null, 1,0, 0, 1, 0.1, 1l);
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessLinearActuator#LinearActuatorChannelAccess(String, double, double, double)}.
* Check whether Exception is thrown if a zero step size is specified.
*/
@Test(expected=IllegalArgumentException.class)
public void testChannelAccessLinearActuatorZeroStepSize() throws SocketException, Exception {
// Zero step size need to cause an exception
new ChannelAccessLinearActuator<Object>(channel, null, 1,0, 0, 1, 0, timeout);
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessLinearActuator#LinearActuatorChannelAccess(String, double, double, double)}.
* Check correct initialization
*/
public void testChannelAccessLinearActuator() throws SocketException, Exception {
// Zero step size need to cause an exception
new ChannelAccessLinearActuator<Object>(channel, null, 1,0, 0, 10, 1, timeout);
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessLinearActuator#set()}.
* @throws InterruptedException
*/
@Test
public void testSet() throws InterruptedException {
ChannelAccessLinearActuator<Object> actuator = new ChannelAccessLinearActuator<Object>(channel, null, 1,0, 0, 0.09999, 0.1, timeout);
actuator.set();
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessLinearActuator#hasNext()}.
* Check whether the actuator throws an Exception if there is no next point but set() is called
* @throws InterruptedException
*/
@Test(expected=IllegalStateException.class)
public void testSetNoNext() throws InterruptedException {
ChannelAccessLinearActuator<Object> actuator = new ChannelAccessLinearActuator<Object>(channel, null, 1,0, 0, 0.09999, 0.1, timeout);
actuator.set();
actuator.set();
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessLinearActuator#hasNext()}.
* @throws InterruptedException
*/
@Test
public void testHasNextOneStep() throws InterruptedException {
ChannelAccessLinearActuator<Object> actuator = new ChannelAccessLinearActuator<Object>(channel, null, 1,0, 0, 0.09999, 0.1, timeout);
// Execute first set (because there is always a first move)
actuator.set();
// Check whether actuator returns that there is no next point
boolean next = actuator.hasNext();
if(next){
fail("There must be no next step");
}
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessLinearActuator#hasNext()}.
* @throws InterruptedException
*/
@Test
public void testHasNext() throws InterruptedException {
ChannelAccessLinearActuator<Object> actuator = new ChannelAccessLinearActuator<Object>(channel, null, 1,0, 0, 10, 0.1, timeout);
int count = 0;
int steps = (int) ((10-0)/0.1)+1;
while(actuator.hasNext()){
actuator.set();
count++;
}
logger.fine("Actual steps: "+count+" - Needed steps: "+steps);
if(count != steps){
fail("Actuator set more steps than specified");
}
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessLinearActuator#set()}.
* Test actuator move start<end and start>end ...
* @throws InterruptedException
*/
@Test
public void testSetLoop() throws InterruptedException {
List<double[]> settings = new ArrayList<double[]>();
// start end stepsize
settings.add(new double[] {0, 10, 0.1});
settings.add(new double[] {0, -1, 0.1});
for(double[] svalue: settings){
ChannelAccessLinearActuator<Integer> actuator = new ChannelAccessLinearActuator<Integer>(channel, null, 1,0, svalue[0], svalue[1], svalue[2], timeout);
int count =0;
while(actuator.hasNext()){
actuator.set();
count++;
}
int cnt = (int) Math.floor(Math.abs(svalue[0]-svalue[1])/svalue[2])+1;
if(count != cnt){
fail("Actuator did not move required steps [actual count: "+count+" needed count: "+cnt+" ]");
}
}
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessLinearActuator#set()}.
* Test actuator move start<end and start>end ...
* @throws ExecutionException
* @throws ChannelException
* @throws TimeoutException
* @throws CAException
*/
@Test
public void testReverse() throws InterruptedException, TimeoutException, ChannelException, ExecutionException {
List<double[]> settings = new ArrayList<double[]>();
// start end stepsize
settings.add(new double[] {0, 10, 0.1});
settings.add(new double[] {0, -1, 0.1});
for(double[] svalue: settings){
ChannelAccessLinearActuator<Object> actuator = new ChannelAccessLinearActuator<Object>(channel, null, 1,0, svalue[0], svalue[1], svalue[2], timeout);
actuator.init();
int count =0;
while(actuator.hasNext()){
actuator.set();
// Check set value
double val = channel.getValue();
logger.info("Value: "+val);
if(svalue[0]<svalue[1]){
if(Math.abs(val-(svalue[0]+(count*svalue[2]))) > 0.001){ // 0.001 is precision
fail("Set value does not match actual value");
}
}
else{
if(Math.abs(val-(svalue[0]-(count*svalue[2]))) > 0.001){ // 0.001 is precision
fail("Set value does not match actual value");
}
}
count++;
}
int cnt = (int) Math.floor(Math.abs(svalue[0]-svalue[1])/svalue[2])+1;
if(count != cnt){
fail("Actuator did not move required steps [actual count: "+count+" needed count: "+cnt+" ]");
}
actuator.reverse();
actuator.init();
count =0;
while(actuator.hasNext()){
actuator.set();
// Check set value
double val = channel.getValue();
logger.info("Value: "+val);
if(svalue[1]<svalue[0]){
if(Math.abs(val-(svalue[1]+(count*svalue[2]))) > 0.001){ // 0.001 is precision
fail("Set value does not match actual value");
}
}
else{
if(Math.abs(val-(svalue[1]-(count*svalue[2]))) > 0.001){ // 0.001 is precision
fail("Set value does not match actual value");
}
}
count++;
}
cnt = (int) Math.floor(Math.abs(svalue[0]-svalue[1])/svalue[2])+1;
if(count != cnt){
fail("Actuator did not move required steps [actual count: "+count+" needed count: "+cnt+" ]");
}
}
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessLinearActuator#set()}.
* Test actuator move start<end and start>end ...
* @throws ExecutionException
* @throws ChannelException
* @throws TimeoutException
* @throws CAException
*/
@Test
public void testReverseReset() throws InterruptedException, TimeoutException, ChannelException, ExecutionException {
List<double[]> settings = new ArrayList<double[]>();
// start end stepsize
settings.add(new double[] {0, 10, 0.1});
settings.add(new double[] {0, -1, 0.1});
for(double[] svalue: settings){
ChannelAccessLinearActuator<Object> actuator = new ChannelAccessLinearActuator<Object>(channel, null, 1,0, svalue[0], svalue[1], svalue[2], timeout);
actuator.init();
int count =0;
while(actuator.hasNext()){
actuator.set();
// Check set value
double val = channel.getValue();
logger.info("Value: "+val);
if(svalue[0]<svalue[1]){
if(Math.abs(val-(svalue[0]+(count*svalue[2]))) > 0.001){ // 0.001 is precision
fail("Set value does not match actual value");
}
}
else{
if(Math.abs(val-(svalue[0]-(count*svalue[2]))) > 0.001){ // 0.001 is precision
fail("Set value does not match actual value");
}
}
count++;
}
int cnt = (int) Math.floor(Math.abs(svalue[0]-svalue[1])/svalue[2])+1;
if(count != cnt){
fail("Actuator did not move required steps [actual count: "+count+" needed count: "+cnt+" ]");
}
actuator.reverse();
actuator.reset();
actuator.init();
count =0;
while(actuator.hasNext()){
actuator.set();
// Check set value
double val = channel.getValue();
logger.info("Value: "+val);
if(svalue[0]<svalue[1]){
if(Math.abs(val-(svalue[0]+(count*svalue[2]))) > 0.001){ // 0.001 is precision
fail("Set value does not match actual value");
}
}
else{
if(Math.abs(val-(svalue[0]-(count*svalue[2]))) > 0.001){ // 0.001 is precision
fail("Set value does not match actual value");
}
}
count++;
}
cnt = (int) Math.floor(Math.abs(svalue[0]-svalue[1])/svalue[2])+1;
if(count != cnt){
fail("Actuator did not move required steps [actual count: "+count+" needed count: "+cnt+" ]");
}
}
}
// TODO Check if actuator makes the required number of steps
// TODO Check if actuator makes the steps in the right direction
// TODO Check if actuator really blocks until motor/actuator is moved.
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessGPLinearActuator#LinearActuatorChannelAccess(String, double, double, double)}.
* Check whether Exception is thrown if a negative step size is specified.
*/
@Test(expected=IllegalArgumentException.class)
public void testChannelAccessGPLinearActuatorNegativeStepSize() throws SocketException, Exception {
// Need to throw exception because of negative step size
new ChannelAccessLinearActuator<>(channel, doneChannel, doneValue, doneDelay, 0, 1, -0.1, timeout);
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessGPLinearActuator#LinearActuatorChannelAccess(String, double, double, double)}.
* Check whether Exception is thrown if a zero step size is specified.
*/
@Test(expected=IllegalArgumentException.class)
public void testChannelAccessGPLinearActuatorZeroStepSize() throws SocketException, Exception {
// Zero step size need to cause an exception
new ChannelAccessLinearActuator<>(channel, doneChannel, doneValue, doneDelay, 0, 1, 0, timeout);
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessGPLinearActuator#LinearActuatorChannelAccess(String, double, double, double)}.
* Check correct initialization
*/
public void testChannelAccessGPLinearActuator() throws SocketException, Exception {
// Zero step size need to cause an exception
new ChannelAccessLinearActuator<>(channel, doneChannel, doneValue, doneDelay, 0, 10, 1, timeout);
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessGPLinearActuator#set()}.
* @throws ChannelException
* @throws ExecutionException
* @throws CAException
*/
@Test
public void testDoneSet() throws InterruptedException, ExecutionException, ChannelException {
ChannelAccessLinearActuator<Integer> actuator = new ChannelAccessLinearActuator<>(channel, doneChannel, doneValue, doneDelay, 0, 0.09999, 0.1, timeout);
// Simulate done channel
doneChannel.setValue(0);
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
doneChannel.setValue(1);
} catch (Exception e) {
}
}
}).start();
actuator.set();
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessGPLinearActuator#set()}.
* @throws ChannelException
* @throws ExecutionException
* @throws CAException
*/
@Test
public void testDoneDelay() throws InterruptedException, ExecutionException, ChannelException {
ChannelAccessLinearActuator<Integer> actuator = new ChannelAccessLinearActuator<>(channel, doneChannel, doneValue, 1.5, 0, 1, 0.1, timeout);
// Simulate done channel
doneChannel.setValue(1);
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
doneChannel.setValue(0);
Thread.sleep(4000);
doneChannel.setValue(1);
} catch (Exception e) {
}
}
}).start();
long start = System.currentTimeMillis();
actuator.set();
long end = System.currentTimeMillis();
logger.fine("Elapsed time: "+(end-start));
if((end-start)<4000){ // Check whether all the moves took less than 6 seconds (thats the delay the done is set to 1)
fail("Done delay does not work");
}
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessGPLinearActuator#hasNext()}.
* Check whether the actuator throws an Exception if there is no next point but set() is called
* @throws ChannelException
* @throws ExecutionException
* @throws CAException
*/
@Test(expected=IllegalStateException.class)
public void testDoneSetNoNext() throws InterruptedException, ExecutionException, ChannelException {
ChannelAccessLinearActuator<Integer> actuator = new ChannelAccessLinearActuator<>(channel, doneChannel, doneValue, doneDelay, 0, 0.09999, 0.1, timeout);
// Simulate done channel
doneChannel.setValue(0);
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
doneChannel.setValue(1);
} catch (Exception e) {
}
}
}).start();
actuator.set();
// Simulate done channel
doneChannel.setValue(0);
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
doneChannel.setValue(1);
} catch (Exception e) {
}
}
}).start();
actuator.set();
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessGPLinearActuator#hasNext()}.
* @throws ChannelException
* @throws ExecutionException
* @throws CAException
*/
@Test
public void testDoneHasNextOneStep() throws InterruptedException, ExecutionException, ChannelException {
ChannelAccessLinearActuator<Integer> actuator = new ChannelAccessLinearActuator<>(channel, doneChannel, doneValue, doneDelay, 0, 0.09999, 0.1, timeout);
// Simulate done channel
doneChannel.setValue(0);
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
doneChannel.setValue(1);
} catch (Exception e) {
}
}
}).start();
// Execute first set (because there is always a first move)
actuator.set();
// Check whether actuator returns that there is no next point
boolean next = actuator.hasNext();
if(next){
fail("There must be no next step");
}
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessGPLinearActuator#hasNext()}.
* @throws ChannelException
* @throws ExecutionException
* @throws CAException
*/
@Test
public void testDoneHasNext() throws InterruptedException, ExecutionException, ChannelException {
ChannelAccessLinearActuator<Integer> actuator = new ChannelAccessLinearActuator<>(channel, doneChannel, doneValue, doneDelay, 0, 10, 0.1, timeout);
int count = 0;
int steps = (int) ((10-0)/0.1)+1;
while(actuator.hasNext()){
// Simulate done channel
doneChannel.setValue(0);
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(10);
doneChannel.setValue(1);
} catch (Exception e) {
}
}
}).start();
actuator.set();
count++;
}
logger.fine("Actual steps: "+count+" - Needed steps: "+steps);
if(count != steps){
fail("Actuator set more steps than specified");
}
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessGPLinearActuator#set()}.
* Test actuator move start<end and start>end ...
* @throws ChannelException
* @throws ExecutionException
* @throws CAException
*/
@Test
public void testDoneSetLoop() throws InterruptedException, ExecutionException, ChannelException {
List<double[]> settings = new ArrayList<double[]>();
// start end stepsize
settings.add(new double[] {0, 10, 0.1});
settings.add(new double[] {0, -1, 0.1});
for(double[] svalue: settings){
ChannelAccessLinearActuator<Integer> actuator = new ChannelAccessLinearActuator<>(channel, doneChannel, doneValue, doneDelay, svalue[0], svalue[1], svalue[2], timeout);
int count =0;
while(actuator.hasNext()){
// Simulate done channel
doneChannel.setValue(0);
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(10);
doneChannel.setValue(1);
} catch (Exception e) {
}
}
}).start();
actuator.set();
count++;
}
int cnt = (int) Math.floor(Math.abs(svalue[0]-svalue[1])/svalue[2])+1;
if(count != cnt){
fail("Actuator did not move required steps [actual count: "+count+" needed count: "+cnt+" ]");
}
}
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessGPLinearActuator#set()}.
* Test whether the actuator returns if the actuator is already on the position it should be before the move ...
* (see issue XASEC-278)
* @throws ChannelException
* @throws ExecutionException
* @throws TimeoutException
* @throws CAException
*/
@Test
public void testMoveToActualPosition() throws InterruptedException, ExecutionException, ChannelException, TimeoutException {
double start = 0;
double end = 2;
double stepSize = 1;
channel.setValue(start);
Thread.sleep(1000);
logger.info("Current channel value: "+channel.getValue());
ChannelAccessLinearActuator<Integer> actuator = new ChannelAccessLinearActuator<>(channel, doneChannel, doneValue, doneDelay, start, end, stepSize, timeout);
while(actuator.hasNext()){
// Simulate done channel
doneChannel.setValue(1);
// new Thread(new Runnable() {
//
// @Override
// public void run() {
//
// try {
// Thread.sleep(10);
//// doneChannel.setValue(1);
// } catch (Exception e) {
// }
//
// }
// }).start();
actuator.set();
}
}
}
@@ -0,0 +1,490 @@
/**
*
* 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 static org.junit.Assert.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import java.util.logging.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import ch.psi.fda.core.TestChannels;
import ch.psi.fda.core.actors.ChannelAccessTableActuator;
import ch.psi.jcae.Channel;
import ch.psi.jcae.ChannelDescriptor;
import ch.psi.jcae.ChannelException;
import ch.psi.jcae.ChannelService;
import ch.psi.jcae.impl.DefaultChannelService;
public class ChannelAccessTableActuatorTest {
private static Logger logger = Logger.getLogger(ChannelAccessTableActuatorTest.class.getName());
private static final String channelName = TestChannels.BINARY_OUT;
private static final String channelNameDone = TestChannels.BINARY_OUT_TWO;
private static final int doneValue = 1;
private static final double doneDelay = 0;
private static final Long timeout = 1800000l; // 30 minutes
private ChannelService cservice;
private Channel<Double> channel;
private Channel<Integer> doneChannel;
@Before
public void setUp() throws Exception {
cservice = new DefaultChannelService();
channel = cservice.createChannel(new ChannelDescriptor<>(Double.class, channelName));
doneChannel = cservice.createChannel(new ChannelDescriptor<>(Integer.class, channelNameDone));
}
@After
public void tearDown() throws Exception {
cservice.destroy();
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessTableActuator#ChannelAccessTableActuator(java.lang.String, double[])}.
*/
@Test
public void testChannelAccessTableActuator() {
new ChannelAccessTableActuator<>(channel, new double[]{1}, timeout);
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessTableActuator#ChannelAccessTableActuator(java.lang.String, double[])}.
*/
@Test
public void testChannelAccessTableActuatorTimeout() {
// Negative timeout
boolean flag = false;
try{
// Need to return IllegalArgumentException due to negative Timeout
new ChannelAccessTableActuator<>(channel, new double[]{1}, -1l);
}
catch(IllegalArgumentException e){
flag=true;
}
if(!flag){
fail("Negative timeout is not handeled correctly");
}
// 0 timeout
flag=false;
try{
// Need to return IllegalArgumentException
new ChannelAccessTableActuator<>(channel, new double[]{1}, 0l);
}
catch(IllegalArgumentException e){
flag=true;
}
if(!flag){
fail("0 timeout is not handled correctly");
}
// Accept null timeout
new ChannelAccessTableActuator<>(channel, new double[]{1}, null);
// Accept positive timeout
new ChannelAccessTableActuator<>(channel, new double[]{1}, 1l);
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessTableActuator#ChannelAccessTableActuator(java.lang.String, double[])}.
* Test that constructor fails while providing a null table
*/
@Test(expected=IllegalArgumentException.class)
public void testChannelAccessTableActuatorNull() {
new ChannelAccessTableActuator<>(channel, null, timeout);
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessTableActuator#ChannelAccessTableActuator(java.lang.String, double[])}.
* Test that constructor fails while providing an empty table
*/
@Test(expected=IllegalArgumentException.class)
public void testChannelAccessTableActuatorEmptyTable() {
new ChannelAccessTableActuator<>(channel, new double[0], timeout);
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessTableActuator#set()}.
* @throws ExecutionException
* @throws ChannelException
* @throws TimeoutException
* @throws CAException
*/
@Test
public void testSet() throws InterruptedException, TimeoutException, ChannelException, ExecutionException {
double[] table = new double[]{1,2,3,4,5,6};
ChannelAccessTableActuator<Object> actor = new ChannelAccessTableActuator<>(channel, table, timeout);
int count=0;
while(actor.hasNext()){
actor.set();
if(channel.getValue()!=table[count]){
fail("Channel value does not match expected set value");
}
count++;
}
if(count != table.length){
fail("Not all points given in the table were set");
}
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessTableActuator#set()}.
* @throws InterruptedException
*/
@Test(expected=RuntimeException.class)
public void testSetFail() throws InterruptedException {
double[] table = new double[]{1};
ChannelAccessTableActuator<Object> actor = new ChannelAccessTableActuator<>(channel, table, timeout);
actor.set();
// This set() call has to fail with an RuntimeException
actor.set();
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessTableActuator#hasNext()}.
* @throws InterruptedException
*/
@Test
public void testHasNext() throws InterruptedException {
double[] table = new double[]{1};
ChannelAccessTableActuator<Object> actor = new ChannelAccessTableActuator<>(channel, table, timeout);
actor.set();
boolean next = actor.hasNext();
if(next){
fail("There must be no next step");
}
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessTableActuator#set()}.
* @throws ExecutionException
* @throws ChannelException
* @throws TimeoutException
* @throws CAException
*/
@Test
public void testReverse() throws InterruptedException, TimeoutException, ChannelException, ExecutionException {
double[] table = new double[]{1,2,3,4,5,6};
ChannelAccessTableActuator<Object> actor = new ChannelAccessTableActuator<Object>(channel, table, timeout);
actor.init();
int count=0;
while(actor.hasNext()){
actor.set();
if(channel.getValue()!=table[count]){
fail("Channel value does not match expected set value");
}
count++;
}
if(count != table.length){
fail("Not all points given in the table were set");
}
actor.reverse();
actor.init();
count=0;
while(actor.hasNext()){
actor.set();
if(channel.getValue()!=table[table.length-1-count]){
fail("Channel value does not match expected set value");
}
count++;
}
if(count != table.length){
fail("Not all points given in the table were set");
}
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessTableActuator#set()}.
* @throws ExecutionException
* @throws ChannelException
* @throws TimeoutException
* @throws CAException
*/
@Test
public void testReverseReset() throws InterruptedException, TimeoutException, ChannelException, ExecutionException {
double[] table = new double[]{1,2,3,4,5,6};
ChannelAccessTableActuator<Object> actor = new ChannelAccessTableActuator<Object>(channel, table, timeout);
actor.init();
int count=0;
while(actor.hasNext()){
actor.set();
if(channel.getValue()!=table[count]){
fail("Channel value does not match expected set value");
}
count++;
}
if(count != table.length){
fail("Not all points given in the table were set");
}
actor.reverse();
actor.reset();
actor.init();
count=0;
while(actor.hasNext()){
actor.set();
if(channel.getValue()!=table[count]){
fail("Channel value does not match expected set value");
}
count++;
}
if(count != table.length){
fail("Not all points given in the table were set");
}
}
// DONE
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessTableActuator#ChannelAccessTableActuator(java.lang.String, double[])}.
*/
@Test
public void testDoneChannelAccessTableActuator() {
new ChannelAccessTableActuator<>(channel, doneChannel, doneValue, doneDelay, new double[]{1}, timeout);
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessTableActuator#ChannelAccessTableActuator(java.lang.String, double[])}.
* Test that constructor fails while providing a null table
*/
@Test(expected=IllegalArgumentException.class)
public void testDoneChannelAccessTableActuatorNull() {
new ChannelAccessTableActuator<>(channel, doneChannel, doneValue, doneDelay, null, timeout);
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessTableActuator#ChannelAccessTableActuator(java.lang.String, double[])}.
* Test that constructor fails while providing an empty table
*/
@Test(expected=IllegalArgumentException.class)
public void testDoneChannelAccessTableActuatorEmptyTable() {
new ChannelAccessTableActuator<>(channel, doneChannel, doneValue, doneDelay, new double[0], timeout);
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessTableActuator#set()}.
* @throws ChannelException
* @throws ExecutionException
* @throws TimeoutException
* @throws CAException
*/
@Test
public void testDoneSet() throws InterruptedException, ExecutionException, ChannelException, TimeoutException {
double[] table = new double[]{1,2,3,4,5,6};
ChannelAccessTableActuator<Integer> actor = new ChannelAccessTableActuator<>(channel, doneChannel, doneValue, doneDelay, table, timeout);
int count=0;
while(actor.hasNext()){
// Simulate done channel
doneChannel.setValue(0);
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1000);
doneChannel.setValue(1);
} catch (Exception e) {
}
}
}).start();
actor.set();
if(channel.getValue()!=table[count]){
fail("Channel value does not match expected set value");
}
count++;
}
if(count != table.length){
fail("Not all points given in the table were set");
}
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessTableActuator#set()}.
* @throws ChannelException
* @throws ExecutionException
* @throws TimeoutException
* @throws CAException
*/
@Test
public void testDoneDelay() throws InterruptedException, ExecutionException, ChannelException, TimeoutException {
double[] table = new double[]{1,2,3,4,5,6};
ChannelAccessTableActuator<Integer> actor = new ChannelAccessTableActuator<>(channel, doneChannel, doneValue, 0.1, table, timeout);
int count=0;
long start = System.currentTimeMillis();
while(actor.hasNext()){
// Simulate done channel
doneChannel.setValue(1);
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(10);
doneChannel.setValue(0);
Thread.sleep(1000);
doneChannel.setValue(1);
} catch (Exception e) {
}
}
}).start();
actor.set();
if(channel.getValue()!=table[count]){
fail("Channel value does not match expected set value");
}
count++;
}
long end = System.currentTimeMillis();
if(count != table.length){
fail("Not all points given in the table were set");
}
logger.fine("Elapsed time: "+(end-start));
if((end-start)<6000){ // check whether all the moves took less than 6 seconds (thats the delay the done is set to 1)
fail("Done delay does not work");
}
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessTableActuator#set()}.
* @throws ChannelException
* @throws ExecutionException
* @throws CAException
*/
@Test(expected=RuntimeException.class)
public void testDoneSetFail() throws InterruptedException, ExecutionException, ChannelException {
double[] table = new double[]{1};
ChannelAccessTableActuator<Integer> actor = new ChannelAccessTableActuator<>(channel, doneChannel, doneValue, doneDelay, table, timeout);
// Simulate done channel
doneChannel.setValue(0);
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
doneChannel.setValue(1);
} catch (Exception e) {
}
}
}).start();
actor.set();
// This set() call has to fail with an RuntimeException
// Simulate done channel
doneChannel.setValue(0);
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
doneChannel.setValue(1);
} catch (Exception e) {
}
}
}).start();
actor.set();
}
/**
* Test method for {@link ch.psi.fda.core.actors.ChannelAccessTableActuator#hasNext()}.
* @throws ChannelException
* @throws ExecutionException
* @throws CAException
*/
@Test
public void testDoneHasNext() throws InterruptedException, ExecutionException, ChannelException {
double[] table = new double[]{1};
ChannelAccessTableActuator<Integer> actor = new ChannelAccessTableActuator<>(channel, doneChannel, doneValue, doneDelay, table, timeout);
// Simulate done channel
doneChannel.setValue(0);
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(3000);
doneChannel.setValue(1);
} catch (Exception e) {
}
}
}).start();
actor.set();
boolean next = actor.hasNext();
if(next){
fail("There must be no next step");
}
}
}
@@ -0,0 +1,86 @@
/**
*
* 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 static org.junit.Assert.fail;
import java.util.concurrent.TimeoutException;
import java.util.logging.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import ch.psi.fda.core.TestChannels;
import ch.psi.fda.core.actors.ChannelAccessLinearActuator;
import ch.psi.fda.core.actors.ComplexActuator;
import ch.psi.jcae.ChannelDescriptor;
import ch.psi.jcae.ChannelException;
import ch.psi.jcae.ChannelService;
import ch.psi.jcae.impl.DefaultChannelService;
/**
* Prerequisites for this test are:
* All Actors that are used inside this test (except the ComplexActuator) need to be tested and
* fully compliant to their specification.
*/
public class ComplexActorTest {
private static Logger logger = Logger.getLogger(ComplexActorTest.class.getName());
private static final Long timeout = 1800000l;
private ChannelService cservice;
@Before
public void setUp() throws Exception {
cservice = new DefaultChannelService();
}
@After
public void tearDown() throws Exception {
cservice.destroy();
}
@Test
public void testSet() throws InterruptedException, ChannelException, TimeoutException {
ComplexActuator actuator = new ComplexActuator();
actuator.getActors().add(new ChannelAccessLinearActuator<Object>(cservice.createChannel(new ChannelDescriptor<>(Double.class, TestChannels.ANALOG_OUT)), null, 1,0, 0, 0.09999, 0.1, timeout));
actuator.getActors().add(new ChannelAccessLinearActuator<Object>(cservice.createChannel(new ChannelDescriptor<>(Double.class, TestChannels.ANALOG_OUT)), null, 1,0, 1, 2, 0.1, timeout));
actuator.getActors().add(new ChannelAccessLinearActuator<Object>(cservice.createChannel(new ChannelDescriptor<>(Double.class, TestChannels.ANALOG_OUT)), null, 1,0, -1, -2, 0.1, timeout));
// Initialize actuator
actuator.init();
int count = 0;
while(actuator.hasNext()){
actuator.set();
count++;
}
logger.fine("Execution count: "+count);
if(count!=23){ // There are 23 steps to do with the registered actors
fail("Actor did not perform all the steps");
}
}
}
@@ -0,0 +1,99 @@
/**
*
* 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.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import org.junit.Test;
import ch.psi.fda.core.scripting.JythonGlobalVariable;
public class JythonFunctionTest {
private static final Logger logger = Logger.getLogger(JythonFunctionTest.class.getName());
/**
* Test method for {@link ch.psi.fda.core.actors.JythonFunction#calculate(double)}.
*/
@Test
public void testCalculate() {
Map<String, JythonGlobalVariable> map = new HashMap<String, JythonGlobalVariable>();
String script = "import math\ndef "+JythonFunction.ENTRY_FUNCTION_NAME+"(parameter):\n return math.sqrt(parameter)";
JythonFunction f = new JythonFunction(script, map);
double rval = f.calculate(4);
logger.info("Return value: "+rval);
}
/**
* Test method for {@link ch.psi.fda.core.actors.JythonFunction#calculate(double)}.
*/
@Test(expected=IllegalArgumentException.class)
public void testCalculateNoVariableMapping() {
Map<String, JythonGlobalVariable> map = new HashMap<String, JythonGlobalVariable>();
String script = "import math\ndef "+JythonFunction.ENTRY_FUNCTION_NAME+"(parameter, a):\n return math.sqrt(parameter)+a.getValue()";
new JythonFunction(script, map); // Must throw IllegalArgumentException !!!!
}
/**
* Test method for {@link ch.psi.fda.core.actors.JythonFunction#calculate(double)}.
*/
@Test
public void testCalculateVariableMapping() {
Map<String, JythonGlobalVariable> map = new HashMap<String, JythonGlobalVariable>();
JythonGlobalVariable v = new JythonGlobalVariable();
v.setName("a");
v.setValue(1.5);
map.put("a", v);
String script = "import math\ndef "+JythonFunction.ENTRY_FUNCTION_NAME+"(parameter, a):\n return math.sqrt(parameter)+a.getValue()";
JythonFunction f = new JythonFunction(script, map);
double rval = f.calculate(4);
logger.info("Return value: "+rval);
}
/**
* Test whether there is a mapping of global variable that is not declared in the entry function as parameter
*/
@Test
public void testVariableMappingMissingParameterDeclaration() {
// Add two global variables a and b.
Map<String, JythonGlobalVariable> map = new HashMap<String, JythonGlobalVariable>();
JythonGlobalVariable v = new JythonGlobalVariable();
v.setName("a");
v.setValue(1.5);
map.put(v.getName(), v);
v = new JythonGlobalVariable();
v.setName("b");
v.setValue(3.5);
map.put(v.getName(), v);
String script = "import math\ndef "+JythonFunction.ENTRY_FUNCTION_NAME+"(parameter, b):\n return b.getValue()";
JythonFunction f = new JythonFunction(script, map);
double rval = f.calculate(4);
logger.info("Return value: "+rval);
}
}
@@ -0,0 +1,118 @@
/**
*
* 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 static org.junit.Assert.*;
import org.junit.Test;
import ch.psi.fda.core.actors.PseudoActuatorSensor;
public class PseudoActorSensorTest {
private final String id = "xid";
/**
* Test method for {@link ch.psi.fda.core.actors.PseudoActuatorSensor#PseudoActor(int)}.
*/
@Test
public void testPseudoActor() {
new PseudoActuatorSensor(id, 1);
}
/**
* Test method for {@link ch.psi.fda.core.actors.PseudoActuatorSensor#PseudoActor(int)}.
*/
@Test(expected=IllegalArgumentException.class)
public void testPseudoActorFail() {
new PseudoActuatorSensor(id, 0);
}
/**
* Test method for {@link ch.psi.fda.core.actors.PseudoActuatorSensor#set()}.
*/
@Test
public void testSet() {
PseudoActuatorSensor actor = new PseudoActuatorSensor(id, 1);
actor.set();
if(((Double)actor.read()).intValue()!=1){
fail("Read value does not match set value");
}
}
/**
* Test method for {@link ch.psi.fda.core.actors.PseudoActuatorSensor#set()}.
* Check whether IllegalStateException is thrown if set() called more than allowed
*/
@Test(expected=IllegalStateException.class)
public void testSetFail() {
PseudoActuatorSensor actor = new PseudoActuatorSensor(id, 1);
actor.set();
if(((Double)actor.read()).intValue()!=1){
fail("Read value does not match set value");
}
actor.set();
}
/**
* Test method for {@link ch.psi.fda.core.actors.PseudoActuatorSensor#hasNext()}.
*/
@Test
public void testHasNext() {
PseudoActuatorSensor actor = new PseudoActuatorSensor(id, 1);
if(!actor.hasNext()){
fail("Actor has no step when he should have one");
}
actor.set();
if(((Double)actor.read()).intValue()!=1){
fail("Read value does not match set value");
}
if(actor.hasNext()){
fail("Actor has a step when he should not have one");
}
}
/**
* Test method for {@link ch.psi.fda.core.actors.PseudoActuatorSensor#init()}.
*/
@Test
public void testInit() {
PseudoActuatorSensor actor = new PseudoActuatorSensor(id, 1);
actor.init();
actor.set();
if(((Double)actor.read()).intValue()!=1){
fail("Read value does not match set value");
}
if(actor.hasNext()){
fail("Actor has a step when he should not have one");
}
// Initialize actor to start
actor.init();
actor.set();
if(((Double)actor.read()).intValue()!=1){
fail("Read value does not match set value");
}
if(actor.hasNext()){
fail("Actor has a step when he should not have one");
}
}
}
@@ -0,0 +1,115 @@
/**
*
* 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 static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import ch.psi.fda.core.Guard;
import ch.psi.fda.core.TestChannels;
import ch.psi.fda.core.guard.ChannelAccessGuard;
import ch.psi.fda.core.guard.ChannelAccessGuardCondition;
import ch.psi.jcae.Channel;
import ch.psi.jcae.ChannelDescriptor;
import ch.psi.jcae.ChannelException;
import ch.psi.jcae.ChannelService;
import ch.psi.jcae.impl.DefaultChannelService;
public class ChannelAccessGuardTest {
private ChannelService cservice;
@Before
public void setUp() throws Exception {
cservice = new DefaultChannelService();
}
@After
public void tearDown() throws Exception {
cservice.destroy();
}
/**
* Test method for {@link ch.psi.fda.core.guard.ChannelAccessGuard#check()}.
* @throws InterruptedException
* @throws ChannelException
* @throws ExecutionException
* @throws TimeoutException
* @throws CAException
*/
@Test
public void testCheck() throws InterruptedException, ExecutionException, ChannelException, TimeoutException {
String guardChannel = TestChannels.ANALOG_OUT;
Channel<Integer> channel = cservice.createChannel(new ChannelDescriptor<>(Integer.class, guardChannel));
Integer channelOkValue = 10;
List<ChannelAccessGuardCondition<?>> conditions = new ArrayList<>();
conditions.add(new ChannelAccessGuardCondition<>(channel, channelOkValue));
Guard guard = new ChannelAccessGuard(conditions);
channel.setValue(channelOkValue);
guard.init();
if(!guard.check()){
fail("Guard not in correct state");
}
channel.setValue(channelOkValue+1);
channel.setValue(channelOkValue);
Thread.sleep(1000);
// Check whether guard is really on NOT OK
if(guard.check()){
fail("Guard not in correct state");
}
channel.setValue(channelOkValue);
// Check if after init the guard is ok again
guard.init();
if(!guard.check()){
fail("Guard not in correct state");
}
// Check if after init the guard is not ok
channel.setValue(channelOkValue+1);
guard.init();
if(guard.check()){
fail("Guard not in correct state");
}
}
}
@@ -0,0 +1,302 @@
/**
*
* 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 static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.google.common.eventbus.Subscribe;
import ch.psi.fda.core.Actor;
import ch.psi.fda.core.Guard;
import ch.psi.fda.core.Sensor;
import ch.psi.fda.core.TestChannels;
import ch.psi.fda.core.actions.Delay;
import ch.psi.fda.core.actors.ChannelAccessLinearActuator;
import ch.psi.fda.core.guard.ChannelAccessGuard;
import ch.psi.fda.core.guard.ChannelAccessGuardCondition;
import ch.psi.fda.core.loops.ActorSensorLoop;
import ch.psi.fda.core.sensors.ChannelAccessSensor;
import ch.psi.fda.messages.Message;
import ch.psi.jcae.Channel;
import ch.psi.jcae.ChannelDescriptor;
import ch.psi.jcae.ChannelException;
import ch.psi.jcae.ChannelService;
import ch.psi.jcae.impl.DefaultChannelService;
public class ActorSensorLoopTest {
// Get Logger
private static Logger logger = Logger.getLogger(ActorSensorLoopTest.class.getName());
private static final String boChannel = TestChannels.BINARY_OUT;
private static final String aoChannel = TestChannels.ANALOG_OUT;
private static final String wfChannel = TestChannels.DOUBLE_WAVEFORM;
private static final Long timeout = 1800000l; // 30 minutes
private ChannelService cservice;
private ActorSensorLoop loopOne;
@Before
public void setUp() throws Exception {
cservice = new DefaultChannelService();
loopOne = new ActorSensorLoop();
ChannelAccessLinearActuator<Integer> a = new ChannelAccessLinearActuator<>(cservice.createChannel(new ChannelDescriptor<>(Double.class, aoChannel)), null, 1,0, 0, 10, 0.1, timeout); // Positioner
Sensor s = new ChannelAccessSensor<>("id0", cservice.createChannel(new ChannelDescriptor<>(Double.class, boChannel))); // Positioner Readback
Sensor s1 = new ChannelAccessSensor<>("id1", cservice.createChannel(new ChannelDescriptor<>(Double.class, aoChannel))); // Scalar Detector
Sensor s1string = new ChannelAccessSensor<>("id3", cservice.createChannel(new ChannelDescriptor<>(String.class, boChannel+".NAME"))); // Scalar String Detector
Sensor s2 = new ChannelAccessSensor<>("id2", cservice.createChannel(new ChannelDescriptor<>(double[].class, wfChannel,false, 10)));
loopOne.getActors().add(a);
loopOne.getSensors().add(s);
loopOne.getSensors().add(s1);
loopOne.getSensors().add(s1string);
loopOne.getSensors().add(s2);
loopOne.prepare();
}
@After
public void tearDown() throws Exception {
cservice.destroy();
}
/**
* Test method for {@link ch.psi.fda.core.loops.ActorSensorLoop#execute()}.
* @throws InterruptedException
*/
@Test
public void testExecute() throws InterruptedException {
loopOne.getEventBus().register(new Object(){
@Subscribe
public void onMessage(Message m){
logger.info( m.toString() );
}
});
loopOne.execute();
}
/**
* Test method for {@link ch.psi.fda.core.loops.ActorSensorLoop#execute()}.
* @throws InterruptedException
* @throws TimeoutException
* @throws ChannelException
* @throws ExecutionException
* @throws CAException
*/
@Test
public void testExecuteGuard() throws InterruptedException, ChannelException, TimeoutException, ExecutionException {
ActorSensorLoop loop = new ActorSensorLoop();
ChannelAccessLinearActuator<Integer> a = new ChannelAccessLinearActuator<>(cservice.createChannel(new ChannelDescriptor<>(Double.class, aoChannel)), null, 1,0, 0, 1.5, 0.1,timeout); // Positioner
Sensor s = new ChannelAccessSensor<>("id0", cservice.createChannel(new ChannelDescriptor<>(Double.class, aoChannel)));
Sensor s1 = new ChannelAccessSensor<>("id1", cservice.createChannel(new ChannelDescriptor<>(Double.class, boChannel)));
Sensor s2 = new ChannelAccessSensor<>("id2", cservice.createChannel(new ChannelDescriptor<>(double[].class,wfChannel, false,10)));
loop.getActors().add(a);
loop.getPostActorActions().add(new Delay(500));
loop.getSensors().add(s);
loop.getSensors().add(s1);
loop.getSensors().add(s2);
// Guard
final Integer okValue = 0;
final Channel<Integer> b = cservice.createChannel(new ChannelDescriptor<>(Integer.class, boChannel));
List<ChannelAccessGuardCondition<?>> conditions = new ArrayList<>();
conditions.add(new ChannelAccessGuardCondition<Integer>(b, 0));
Guard guard = new ChannelAccessGuard(conditions);
loop.setGuard(guard);
Thread tguard = new Thread(new Runnable() {
@Override
public void run() {
try {
// Wait some seconds and set channel to ok
Thread.sleep(1000);
b.setValue(okValue);
// Set channel to not ok
Thread.sleep(1000);
b.setValue(okValue+1);
// Wait some seconds and set channel to ok again
Thread.sleep(4000);
b.setValue(okValue);
} catch (Exception e) {
logger.log(Level.SEVERE, "An Exception occured while setting guard channel", e);
}
}
});
loop.getEventBus().register(new Object(){
@Subscribe
public void onMessage(Message m){
logger.info( m.toString() );
}
});
// Set the guard channel to not OK
b.setValue(okValue+1);
tguard.start();
loop.prepare();
loop.execute();
logger.info("End of test");
}
@Test
public void testParallelSet() throws InterruptedException, ChannelException, TimeoutException {
final int steps = 2;
final HashMap<String,Long> timestamps = new HashMap<String, Long>();
ActorSensorLoop loop = new ActorSensorLoop();
ChannelAccessLinearActuator<Integer> a = new ChannelAccessLinearActuator<>(cservice.createChannel(new ChannelDescriptor<>(Double.class, aoChannel)), null, 1,0, 0, (steps*0.1), 0.1, timeout); // Positioner
Sensor s = new ChannelAccessSensor<>("id0", cservice.createChannel(new ChannelDescriptor<>(Double.class, aoChannel)));
loop.getActors().add(a);
loop.getActors().add(new Actor() {
int c = 0;
@Override
public void set() {
logger.info("Start actor 1");
timestamps.put("sa1", System.currentTimeMillis());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
logger.info("Done actor 1");
timestamps.put("da1", System.currentTimeMillis());
c++;
}
@Override
public void reverse() {
}
@Override
public void reset() {
}
@Override
public void init() {
}
@Override
public boolean hasNext() {
if(c<steps){
return true;
}
return false;
}
});
loop.getActors().add(new Actor() {
int c=0;
@Override
public void set() {
logger.info("Start actor 2");
timestamps.put("sa2", System.currentTimeMillis());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
logger.info("Done actor 2");
timestamps.put("da2", System.currentTimeMillis());
c++;
}
@Override
public void reverse() {
}
@Override
public void reset() {
}
@Override
public void init() {
}
@Override
public boolean hasNext() {
if(c<steps){
return true;
}
return false;
}
});
loop.getSensors().add(s);
loop.prepare();
long begin;
long end;
begin = System.currentTimeMillis();
loop.execute();
end = System.currentTimeMillis();
logger.info("Elapsed time: "+(end-begin));
// Check if the timestamps are in the correct order
if(! (timestamps.get("da1")>timestamps.get("sa2")) ){
fail("Done 1 occured before start 2");
}
if(! (timestamps.get("da2")>timestamps.get("sa1")) ){
fail("Done 2 occured before start 1");
}
}
}
@@ -0,0 +1,62 @@
/**
*
* Copyright 2012 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This code is distributed in the hope that it will be useful, but without any
* warranty; without even the implied warranty of merchantability or fitness for
* a particular purpose. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.core.loops;
//import java.util.logging.Logger;
//import jcifs.smb.SmbFile;
//
//import org.junit.Ignore;
//import org.junit.Test;
public class SambaTest {
// private static final Logger logger = Logger.getLogger(SambaTest.class.getName());
// @Test
// public void testSamba() throws Exception{
//// System.setProperty("jcifs.smb.client.dfs.disabled", "true");
//// System.setProperty("jcifs.smb.client.ssnLimit", "1");
//
//// System.setProperty("jcifs.smb.client.domain", "x05la.psi.ch");
//// System.setProperty("jcifs.smb.client.username", "x05laop");
//// System.setProperty("jcifs.smb.client.password", "mXAS");
////
//// System.setProperty("jcifs.util.loglevel", "6");
//
//// System.setProperty("jcifs.smb.lmCompatibility", "0");
//
//// jcifs.smb.client.{domain,username,password
//
// String smbShare = "";
//// smbShare = "smb://x05laop:mXAS@x05la/x05laop/Data1/otfTmp/";
//// smbShare = "smb://x05la.psi.ch/x05laop/Data1/otfTmp/";
//// smbShare = "smb://x05laop:mXAS@x05la/x05laop/Data1/otfTmp/000000.txt.lock";
// smbShare = "smb://:@yoke.psi.ch/nfs/";
//// NtlmPasswordAuthentication auth = new NtlmPasswordAuthentication("x05laop:mXAS");
//// SmbFile tmpDir = new SmbFile(smbShare,auth);
//// SmbFile tmpDir = new SmbFile(smbShare);
//
//// tmpDir.createNewFile();
//// logger.info(""+tmpDir.exists());
//// logger.info(""+tmpDir.isDirectory());
//// SmbFile tmpFile = new SmbFile(tmpDir, "000000.txt.lock");
//// tmpFile.exists();
// }
}
@@ -0,0 +1,97 @@
/**
*
* 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.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import java.util.logging.Logger;
import org.junit.Test;
import com.google.common.eventbus.Subscribe;
import ch.psi.fda.core.TestConfiguration;
import ch.psi.fda.messages.Message;
import ch.psi.jcae.ChannelException;
import ch.psi.jcae.ChannelService;
import ch.psi.jcae.impl.DefaultChannelService;
public class CrlogicLoopStreamTest {
private static final Logger logger = Logger.getLogger(CrlogicLoopStreamTest.class.getName());
@Test(timeout=60000)
public void testExecute() throws InterruptedException, ChannelException, TimeoutException, ExecutionException{
ChannelService cservice = new DefaultChannelService();
TestConfiguration c = TestConfiguration.getInstance();
boolean zigZag = false;
String readback = null;
double start = 0;
double end = 2;
double stepSize = 0.01;
double integrationTime = 0.01;
double additionalBacklash = 0;
CrlogicLoopStream crlogic = new CrlogicLoopStream(cservice, c.getCrlogicPrefix(), c.getIoc(), zigZag);
crlogic.setActuator("cmot", c.getMotor1(), readback, start, end, stepSize, integrationTime, additionalBacklash);
crlogic.getSensors().add(new CrlogicResource("trigger", "TRIGGER0"));
crlogic.getSensors().add(new CrlogicResource("scaler0", "SCALER0", true));
crlogic.getSensors().add(new CrlogicResource("scaler1", "SCALER1", true));
crlogic.getSensors().add(new CrlogicResource("timestamp", "TIMESTAMP"));
// Initialize scaler template
TemplateVSC16Scaler scalertemplate = new TemplateVSC16Scaler();
Map<String,String> macros = new HashMap<>();
macros.put("PREFIX", c.getPrefixScaler());
cservice.createAnnotatedChannels(scalertemplate, macros);
crlogic.getEventBus().register(new Object(){
@Subscribe
public void onMessage(Message m){
logger.info(m.toString());
}
});
logger.info("Start scaler");
scalertemplate.getCommand().setValueNoWait(TemplateVSC16Scaler.Command.Count.ordinal());
logger.info("Execute CRLOGIC");
crlogic.prepare();
crlogic.execute();
crlogic.cleanup();
logger.info("Stop scaler");
scalertemplate.getCommand().setValue(TemplateVSC16Scaler.Command.Done.ordinal());
// Destroy scaler template
cservice.destroyAnnotatedChannels(scalertemplate);
cservice.destroy();
}
}
@@ -0,0 +1,212 @@
/**
*
* 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.Queue;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.logging.Logger;
import org.junit.Assert;
import org.junit.Test;
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import ch.psi.fda.messages.DataMessage;
import ch.psi.fda.messages.EndOfStreamMessage;
import ch.psi.fda.messages.Message;
import ch.psi.fda.messages.Metadata;
public class ParallelCrlogicStreamMergeTest {
private static final Logger logger = Logger.getLogger(ParallelCrlogicStreamMergeTest.class.getName());
@Test
public void testMerge() throws InterruptedException {
final Queue<double[]> expectedValues = new ArrayBlockingQueue<>(3);
expectedValues.add(new double[]{0.0035, 1.0});
expectedValues.add(new double[]{0.015, 1.0});
expectedValues.add(new double[]{0.026, 3.0});
List<Metadata> dmm = new ArrayList<>();
dmm.add(new Metadata("cractuator"));
dmm.add(new Metadata("tstamp"));
EventBus dataQueue2 = new EventBus();
EventBus dataQueue = new EventBus();
EventBus b = new EventBus();
b.register(new Object(){
@Subscribe
public void onMessage(Message m){
logger.info(m.toString());
if(m instanceof DataMessage){
DataMessage dm = (DataMessage) m;
double[] expected = expectedValues.remove();
Assert.assertEquals(expected[0], (double)dm.getData().get(0), 0.001);
Assert.assertEquals(expected[1], (double)dm.getData().get(1), 0.001);
}
}
});
MergeLogic streamMerge = new MergeLogic(dataQueue, dataQueue2, b);
streamMerge.enable();
// We need to fill the second queue first as otherwise the logic will fail
List<Metadata> dmm2 = new ArrayList<>();
dmm2.add(new Metadata("milli"));
dmm2.add(new Metadata("nano"));
dmm2.add(new Metadata("sensor1"));
DataMessage dm2 = new DataMessage(dmm2);
dm2.getData().add(9000d);
dm2.getData().add(122d);
dm2.getData().add(0.1d);
dataQueue2.post(dm2);
dm2 = new DataMessage(dmm2);
dm2.getData().add(10000d);
dm2.getData().add(122d);
dm2.getData().add(1d);
dataQueue2.post(dm2);
dm2 = new DataMessage(dmm2);
dm2.getData().add(10000d);
dm2.getData().add(153d);
dm2.getData().add(2d);
dataQueue2.post(dm2);
dm2 = new DataMessage(dmm2);
dm2.getData().add(10000d);
dm2.getData().add(162d);
dm2.getData().add(3d);
dataQueue2.post(dm2);
dataQueue2.post(new EndOfStreamMessage());
DataMessage dm = new DataMessage(dmm);
dm.getData().add(0.0035d);
dm.getData().add(10.000000123);
dataQueue.post(dm);
dm = new DataMessage(dmm);
dm.getData().add(0.015);
dm.getData().add(10.000000143);
dataQueue.post(dm);
dm = new DataMessage(dmm);
dm.getData().add(0.026);
dm.getData().add(10.000000163);
dataQueue.post(dm);
dataQueue.post(new EndOfStreamMessage());
streamMerge.disable();
}
/**
* For this test the second queue value timestamp must not be before the timestamp of the first queue
* @throws InterruptedException
*/
@Test
public void testMergeSecondQueueLessElements() throws InterruptedException {
EventBus dataQueue = new EventBus();
EventBus dataQueue2 = new EventBus();
EventBus b = new EventBus();
b.register(new Object(){
@Subscribe
public void onMessage(Message m){
logger.info(m.toString());
}
});
MergeLogic streamMerge = new MergeLogic(dataQueue, dataQueue2, b);
streamMerge.enable();
// We need to fill the second queue first as otherwise the logic will fail
List<Metadata> dmm2 = new ArrayList<>();
dmm2.add(new Metadata("milli"));
dmm2.add(new Metadata("nano"));
dmm2.add(new Metadata("sensor1"));
DataMessage dm2 = new DataMessage(dmm2);
dm2.getData().add(9000d);
dm2.getData().add(122d);
dm2.getData().add(0.1d);
dataQueue2.post(dm2);
//
// dm2 = new DataMessage(dmm2);
// dm2.getData().add(10000d);
// dm2.getData().add(122d);
// dm2.getData().add(1d);
// dataQueue2.add(dm2);
// dm2 = new DataMessage(dmm2);
// dm2.getData().add(10000d);
// dm2.getData().add(153d);
// dm2.getData().add(2d);
// dataQueue2.post(dm2);
// dm2 = new DataMessage(dmm2);
// dm2.getData().add(10000d);
// dm2.getData().add(170d);
// dm2.getData().add(3d);
// dataQueue2.add(dm2);
dataQueue2.post(new EndOfStreamMessage());
List<Metadata> dmm = new ArrayList<>();
dmm.add(new Metadata("cractuator"));
dmm.add(new Metadata("tstamp"));
DataMessage dm = new DataMessage(dmm);
dm.getData().add(0.0035d);
dm.getData().add(10.000000123);
dataQueue.post(dm);
dm = new DataMessage(dmm);
dm.getData().add(0.015);
dm.getData().add(10.000000143);
dataQueue.post(dm);
// dm = new DataMessage(dmm);
// dm.getData().add(0.026);
// dm.getData().add(10.000000163);
// dataQueue.add(dm);
dataQueue.post(new EndOfStreamMessage());
streamMerge.disable();
}
}
@@ -0,0 +1,137 @@
/**
*
* 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.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import java.util.logging.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.google.common.eventbus.Subscribe;
import ch.psi.fda.core.TestConfiguration;
import ch.psi.fda.messages.Message;
import ch.psi.jcae.Channel;
import ch.psi.jcae.ChannelDescriptor;
import ch.psi.jcae.ChannelException;
import ch.psi.jcae.ChannelService;
import ch.psi.jcae.impl.DefaultChannelService;
import ch.psi.jcae.impl.type.DoubleTimestamp;
public class ParallelCrlogicTest {
private static final Logger logger = Logger.getLogger(ParallelCrlogicTest.class.getName());
private ChannelService cservice;
@Before
public void setUp() throws Exception {
cservice = new DefaultChannelService();
}
@After
public void tearDown() throws Exception {
cservice.destroy();
}
// @Ignore
@Test(timeout=60000)
public void testExecute() throws InterruptedException, ChannelException, TimeoutException, ExecutionException{
TestConfiguration c = TestConfiguration.getInstance();
boolean zigZag = false;
String readback = null;
double start = 0;
double end = 2;
double stepSize = 0.01;
double integrationTime = 0.01;
double additionalBacklash = 0;
List<String> ids = new ArrayList<>();
List<Channel<DoubleTimestamp>> sensors = new ArrayList<>();
ids.add("mot1");
ids.add("mot2");
// ids.add("mot3");
sensors.add(cservice.createChannel(new ChannelDescriptor<>(DoubleTimestamp.class, c.getMotor1()+".RVAL")));
sensors.add(cservice.createChannel(new ChannelDescriptor<>(DoubleTimestamp.class, c.getMotor1()+".RBV")));
// sensors.add(cservice.createChannel(new ChannelDescriptor<>(DoubleTimestamp.class, "ARIDI-PCT:CURRENT")));
ScrlogicLoop scrlogic = new ScrlogicLoop(ids, sensors);
CrlogicLoopStream crlogic = new CrlogicLoopStream(cservice, c.getCrlogicPrefix(), c.getServer(), zigZag);
crlogic.setActuator("cmot", c.getMotor1(), readback, start, end, stepSize, integrationTime, additionalBacklash);
crlogic.getSensors().add(new CrlogicResource("trigger", "TRIGGER0"));
crlogic.getSensors().add(new CrlogicResource("scaler0", "SCALER0", true));
crlogic.getSensors().add(new CrlogicResource("scaler1", "SCALER1", true));
crlogic.getSensors().add(new CrlogicResource("timestamp", "TIMESTAMP"));
// Initialize scaler template
TemplateVSC16Scaler scalertemplate = new TemplateVSC16Scaler();
Map<String,String> macros = new HashMap<>();
macros.put("PREFIX", c.getPrefixScaler());
cservice.createAnnotatedChannels(scalertemplate, macros);
ParallelCrlogic pcrlogic = new ParallelCrlogic(crlogic, scrlogic);
pcrlogic.getEventBus().register(new Object(){
@Subscribe
public void onMessage(Message m){
logger.info(m.toString());
}
});
logger.info("Start scaler");
scalertemplate.getCommand().setValueNoWait(TemplateVSC16Scaler.Command.Count.ordinal());
pcrlogic.prepare();
pcrlogic.execute();
pcrlogic.cleanup();
logger.info("Stop scaler");
scalertemplate.getCommand().setValue(TemplateVSC16Scaler.Command.Done.ordinal());
// System.out.println("PARALLEL CRLOGIC data:");
// BlockingQueue<Message> queue = pcrlogic.getDataQueue().getQueue();
// Message m = queue.take();
// while(! (m instanceof EndOfStreamMessage)){
// System.out.println(m.toString());
// m = queue.take();
// }
// Destroy scaler template
cservice.destroyAnnotatedChannels(scalertemplate);
}
}
@@ -0,0 +1,143 @@
/**
*
* 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.CountDownLatch;
import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.google.common.eventbus.Subscribe;
import ch.psi.fda.messages.Message;
import ch.psi.jcae.Channel;
import ch.psi.jcae.ChannelDescriptor;
import ch.psi.jcae.ChannelException;
import ch.psi.jcae.ChannelService;
import ch.psi.jcae.impl.DefaultChannelService;
import ch.psi.jcae.impl.type.DoubleTimestamp;
public class ScrlogicLoopTest {
private static final Logger logger = Logger.getLogger(ScrlogicLoopTest.class.getName());
private ChannelService cservice;
@Before
public void setUp() throws Exception {
cservice = new DefaultChannelService();
}
@After
public void tearDown() throws Exception {
cservice.destroy();
}
/**
* Test method for {@link ch.psi.fda.core.loops.cr.ScrlogicLoop#execute()}.
* @throws InterruptedException
* @throws TimeoutException
* @throws ChannelException
* @throws CAException
*/
@Test
public void testExecute() throws InterruptedException, ChannelException, TimeoutException {
List<String> ids = new ArrayList<>();
List<Channel<DoubleTimestamp>> sensors = new ArrayList<>();
ids.add("mot1");
ids.add("mot2");
sensors.add(cservice.createChannel(new ChannelDescriptor<>(DoubleTimestamp.class, "MTEST-HW3:MOT1")));
sensors.add(cservice.createChannel(new ChannelDescriptor<>(DoubleTimestamp.class, "MTEST-HW3:MOT1.RBV")));
final ScrlogicLoop logic = new ScrlogicLoop(ids, sensors);
logic.getEventBus().register(new Object(){
@Subscribe
public void onMessage(Message m){
logger.info(m.toString());
}
});
for(int i=0;i<2;i++){
logic.prepare();
Thread tt = new Thread(new Runnable(){
@Override
public void run() {
try {
logic.execute();
} catch (InterruptedException e) {
logger.log(Level.SEVERE, "", e);
}
}
});
tt.start();
final CountDownLatch l = new CountDownLatch(1);
Thread t = new Thread(new Runnable(){
@Override
public void run() {
try{
System.out.println("MOVE 1");
Channel<Double> channel = cservice.createChannel(new ChannelDescriptor<>(Double.class, "MTEST-HW3:MOT1", false));
// Wait some time until
Thread.sleep(100);
channel.setValue(1.5);
Thread.sleep(100);
channel.setValue(2.5);
System.out.println("MOVE D");
// Thread.sleep(100);
// channel.setValue(6.5);
// Thread.sleep(100);
l.countDown();
}
catch(Exception e){
}
}
});
t.start();
l.await();
logic.abort();
tt.join();
}
}
}
@@ -0,0 +1,118 @@
/**
*
* 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 static org.junit.Assert.*;
import gov.aps.jca.CAException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import java.util.logging.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import ch.psi.fda.core.Sensor;
import ch.psi.fda.core.TestChannels;
import ch.psi.jcae.Channel;
import ch.psi.jcae.ChannelDescriptor;
import ch.psi.jcae.ChannelException;
import ch.psi.jcae.ChannelService;
import ch.psi.jcae.impl.DefaultChannelService;
/**
* Test class for the ScalarDoubleSensorChannelAccess class.
*/
public class ChannelAccessSensorTest {
private static Logger logger = Logger.getLogger(ChannelAccessSensorTest.class.getName());
private ChannelService cservice;
@Before
public void setUp() throws Exception {
cservice = new DefaultChannelService();
}
@After
public void tearDown() throws Exception {
cservice.destroy();
}
@Test
public void testReadDouble() throws InterruptedException, ChannelException, TimeoutException, ExecutionException {
final String channelName = TestChannels.ANALOG_OUT;
Channel<Double> channel = cservice.createChannel(new ChannelDescriptor<>(Double.class, channelName));
Sensor sensor = new ChannelAccessSensor<>("id0", channel);
// Prepare sensor channel
Double setValue = 0.1d;
channel.setValue(setValue);
// Get sensor readout value
Double value = (Double) sensor.read();
logger.finest("Sensor value: "+value);
if(!value.equals(setValue)){
fail("Sensor readout value does not match actual value");
}
}
@Test
public void testReadString() throws CAException, InterruptedException, ChannelException, TimeoutException {
final String channelName = TestChannels.ANALOG_OUT+".NAME";
final String actualValue = TestChannels.ANALOG_OUT;
Channel<String> channel = cservice.createChannel(new ChannelDescriptor<>(String.class, channelName));
Sensor sensor = new ChannelAccessSensor<>("id0", channel);
// Get sensor readout value
String value = (String) sensor.read();
logger.info("Sensor value: "+value);
if(!value.equals(actualValue)){
fail("Sensor readout "+value+" value does not match actual value "+actualValue);
}
}
@Test
public void testReadDoubleArray() throws CAException, InterruptedException, ChannelException, TimeoutException, ExecutionException {
final String channelName = TestChannels.DOUBLE_WAVEFORM;
final int numberOfPoints = 10;
Channel<double[]> channel = cservice.createChannel(new ChannelDescriptor<>(double[].class, channelName, false, numberOfPoints));
Sensor sensor = new ChannelAccessSensor<>("id0", channel);
// Prepare sensor channel
double[] setValue = new double[] { 0.1,0.2,0.3,4,5,6,77,88,99,10.2};
channel.setValue(setValue);
// Get sensor readout value
double[] value = (double[]) sensor.read();
for(int i=0;i<setValue.length;i++){
logger.finest("Sensor value["+i+"]: "+value[i]);
if(Math.abs(value[i] - setValue[i])>0.0000001){ // The precision of the channel is 6
fail("Sensor readout value does not match actual value");
}
}
}
}
@@ -0,0 +1,68 @@
/**
*
* 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 org.junit.After;
import org.junit.Before;
import org.junit.Test;
import ch.psi.fda.core.sensors.TimestampSensor;
/**
* Test class for MillisecondsTimestampSensor
*/
public class MillisecondTimestampSensorTest {
// Get Logger
private static Logger logger = Logger.getLogger(MillisecondTimestampSensorTest.class.getName());
/**
* @throws java.lang.Exception
*/
@Before
public void setUp() throws Exception {
}
/**
* @throws java.lang.Exception
*/
@After
public void tearDown() throws Exception {
}
/**
* Test method for {@link ch.psi.fda.core.sensors.ChannelAccessDoubleSensor#read()}.
* @throws CAException
*/
@Test
public void testRead() throws CAException {
TimestampSensor sensor = new TimestampSensor("id0");
// Get sensor readout value
Double value = (Double) sensor.read();
logger.finest("Sensor value: "+value);
}
}
@@ -0,0 +1,93 @@
/**
*
* Copyright 2010 Paul Scherrer Institute. All rights reserved.
*
* This code is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This code is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this code. If not, see <http://www.gnu.org/licenses/>.
*
*/
package ch.psi.fda.model;
import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.logging.Logger;
import javax.xml.bind.JAXBException;
import javax.xml.bind.MarshalException;
import org.junit.Test;
import org.xml.sax.SAXException;
import ch.psi.fda.model.ModelManager;
import ch.psi.fda.model.v1.Configuration;
import ch.psi.fda.model.v1.LinePlot;
import ch.psi.fda.model.v1.Scan;
public class ModelManagerTest {
private static final String tmpDirectory = "target/tmp";
private static Logger logger = Logger.getLogger(ModelManagerTest.class.getName());
/**
* Test method for {@link ch.psi.fda.model.ModelManager#unmarshall(java.io.File)}.
* @throws SAXException
* @throws JAXBException
* @throws URISyntaxException
*/
@Test
public void testUnmarshall() throws JAXBException, SAXException, URISyntaxException {
URL url = this.getClass().getClassLoader().getResource("home/scans/templates/scan1d.xml");
Configuration c = ModelManager.unmarshall(new File(new URI(url.toString())));
logger.info(""+c.getData().getFormat());
logger.info("FILENAME: "+c.getData().getFileName());
}
/**
* Test method for {@link ch.psi.fda.model.ModelManager#marshall(ch.psi.fda.model.v1.Configuration, java.io.File)}.
* @throws SAXException
* @throws JAXBException
*/
@Test(expected=MarshalException.class)
public void testMarshallFail() throws JAXBException, SAXException {
Configuration c = new Configuration();
c.setDescription("My Description");
Scan s = new Scan();
c.setScan(s);
// Add component that is not match to xsd
LinePlot v = new LinePlot();
c.getVisualization().add(v);
ModelManager.marshall(c, new File(tmpDirectory+"/scan.xml"));
}
@Test
public void testMarshall() throws JAXBException, SAXException {
Configuration c = new Configuration();
c.setDescription("My Description");
Scan s = new Scan();
c.setScan(s);
ModelManager.marshall(c, new File(tmpDirectory+"/scan1234.xml"));
}
}
+1
View File
@@ -0,0 +1 @@
ch.psi.jcae.ContextFactory.addressList=MTEST-VME-HW3.psi.ch