diff --git a/ch.psi.fda/Readme.md b/ch.psi.fda/Readme.md
index 1a9aee4..1fa21b9 100644
--- a/ch.psi.fda/Readme.md
+++ b/ch.psi.fda/Readme.md
@@ -16,7 +16,7 @@ To use the FDA libary in an other project use:
# Development
When checking out the project from the repository there is the `target/generated-sources/xjc` folder missing.
-After checking out the project execute `mvn compile` to create the folder and the required classes.
+After checking out the project execute `mvn compile` to create the folder and the required classes.
To build project use `mvn clean install`.
diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/AcquisitionRMain.java b/ch.psi.fda/src/main/java/ch/psi/fda/AcquisitionRMain.java
new file mode 100644
index 0000000..37f8888
--- /dev/null
+++ b/ch.psi.fda/src/main/java/ch/psi/fda/AcquisitionRMain.java
@@ -0,0 +1,426 @@
+/**
+ *
+ * Copyright 2010 Paul Scherrer Institute. All rights reserved.
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This code is distributed in the hope that it will be useful,
+ * but without any warranty; without even the implied warranty of
+ * merchantability or fitness for a particular purpose. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this code. If not, see .
+ *
+ */
+
+package ch.psi.fda;
+
+import java.awt.FlowLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.io.File;
+import java.io.PrintWriter;
+import java.util.HashMap;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JSplitPane;
+import javax.swing.JTabbedPane;
+import javax.swing.ScrollPaneLayout;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.GnuParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.OptionBuilder;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+
+import com.google.common.eventbus.AsyncEventBus;
+import com.google.common.eventbus.EventBus;
+
+import sun.misc.Signal;
+import sun.misc.SignalHandler;
+import ch.psi.fda.aq.VisualizationMapper;
+import ch.psi.fda.gui.ProgressPanel;
+import ch.psi.fda.gui.ScrollableFlowPanel;
+import ch.psi.fda.install.ApplicationConfigurator;
+import ch.psi.fda.model.ModelManager;
+import ch.psi.fda.model.v1.Configuration;
+import ch.psi.fda.model.v1.Data;
+import ch.psi.fda.rest.RestClient;
+import ch.psi.fda.visualizer.Visualizer;
+
+/**
+ * Entry class for command line based data acquisition
+ */
+@SuppressWarnings("restriction")
+public class AcquisitionRMain {
+
+ private static Logger logger = Logger.getLogger(AcquisitionRMain.class.getName());
+
+ private static boolean abortedViaSignal = false;
+
+ /**
+ * Main Program
+ * Process exit code: -1 if wrong number of arguments are passed
+ * Process exit code: 3 if aborted via Ctrl+C
+ *
+ * @param args Arguments of the program
+ */
+ public static void main(String[] args) {
+
+ String scriptname = "fda_scan";
+
+ Integer iterations = null;
+ boolean autoclose = false;
+ boolean nogui = false;
+ String files[] = null;
+
+ HashMap varTable = new HashMap();
+
+ // Iterations option
+ OptionBuilder.hasArg();
+ OptionBuilder.withArgName("iterations");
+ OptionBuilder.withDescription("Number of iterations");
+ OptionBuilder.withType(new Integer(1));
+ Option o_iterations = OptionBuilder.create( "iterations");
+
+ // Variables option
+ OptionBuilder.hasArg();
+ OptionBuilder.withArgName("variables");
+ OptionBuilder.withDescription("Scan variables - variables are specified in the form var=value,var2=value2");
+ OptionBuilder.withType(new Integer(1));
+ Option o_variables = OptionBuilder.create( "variables");
+
+ Option o_autoclose = new Option( "autoclose", "Close down application after scan" );
+ Option o_init = new Option( "initialize", "Initialize application directories and configuration files" );
+ Option o_nogui = new Option( "nogui", "Do not show scan GUI" );
+
+ Options options = new Options();
+ options.addOption(o_variables);
+ options.addOption(o_iterations);
+ options.addOption(o_autoclose);
+ options.addOption(o_init);
+ options.addOption(o_nogui);
+
+ CommandLineParser parser = new GnuParser();
+ // Parse the command line arguments
+ try {
+ CommandLine line = parser.parse( options, args );
+
+ // Initialize application
+ if( line.hasOption(o_init.getOpt()) ){
+ // Initialize application
+ ApplicationConfigurator ac = new ApplicationConfigurator();
+ ac.initializeApplication();
+ System.exit(0);
+ }
+
+ if(line.getArgs().length<1){
+ throw new ParseException("One argument is required");
+ }
+
+ files=line.getArgs();
+
+ // Iterations option
+ if( line.hasOption(o_iterations.getOpt()) ){
+ iterations = Integer.parseInt(line.getOptionValue(o_iterations.getOpt()));
+ }
+
+ // Variables
+ if( line.hasOption(o_variables.getOpt()) ){
+ String variables = line.getOptionValue(o_variables.getOpt() );
+ String[] vars = variables.split(",");
+ for(String varp:vars){
+ String[] pair = varp.split("=");
+ if(pair.length!=2){
+ throw new ParseException("Variables are not specified the correct way. -variables var1=val1,var2=val2");
+ }
+ varTable.put(pair[0], pair[1]);
+ }
+ }
+
+ // Autoclose option
+ if( line.hasOption( o_autoclose.getOpt() ) ) {
+ autoclose = true;
+ }
+
+ // No GUI option
+ if( line.hasOption( o_nogui.getOpt() ) ) {
+ nogui = true;
+ }
+
+ } catch (ParseException e) {
+ System.err.println(e.getMessage());
+ HelpFormatter formatter = new HelpFormatter();
+ formatter.printUsage(new PrintWriter(System.out, true), HelpFormatter.DEFAULT_WIDTH, scriptname, options);
+ System.exit(-1);
+ }
+
+ // Run application
+ try{
+ for(String file: files){
+ run(new File(file), iterations, autoclose, nogui);
+ }
+
+ // Close application automatically if autoclose option is set (and visualizations are specified)
+ if(nogui || autoclose ){
+ System.exit(0);
+ }
+
+ }
+ catch(Exception ee){
+ System.out.println("Acquisition failed due to: "+ee.getMessage());
+ logger.log(Level.SEVERE, "Acquisition failed due to: ", ee); // Do not print stack trace
+ System.exit(-1);
+ }
+
+ }
+
+ /**
+ * Run scan
+ * @param file Scan file
+ * @param iterations Number of iterations
+ * @param autoclose Flag whether to close the application automatically after the scan
+ * @param nogui Flag whether to run the scan with a GUI
+ */
+ public static void run(File file, Integer iterations, boolean autoclose, boolean nogui){
+
+// // Initialize application
+// ApplicationConfigurator ac = new ApplicationConfigurator();
+// ac.initializeApplication();
+
+ if(!file.exists()){
+ throw new RuntimeException("File "+file.getAbsolutePath()+" does not exist");
+ }
+
+ Configuration c;
+ try {
+ c = ModelManager.unmarshall(file);
+ } catch (Exception e) {
+ throw new RuntimeException("Unable to deserialize configuration: "+e.getMessage(), e);
+ }
+
+ // Set data file name
+ // Determine name used for the data file
+ String name = file.getName();
+ name = name.replaceAll("\\.xml$", "");
+
+ if(c.getData()!=null){
+ Data data = c.getData();
+ // Only update filename if no name is specified in xml file
+ if(data.getFileName()==null){
+ data.setFileName(name);
+ }
+ }
+ else{
+ Data data = new Data();
+ data.setFileName(name);
+ c.setData(data);
+ }
+
+
+ // Override number of executions
+ if(iterations != null){
+ c.setNumberOfExecution(iterations);
+ }
+ // Fix configuration if iterations is specified with 0 and no iterations option is specified
+ if(c.getNumberOfExecution()==0){
+ c.setNumberOfExecution(1);
+ }
+
+ // Create/get acquisition engine
+// final Acquisition acquisition = new Acquisition(new DefaultChannelService(), new AcquisitionConfiguration());
+
+ boolean vis = false;
+ // Only register data visualization task/processor if there are visualizations
+ if(c.getVisualization().size()>0 && !nogui){
+ vis=true;
+ }
+
+ EventBus b = new AsyncEventBus(Executors.newSingleThreadExecutor());
+
+ final RestClient client = new RestClient();
+
+
+// acquisition.initalize(b, c);
+
+ Visualizer visualizer = null;
+ // Only register data visualization task/processor if there are visualizations
+ if(vis){
+ final StreamClient sclient = new StreamClient(b);
+ Executors.newSingleThreadExecutor().execute(new Runnable() {
+ @Override
+ public void run() {
+ sclient.listen();
+ }
+ });
+
+ visualizer = new Visualizer(VisualizationMapper.mapVisualizations(c.getVisualization()));
+ b.register(visualizer);
+
+ // TODO eventually set update on delimiter/dim boundary here
+
+ // If there is a continous dimension only update plot at the end of a line
+ if(c.getScan() != null && c.getScan().getCdimension()!=null){
+ visualizer.setUpdateAtStreamElement(false);
+ visualizer.setUpdateAtStreamDelimiter(true);
+ visualizer.setUpdateAtEndOfStream(true);
+ }
+ }
+
+ // GUI GUI GUI GUI GUI GUI GUI
+ ProgressPanel progressPanel = null;
+ if(visualizer != null){ // Only bring up GUI if there are some plots ...
+ // Visualizations
+ JPanel opanel = new ScrollableFlowPanel();
+ opanel.setLayout(new FlowLayout());
+
+ JScrollPane spane = new JScrollPane(opanel, ScrollPaneLayout.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneLayout.HORIZONTAL_SCROLLBAR_NEVER);
+ JTabbedPane tpane = new JTabbedPane();
+ tpane.addTab("Overview", spane);
+
+ for(JPanel p: visualizer.getPlotPanels()){
+ opanel.add(p);
+ }
+
+ final JFrame frame = new JFrame("FDA: "+file);
+ frame.setSize(1200,800);
+
+ // Create progress panel
+ progressPanel = new ProgressPanel();
+ progressPanel.addActionListener(new ActionListener() {
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ try {
+ client.stop();
+ } catch (Exception e1) {
+ logger.log(Level.SEVERE, "Exception occured while aborting scan", e1);
+ }
+
+ }
+ });
+
+
+ JSplitPane splitPane = new JSplitPane();
+ splitPane.setLeftComponent(progressPanel);
+ splitPane.setRightComponent(tpane);
+
+ frame.add(splitPane);
+ frame.addWindowListener(new WindowAdapter(){
+ @Override
+ public void windowClosing(WindowEvent we){
+ if(client.isActive()){
+ // Abort acquisition
+ client.stop();
+ }
+
+ // Wait until acquisition is aborted. Maximum wait 10*100milliseconds before forcefully
+ // terminate application
+ int count=0;
+ while(client.isActive()){
+ if(count == 10){
+ break;
+ }
+
+ // Sleep 100 milliseconds
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ count++;
+ }
+
+ // Terminate program
+ System.exit(0);
+ }
+ });
+ frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);//.DO_NOTHING_ON_CLOSE);
+// frame.setVisible(true);
+
+ java.awt.EventQueue.invokeLater(new Runnable() {
+ public void run() {
+ frame.setVisible(true);
+ }
+ });
+ }
+ // GUI GUI GUI GUI GUI GUI GUI
+
+ // CLI CLI CLI CLI
+ // Register the scan engine as Signal Handler
+ Signal.handle(new Signal("INT"), new SignalHandler() {
+ /**
+ * Thread save signal counter
+ */
+ private AtomicInteger signalCount= new AtomicInteger(0);
+
+ /**
+ * Testing signal handler (in Eclipse) use this after starting scan:
+ *
+ * SL5: A=`ps -ef | tail -10 | grep jav[a] | awk '{printf $2}'`;kill -2 $A
+ * MacOS X: A=`ps -ef | grep AcquisitionMai[n] | awk '{printf $2}'`;kill -2 $A
+ *
+ * on the command line use CTRL-C
+ */
+ @Override
+ public void handle(Signal signal) {
+
+ logger.finest("Received signal: "+signal);
+
+ int count = signalCount.incrementAndGet();
+
+ // If signal is received more than 1 time forcefully abort application
+ if(count>1){
+ logger.info("Terminate application");
+ System.exit(2);
+ }
+
+ abortedViaSignal = true;
+ // Abort acquisition engine
+ if(client.isActive()){
+ // Abort acquisition
+ client.stop();
+ }
+ }
+ });
+ // CLI CLI CLI CLI
+
+
+ if(visualizer != null){
+ visualizer.configure();
+ }
+ client.acquire(c);
+
+
+ // GUI GUI GUI GUI GUI GUI GUI
+ // Set progress panel to done
+// if(progressPanel != null){
+// // Can this be done via a Listener?
+// progressPanel.done();
+// }
+ // GUI GUI GUI GUI GUI GUI GUI
+
+ if(abortedViaSignal){
+ System.exit(3);
+ }
+ }
+
+
+
+}
diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/StreamClient.java b/ch.psi.fda/src/main/java/ch/psi/fda/StreamClient.java
new file mode 100644
index 0000000..c867296
--- /dev/null
+++ b/ch.psi.fda/src/main/java/ch/psi/fda/StreamClient.java
@@ -0,0 +1,73 @@
+/**
+ *
+ * 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 .
+ *
+ */
+package ch.psi.fda;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectInputStream;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.jeromq.ZMQ;
+
+import com.google.common.eventbus.EventBus;
+
+/**
+ *
+ */
+public class StreamClient {
+
+ private static final Logger logger = Logger.getLogger(StreamClient.class.getName());
+
+ private final EventBus bus;
+
+ public StreamClient(EventBus bus){
+ this.bus = bus;
+ }
+
+ public void listen() {
+ ZMQ.Context context = ZMQ.context();
+ zmq.ZError.clear(); // Clear error code
+ ZMQ.Socket socket = context.socket(ZMQ.SUB);
+ socket.connect("tcp://emac:10000");
+ socket.subscribe(""); // SUBSCRIBE !
+
+ while (true) {
+ byte[] content = null;
+ // String header = socket.recvStr(); // header
+ socket.recvStr(); // header
+ while (socket.hasReceiveMore()) {
+ content = socket.recv();
+ }
+ if (content == null) { // we lost something discard read messages
+ logger.warning("Lost some message - discard and continue");
+ continue;
+ }
+
+ try (ByteArrayInputStream bis = new ByteArrayInputStream(content); ObjectInput in = new ObjectInputStream(bis);) {
+ Object o = in.readObject();
+ bus.post(o);
+ } catch (IOException | ClassNotFoundException e) {
+ logger.log(Level.WARNING, "Unable to deserialize message", e);
+ }
+
+ }
+ }
+}
diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/ViewerMain.java b/ch.psi.fda/src/main/java/ch/psi/fda/ViewerMain.java
index 79a51e8..16436ba 100644
--- a/ch.psi.fda/src/main/java/ch/psi/fda/ViewerMain.java
+++ b/ch.psi.fda/src/main/java/ch/psi/fda/ViewerMain.java
@@ -22,10 +22,6 @@ package ch.psi.fda;
import java.awt.FlowLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.ObjectInput;
-import java.io.ObjectInputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
@@ -37,8 +33,6 @@ import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.ScrollPaneLayout;
-import org.jeromq.ZMQ;
-
import com.google.common.eventbus.EventBus;
import ch.psi.fda.gui.ScrollableFlowPanel;
@@ -107,35 +101,7 @@ public class ViewerMain {
bus.register(visualizer);
visualizer.configure();
- ZMQ.Context context = ZMQ.context();
- zmq.ZError.clear(); // Clear error code
- ZMQ.Socket socket = context.socket(ZMQ.SUB);
- socket.connect("tcp://emac:10000");
- socket.subscribe(""); // SUBSCRIBE !
-
- while(true){
- byte[] content = null;
-// String header = socket.recvStr(); // header
- socket.recvStr(); // header
- while(socket.hasReceiveMore()){
- content = socket.recv();
- }
- if(content==null){ // we lost something discard read messages
- logger.warning("Lost some message - discard and continue");
- continue;
- }
-
- try (
- ByteArrayInputStream bis = new ByteArrayInputStream(content);
- ObjectInput in = new ObjectInputStream(bis);
- ) {
- Object o = in.readObject();
- bus.post(o);
- } catch (IOException | ClassNotFoundException e) {
- logger.log(Level.WARNING,"Unable to deserialize message",e);
- }
-
- }
+ new StreamClient(bus).listen();
}
/**
diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/rest/AcquisitionEngine.java b/ch.psi.fda/src/main/java/ch/psi/fda/rest/AcquisitionEngine.java
index 027e9a4..e489076 100644
--- a/ch.psi.fda/src/main/java/ch/psi/fda/rest/AcquisitionEngine.java
+++ b/ch.psi.fda/src/main/java/ch/psi/fda/rest/AcquisitionEngine.java
@@ -167,4 +167,14 @@ public class AcquisitionEngine {
}
}
}
+
+ /**
+ * @return
+ */
+ public boolean isActive() {
+ if(acquisition==null){
+ return false;
+ }
+ return acquisition.isActive();
+ }
}
diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/rest/RestClient.java b/ch.psi.fda/src/main/java/ch/psi/fda/rest/RestClient.java
new file mode 100644
index 0000000..c03b4f3
--- /dev/null
+++ b/ch.psi.fda/src/main/java/ch/psi/fda/rest/RestClient.java
@@ -0,0 +1,58 @@
+/**
+ *
+ * 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 .
+ *
+ */
+package ch.psi.fda.rest;
+
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+import javax.xml.bind.JAXBElement;
+import javax.xml.namespace.QName;
+
+import org.glassfish.jersey.jackson.JacksonFeature;
+
+import ch.psi.fda.model.v1.Configuration;
+
+/**
+ *
+ */
+public class RestClient {
+
+
+
+ private Client client = ClientBuilder.newClient().register(JacksonFeature.class);
+ private WebTarget target = client.target("http://emac:8080").path("fda");
+
+
+
+ public String acquire(Configuration c){
+ // Wrap configuration in JAXBElement as there is no @XmlRootElement available within the generated Configuration class
+ JAXBElement jaxbElement = new JAXBElement<>(new QName("ROOT"), Configuration.class, c);
+ return target.request().post(Entity.entity(jaxbElement, MediaType.APPLICATION_XML), String.class);
+ }
+
+ public void stop(){
+ target.request().delete();
+ }
+
+ public boolean isActive(){
+ return target.request().get(Boolean.class);
+ }
+}
diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/rest/RestServer.java b/ch.psi.fda/src/main/java/ch/psi/fda/rest/RestServer.java
index eb75759..8a5879f 100644
--- a/ch.psi.fda/src/main/java/ch/psi/fda/rest/RestServer.java
+++ b/ch.psi.fda/src/main/java/ch/psi/fda/rest/RestServer.java
@@ -55,11 +55,17 @@ public class RestServer {
ResourceBinder binder = new ResourceBinder();
- ResourceConfig resourceConfig = new ResourceConfig(JacksonFeature.class);
+ ResourceConfig resourceConfig = new ResourceConfig();
resourceConfig.packages(RestServer.class.getPackage().getName()+".services"); // Services are located in services package
resourceConfig.register(binder);
+
+ resourceConfig.register(JacksonFeature.class);
+
+
HttpServer server = GrizzlyHttpServerFactory.createHttpServer(baseUri, resourceConfig);
+
+
// Static content
// String home = System.getenv("FDA_BASE");
// if (home == null) {
diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/rest/services/ScanService.java b/ch.psi.fda/src/main/java/ch/psi/fda/rest/services/ScanService.java
index 98b04d4..7e5449e 100644
--- a/ch.psi.fda/src/main/java/ch/psi/fda/rest/services/ScanService.java
+++ b/ch.psi.fda/src/main/java/ch/psi/fda/rest/services/ScanService.java
@@ -22,6 +22,7 @@ package ch.psi.fda.rest.services;
import javax.inject.Inject;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.MediaType;
@@ -37,7 +38,6 @@ public class ScanService {
@POST
@Consumes({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
-// @Produces({MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML})
public String execute(Configuration configuration) throws InterruptedException{
return aengine.submit(configuration);
}
@@ -46,4 +46,10 @@ public class ScanService {
public void stop(){
aengine.terminate();
}
+
+ @GET
+ public boolean isActive(){
+ return aengine.isActive();
+ }
+
}