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);
+			}
+		});
+
+	}
 }
