Index: java/main/src/main/java/com/framsticks/experiment/AbstractExperimentLogic.java
===================================================================
--- java/main/src/main/java/com/framsticks/experiment/AbstractExperimentLogic.java	(revision 102)
+++ java/main/src/main/java/com/framsticks/experiment/AbstractExperimentLogic.java	(revision 102)
@@ -0,0 +1,21 @@
+package com.framsticks.experiment;
+
+public class AbstractExperimentLogic implements ExperimentLogic {
+
+	protected final Experiment experiment;
+
+	/**
+	 * @param experiment
+	 */
+	public AbstractExperimentLogic(Experiment experiment) {
+		this.experiment = experiment;
+	}
+
+	/**
+	 * @return the experiment
+	 */
+	public Experiment getExperiment() {
+		return experiment;
+	}
+
+}
Index: java/main/src/main/java/com/framsticks/experiment/Experiment.java
===================================================================
--- java/main/src/main/java/com/framsticks/experiment/Experiment.java	(revision 101)
+++ java/main/src/main/java/com/framsticks/experiment/Experiment.java	(revision 102)
@@ -2,17 +2,74 @@
 
 import java.util.Map;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.LogManager;
 
 import com.framsticks.core.ListChange;
 import com.framsticks.params.EventListener;
+import com.framsticks.params.ParamFlags;
 import com.framsticks.params.SimpleUniqueList;
 import com.framsticks.params.annotations.AutoAppendAnnotation;
 import com.framsticks.params.annotations.FramsClassAnnotation;
 import com.framsticks.params.annotations.ParamAnnotation;
+import com.framsticks.params.types.ProcedureParam;
+import com.framsticks.remote.RemoteTree;
+import com.framsticks.util.FramsticksException;
+import com.framsticks.util.dispatching.AbstractJoinable;
+import com.framsticks.util.dispatching.BufferedDispatcher;
+import com.framsticks.util.dispatching.Dispatcher;
+import com.framsticks.util.dispatching.DispatcherSetable;
+import com.framsticks.util.dispatching.Dispatching;
+import com.framsticks.util.dispatching.ExceptionResultHandler;
+import com.framsticks.util.dispatching.Joinable;
 import com.framsticks.util.dispatching.JoinableCollection;
+import com.framsticks.util.dispatching.JoinableParent;
+import com.framsticks.util.dispatching.JoinableState;
+import com.framsticks.util.dispatching.RunAt;
 
 @FramsClassAnnotation
