diff --git a/ch.psi.fda/pom.xml b/ch.psi.fda/pom.xml index c4259a2..e6acca9 100644 --- a/ch.psi.fda/pom.xml +++ b/ch.psi.fda/pom.xml @@ -9,7 +9,7 @@ ch.psi jcae - 2.1.8 + 2.1.9 @@ -32,6 +32,7 @@ org.python jython + 2.5.3 diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/aq/AcquisitionEngineNG.java b/ch.psi.fda/src/main/java/ch/psi/fda/aq/AcquisitionEngineNG.java new file mode 100644 index 0000000..682a67a --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/aq/AcquisitionEngineNG.java @@ -0,0 +1,50 @@ +/** + * + * 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.aq; + +import javax.script.ScriptEngine; +import javax.script.ScriptEngineManager; +import javax.script.ScriptException; + +/** + * @author ebner + * + */ +public class AcquisitionEngineNG { + + private ScriptEngine engine; + + public AcquisitionEngineNG() { + // 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"); + } + + public void execute(String script){ + try { + engine.eval(script); + } catch (ScriptException e) { + throw new RuntimeException("Action failed while executing the Jython script",e); + } + } + +} diff --git a/ch.psi.fda/src/main/java/ch/psi/fda/aq/ScanMapperNG.java b/ch.psi.fda/src/main/java/ch/psi/fda/aq/ScanMapperNG.java new file mode 100644 index 0000000..eeb63da --- /dev/null +++ b/ch.psi.fda/src/main/java/ch/psi/fda/aq/ScanMapperNG.java @@ -0,0 +1,490 @@ +/** + * + * 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.aq; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.UUID; + +import ch.psi.fda.core.Actor; +import ch.psi.fda.core.actions.ChannelAccessCondition; +import ch.psi.fda.core.actions.ChannelAccessConditionAnd; +import ch.psi.fda.core.actions.ChannelAccessConditionOr; +import ch.psi.fda.core.actions.ChannelAccessConditionRegex; +import ch.psi.fda.core.actions.ChannelAccessPut; +import ch.psi.fda.core.actions.Delay; +import ch.psi.fda.core.actors.ChannelAccessFunctionActuator; +import ch.psi.fda.core.actors.ChannelAccessLinearActuator; +import ch.psi.fda.core.actors.ChannelAccessTableActuator; +import ch.psi.fda.core.actors.ComplexActuator; +import ch.psi.fda.core.actors.JythonFunction; +import ch.psi.fda.core.actors.PseudoActuatorSensor; +import ch.psi.fda.core.scripting.JythonParameterMappingChannel; +import ch.psi.fda.core.sensors.ChannelAccessDoubleSensor; +import ch.psi.fda.model.v1.Action; +import ch.psi.fda.model.v1.ArrayPositioner; +import ch.psi.fda.model.v1.ChannelAction; +import ch.psi.fda.model.v1.ChannelParameterMapping; +import ch.psi.fda.model.v1.Configuration; +import ch.psi.fda.model.v1.ContinuousDimension; +import ch.psi.fda.model.v1.Dimension; +import ch.psi.fda.model.v1.DiscreteStepDimension; +import ch.psi.fda.model.v1.DiscreteStepPositioner; +import ch.psi.fda.model.v1.FunctionPositioner; +import ch.psi.fda.model.v1.LinearPositioner; +import ch.psi.fda.model.v1.PseudoPositioner; +import ch.psi.fda.model.v1.Region; +import ch.psi.fda.model.v1.RegionPositioner; +import ch.psi.fda.model.v1.Scan; +import ch.psi.fda.model.v1.ScriptAction; +import ch.psi.fda.model.v1.ShellAction; + +/** + * @author ebner + * + */ +public class ScanMapperNG { + + private static final String INDENT = " "; + + private StringBuilder script = new StringBuilder(); + private Set imports = new HashSet<>(); + private String indentation=""; + + + + public void map(Configuration configuration){ + + Scan scan = configuration.getScan(); + mapActions(scan.getPreAction()); + + + // Bring the dimensions into the correct order and then map them + // to the script + List dimensions = new ArrayList<>(); + int size = scan.getDimension().size(); + for(int i=size-1; i>=0; i--){ + dimensions.add(scan.getDimension().get(i)); + } + // Continuous dimensions are always at the very end + if(scan.getCdimension()!=null){ + dimensions.add(scan.getCdimension()); + } + mapDimensions(dimensions, 0); + + + // map + mapActions(scan.getPostAction()); + + // TODO CONSIDER TO PROVIDE RESOURCE MAP AND SCRIPT IN ONE OBJECT AS RETURN TYPE + // WOULD MAKE MORE SENSE + } + + + /** + * Get the generated script + * @return + */ + public String getScript(){ + StringBuilder scriptlines= new StringBuilder(); + for(String s: imports){ + scriptlines.append("import "+s+"\n"); + } + scriptlines.append(script); + return scriptlines.toString(); + } + + private void mapDimensions(List dimensions, int index) { + Dimension dimension = dimensions.get(index); + if (dimension instanceof DiscreteStepDimension) { + DiscreteStepDimension d = (DiscreteStepDimension) dimension; + + mapActions(d.getPreAction()); + // TODO Do mapping + script.append(indentation + "for i in test\n"); + indentation = indentation + INDENT; + mapPositioners(d.getPositioner()); + mapActions(d.getAction()); + + // TODO map guard and sensors + + // Next dimension + if((index+1) actions){ + for(Action action: actions){ + if(action instanceof ChannelAction){ + ChannelAction ca = (ChannelAction) action; + switch (ca.getOperation()) { + case "put": + break; + case "putq": + break; + case "wait": + break; + case "waitREGEX": + break; + case "waitOR": + break; + case "waitAND": + break; + } + } + else if(action instanceof ShellAction){ + ShellAction saction = (ShellAction) action; + // TODO Need to indicate that shell resource is required + script.append(indentation+String.format("shell.execute('%s', %d, %s)\n", saction.getCommand(), saction.getExitValue(), saction.isCheckExitValue())); + } + else if(action instanceof ScriptAction){ + ScriptAction saction = (ScriptAction) action; + + // TODO Need to indicate that that mapped resources are required + // Read script line by line and prepend the current indentation level + String[] lines = saction.getScript().split(System.getProperty("line.separator")); + for(String s:lines){ + script.append(indentation+s); + } + script.append(indentation+"\n"); + } + } + } + + public void mapPositioners(List positioners) { +// for (DiscreteStepPositioner p : positioners) { +// +// if (p.getSettlingTime() > stime) { +// stime = p.getSettlingTime(); +// } +// +// if (p instanceof LinearPositioner) { +// LinearPositioner lp = (LinearPositioner) p; +// ChannelAccessLinearActuator a; +// if (lp.getType().equals("String")) { +// a = new ChannelAccessLinearActuator(cservice, lp.getName(), lp.getDone(), lp.getDoneValue(), lp.getDoneDelay(), lp.getStart(), lp.getEnd(), lp.getStepSize(), moveTimeout); +// } else if (lp.getType().equals("Double")) { +// a = new ChannelAccessLinearActuator(cservice, lp.getName(), lp.getDone(), Double.parseDouble(lp.getDoneValue()), lp.getDoneDelay(), lp.getStart(), lp.getEnd(), +// lp.getStepSize(), moveTimeout); +// } else { +// // Default +// a = new ChannelAccessLinearActuator(cservice, lp.getName(), lp.getDone(), Integer.parseInt(lp.getDoneValue()), lp.getDoneDelay(), lp.getStart(), lp.getEnd(), +// lp.getStepSize(), moveTimeout); +// } +// +// a.setAsynchronous(lp.isAsynchronous()); +// Actor actuator = a; +// +// aLoop.getActors().add(actuator); +// +// // Add a sensor for the readback +// String name = lp.getReadback(); +// if (name == null) { +// name = lp.getName(); +// } +// ChannelAccessDoubleSensor sensor = new ChannelAccessDoubleSensor(cservice, lp.getId(), name); +// aLoop.getSensors().add(sensor); +// } else if (p instanceof FunctionPositioner) { +// FunctionPositioner lp = (FunctionPositioner) p; +// +// // Create function object +// JythonFunction function = mapFunction(lp.getFunction()); +// +// // Create actuator +// ChannelAccessFunctionActuator a; +// if (lp.getType().equals("String")) { +// a = new ChannelAccessFunctionActuator(cservice, lp.getName(), lp.getDone(), lp.getDoneValue(), lp.getDoneDelay(), function, lp.getStart(), lp.getEnd(), lp.getStepSize(), +// moveTimeout); +// } else if (lp.getType().equals("Double")) { +// a = new ChannelAccessFunctionActuator(cservice, lp.getName(), lp.getDone(), Double.parseDouble(lp.getDoneValue()), lp.getDoneDelay(), function, lp.getStart(), lp.getEnd(), +// lp.getStepSize(), moveTimeout); +// } else { +// // Default +// a = new ChannelAccessFunctionActuator(cservice, lp.getName(), lp.getDone(), Integer.parseInt(lp.getDoneValue()), lp.getDoneDelay(), function, lp.getStart(), lp.getEnd(), +// lp.getStepSize(), moveTimeout); +// } +// +// a.setAsynchronous(lp.isAsynchronous()); +// Actor actuator = a; +// +// aLoop.getActors().add(actuator); +// +// // Add a sensor for the readback +// String name = lp.getReadback(); +// if (name == null) { +// name = lp.getName(); +// } +// ChannelAccessDoubleSensor sensor = new ChannelAccessDoubleSensor(cservice, lp.getId(), name); +// aLoop.getSensors().add(sensor); +// } else if (p instanceof ArrayPositioner) { +// ArrayPositioner ap = (ArrayPositioner) p; +// String[] positions = (ap.getPositions().trim()).split(" +"); +// double[] table = new double[positions.length]; +// for (int i = 0; i < positions.length; i++) { +// table[i] = Double.parseDouble(positions[i]); +// } +// +// ChannelAccessTableActuator a; +// if (p.getType().equals("String")) { +// a = new ChannelAccessTableActuator(cservice, p.getName(), p.getDone(), p.getDoneValue(), p.getDoneDelay(), table, moveTimeout); +// } else if (p.getType().equals("Double")) { +// a = new ChannelAccessTableActuator(cservice, p.getName(), p.getDone(), Double.parseDouble(p.getDoneValue()), p.getDoneDelay(), table, moveTimeout); +// } else { +// // Default +// a = new ChannelAccessTableActuator(cservice, p.getName(), p.getDone(), Integer.parseInt(p.getDoneValue()), p.getDoneDelay(), table, moveTimeout); +// } +// +// a.setAsynchronous(p.isAsynchronous()); +// Actor actuator = a; +// +// aLoop.getActors().add(actuator); +// +// // Add a sensor for the readback +// String name = ap.getReadback(); +// if (name == null) { +// name = ap.getName(); +// } +// ChannelAccessDoubleSensor sensor = new ChannelAccessDoubleSensor(cservice, ap.getId(), name); +// aLoop.getSensors().add(sensor); +// } else if (p instanceof RegionPositioner) { +// RegionPositioner rp = (RegionPositioner) p; +// +// ComplexActuator actuator = new ComplexActuator(); +// /* +// * Regions are translated into a complex actor consisting of a +// * LinearActuator If consecutive regions are overlapping, i.e. +// * end point of region a equals the start point of region b then +// * the start point for the LinearActuator of region b is changes +// * to its next step (start+/-stepSize depending on whether end +// * position of the region is > or < start of the region) +// */ +// Region lastRegion = null; +// for (Region r : rp.getRegion()) { +// // Normal region +// if (r.getFunction() == null) { +// +// // Check whether regions are consecutive +// double start = r.getStart(); +// if (lastRegion != null && start == lastRegion.getEnd()) { // TODO +// // verify +// // whether +// // double +// // comparison +// // is +// // ok +// if (r.getStart() < r.getEnd()) { +// start = start + r.getStepSize(); +// } else { +// start = start - r.getStepSize(); +// } +// } +// +// // Create actuator +// ChannelAccessLinearActuator act; +// if (rp.getType().equals("String")) { +// act = new ChannelAccessLinearActuator(cservice, rp.getName(), rp.getDone(), rp.getDoneValue(), rp.getDoneDelay(), start, r.getEnd(), r.getStepSize(), moveTimeout); +// } else if (rp.getType().equals("Double")) { +// act = new ChannelAccessLinearActuator(cservice, rp.getName(), rp.getDone(), Double.parseDouble(rp.getDoneValue()), rp.getDoneDelay(), start, r.getEnd(), +// r.getStepSize(), moveTimeout); +// } else { +// // Default +// act = new ChannelAccessLinearActuator(cservice, rp.getName(), rp.getDone(), Integer.parseInt(rp.getDoneValue()), rp.getDoneDelay(), start, r.getEnd(), +// r.getStepSize(), moveTimeout); +// } +// +// act.setAsynchronous(rp.isAsynchronous()); +// Actor a = act; +// +// ComplexActuator ca = new ComplexActuator(); +// ca.getActors().add(a); +// ca.getPreActions().addAll(mapActions(r.getPreAction())); +// actuator.getActors().add(ca); +// lastRegion = r; +// } else { +// // Function based region +// +// // Cannot check whether the regions are consecutive as +// // the function +// // used might change the start value to something else +// // [THIS LIMITATION NEEDS TO BE SOMEHOW RESOLVED IN THE +// // NEXT VERSIONS] +// JythonFunction function = mapFunction(r.getFunction()); +// ChannelAccessFunctionActuator act; +// if (rp.getType().equals("String")) { +// act = new ChannelAccessFunctionActuator(cservice, rp.getName(), rp.getDone(), rp.getDoneValue(), rp.getDoneDelay(), function, r.getStart(), r.getEnd(), +// r.getStepSize(), moveTimeout); +// } else if (rp.getType().equals("Double")) { +// act = new ChannelAccessFunctionActuator(cservice, rp.getName(), rp.getDone(), Double.parseDouble(rp.getDoneValue()), rp.getDoneDelay(), function, r.getStart(), +// r.getEnd(), r.getStepSize(), moveTimeout); +// } else { +// // Default +// act = new ChannelAccessFunctionActuator(cservice, rp.getName(), rp.getDone(), Integer.parseInt(rp.getDoneValue()), rp.getDoneDelay(), function, r.getStart(), +// r.getEnd(), r.getStepSize(), moveTimeout); +// } +// +// act.setAsynchronous(rp.isAsynchronous()); +// Actor a = act; +// +// ComplexActuator ca = new ComplexActuator(); +// ca.getActors().add(a); +// ca.getPreActions().addAll(mapActions(r.getPreAction())); +// actuator.getActors().add(ca); +// lastRegion = r; +// } +// } +// aLoop.getActors().add(actuator); +// +// // Add a sensor for the readback +// String name = rp.getReadback(); +// if (name == null) { +// name = rp.getName(); +// } +// ChannelAccessDoubleSensor sensor = new ChannelAccessDoubleSensor(cservice, rp.getId(), name); +// aLoop.getSensors().add(sensor); +// } else if (p instanceof PseudoPositioner) { +// PseudoPositioner pp = (PseudoPositioner) p; +// PseudoActuatorSensor actorSensor = new PseudoActuatorSensor(pp.getId(), pp.getCounts()); +// +// // Register as actor +// aLoop.getActors().add(actorSensor); +// +// // Register as sensor +// aLoop.getSensors().add(actorSensor); +// } else { +// // Not supported +// logger.warning("Mapping for " + p.getClass().getName() + " not available"); +// } +// } + } + + /** + * Get unique variable names that do not collide with others. (at least have an extreme unlikeness to do so) + * @return + */ + private String getUniqueVariableName(){ + return "v"+UUID.randomUUID().toString().replaceAll("-", ""); + } +// +// String operation = ca.getOperation(); // Default = put +// String type=ca.getType(); // Default = String +// +// if(operation.equals("put")){ +// Long timeout = null; +// if(ca.getTimeout()!=null){ +// timeout = Math.round(ca.getTimeout()*1000); +// } +// if(type.equals("String")){ +// alist.add(new ChannelAccessPut(cservice, ca.getChannel(), ca.getValue(), false, timeout)); +// } +// else if(type.equals("Integer")){ +// alist.add(new ChannelAccessPut(cservice, ca.getChannel(), new Integer(ca.getValue()), false, timeout)); +// } +// else if(type.equals("Double")){ +// alist.add(new ChannelAccessPut(cservice, ca.getChannel(), new Double(ca.getValue()), false, timeout)); +// } +// } +// else if(operation.equals("putq")){ +// if(type.equals("String")){ +// alist.add(new ChannelAccessPut(cservice, ca.getChannel(), ca.getValue(), true, null)); +// } +// else if(type.equals("Integer")){ +// alist.add(new ChannelAccessPut(cservice, ca.getChannel(), new Integer(ca.getValue()), true, null)); +// } +// else if(type.equals("Double")){ +// alist.add(new ChannelAccessPut(cservice, ca.getChannel(), new Double(ca.getValue()), true, null)); +// } +// } +// else if(operation.equals("wait")){ +// Long timeout = null ; // Default timeout = wait forever +// if(ca.getTimeout()!=null){ +// timeout = Math.round(ca.getTimeout()*1000); +// } +// if(type.equals("String")){ +// alist.add(new ChannelAccessCondition(cservice, ca.getChannel(), ca.getValue(), timeout)); +// } +// else if(type.equals("Integer")){ +// alist.add(new ChannelAccessCondition(cservice, ca.getChannel(), new Integer(ca.getValue()), timeout)); +// } +// else if(type.equals("Double")){ +// alist.add(new ChannelAccessCondition(cservice, ca.getChannel(), new Double(ca.getValue()), timeout)); +// } +// } +// else if(operation.equals("waitREGEX")){ +// Long timeout = null ; // Default timeout = wait forever +// if(ca.getTimeout()!=null){ +// timeout = Math.round(ca.getTimeout()*1000); +// } +// if(type.equals("String")){ +// alist.add(new ChannelAccessConditionRegex(cservice, ca.getChannel(), ca.getValue(), timeout)); +// } +// else{ +// logger.warning("Operation "+operation+" wity type "+type+" for action is not supported"); +// } +// } +// else if(operation.equals("waitOR")){ +// Long timeout = null ; // Default timeout = wait forever +// if(ca.getTimeout()!=null){ +// timeout = Math.round(ca.getTimeout()*1000); +// } +// +// if(type.equals("Integer")){ +// alist.add(new ChannelAccessConditionOr(cservice, ca.getChannel(), new Integer(ca.getValue()), timeout)); +// } +// else{ +// logger.warning("Operation "+operation+" wity type "+type+" for action is not supported"); +// } +// } +// else if(operation.equals("waitAND")){ +// Long timeout = null ; // Default timeout = wait forever +// if(ca.getTimeout()!=null){ +// timeout = Math.round(ca.getTimeout()*1000); +// } +// if(type.equals("Integer")){ +// alist.add(new ChannelAccessConditionAnd(cservice, ca.getChannel(), new Integer(ca.getValue()), timeout)); +// } +// else { +// logger.warning("Operation "+operation+" wity type "+type+" for action is not supported"); +// } +// } +// else{ +// // Operation not supported +// logger.warning("Operation "+operation+" for action is not supported"); +// } +// +// // Translate delay attribute to delay action +// if(ca.getDelay()!=null){ +// Double x = ca.getDelay()*1000; +// alist.add(new Delay(x.longValue())); +// } +// +//} + +} diff --git a/ch.psi.fda/src/test/java/ch/psi/fda/aq/AcquisitionEngineNGTest.java b/ch.psi.fda/src/test/java/ch/psi/fda/aq/AcquisitionEngineNGTest.java new file mode 100644 index 0000000..ba5387a --- /dev/null +++ b/ch.psi.fda/src/test/java/ch/psi/fda/aq/AcquisitionEngineNGTest.java @@ -0,0 +1,60 @@ +/** + * + * 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.aq; + +import static org.junit.Assert.*; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +/** + * @author ebner + * + */ +public class AcquisitionEngineNGTest { + + private AcquisitionEngineNG engine; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + engine = new AcquisitionEngineNG(); + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + /** + * Test method for {@link ch.psi.fda.aq.AcquisitionEngineNG#execute(java.lang.String)}. + */ + @Test + public void testExecute() { +// engine.execute("import os"); + engine.execute("print 'hello'"); +// engine.execute("os.system('echo hello world this is a test')"); + } + +} diff --git a/ch.psi.fda/src/test/java/ch/psi/fda/aq/ScanMapperNGTest.java b/ch.psi.fda/src/test/java/ch/psi/fda/aq/ScanMapperNGTest.java new file mode 100644 index 0000000..29ed1af --- /dev/null +++ b/ch.psi.fda/src/test/java/ch/psi/fda/aq/ScanMapperNGTest.java @@ -0,0 +1,98 @@ +/** + * + * 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.aq; + +import static org.junit.Assert.*; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import ch.psi.fda.model.v1.Configuration; +import ch.psi.fda.model.v1.DiscreteStepDimension; +import ch.psi.fda.model.v1.Scan; +import ch.psi.fda.model.v1.ScriptAction; +import ch.psi.fda.model.v1.ShellAction; +import ch.psi.fda.model.v1.Action; + +/** + * @author ebner + * + */ +public class ScanMapperNGTest { + + private ScanMapperNG mapper; + + /** + * @throws java.lang.Exception + */ + @Before + public void setUp() throws Exception { + mapper = new ScanMapperNG(); + } + + /** + * @throws java.lang.Exception + */ + @After + public void tearDown() throws Exception { + } + + /** + * Test method for {@link ch.psi.fda.aq.ScanMapperNG#mapActions(java.util.List)}. + */ + @Test + public void testMap() { + System.out.println("juhu"+("This is a Java string".hashCode())); + + + Configuration config = new Configuration(); + config.setScan(new Scan()); + + ShellAction a = new ShellAction(); + a.setCommand("echo hello world this is a test"); + + config.getScan().getPreAction().add(a); + + + ScriptAction sa = new ScriptAction(); + sa.setScript("print 'hello'"); + + config.getScan().getPostAction().add(sa); + config.getScan().getPostAction().add(a); + + DiscreteStepDimension d = new DiscreteStepDimension(); + d.getPreAction().add(a); + d.getAction().add(sa); + d.getPostAction().add(a); + + config.getScan().getDimension().add(d); + config.getScan().getDimension().add(d); + + + mapper.map(config); + String script = mapper.getScript(); + + System.out.println(script); + } + +}