package com.framsticks.experiment; import com.framsticks.communication.File; import com.framsticks.communication.queries.NeedFile; import com.framsticks.communication.queries.NeedFileAcceptor; import com.framsticks.core.ListChange; import com.framsticks.core.Path; import com.framsticks.core.Tree; import com.framsticks.core.ValueChange; import com.framsticks.params.AccessOperations; import com.framsticks.params.CastFailure; 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.BooleanParam; 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 final class Simulator extends AbstractJoinable implements Dispatcher, JoinableParent, UniqueObject, ExceptionResultHandler { private static final Logger log = LogManager.getLogger(Simulator.class); protected String uid; protected final RemoteTree remoteTree; protected final Path simulatorPath; protected final FramsClass simulatorClass; protected final Experiment experiment; protected final EventListener runningListener; /** * */ public Simulator(Experiment experiment, RemoteTree remoteTree, Path simulatorPath) { super(); this.remoteTree = remoteTree; this.simulatorPath = simulatorPath.assureResolved(); this.experiment = experiment; this.simulatorClass = getFramsClass(simulatorPath); assert remoteTree.isActive(); assert experiment.isActive(); log.info("simulator ready {}", this); runningListener = new EventListener() { @Override public void action(ValueChange argument) { try { boolean running = simulatorClass.getParamEntry("running", BooleanParam.class).reassign(argument.value, null).getValue(); log.debug("running state of {} changed: {}", Simulator.this, running); if (!running) { Simulator.this.experiment.simulators.fireChildrenChange(Simulator.this, ListChange.Action.Modify, "ready", "stoped"); } } catch (CastFailure e) { log.error("failure: ", e); } } }; addListener(simulatorPath, simulatorClass.getParamEntry("running_changed", EventParam.class), runningListener, ValueChange.class, new FutureHandler(this) { @Override protected void result(Void result) { log.debug("running listener for {} registered", this); } }); } @ParamAnnotation public String getAddress() { return remoteTree.getAddress(); } @Override @ParamAnnotation public String getName() { return getAddress(); } @Override @ParamAnnotation public String getUid() { return uid; } @Override public void setUid(String uid) { this.uid = uid; } /** * @return the tree */ @ParamAnnotation public RemoteTree getRemoteTree() { return remoteTree; } /** * @return the simulatorPath */ public Path getSimulatorPath() { return simulatorPath; } /** * @return the simulatorClass */ public FramsClass getSimulatorClass() { return simulatorClass; } @Override protected void joinableStart() { Dispatching.use(remoteTree, this); } @Override protected void joinableInterrupt() { Dispatching.drop(remoteTree, this); } @Override protected void joinableFinish() { } @Override protected void joinableJoin() throws InterruptedException { Dispatching.join(remoteTree); } @ParamAnnotation(paramType = ProcedureParam.class) public void init() { } @ParamAnnotation(paramType = ProcedureParam.class) public void start() { log.debug("starting simulator {}", this); call(simulatorPath, "start", new Object[] {}, Object.class, FutureHandler.doNothing(Object.class, this)); } @ParamAnnotation(paramType = ProcedureParam.class) public void stop() { log.debug("stoping simulator {}", this); } @ParamAnnotation(paramType = ProcedureParam.class) public void abort() { assert isActive(); log.info("explicitly aborting {}", this); experiment.removeSimulator(this); interruptJoinable(); } @Override public void childChangedState(Joinable joinable, JoinableState state) { proceedToState(state); } @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 runnable) { experiment.dispatch((RunAt) runnable); } protected final AtomicInteger netloadIdCounter = new AtomicInteger(); public void netload(final N net, final Future future) { final String netloadId = "NetLoadSaveLogic" + netloadIdCounter.getAndIncrement(); final File file = AccessOperations.convert(File.class, net, getRemoteTree().getRegistry()); log.debug("uploading file {} to {} identified by {}", file, simulatorPath, netloadId); final Holder 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(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 }, Object.class, new FutureHandler(future) { @Override protected void result(Object result) { log.debug("upload of {} done", file); future.pass(result); } }); } public void netsave(Class netJavaClass, final Future futureNet) { call(simulatorPath, getFramsClass(simulatorPath).getParamEntry("netsave", ProcedureParam.class), new Object[] { }, netJavaClass, new FutureHandler(futureNet) { @Override protected void result(N net) { log.debug("download of {} done", net); futureNet.pass(net); } }); } }