-public class Experiment extends JoinableCollection<Simulator> {
+public class Experiment extends AbstractJoinable implements Dispatcher<Experiment>, DispatcherSetable<Experiment>, JoinableParent, ExceptionResultHandler {
+	private static final Logger log = LogManager.getLogger(Experiment.class);
+
+	protected final JoinableCollection<Simulator> simulatorAsJoinables = new JoinableCollection<Simulator>().setObservableName("simulators");
+
+	protected final JoinableCollection<RemoteTree> simulatorCandidates = new JoinableCollection<RemoteTree>().setObservableName("candidates");
 
 	protected final SimpleUniqueList<Simulator> simulators = new SimpleUniqueList<>(Simulator.class, 's');
+
+	protected final SimpleUniqueList<Simulator> oldSimulators = new SimpleUniqueList<>(Simulator.class, 's');
+
+	protected final BufferedDispatcher<Experiment> bufferedDispatcher = new BufferedDispatcher<>(this);
+
+	protected String expdef;
+
+	/**
+	 *
+	 */
+	public Experiment() {
+		super();
+		bufferedDispatcher.setBuffer(false);
+
+		Dispatching.dispatchLog(this, log, Level.DEBUG, "first task");
+
+
+		simulators.addListener(new EventListener<ListChange>() {
+			@Override
+			public void action(ListChange argument) {
+				if (argument.getAction() == ListChange.Action.Add) {
+					simulators.fireChildrenChange(argument, ListChange.Action.Modify, "ready");
+				}
+			}
+		});
+
+	}
+
+	/**
+	 * @return the simulatorCandidates
+	 */
+	public JoinableCollection<RemoteTree> getSimulatorCandidates() {
+		return simulatorCandidates;
+	}
 
 	@ParamAnnotation
@@ -21,4 +78,41 @@
 	}
 
+	@ParamAnnotation(id = "old_simulators")
+	public Map<String, Simulator> getOldSimulators() {
+		return oldSimulators.getView();
+	}
+
+	/**
+	 * @return the dispatcher
+	 */
+	@Override
+	public Dispatcher<Experiment> getDispatcher() {
+		return bufferedDispatcher;
+	}
+
+	/**
+	 * @param dispatcher the dispatcher to set
+	 */
+	@Override
+	public void setDispatcher(Dispatcher<Experiment> dispatcher) {
+		bufferedDispatcher.setTargetDispatcher(dispatcher);
+	}
+
+	/**
+	 * @return the expdef
+	 */
+	@ParamAnnotation(flags = ParamFlags.USERREADONLY)
+	public String getExpdef() {
+		return expdef;
+	}
+
+	/**
+	 * @param expdef the expdef to set
+	 */
+	@ParamAnnotation
+	public void setExpdef(String expdef) {
+		this.expdef = expdef;
+	}
+
 	@ParamAnnotation(id = "simulators_changed")
 	public void addSimulatorsListener(EventListener<ListChange> listener) {
@@ -34,4 +128,91 @@
 	public void addSimulator(Simulator simulator) {
 		simulators.add(simulator);
-	}
+		simulatorAsJoinables.add(simulator);
+
+
+
+
+
+	}
+
+	protected void removeSimulator(Simulator simulator) {
+		simulatorAsJoinables.remove(simulator);
+		simulators.remove(simulator);
+		oldSimulators.add(simulator);
+	}
+
+	@ParamAnnotation(id = "old_simulators_changed")
+	public void addOldSimulatorsListener(EventListener<ListChange> listener) {
+		oldSimulators.addListener(listener);
+	}
+
+	@ParamAnnotation(id = "old_simulators_changed")
+	public void removeOldSimulatorsListener(EventListener<ListChange> listener) {
+		oldSimulators.removeListener(listener);
+	}
+
+	@Override
+	public String getName() {
+		return "experiment";
+	}
+
+	@Override
+	public void childChangedState(Joinable joinable, JoinableState state) {
+		proceedToState(state);
+	}
+
+	@Override
+	protected void joinableStart() {
+		bufferedDispatcher.createThreadIfNeeded();
+		Dispatching.use(bufferedDispatcher, this);
+
+		Dispatching.use(simulatorAsJoinables, this);
+		Dispatching.use(simulatorCandidates, this);
+	}
+
+	@Override
+	protected void joinableInterrupt() {
+
+		Dispatching.drop(simulatorAsJoinables, this);
+		Dispatching.drop(simulatorCandidates, this);
+
+		finishJoinable();
+	}
+
+	@Override
+	protected void joinableFinish() {
+		log.debug("finishing experiment {}", this);
+	}
+
+	@Override
+	protected void joinableJoin() throws InterruptedException {
+		Dispatching.drop(bufferedDispatcher, this);
+
+		Dispatching.join(simulatorAsJoinables);
+		Dispatching.join(simulatorCandidates);
+		Dispatching.join(bufferedDispatcher.getTargetDispatcher());
+	}
+
+	@Override
+	public void handle(FramsticksException exception) {
+		log.error("caught exception: ", exception);
+	}
+
+	@Override
+	public boolean isActive() {
+		return bufferedDispatcher.isActive();
+	}
+
+	@Override
+	public void dispatch(RunAt<? extends Experiment> runnable) {
+		bufferedDispatcher.dispatch(runnable);
+	}
+
+	@ParamAnnotation(paramType = ProcedureParam.class)
+	public void connectToSimulator(String address) {
+		SimulatorConnector connector = new SimulatorConnector();
+		connector.setAddress(address);
+		connector.attachTo(this);
+	}
+
 }
Index: java/main/src/main/java/com/framsticks/experiment/ExperimentLogic.java
===================================================================
--- java/main/src/main/java/com/framsticks/experiment/ExperimentLogic.java	(revision 102)
+++ java/main/src/main/java/com/framsticks/experiment/ExperimentLogic.java	(revision 102)
@@ -0,0 +1,5 @@
+package com.framsticks.experiment;
+
+public interface ExperimentLogic {
+
+}
Index: java/main/src/main/java/com/framsticks/experiment/NetLoadSaveLogic.java
===================================================================
--- java/main/src/main/java/com/framsticks/experiment/NetLoadSaveLogic.java	(revision 102)
+++ java/main/src/main/java/com/framsticks/experiment/NetLoadSaveLogic.java	(revision 102)
@@ -0,0 +1,76 @@
+package com.framsticks.experiment;
+
+
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.LogManager;
+
+import com.framsticks.communication.File;
+import com.framsticks.core.ListChange;
+import com.framsticks.params.EventListener;
+import com.framsticks.params.annotations.FramsClassAnnotation;
+import com.framsticks.params.annotations.ParamAnnotation;
+import com.framsticks.util.dispatching.Future;
+import com.framsticks.util.dispatching.FutureHandler;
+
+@FramsClassAnnotation
+public abstract class NetLoadSaveLogic extends AbstractExperimentLogic {
+	private static final Logger log = LogManager.getLogger(NetLoadSaveLogic.class);
+
+
+	protected String option = "an option";
+
+	/**
+	 * @param experiment
+	 */
+	public NetLoadSaveLogic(Experiment parentExperiment) {
+		super(parentExperiment);
+
+		experiment.addSimulatorsListener(new EventListener<ListChange>() {
+
+			@Override
+			public void action(ListChange argument) {
+				assert experiment.isActive();
+
+				if (argument.hasHint("ready")) {
+					final Simulator simulator = experiment.getSimulators().get(argument.getIdentifier());
+					log.debug("simulator is ready: {}", simulator);
+
+					netload(simulator, new FutureHandler<File>(simulator) {
+
+						@Override
+						protected void result(final File file) {
+							simulator.uploadNet(file, new FutureHandler<Object>(this) {
+
+								@Override
+								protected void result(Object result) {
+									log.debug("netload of {} done", file);
+								}
+							});
+						}
+					});
+				}
+			}
+		});
+	}
+
+	public abstract void netload(Simulator simulator, Future<File> net);
+
+	public abstract void netsave(Simulator simulator, File net);
+
+	/**
+	 * @return the option
+	 */
+	@ParamAnnotation
+	public String getOption() {
+		return option;
+	}
+
+	/**
+	 * @param option the option to set
+	 */
+	@ParamAnnotation
+	public void setOption(String option) {
+		this.option = option;
+	}
+
+}
Index: java/main/src/main/java/com/framsticks/experiment/Simulator.java
===================================================================
--- java/main/src/main/java/com/framsticks/experiment/Simulator.java	(revision 101)
+++ java/main/src/main/java/com/framsticks/experiment/Simulator.java	(revision 102)
@@ -1,35 +1,80 @@
 package com.framsticks.experiment;
 
-import com.framsticks.params.ParamFlags;
+import com.framsticks.communication.File;
+import com.framsticks.communication.queries.NeedFile;
+import com.framsticks.communication.queries.NeedFileAcceptor;
+import com.framsticks.core.Path;
+import com.framsticks.core.Tree;
+import com.framsticks.core.ValueChange;
+import com.framsticks.params.EventListener;
+import com.framsticks.params.FramsClass;
 import com.framsticks.params.UniqueObject;
 import com.framsticks.params.annotations.FramsClassAnnotation;
 import com.framsticks.params.annotations.ParamAnnotation;
+import com.framsticks.params.types.EventParam;
 import com.framsticks.params.types.ProcedureParam;
 import com.framsticks.remote.RemoteTree;
+import com.framsticks.util.FramsticksException;
 import com.framsticks.util.dispatching.AbstractJoinable;
+import com.framsticks.util.dispatching.Dispatcher;
 import com.framsticks.util.dispatching.Dispatching;
+import com.framsticks.util.dispatching.ExceptionResultHandler;
+import com.framsticks.util.dispatching.Future;
+import com.framsticks.util.dispatching.FutureHandler;
 import com.framsticks.util.dispatching.Joinable;
 import com.framsticks.util.dispatching.JoinableParent;
 import com.framsticks.util.dispatching.JoinableState;
+import com.framsticks.util.dispatching.RunAt;
+import com.framsticks.util.dispatching.ThrowExceptionHandler;
+import com.framsticks.util.lang.Holder;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import static com.framsticks.core.TreeOperations.*;
 
 @FramsClassAnnotation
-public class Simulator extends AbstractJoinable implements JoinableParent, UniqueObject {
+public final class Simulator extends AbstractJoinable implements Dispatcher<Simulator>, JoinableParent, UniqueObject, ExceptionResultHandler {
+
+	private static final Logger log = LogManager.getLogger(Simulator.class);
+
 	protected String uid;
 
-	protected final RemoteTree remoteTree = new RemoteTree();
+	protected final RemoteTree remoteTree;
+	protected final Path simulatorPath;
+	protected final FramsClass simulatorClass;
+	protected final Experiment experiment;
+	protected final EventListener<ValueChange> runningListener;
 
 	/**
 	 *
 	 */
-	public Simulator() {
+	public Simulator(Experiment experiment, RemoteTree remoteTree, Path simulatorPath) {
 		super();
-	}
-
-	@ParamAnnotation
-	public void setAddress(String address) {
-		remoteTree.setAddress(address);
-	}
-
-	@ParamAnnotation(flags = ParamFlags.USERREADONLY)
+		this.remoteTree = remoteTree;
+		this.simulatorPath = simulatorPath.assureResolved();
+		this.experiment = experiment;
+		this.simulatorClass = getFramsClass(simulatorPath);
+
+		assert remoteTree.isActive();
+		assert experiment.isActive();
+
+		log.debug("simulator ready {}", this);
+
+		runningListener = new EventListener<ValueChange>() {
+			@Override
+			public void action(ValueChange argument) {
+				log.debug("running state of {} changed: {}", this, argument);
+			}
+		};
+
+		addListener(simulatorPath, simulatorClass.getParamEntry("running_changed", EventParam.class), runningListener, ValueChange.class, new FutureHandler<Void>(this) {
+			@Override
+			protected void result(Void result) {
+				log.debug("running listener for {} registered", this);
+			}
+		});
+	}
+
+	@ParamAnnotation
 	public String getAddress() {
 		return remoteTree.getAddress();
@@ -39,6 +84,5 @@
 	@ParamAnnotation
 	public String getName() {
-
-		return "simulator " + uid + " " + getAddress();
+		return getAddress();
 	}
 
@@ -62,8 +106,21 @@
 	}
 
+	/**
+	 * @return the simulatorPath
+	 */
+	public Path getSimulatorPath() {
+		return simulatorPath;
+	}
+
+	/**
+	 * @return the simulatorClass
+	 */
+	public FramsClass getSimulatorClass() {
+		return simulatorClass;
+	}
+
 	@Override
 	protected void joinableStart() {
 		Dispatching.use(remoteTree, this);
-
 	}
 
@@ -96,4 +153,12 @@
 	}
 
+	@ParamAnnotation(paramType = ProcedureParam.class)
+	public void abort() {
+		assert isActive();
+		log.debug("explicitly aborting {}", this);
+		experiment.removeSimulator(this);
+		interruptJoinable();
+	}
+
 	@Override
 	public void childChangedState(Joinable joinable, JoinableState state) {
@@ -101,3 +166,62 @@
 	}
 
+	@Override
+	public void handle(FramsticksException exception) {
+		experiment.handle(new FramsticksException().msg("exception caught in simulator").arg("simulator", this).cause(exception));
+	}
+
+	@Override
+	public boolean isActive() {
+		return experiment.isActive();
+	}
+
+	@SuppressWarnings({ "rawtypes", "unchecked" })
+	@Override
+	public void dispatch(RunAt<? extends Simulator> runnable) {
+		experiment.dispatch((RunAt) runnable);
+	}
+
+	protected final AtomicInteger netloadIdCounter = new AtomicInteger();
+
+	public void uploadNet(final File file, final Future<Object> future) {
+		final String netloadId = "NetLoadSaveLogic" + netloadIdCounter.getAndIncrement();
+
+		log.debug("uploading file {} to {} identified by {}", file, simulatorPath, netloadId);
+
+		final Holder<NeedFileAcceptor> acceptor = new Holder<>();
+		final Tree tree = simulatorPath.getTree();
+
+		acceptor.set(new NeedFileAcceptor() {
+
+			@Override
+			public boolean acceptNeed(NeedFile needFile) {
+				if (!needFile.getDescription().equals(netloadId)) {
+					return false;
+				}
+				log.debug("accepting netload {}", netloadId);
+				needFile.getFuture().pass(file);
+				tree.dispatch(new RunAt<Tree>(ThrowExceptionHandler.getInstance()) {
+
+					@Override
+					protected void runAt() {
+						tree.removeNeedFileAcceptor(acceptor.get());
+					}
+				});
+				return true;
+			}
+
+		});
+
+		simulatorPath.getTree().addNeedFileAcceptor(Integer.MIN_VALUE, acceptor.get());
+
+		call(simulatorPath, getFramsClass(simulatorPath).getParamEntry("netload_id", ProcedureParam.class), new Object[] { netloadId }, new FutureHandler<Object>(future) {
+
+			@Override
+			protected void result(Object result) {
+				log.debug("upload of {} done", file);
+				future.pass(result);
+			}
+		});
+
+	}
 }
Index: java/main/src/main/java/com/framsticks/experiment/SimulatorConnector.java
===================================================================
--- java/main/src/main/java/com/framsticks/experiment/SimulatorConnector.java	(revision 102)
+++ java/main/src/main/java/com/framsticks/experiment/SimulatorConnector.java	(revision 102)
@@ -0,0 +1,77 @@
+package com.framsticks.experiment;
+
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.LogManager;
+
+import com.framsticks.communication.Address;
+import com.framsticks.core.Path;
+import com.framsticks.params.annotations.FramsClassAnnotation;
+import com.framsticks.params.annotations.ParamAnnotation;
+import com.framsticks.remote.RemoteTree;
+import com.framsticks.util.AutoAttacher;
+import com.framsticks.util.Misc;
+import com.framsticks.util.dispatching.Dispatcher;
+import com.framsticks.util.dispatching.Dispatching;
+import com.framsticks.util.dispatching.FutureHandler;
+import com.framsticks.util.lang.Casting;
+import com.framsticks.util.lang.Strings;
+
+import static com.framsticks.core.TreeOperations.*;
+
+@FramsClassAnnotation
+public class SimulatorConnector implements AutoAttacher {
+	private static final Logger log = LogManager.getLogger(SimulatorConnector.class);
+
+
+	protected Experiment experiment;
+	protected Address address;
+	protected RemoteTree remoteTree;
+
+	@SuppressWarnings({ "rawtypes", "unchecked" })
+	@Override
+	public void attachTo(Object parent) {
+		experiment = Casting.throwCast(Experiment.class, parent);
+
+		Misc.throwIfNull(address);
+		log.debug("connecting to simulator at {}", address);
+		// experiment
+		remoteTree = new RemoteTree();
+		remoteTree.setAddress(address);
+		remoteTree.setDispatcher((Dispatcher) experiment.getDispatcher());
+
+
+		experiment.getSimulatorCandidates().add(remoteTree);
+
+		Dispatching.dispatchLog(remoteTree, log, Level.DEBUG, "first task in remote tree");
+
+		tryGet(remoteTree, "/simulator", new FutureHandler<Path>(experiment) {
+
+			@Override
+			protected void result(Path result) {
+				log.debug("resolved simulator path: {}", result);
+				Misc.checkEquals(experiment.getExpdef(), bindAccess(result).get("expdef", String.class), "expdef mismatch in connected simulator", this);
+
+				Simulator simulator = new Simulator(experiment, remoteTree, result);
+				experiment.addSimulator(simulator);
+				experiment.getSimulatorCandidates().remove(remoteTree);
+			}
+		});
+	}
+
+	@ParamAnnotation
+	public void setAddress(String address) {
+		this.address = new Address(address);
+	}
+
+	@ParamAnnotation
+	public String getAddress() {
+		return Strings.toStringNullProof(address, "");
+	}
+
+	@Override
+	public String toString() {
+		return "simulator connector to: " + Strings.toStringNullProof(address, "<null>");
+	}
+
+}
Index: java/main/src/main/java/com/framsticks/experiment/WorkPackage.java
===================================================================
--- java/main/src/main/java/com/framsticks/experiment/WorkPackage.java	(revision 102)
+++ java/main/src/main/java/com/framsticks/experiment/WorkPackage.java	(revision 102)
@@ -0,0 +1,5 @@
+package com.framsticks.experiment;
+
+public interface WorkPackage {
+
+}
Index: java/main/src/main/java/com/framsticks/experiment/WorkPackageLogic.java
===================================================================
--- java/main/src/main/java/com/framsticks/experiment/WorkPackageLogic.java	(revision 102)
+++ java/main/src/main/java/com/framsticks/experiment/WorkPackageLogic.java	(revision 102)
@@ -0,0 +1,57 @@
+package com.framsticks.experiment;
+
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.LogManager;
+
+import com.framsticks.communication.File;
+import com.framsticks.params.ListSource;
+import com.framsticks.params.annotations.FramsClassAnnotation;
+import com.framsticks.params.annotations.ParamAnnotation;
+import com.framsticks.util.dispatching.Future;
+import com.framsticks.util.dispatching.FutureHandler;
+
+@FramsClassAnnotation
+public abstract class WorkPackageLogic<WP extends WorkPackage> extends AbstractExperimentLogic {
+
+	private static final Logger log = LogManager.getLogger(WorkPackageLogic.class);
+
+	@ParamAnnotation
+	public final NetLoadSaveLogic netLoadSaveLogic;
+
+	/**
+	 * @param experiment
+	 */
+	public WorkPackageLogic(Experiment experiment) {
+		super(experiment);
+
+		netLoadSaveLogic = new NetLoadSaveLogic(experiment) {
+
+			@Override
+			public void netload(Simulator simulator, final Future<File> net) {
+				assert experiment.isActive();
+				log.debug("providing netload file for {}", simulator);
+
+				generateNextPackage(new FutureHandler<WP> (net) {
+
+					@Override
+					protected void result(WP result) {
+						log.debug("sending package: {}", result);
+						File file = new File("test-file", ListSource.createFrom("# a test"));
+
+						net.pass(file);
+					}
+				});
+
+			}
+
+			@Override
+			public void netsave(Simulator simulator, File net) {
+				// processFile();
+			}
+		};
+	}
+
+
+	protected abstract void generateNextPackage(Future<WP> future);
+
+}
