Unverified Commit 342fea6f authored by Juon Kawakami's avatar Juon Kawakami 🥗
Browse files

init

parent 54f6cedf
package kernel;
import rescuecore2.connection.Connection;
import rescuecore2.connection.ConnectionListener;
import rescuecore2.messages.Message;
import rescuecore2.messages.Command;
import rescuecore2.messages.control.SKUpdate;
import rescuecore2.messages.control.KSUpdate;
import rescuecore2.messages.control.KSCommands;
import rescuecore2.messages.control.EntityIDRequest;
import rescuecore2.messages.control.EntityIDResponse;
import rescuecore2.worldmodel.ChangeSet;
import rescuecore2.worldmodel.EntityID;
import rescuecore2.log.Logger;
import java.util.Collection;
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
/**
This class is the kernel interface to a simulator.
*/
public class SimulatorProxy extends AbstractKernelComponent {
private Map<Integer, ChangeSet> updates;
private int id;
private EntityIDGenerator idGenerator;
/**
Construct a new simulator.
@param name The name of the simulator.
@param id The ID of the simulator.
@param c The connection this simulator is using.
*/
public SimulatorProxy(String name, int id, Connection c) {
super(name, c);
this.id = id;
updates = new HashMap<Integer, ChangeSet>();
c.addConnectionListener(new SimulatorConnectionListener());
}
/**
Get updates from this simulator. This method may block until updates are available.
@param time The timestep to get updates for.
@return A ChangeSet representing the updates from this simulator.
@throws InterruptedException If this thread is interrupted while waiting for updates.
*/
public ChangeSet getUpdates(int time) throws InterruptedException {
ChangeSet result = null;
synchronized (updates) {
while (result == null) {
result = updates.get(time);
if (result == null) {
updates.wait(1000);
}
}
}
return result;
}
/**
Send an update message to this simulator.
@param time The simulation time.
@param update The updated entities.
*/
public void sendUpdate(int time, ChangeSet update) {
send(new KSUpdate(id, time, update));
}
/**
Send a set of agent commands to this simulator.
@param time The current time.
@param commands The agent commands to send.
*/
public void sendAgentCommands(int time, Collection<? extends Command> commands) {
send(new KSCommands(id, time, commands));
}
@Override
public String toString() {
return getName() + " (" + id + "): " + getConnection().toString();
}
/**
Set the EntityIDGenerator.
@param generator The new EntityIDGenerator.
*/
public void setEntityIDGenerator(EntityIDGenerator generator) {
idGenerator = generator;
}
/**
Register an update from the simulator.
@param time The timestep of the update.
@param changes The set of changes.
*/
protected void updateReceived(int time, ChangeSet changes) {
synchronized (updates) {
ChangeSet c = updates.get(time);
if (c == null) {
c = new ChangeSet();
updates.put(time, c);
}
c.merge(changes);
updates.notifyAll();
}
}
private class SimulatorConnectionListener implements ConnectionListener {
@Override
public void messageReceived(Connection connection, Message msg) {
Logger.pushLogContext(Kernel.KERNEL_LOG_CONTEXT);
try {
if (msg instanceof SKUpdate) {
SKUpdate update = (SKUpdate)msg;
if (update.getSimulatorID() == id) {
updateReceived(update.getTime(), update.getChangeSet());
}
}
if (msg instanceof EntityIDRequest) {
EntityIDRequest req = (EntityIDRequest)msg;
Logger.debug("Simulator proxy " + id + " received entity ID request: " + msg);
if (req.getSimulatorID() == id) {
int requestID = req.getRequestID();
int count = req.getCount();
List<EntityID> result = new ArrayList<EntityID>(count);
for (int i = 0; i < count; ++i) {
result.add(idGenerator.generateID());
}
Logger.debug("Simulator proxy " + id + " sending new IDs: " + result);
send(new EntityIDResponse(id, requestID, result));
}
}
}
finally {
Logger.popLogContext();
}
}
}
@Override
public int hashCode() {
return Integer.hashCode(id);
}
}
package kernel;
import static rescuecore2.misc.java.JavaTools.instantiate;
import static rescuecore2.misc.java.JavaTools.instantiateFactory;
import java.awt.BorderLayout;
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.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import kernel.ui.KernelGUI;
import kernel.ui.KernelStartupPanel;
import kernel.ui.ScoreGraph;
import kernel.ui.ScoreTable;
import org.dom4j.DocumentException;
import rescuecore2.Constants;
import rescuecore2.GUIComponent;
import rescuecore2.components.Component;
import rescuecore2.components.ComponentConnectionException;
import rescuecore2.components.ComponentInitialisationException;
import rescuecore2.components.ComponentLauncher;
import rescuecore2.config.ClassNameSetValueConstraint;
import rescuecore2.config.ClassNameValueConstraint;
import rescuecore2.config.Config;
import rescuecore2.config.ConfigException;
import rescuecore2.config.IntegerValueConstraint;
import rescuecore2.connection.ConnectionException;
import rescuecore2.connection.ConnectionManager;
import rescuecore2.log.LogException;
import rescuecore2.log.Logger;
import rescuecore2.misc.CommandLineOptions;
import rescuecore2.misc.MutableBoolean;
import rescuecore2.misc.Pair;
import rescuecore2.misc.java.LoadableType;
import rescuecore2.misc.java.LoadableTypeProcessor;
import rescuecore2.registry.Factory;
import rescuecore2.registry.Registry;
import rescuecore2.scenario.Scenario;
import rescuecore2.score.ScoreFunction;
import rescuecore2.worldmodel.Entity;
import rescuecore2.worldmodel.WorldModel;
/**
* A class for launching the kernel.
*/
public final class StartKernel {
private static final String NO_STARTUP_MENU = "--nomenu";
private static final String NO_GUI = "--nogui";
private static final String AUTORUN = "--autorun";
private static final String GIS_MANIFEST_KEY = "Gis";
private static final String PERCEPTION_MANIFEST_KEY = "Perception";
private static final String COMMUNICATION_MANIFEST_KEY = "CommunicationModel";
private static final String COMMAND_COLLECTOR_KEY = "kernel.commandcollectors";
private static final String TERMINATION_KEY = "kernel.termination";
private static final String GIS_REGEX = "(.+WorldModelCreator).class";
private static final String PERCEPTION_REGEX = "(.+Perception).class";
private static final String COMMUNICATION_REGEX = "(.+CommunicationModel).class";
private static final LoadableType GIS_LOADABLE_TYPE = new LoadableType(GIS_MANIFEST_KEY, GIS_REGEX,
WorldModelCreator.class);
private static final LoadableType PERCEPTION_LOADABLE_TYPE = new LoadableType(PERCEPTION_MANIFEST_KEY,
PERCEPTION_REGEX, Perception.class);
private static final LoadableType COMMUNICATION_LOADABLE_TYPE = new LoadableType(COMMUNICATION_MANIFEST_KEY,
COMMUNICATION_REGEX, CommunicationModel.class);
private static final String KERNEL_STARTUP_TIME_KEY = "kernel.startup.connect-time";
private static final String COMMAND_FILTERS_KEY = "kernel.commandfilters";
private static final String AGENT_REGISTRAR_KEY = "kernel.agents.registrar";
private static final String GUI_COMPONENTS_KEY = "kernel.ui.components";
/** Utility class: private constructor. */
private StartKernel() {
}
/**
* Start a kernel.
*
* @param args Command line arguments.
* @throws DocumentException
*/
public static void main(String[] args) throws DocumentException {
Config config = new Config();
boolean showStartupMenu = true;
boolean showGUI = true;
boolean autorun = false;
Logger.setLogContext("startup");
try {
args = CommandLineOptions.processArgs(args, config);
for (String arg : args) {
if (arg.equalsIgnoreCase(NO_GUI)) {
showGUI = false;
} else if (arg.equalsIgnoreCase(NO_STARTUP_MENU)) {
showStartupMenu = false;
} else if (arg.equalsIgnoreCase(AUTORUN)) {
autorun = true;
} else {
Logger.warn("Unrecognised option: " + arg);
}
}
// Process jar files
processJarFiles(config);
Registry localRegistry = new Registry("Kernel local registry");
// Register preferred message, entity and property factories
for (String next : config.getArrayValue(Constants.FACTORY_KEY, "")) {
Factory factory = instantiateFactory(next, Factory.class);
if (factory != null) {
localRegistry.registerFactory(factory);
Logger.info("Registered local factory: " + next);
}
}
config.addConstraint(new IntegerValueConstraint(Constants.KERNEL_PORT_NUMBER_KEY, 1, 65535));
config.addConstraint(new IntegerValueConstraint(KERNEL_STARTUP_TIME_KEY, 0, Integer.MAX_VALUE));
config.addConstraint(new ClassNameSetValueConstraint(Constants.FACTORY_KEY, Factory.class));
config.addConstraint(new ClassNameSetValueConstraint(COMMAND_FILTERS_KEY, CommandFilter.class));
config.addConstraint(new ClassNameSetValueConstraint(TERMINATION_KEY, TerminationCondition.class));
config.addConstraint(new ClassNameSetValueConstraint(COMMAND_COLLECTOR_KEY, CommandCollector.class));
config.addConstraint(new ClassNameSetValueConstraint(GUI_COMPONENTS_KEY, GUIComponent.class));
config.addConstraint(new ClassNameValueConstraint(AGENT_REGISTRAR_KEY, AgentRegistrar.class));
config.addConstraint(new ClassNameValueConstraint(Constants.SCORE_FUNCTION_KEY, ScoreFunction.class));
Logger.setLogContext("kernel");
final KernelInfo kernelInfo = createKernel(config, showStartupMenu);
if (kernelInfo == null) {
System.exit(0);
}
KernelGUI gui = null;
if (showGUI) {
gui = new KernelGUI(kernelInfo.kernel, kernelInfo.componentManager, config, localRegistry, !autorun);
for (GUIComponent next : kernelInfo.guiComponents) {
gui.addGUIComponent(next);
if (next instanceof KernelListener) {
kernelInfo.kernel.addKernelListener((KernelListener) next);
}
}
JFrame frame = new JFrame("Kernel GUI");
frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
frame.getContentPane().add(gui);
frame.pack();
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
kernelInfo.kernel.shutdown();
System.exit(0);
}
});
frame.setVisible(true);
}
initialiseKernel(kernelInfo, config, localRegistry);
autostartComponents(kernelInfo, localRegistry, gui, config);
if (!showGUI || autorun) {
waitForComponentManager(kernelInfo, config);
Kernel kernel = kernelInfo.kernel;
while (!kernel.hasTerminated()) {
kernel.timestep();
}
kernel.shutdown();
}
} catch (ConfigException e) {
Logger.fatal("Couldn't start kernel", e);
} catch (KernelException e) {
Logger.fatal("Couldn't start kernel", e);
} catch (IOException e) {
Logger.fatal("Couldn't start kernel", e);
} catch (LogException e) {
Logger.fatal("Couldn't write log", e);
} catch (InterruptedException e) {
Logger.fatal("Kernel interrupted");
} catch (DocumentException e) {
Logger.fatal("Document Exception ", e);
}
}
private static KernelInfo createKernel(Config config, boolean showMenu) throws KernelException, DocumentException {
KernelStartupOptions options = new KernelStartupOptions(config);
// Show the chooser GUI
if (showMenu) {
JFrame frame = new JFrame() {
private static final long serialVersionUID = 1L;
{
setUndecorated(true);
setVisible(true);
setLocationRelativeTo(null);
setTitle("RCRS Start options");
setVisible(true);
java.awt.Toolkit.getDefaultToolkit().beep();
setAlwaysOnTop(true);
setAlwaysOnTop(false);
}
};
final JDialog dialog = new JDialog(frame, "Setup kernel options", true) {
private static final long serialVersionUID = 1L;
@Override
public void setVisible(boolean b) {
super.setVisible(b);
if (!isVisible())
frame.dispose();
}
};
KernelStartupPanel panel = new KernelStartupPanel(config, options);
JButton okButton = new JButton("OK");
JButton cancelButton = new JButton("Cancel");
JPanel buttons = new JPanel(new FlowLayout());
buttons.add(okButton);
buttons.add(cancelButton);
dialog.getContentPane().add(panel, BorderLayout.CENTER);
dialog.getContentPane().add(buttons, BorderLayout.SOUTH);
final MutableBoolean ok = new MutableBoolean(true);
okButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
ok.set(true);
dialog.setVisible(false);
dialog.dispose();
}
});
cancelButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
ok.set(false);
dialog.setVisible(false);
dialog.dispose();
}
});
dialog.pack();
dialog.setAlwaysOnTop(true);
dialog.setAlwaysOnTop(false);
dialog.setVisible(true);
if (!ok.get()) {
return null;
}
}
WorldModelCreator gis = options.getWorldModelCreator();
Perception perception = options.getPerception();
CommunicationModel comms = options.getCommunicationModel();
CommandFilter filter = makeCommandFilter(config);
TerminationCondition termination = makeTerminationCondition(config);
ScoreFunction score = makeScoreFunction(config);
CommandCollector collector = makeCommandCollector(config);
// Get the world model
WorldModel<? extends Entity> worldModel = gis.buildWorldModel(config);
Scenario scenario = gis.getScenario(config);
// Create the kernel
ScoreGraph graph = new ScoreGraph(score);
Kernel kernel = new Kernel(config, perception, comms, worldModel, gis, filter, termination, graph, collector);
// Create the component manager
ComponentManager componentManager = new ComponentManager(kernel, worldModel, config, scenario);
KernelInfo result = new KernelInfo(kernel, options, componentManager,
makeGUIComponents(config, componentManager, perception, comms, termination, filter, graph, collector, score));
return result;
}
private static void initialiseKernel(KernelInfo kernel, Config config, Registry registry) throws KernelException {
registerInitialAgents(config, kernel.componentManager, kernel.kernel.getWorldModel());
if (!config.getBooleanValue(KernelConstants.INLINE_ONLY_KEY, false)) {
// Start the connection manager
ConnectionManager connectionManager = new ConnectionManager();
try {
connectionManager.listen(config.getIntValue(Constants.KERNEL_PORT_NUMBER_KEY), registry,
kernel.componentManager);
} catch (IOException e) {
throw new KernelException("Couldn't open kernel port", e);
}
}
}
private static void waitForComponentManager(final KernelInfo kernel, Config config) throws KernelException {
// Wait for all connections
// Set up a CountDownLatch
final CountDownLatch latch = new CountDownLatch(1);
final long timeout = config.getIntValue(KERNEL_STARTUP_TIME_KEY, -1);
Thread timeoutThread = null;
if (timeout > 0) {
timeoutThread = new Thread() {
public void run() {
try {
Thread.sleep(timeout);
latch.countDown();
} catch (InterruptedException e) {
}
}
};
}
Thread waitThread = new Thread() {
public void run() {
try {
kernel.componentManager.waitForAllAgents();
kernel.componentManager.waitForAllSimulators();
kernel.componentManager.waitForAllViewers();
} catch (InterruptedException e) {
}
latch.countDown();
}
};
waitThread.start();
if (timeoutThread != null) {
timeoutThread.start();
}
// Wait at the latch until either everything is connected or the
// connection timeout expires
Logger.info("Waiting for all agents, simulators and viewers to connect.");
if (timeout > -1) {
Logger.info("Connection timeout is " + timeout + "ms");
}
try {
latch.await();
} catch (InterruptedException e) {
waitThread.interrupt();
if (timeoutThread != null) {
timeoutThread.interrupt();
}
throw new KernelException("Interrupted");
}
}
private static void autostartComponents(KernelInfo info, Registry registry, KernelGUI gui, Config config)
throws InterruptedException {
KernelStartupOptions options = info.options;
Collection<Callable<Void>> all = new ArrayList<Callable<Void>>();
Config launchConfig = new Config(config);
launchConfig.removeExcept(Constants.RANDOM_SEED_KEY, Constants.RANDOM_CLASS_KEY);
for (Pair<String, Integer> next : options.getInlineComponents()) {
if (next.second() > 0) {
all.add(new ComponentStarter(next.first(), info.componentManager, next.second(), registry, gui, launchConfig));
}
}
ExecutorService service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
service.invokeAll(all);
}
private static void registerInitialAgents(Config config, ComponentManager c, WorldModel<? extends Entity> model)
throws KernelException {
AgentRegistrar ar = instantiate(config.getValue(AGENT_REGISTRAR_KEY), AgentRegistrar.class);
if (ar == null) {
throw new KernelException("Couldn't instantiate agent registrar");
}
ar.registerAgents(model, config, c);
}
private static CommandFilter makeCommandFilter(Config config) {
ChainedCommandFilter result = new ChainedCommandFilter();
List<String> classNames = config.getArrayValue(COMMAND_FILTERS_KEY, null);
for (String next : classNames) {
Logger.debug("Command filter found: '" + next + "'");
CommandFilter f = instantiate(next, CommandFilter.class);
if (f != null) {
result.addFilter(f);
}
}
return result;
}
private static TerminationCondition makeTerminationCondition(Config config) {
List<TerminationCondition> result = new ArrayList<TerminationCondition>();
for (String next : config.getArrayValue(TERMINATION_KEY, null)) {
TerminationCondition t = instantiate(next, TerminationCondition.class);
if (t != null) {
result.add(t);
}
}
return new OrTerminationCondition(result);
}
private static ScoreFunction makeScoreFunction(Config config) {
String className = config.getValue(Constants.SCORE_FUNCTION_KEY);
ScoreFunction result = instantiate(className, ScoreFunction.class);
return new ScoreTable(result);
}
private static CommandCollector makeCommandCollector(Config config) {
List<String> classNames = config.getArrayValue(COMMAND_COLLECTOR_KEY);
CompositeCommandCollector result = new CompositeCommandCollector();
for (String next : classNames) {
CommandCollector c = instantiate(next, CommandCollector.class);
if (c != null) {
result.addCommandCollector(c);
}
}
return result;
}
private static List<GUIComponent> makeGUIComponents(Config config, ComponentManager componentManager,
Object... objectsToTest) {
List<GUIComponent> result = new ArrayList<GUIComponent>();
result.add(componentManager);
List<String> classNames = config.getArrayValue(GUI_COMPONENTS_KEY, null);
for (String next : classNames) {
Logger.debug("GUI component found: '" + next + "'");
GUIComponent c = instantiate(next, GUIComponent.class);
if (c != null) {
result.add(c);
}
}
for (Object next : objectsToTest) {
if (next instanceof GUIComponent) {
result.add((GUIComponent) next);
}
}
return result;
}
private static void processJarFiles(Config config) throws IOException {
LoadableTypeProcessor processor = new LoadableTypeProcessor(config);
processor.addFactoryRegisterCallbacks(Registry.SYSTEM_REGISTRY);
processor.addConfigUpdater(LoadableType.AGENT, config, KernelConstants.AGENTS_KEY);
processor.addConfigUpdater(LoadableType.SIMULATOR, config, KernelConstants.SIMULATORS_KEY);
processor.addConfigUpdater(LoadableType.VIEWER, config, KernelConstants.VIEWERS_KEY);
processor.addConfigUpdater(LoadableType.COMPONENT, config, KernelConstants.COMPONENTS_KEY);
processor.addConfigUpdater(GIS_LOADABLE_TYPE, config, KernelConstants.GIS_KEY);
processor.addConfigUpdater(PERCEPTION_LOADABLE_TYPE, config, KernelConstants.PERCEPTION_KEY);
processor.addConfigUpdater(COMMUNICATION_LOADABLE_TYPE, config, KernelConstants.COMMUNICATION_MODEL_KEY);
Logger.info("Looking for gis, perception, communication, agent, simulator and viewer implementations");
processor.process();
}
private static class ComponentStarter implements Callable<Void> {
private String className;
private ComponentManager componentManager;
private int count;
private Registry registry;
private KernelGUI gui;
private Config config;
public ComponentStarter(String className, ComponentManager componentManager, int count, Registry registry,
KernelGUI gui, Config config) {
this.className = className;
this.componentManager = componentManager;
this.count = count;
this.registry = registry;
this.gui = gui;
this.config = config;
Logger.debug("New ComponentStarter: " + className + " * " + count);
}
public Void call() throws InterruptedException {
Logger.debug("ComponentStarter running: " + className + " * " + count);
ComponentLauncher launcher = new InlineComponentLauncher(componentManager, config);
launcher.setDefaultRegistry(registry);
Logger.info("Launching " + count + " instances of component '" + className + "'...");
for (int i = 0; i < count; ++i) {
Component c = instantiate(className, Component.class);
if (c == null) {
break;
}
Logger.info("Launching " + className + " instance " + (i + 1) + "...");
try {
c.initialise();
launcher.connect(c);
if (gui != null && c instanceof GUIComponent) {
gui.addGUIComponent((GUIComponent) c);
}
Logger.info(className + "instance " + (i + 1) + " launched successfully");
} catch (ComponentConnectionException e) {
Logger.info(className + "instance " + (i + 1) + " failed: " + e.getMessage());
break;
} catch (ComponentInitialisationException e) {
Logger.info(className + "instance " + (i + 1) + " failed", e);
} catch (ConnectionException e) {
Logger.info(className + "instance " + (i + 1) + " failed", e);
}
}
return null;
}
}
private static class KernelInfo {
Kernel kernel;
KernelStartupOptions options;
ComponentManager componentManager;
List<GUIComponent> guiComponents;
public KernelInfo(Kernel kernel, KernelStartupOptions options, ComponentManager componentManager,
List<GUIComponent> otherComponents) {
this.kernel = kernel;
this.options = options;
this.componentManager = componentManager;
guiComponents = new ArrayList<GUIComponent>(otherComponents);
}
}
public class DummyFrame extends JFrame {
public DummyFrame(String title) {
super(title);
setUndecorated(true);
setVisible(true);
setLocationRelativeTo(null);
}
}
}
\ No newline at end of file
package kernel;
import rescuecore2.config.Config;
/**
Termination conditions tell the kernel when to stop running a simulation.
*/
public interface TerminationCondition {
/**
Initialise this termination condition.
@param config The configuration.
*/
void initialise(Config config);
/**
Return whether this termination condition has been met.
@param state The state of the kernel.
@return True if this termination condition has been met and the simulation should stop, false otherwise.
*/
boolean shouldStop(KernelState state);
}
package kernel;
import rescuecore2.config.Config;
import rescuecore2.messages.Command;
import rescuecore2.log.Logger;
import java.util.Collection;
import java.util.ArrayList;
/**
A CommandCollector that waits for a certain amount of time before returning agent commands.
*/
public class TimedCommandCollector implements CommandCollector {
private static final int DEFAULT_TIME = 1000;
private static final String TIME_KEY = "kernel.agents.think-time";
private long time;
@Override
public void initialise(Config config) {
time = config.getIntValue(TIME_KEY, DEFAULT_TIME);
}
@Override
public Collection<Command> getAgentCommands(Collection<AgentProxy> agents, int timestep) throws InterruptedException {
long now = System.currentTimeMillis();
long end = now + time;
while (now < end) {
long diff = end - now;
Logger.trace(this + " waiting for " + diff + "ms");
Thread.sleep(diff);
now = System.currentTimeMillis();
}
Collection<Command> result = new ArrayList<Command>();
for (AgentProxy next : agents) {
Collection<Command> commands = next.getAgentCommands(timestep);
result.addAll(commands);
}
Logger.trace(this + " returning " + result.size() + " commands");
Logger.trace(this + " returning " + result);
return result;
}
@Override
public String toString() {
return "Timed command collector";
}
}
package kernel;
import rescuecore2.config.Config;
import rescuecore2.log.Logger;
/**
A TerminationCondition that terminates the simulation after a specified timestep.
*/
public class TimestepTerminationCondition implements TerminationCondition {
/**
The config key describing the number of timesteps to run.
*/
private static final String TIMESTEPS_KEY = "kernel.timesteps";
private int time;
@Override
public void initialise(Config config) {
time = config.getIntValue(TIMESTEPS_KEY);
}
@Override
public boolean shouldStop(KernelState state) {
if (state.getTime() >= time) {
Logger.info("TimestepTerminationCondition fired: " + state.getTime() + " >= " + time);
return true;
}
return false;
}
@Override
public String toString() {
return "Timestep >= " + time + "";
}
}
package kernel;
import rescuecore2.connection.Connection;
import rescuecore2.messages.control.KVTimestep;
import rescuecore2.Timestep;
/**
This class is the kernel interface to a viewer.
*/
public class ViewerProxy extends AbstractKernelComponent {
private int id;
/**
Construct a viewer.
@param name The name of the viewer.
@param id The ID of the viewer.
@param c The connection to the viewer.
*/
public ViewerProxy(String name, int id, Connection c) {
super(name, c);
this.id = id;
}
/**
Send a Timestep structure to this viewer.
@param time The Timestep to send.
*/
public void sendTimestep(Timestep time) {
send(new KVTimestep(id, time.getTime(), time.getCommands(), time.getChangeSet()));
}
@Override
public String toString() {
return getName() + " (" + id + "): " + getConnection().toString();
}
@Override
public int hashCode() {
return Integer.hashCode(id);
}
}
package kernel;
import org.dom4j.DocumentException;
import rescuecore2.scenario.Scenario;
import rescuecore2.worldmodel.WorldModel;
import rescuecore2.worldmodel.Entity;
import rescuecore2.config.Config;
/**
* The interface for world model creators, e.g. GIS systems.
*/
public interface WorldModelCreator extends EntityIDGenerator {
/**
* Create a new WorldModel.
*
* @param config
* The config to use.
* @return A new world model.
* @throws KernelException
* If there is a problem building the world model.
*/
WorldModel<? extends Entity> buildWorldModel(Config config)
throws KernelException;
/**
* Returns the scenario of the simulation
*
* @param config
* @return a scenario
*/
public Scenario getScenario(Config config) throws DocumentException;
}
package kernel;
import rescuecore2.messages.Command;
import rescuecore2.log.Logger;
/**
A CommandFilter that ignores agent commands that have the wrong timestamp.
*/
public class WrongTimeCommandFilter extends AbstractCommandFilter {
@Override
protected boolean allowed(Command command, KernelState state) {
if (command.getTime() == state.getTime()) {
return true;
}
Logger.info("Ignoring command " + command + ": Wrong timestamp: " + command.getTime() + " should be " + state.getTime());
return false;
}
}
package kernel.ui;
import javax.swing.JPanel;
import javax.swing.JList;
import javax.swing.JScrollPane;
import javax.swing.BorderFactory;
import javax.swing.SwingUtilities;
import java.awt.GridLayout;
import java.util.List;
import rescuecore2.misc.gui.ListModelList;
/**
A user interface component for viewing that state of the ComponentManager.
*/
public class ComponentManagerGUI extends JPanel {
private JList uncontrolledAgents;
private JList agentAck;
private JList simulatorAck;
private JList viewerAck;
private ListModelList<String> uncontrolledAgentsModel;
private ListModelList<String> agentAckModel;
private ListModelList<String> simulatorAckModel;
private ListModelList<String> viewerAckModel;
/**
Construct a new ComponentManagerGUI.
*/
public ComponentManagerGUI() {
// CHECKSTYLE:OFF:MagicNumber
super(new GridLayout(4, 1));
// CHECKSTYLE:ON:MagicNumber
uncontrolledAgentsModel = new ListModelList<String>();
agentAckModel = new ListModelList<String>();
simulatorAckModel = new ListModelList<String>();
viewerAckModel = new ListModelList<String>();
uncontrolledAgents = new JList(uncontrolledAgentsModel);
agentAck = new JList(agentAckModel);
simulatorAck = new JList(simulatorAckModel);
viewerAck = new JList(viewerAckModel);
add(uncontrolledAgents, "Agents with no controller");
add(agentAck, "Agents that have not acknowledged");
add(simulatorAck, "Simulators that have not acknowledged");
add(viewerAck, "Viewers that have not acknowledged");
}
/**
Update the list of uncontrolled agents.
@param data A list of uncontrolled agent descriptions. This list will be displated verbatim.
*/
public void updateUncontrolledAgents(final List<String> data) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
uncontrolledAgentsModel.clear();
uncontrolledAgentsModel.addAll(data);
}
});
}
/**
Update the list of agents that have not acknowledged the connection.
@param data A list of unacknowledged agent descriptions. This list will be displayed verbatim.
*/
public void updateAgentAck(final List<String> data) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
agentAckModel.clear();
agentAckModel.addAll(data);
}
});
}
/**
Update the list of simulators that have not acknowledged the connection.
@param data A list of unacknowledged simulator descriptions. This list will be displayed verbatim.
*/
public void updateSimulatorAck(final List<String> data) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
simulatorAckModel.clear();
simulatorAckModel.addAll(data);
}
});
}
/**
Update the list of viewers that have not acknowledged the connection.
@param data A list of unacknowledged viewer descriptions. This list will be displayed verbatim.
*/
public void updateViewerAck(final List<String> data) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
viewerAckModel.clear();
viewerAckModel.addAll(data);
}
});
}
private void add(JList list, String title) {
JScrollPane scroll = new JScrollPane(list);
scroll.setBorder(BorderFactory.createTitledBorder(title));
add(scroll);
}
}
package kernel.ui;
import static rescuecore2.misc.java.JavaTools.instantiate;
import java.awt.GridLayout;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import javax.swing.JPanel;
import javax.swing.JButton;
import javax.swing.SwingUtilities;
import javax.swing.JOptionPane;
import java.util.Collection;
import java.util.List;
import java.util.ArrayList;
import kernel.Kernel;
import kernel.KernelException;
import kernel.ComponentManager;
import kernel.InlineComponentLauncher;
import rescuecore2.misc.WorkerThread;
import rescuecore2.config.Config;
import rescuecore2.config.NoSuchConfigOptionException;
import rescuecore2.connection.ConnectionException;
import rescuecore2.components.Component;
import rescuecore2.components.ComponentLauncher;
import rescuecore2.components.ComponentInitialisationException;
import rescuecore2.components.ComponentConnectionException;
import rescuecore2.log.LogException;
import rescuecore2.log.Logger;
import rescuecore2.registry.Registry;
/**
A JComponent containing various controls for the kernel GUI.
*/
public class KernelControlPanel extends JPanel {
private Kernel kernel;
private Config config;
private Registry registry;
private ComponentManager componentManager;
private ComponentLauncher launcher;
private Collection<JButton> controlButtons;
private JButton stepButton;
private JButton runButton;
private volatile boolean running;
private volatile boolean step;
private RunThread runThread;
private final Object runLock = new Object();
/**
Create a KernelControlPanel component.
@param kernel The kernel to control.
@param config The kernel configuration.
@param componentManager The kernel component manager.
@param registry The registry to use for new connections.
*/
public KernelControlPanel(Kernel kernel, Config config, ComponentManager componentManager, Registry registry) {
super(new GridLayout(0, 1));
this.kernel = kernel;
this.config = config;
this.componentManager = componentManager;
this.registry = registry;
controlButtons = new ArrayList<JButton>();
JButton addAgent = new JButton("Add agent");
JButton removeAgent = new JButton("Remove agent");
JButton addSim = new JButton("Add simulator");
JButton removeSim = new JButton("Remove simulator");
JButton addViewer = new JButton("Add viewer");
JButton removeViewer = new JButton("Remove viewer");
stepButton = new JButton("Step");
runButton = new JButton("Run");
add(addAgent);
add(removeAgent);
add(addSim);
add(removeSim);
add(addViewer);
add(removeViewer);
add(stepButton);
add(runButton);
controlButtons.add(addAgent);
controlButtons.add(removeAgent);
controlButtons.add(addSim);
controlButtons.add(removeSim);
controlButtons.add(addViewer);
controlButtons.add(removeViewer);
addAgent.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
addAgent();
}
});
removeAgent.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
removeAgent();
}
});
addSim.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
addSim();
}
});
removeSim.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
removeSim();
}
});
addViewer.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
addViewer();
}
});
removeViewer.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
removeViewer();
}
});
stepButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
stepButtonPressed();
}
});
runButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
runButtonPressed();
}
});
runThread = new RunThread();
running = false;
}
/**
Activate this control panel.
*/
public void activate() {
runThread.start();
launcher = new InlineComponentLauncher(componentManager, config);
launcher.setDefaultRegistry(Registry.getCurrentRegistry());
}
private void addAgent() {
Component[] as = createComponents("agents");
addComponent(as, "agent");
}
private void removeAgent() {
}
private void addSim() {
Component[] ss = createComponents("simulators");
addComponent(ss, "simulator");
}
private void removeSim() {
}
private void addViewer() {
Component[] vs = createComponents("viewers");
addComponent(vs, "viewer");
}
private void removeViewer() {
}
private void addComponent(Component[] options, String type) {
if (options.length == 0) {
return;
}
Component c = (Component)JOptionPane.showInputDialog(this, "Choose a " + type, "Choose a " + type, JOptionPane.QUESTION_MESSAGE, null, options, options[0]);
if (c == null) {
return;
}
try {
c.initialise();
launcher.connect(c);
}
catch (NoSuchConfigOptionException e) {
JOptionPane.showMessageDialog(this, "Adding " + type + " failed: " + e);
}
catch (ComponentInitialisationException e) {
JOptionPane.showMessageDialog(this, "Adding " + type + " failed: " + e);
}
catch (ComponentConnectionException e) {
JOptionPane.showMessageDialog(this, "Adding " + type + " failed: " + e.getMessage());
}
catch (ConnectionException e) {
JOptionPane.showMessageDialog(this, "Adding " + type + " failed: " + e);
}
catch (InterruptedException e) {
JOptionPane.showMessageDialog(this, "Adding " + type + " failed: " + e);
}
}
private void stepButtonPressed() {
// This method should only be called on the event dispatch thread so it's OK to update the GUI.
// Do a sanity check just in case.
if (!SwingUtilities.isEventDispatchThread()) {
throw new RuntimeException("stepButtonPressed called by thread " + Thread.currentThread() + ", not the event dispatch thread.");
}
synchronized (runLock) {
if (!running) {
step = true;
stepButton.setText("Working");
stepButton.setEnabled(false);
runButton.setEnabled(false);
setControlButtonsEnabled(false);
runLock.notifyAll();
}
}
}
private void endStep() {
step = false;
stepButton.setText("Step");
stepButton.setEnabled(true);
runButton.setEnabled(true);
setControlButtonsEnabled(true);
}
private void runButtonPressed() {
// This method should only be called on the event dispatch thread so it's OK to update the GUI.
// Do a sanity check just in case.
if (!SwingUtilities.isEventDispatchThread()) {
throw new RuntimeException("runButtonPressed called by thread " + Thread.currentThread() + ", not the event dispatch thread.");
}
synchronized (runLock) {
if (running) {
setControlButtonsEnabled(true);
stepButton.setEnabled(true);
runButton.setText("Run");
}
else {
setControlButtonsEnabled(false);
stepButton.setEnabled(false);
runButton.setText("Stop");
}
running = !running;
runLock.notifyAll();
}
}
private void setControlButtonsEnabled(boolean b) {
for (JButton next : controlButtons) {
next.setEnabled(b);
}
}
private void disableAllButtons() throws InterruptedException {
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
setControlButtonsEnabled(false);
stepButton.setEnabled(false);
runButton.setEnabled(false);
}
});
}
catch (java.lang.reflect.InvocationTargetException e) {
// Should never happen
Logger.error("KernelControlPanel.disableAllButtons", e);
}
}
private boolean shouldStep() {
synchronized (runLock) {
return running || step;
}
}
private Component[] createComponents(String type) {
List<String> classNames = config.getArrayValue("kernel." + type, null);
List<Component> instances = new ArrayList<Component>();
for (String next : classNames) {
Component c = instantiate(next, Component.class);
if (c != null) {
instances.add(c);
}
}
return instances.toArray(new Component[0]);
}
private class RunThread extends WorkerThread {
@Override
public boolean work() throws InterruptedException {
if (shouldStep()) {
if (!kernel.hasTerminated()) {
try {
kernel.timestep();
}
catch (KernelException e) {
Logger.error("Kernel error", e);
kernel.shutdown();
disableAllButtons();
return false;
}
catch (LogException e) {
Logger.error("Log error", e);
kernel.shutdown();
disableAllButtons();
return false;
}
synchronized (runLock) {
if (step) {
endStep();
}
}
return true;
}
else {
kernel.shutdown();
disableAllButtons();
return false;
}
}
else {
synchronized (runLock) {
runLock.wait(1000);
}
return true;
}
}
}
}
package kernel.ui;
import java.awt.BorderLayout;
import java.awt.Dimension;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;
import kernel.Kernel;
import kernel.ComponentManager;
import rescuecore2.config.Config;
import rescuecore2.registry.Registry;
import rescuecore2.GUIComponent;
/**
A GUI for the kernel.
*/
public class KernelGUI extends JPanel {
private static final int STATUS_SIZE = 300;
private Kernel kernel;
private KernelStatus status;
private KernelControlPanel control;
private JTabbedPane tabs;
private Config config;
/**
Construct a KernelGUI component.
@param kernel The kernel to watch.
@param componentManager The kernel component manager.
@param config The kernel configuration.
@param registry The registry to use for new connections.
@param controls Whether to show the control panel or not.
*/
public KernelGUI(Kernel kernel, ComponentManager componentManager, Config config, Registry registry, boolean controls) {
super(new BorderLayout());
this.kernel = kernel;
this.config = config;
status = new KernelStatus(kernel);
status.setPreferredSize(new Dimension(STATUS_SIZE, STATUS_SIZE));
add(status, BorderLayout.EAST);
tabs = new JTabbedPane();
add(tabs, BorderLayout.CENTER);
if (controls) {
control = new KernelControlPanel(kernel, config, componentManager, registry);
add(control, BorderLayout.WEST);
control.activate();
}
addGUIComponent(componentManager);
}
/**
Add a kernel GUI component.
@param c The GUI component to add.
*/
public void addGUIComponent(final GUIComponent c) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
if (c.getGUIComponent() != null) {
tabs.addTab(c.getGUIComponentName(), c.getGUIComponent());
}
}
});
}
}
package kernel.ui;
import javax.swing.JPanel;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;
import javax.swing.JScrollPane;
import javax.swing.BorderFactory;
import javax.swing.JComboBox;
import javax.swing.JSplitPane;
import javax.swing.event.ChangeListener;
import javax.swing.event.ChangeEvent;
import java.awt.BorderLayout;
import java.awt.GridBagLayout;
import java.awt.GridBagConstraints;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
import java.util.Collection;
import java.util.List;
import kernel.WorldModelCreator;
import kernel.Perception;
import kernel.CommunicationModel;
import kernel.KernelStartupOptions;
import rescuecore2.config.Config;
import rescuecore2.components.Component;
import rescuecore2.misc.gui.ConfigTree;
/**
A JPanel for displaying and editing kernel startup options.
*/
public class KernelStartupPanel extends JPanel {
private static final String AUTO_SUFFIX = ".auto";
/**
Create a kernel launch GUI.
@param config The system configuration.
@param options The kernel startup options.
*/
public KernelStartupPanel(Config config, final KernelStartupOptions options) {
super(new BorderLayout());
final JComboBox gis = createComboBox(options.getAvailableWorldModelCreators(), options.getWorldModelCreator());
final JComboBox perception = createComboBox(options.getAvailablePerceptions(), options.getPerception());
final JComboBox comms = createComboBox(options.getAvailableCommunicationModels(), options.getCommunicationModel());
CheckboxPanel simulators = new CheckboxPanel(options.getAvailableSimulators(), options);
CheckboxPanel viewers = new CheckboxPanel(options.getAvailableViewers(), options);
SpinnerPanel agents = new SpinnerPanel(options.getAvailableAgents(), options);
CheckboxPanel otherComponents = new CheckboxPanel(options.getAvailableComponents(), options);
JScrollPane simulatorsScroll = new JScrollPane(simulators);
simulatorsScroll.setBorder(BorderFactory.createTitledBorder("Simulators"));
JScrollPane viewersScroll = new JScrollPane(viewers);
viewersScroll.setBorder(BorderFactory.createTitledBorder("Viewers"));
JScrollPane agentsScroll = new JScrollPane(agents);
agentsScroll.setBorder(BorderFactory.createTitledBorder("Agents"));
JScrollPane componentsScroll = new JScrollPane(otherComponents);
componentsScroll.setBorder(BorderFactory.createTitledBorder("Other components"));
ConfigTree configTree = new ConfigTree(config);
JScrollPane configTreeScroll = new JScrollPane(configTree);
configTreeScroll.setBorder(BorderFactory.createTitledBorder("Config"));
GridBagLayout layout = new GridBagLayout();
GridBagConstraints c = new GridBagConstraints();
JPanel optionsPanel = new JPanel(layout);
JSplitPane top = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, optionsPanel, configTreeScroll);
add(top, BorderLayout.CENTER);
c.gridx = 0;
c.gridy = 0;
c.gridwidth = 1;
c.gridheight = 1;
c.weightx = 0;
c.weighty = 0;
c.fill = GridBagConstraints.BOTH;
c.anchor = GridBagConstraints.CENTER;
JLabel l = new JLabel("GIS:");
layout.setConstraints(l, c);
optionsPanel.add(l);
c.gridy = 1;
l = new JLabel("Perception:");
layout.setConstraints(l, c);
optionsPanel.add(l);
c.gridy = 2;
l = new JLabel("Communication model:");
layout.setConstraints(l, c);
optionsPanel.add(l);
c.gridy = 0;
c.gridx = 1;
c.weightx = 1;
layout.setConstraints(gis, c);
optionsPanel.add(gis);
c.gridy = 1;
layout.setConstraints(perception, c);
optionsPanel.add(perception);
c.gridy = 2;
layout.setConstraints(comms, c);
optionsPanel.add(comms);
// Simulators, viewers, agents, other components
c.gridx = 0;
++c.gridy;
c.gridwidth = 2;
c.weightx = 1;
c.weighty = 1;
layout.setConstraints(simulatorsScroll, c);
optionsPanel.add(simulatorsScroll);
++c.gridy;
layout.setConstraints(viewersScroll, c);
optionsPanel.add(viewersScroll);
++c.gridy;
layout.setConstraints(agentsScroll, c);
optionsPanel.add(agentsScroll);
++c.gridy;
layout.setConstraints(componentsScroll, c);
optionsPanel.add(componentsScroll);
// Event listeners
gis.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
options.setWorldModelCreator((WorldModelCreator)gis.getSelectedItem());
}
});
perception.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
options.setPerception((Perception)perception.getSelectedItem());
}
});
comms.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
options.setCommunicationModel((CommunicationModel)comms.getSelectedItem());
}
});
}
private <T> JComboBox createComboBox(List<T> options, T selected) {
Object[] choices = options.toArray();
JComboBox result = new JComboBox(choices);
result.setSelectedItem(selected);
result.setEnabled(choices.length > 1);
return result;
}
private static final class CheckboxPanel extends JPanel {
private CheckboxPanel(Collection<? extends Component> available, final KernelStartupOptions options) {
GridBagLayout layout = new GridBagLayout();
GridBagConstraints c = new GridBagConstraints();
c.gridx = 0;
c.gridy = 0;
c.weightx = 1;
c.weighty = 1;
c.gridwidth = 1;
c.gridheight = 1;
c.fill = GridBagConstraints.BOTH;
c.anchor = GridBagConstraints.CENTER;
setLayout(layout);
for (Component t : available) {
c.gridx = 0;
c.weightx = 1;
JLabel l = new JLabel(t.getName());
layout.setConstraints(l, c);
add(l);
c.gridx = 1;
c.weightx = 0;
final JCheckBox check = new JCheckBox();
check.setSelected(options.getInstanceCount(t) > 0);
options.setInstanceCount(t, check.isSelected() ? 1 : 0);
layout.setConstraints(check, c);
add(check);
++c.gridy;
final Component comp = t;
check.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
options.setInstanceCount(comp, check.isSelected() ? 1 : 0);
}
});
}
}
}
private static final class SpinnerPanel extends JPanel {
private SpinnerPanel(Collection<? extends Component> available, final KernelStartupOptions options) {
GridBagLayout layout = new GridBagLayout();
GridBagConstraints c = new GridBagConstraints();
c.gridx = 0;
c.gridy = 0;
c.weightx = 1;
c.weighty = 1;
c.fill = GridBagConstraints.BOTH;
c.anchor = GridBagConstraints.CENTER;
setLayout(layout);
for (Component t : available) {
c.gridx = 0;
c.weightx = 1;
JLabel l = new JLabel(t.getName());
layout.setConstraints(l, c);
add(l);
int count = options.getInstanceCount(t);
boolean all = count == Integer.MAX_VALUE;
final JSpinner spinner = new JSpinner(new SpinnerNumberModel(count == Integer.MAX_VALUE ? 0 : count, 0, Integer.MAX_VALUE, 1));
final JCheckBox check = new JCheckBox("Maximum");
check.setSelected(all);
spinner.setEnabled(!all);
final Component comp = t;
check.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
spinner.setEnabled(!check.isSelected());
if (check.isSelected()) {
options.setInstanceCount(comp, Integer.MAX_VALUE);
}
else {
options.setInstanceCount(comp, ((Number)spinner.getValue()).intValue());
}
}
});
spinner.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
options.setInstanceCount(comp, ((Number)spinner.getValue()).intValue());
}
});
c.gridx = 1;
c.weightx = 0;
layout.setConstraints(spinner, c);
add(spinner);
c.gridx = 2;
layout.setConstraints(check, c);
add(check);
++c.gridy;
}
}
}
}
package kernel.ui;
import java.util.ArrayList;
import java.awt.BorderLayout;
import java.awt.GridLayout;
import javax.swing.JPanel;
import javax.swing.JList;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.BorderFactory;
import javax.swing.SwingUtilities;
import kernel.KernelListener;
import kernel.AgentProxy;
import kernel.SimulatorProxy;
import kernel.ViewerProxy;
import kernel.Kernel;
import rescuecore2.misc.gui.ListModelList;
import rescuecore2.Timestep;
/**
A status panel for the kernel.
*/
public class KernelStatus extends JPanel implements KernelListener {
private Kernel kernel;
private ListModelList<AgentProxy> agents;
private ListModelList<SimulatorProxy> simulators;
private ListModelList<ViewerProxy> viewers;
private JList agentsList;
private JList simulatorsList;
private JList viewersList;
private JLabel timeLabel;
private JLabel scoreLabel;
/**
Construct a KernelStatus component.
@param kernel The Kernel to watch.
*/
public KernelStatus(Kernel kernel) {
super(new BorderLayout());
this.kernel = kernel;
agents = new ListModelList<AgentProxy>(new ArrayList<AgentProxy>());
simulators = new ListModelList<SimulatorProxy>(new ArrayList<SimulatorProxy>());
viewers = new ListModelList<ViewerProxy>(new ArrayList<ViewerProxy>());
kernel.addKernelListener(this);
agentsList = new JList(agents);
simulatorsList = new JList(simulators);
viewersList = new JList(viewers);
// CHECKSTYLE:OFF:MagicNumber
JPanel lists = new JPanel(new GridLayout(3, 1));
// CHECKSTYLE:ON:MagicNumber
JScrollPane agentsScroll = new JScrollPane(agentsList);
JScrollPane simulatorsScroll = new JScrollPane(simulatorsList);
JScrollPane viewersScroll = new JScrollPane(viewersList);
agentsScroll.setBorder(BorderFactory.createTitledBorder("Agents"));
simulatorsScroll.setBorder(BorderFactory.createTitledBorder("Simulators"));
viewersScroll.setBorder(BorderFactory.createTitledBorder("Viewers"));
lists.add(agentsScroll);
lists.add(simulatorsScroll);
lists.add(viewersScroll);
add(lists, BorderLayout.CENTER);
timeLabel = new JLabel("Time: not started", JLabel.CENTER);
scoreLabel = new JLabel("Score: not started", JLabel.CENTER);
JPanel labels = new JPanel(new GridLayout(1, 2));
labels.add(timeLabel);
labels.add(scoreLabel);
add(labels, BorderLayout.NORTH);
agents.addAll(kernel.getAllAgents());
simulators.addAll(kernel.getAllSimulators());
viewers.addAll(kernel.getAllViewers());
}
@Override
public void simulationStarted(Kernel k) {
}
@Override
public void simulationEnded(Kernel k) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
timeLabel.setText("Time: ended");
}
});
}
@Override
public void timestepCompleted(Kernel k, final Timestep time) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
timeLabel.setText("Time: " + time.getTime());
scoreLabel.setText("Score: " + time.getScore());
}
});
}
@Override
public void agentAdded(Kernel k, AgentProxy info) {
agents.add(info);
}
@Override
public void agentRemoved(Kernel k, AgentProxy info) {
agents.remove(info);
}
@Override
public void simulatorAdded(Kernel k, SimulatorProxy info) {
simulators.add(info);
}
@Override
public void simulatorRemoved(Kernel k, SimulatorProxy info) {
simulators.remove(info);
}
@Override
public void viewerAdded(Kernel k, ViewerProxy info) {
viewers.add(info);
}
@Override
public void viewerRemoved(Kernel k, ViewerProxy info) {
viewers.remove(info);
}
}
package kernel.ui;
import rescuecore2.score.ScoreFunction;
import rescuecore2.score.CompositeScoreFunction;
import rescuecore2.score.DelegatingScoreFunction;
import rescuecore2.worldmodel.Entity;
import rescuecore2.worldmodel.WorldModel;
import rescuecore2.config.Config;
import rescuecore2.Timestep;
import rescuecore2.GUIComponent;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.Box;
import javax.swing.JCheckBox;
import javax.swing.event.ChangeListener;
import javax.swing.event.ChangeEvent;
import java.awt.BorderLayout;
import java.util.Set;
import java.util.List;
import java.util.ArrayList;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYItemRenderer;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.data.xy.XYSeries;
/**
A ScoreFunction that also provides a components for graphing the components of the score.
*/
public class ScoreGraph extends DelegatingScoreFunction implements GUIComponent {
private JFreeChart chart;
private List<SeriesInfo> allSeries;
/**
Construct a ScoreGraph that wraps a child score function.
@param child The child score function.
*/
public ScoreGraph(ScoreFunction child) {
super("Score graph", child);
}
@Override
public void initialise(WorldModel<? extends Entity> world, Config config) {
super.initialise(world, config);
allSeries = new ArrayList<SeriesInfo>();
XYSeriesCollection data = new XYSeriesCollection();
createSeries(child, data);
PlotOrientation orientation = PlotOrientation.VERTICAL;
chart = ChartFactory.createXYLineChart("Score", "Time", "Score", data, orientation, true, false, false);
}
@Override
public double score(WorldModel<? extends Entity> world, Timestep timestep) {
update(world, timestep);
return super.score(world, timestep);
}
@Override
public JComponent getGUIComponent() {
JComponent selectionPanel = Box.createVerticalBox();
final XYItemRenderer renderer = ((XYPlot)chart.getPlot()).getRenderer();
for (SeriesInfo next : allSeries) {
final ScoreFunction f = next.function;
final int index = next.index;
final JCheckBox checkBox = new JCheckBox(f.getName(), true);
selectionPanel.add(checkBox);
checkBox.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
boolean selected = checkBox.isSelected();
renderer.setSeriesVisible(index, selected);
}
});
}
JPanel result = new JPanel();
result.add(new ChartPanel(chart), BorderLayout.CENTER);
result.add(selectionPanel, BorderLayout.EAST);
return result;
}
@Override
public String getGUIComponentName() {
return "Score chart";
}
private void createSeries(ScoreFunction root, XYSeriesCollection data) {
if (!(root instanceof ScoreTable || root instanceof ScoreGraph)) {
XYSeries next = new XYSeries(root.getName());
allSeries.add(new SeriesInfo(root, next, allSeries.size()));
data.addSeries(next);
}
if (root instanceof DelegatingScoreFunction) {
createSeries(((DelegatingScoreFunction)root).getChildFunction(), data);
}
if (root instanceof CompositeScoreFunction) {
Set<ScoreFunction> children = ((CompositeScoreFunction)root).getChildFunctions();
for (ScoreFunction f : children) {
createSeries(f, data);
}
}
}
private void update(WorldModel<? extends Entity> world, Timestep timestep) {
for (SeriesInfo next : allSeries) {
ScoreFunction f = next.function;
XYSeries data = next.series;
double d = f.score(world, timestep);
data.add(timestep.getTime(), d);
}
}
private static class SeriesInfo {
ScoreFunction function;
XYSeries series;
int index;
public SeriesInfo(ScoreFunction function, XYSeries series, int index) {
this.function = function;
this.series = series;
this.index = index;
}
}
}
package kernel.ui;
import rescuecore2.score.ScoreFunction;
import rescuecore2.score.CompositeScoreFunction;
import rescuecore2.score.DelegatingScoreFunction;
import rescuecore2.worldmodel.Entity;
import rescuecore2.worldmodel.WorldModel;
import rescuecore2.config.Config;
import rescuecore2.Timestep;
import rescuecore2.GUIComponent;
import javax.swing.JComponent;
import javax.swing.JTable;
import javax.swing.JList;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.AbstractListModel;
import javax.swing.UIManager;
import javax.swing.ListCellRenderer;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.JTableHeader;
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.Set;
/**
A ScoreFunction that also provides a JTable for viewing the components of the score.
*/
public class ScoreTable extends DelegatingScoreFunction implements GUIComponent {
private ScoreModel model;
/**
Construct a ScoreTable that wraps a child score function.
@param child The child score function.
*/
public ScoreTable(ScoreFunction child) {
super("Score Table", child);
}
@Override
public void initialise(WorldModel<? extends Entity> world, Config config) {
super.initialise(world, config);
model = new ScoreModel(child);
}
@Override
public double score(WorldModel<? extends Entity> world, Timestep timestep) {
model.update(world, timestep);
return super.score(world, timestep);
}
@Override
public JComponent getGUIComponent() {
JTable table = new JTable(model.table);
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
JScrollPane scroll = new JScrollPane(table);
JList rowHeader = new JList(model.list);
rowHeader.setFixedCellHeight(table.getRowHeight());
rowHeader.setCellRenderer(new RowHeaderRenderer(table));
rowHeader.setBackground(table.getBackground());
rowHeader.setOpaque(true);
scroll.setRowHeaderView(rowHeader);
return scroll;
}
@Override
public String getGUIComponentName() {
return "Score";
}
private static class RowHeaderRenderer extends JLabel implements ListCellRenderer {
RowHeaderRenderer(JTable table) {
JTableHeader header = table.getTableHeader();
setOpaque(true);
setBorder(UIManager.getBorder("TableHeader.cellBorder"));
setHorizontalAlignment(LEFT);
setForeground(header.getForeground());
setBackground(header.getBackground());
setFont(header.getFont());
}
@Override
public JLabel getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
setText((value == null) ? "" : value.toString());
return this;
}
}
private static class ScoreModel {
ScoreTableModel table;
ScoreListModel list;
private int steps;
private List<ScoreFunctionEntry> entries;
ScoreModel(ScoreFunction root) {
steps = 0;
entries = new ArrayList<ScoreFunctionEntry>();
populateEntries(root, "");
table = new ScoreTableModel();
list = new ScoreListModel();
}
private void populateEntries(ScoreFunction root, String prefix) {
String suffix = "";
if (!(root instanceof ScoreTable || root instanceof ScoreGraph)) {
entries.add(new ScoreFunctionEntry(root, prefix));
suffix = "--";
}
if (root instanceof DelegatingScoreFunction) {
populateEntries(((DelegatingScoreFunction)root).getChildFunction(), prefix + suffix);
}
if (root instanceof CompositeScoreFunction) {
Set<ScoreFunction> children = ((CompositeScoreFunction)root).getChildFunctions();
for (ScoreFunction next : children) {
populateEntries(next, prefix + suffix);
}
}
}
void update(WorldModel<? extends Entity> world, Timestep timestep) {
for (ScoreFunctionEntry next : entries) {
next.update(world, timestep);
}
steps = timestep.getTime();
table.fireTableStructureChanged();
}
private class ScoreTableModel extends AbstractTableModel {
@Override
public String getColumnName(int col) {
return String.valueOf(col + 1);
}
@Override
public int getRowCount() {
return entries.size();
}
@Override
public int getColumnCount() {
return steps;
}
@Override
public Object getValueAt(int row, int column) {
return entries.get(row).getScore(column + 1);
}
}
private class ScoreListModel extends AbstractListModel {
@Override
public int getSize() {
return entries.size();
}
@Override
public Object getElementAt(int row) {
return entries.get(row).getScoreFunctionName();
}
}
private static class ScoreFunctionEntry {
private ScoreFunction function;
private String prefix;
private Map<Integer, Double> scores;
public ScoreFunctionEntry(ScoreFunction f, String prefix) {
function = f;
this.prefix = prefix;
scores = new HashMap<Integer, Double>();
}
public String getScoreFunctionName() {
return prefix + function.getName();
}
public double getScore(int step) {
return scores.containsKey(step) ? scores.get(step) : Double.NaN;
}
void update(WorldModel<? extends Entity> world, Timestep timestep) {
double d = function.score(world, timestep);
scores.put(timestep.getTime(), d);
}
}
}
}
package maps;
/**
A coordinate conversion that multiplies by a number.
*/
public class ConstantConversion implements CoordinateConversion {
private double constant;
/**
Construct a ConstantConversion.
@param c The constant.
*/
public ConstantConversion(double c) {
constant = c;
}
@Override
public double convertX(double x) {
return x * constant;
}
@Override
public double convertY(double y) {
return y * constant;
}
}
package maps;
/**
Interface for converting coordinates from one system to another.
*/
public interface CoordinateConversion {
/**
Convert an X coordinate.
@param x The coordinate to convert.
@return The converted coordinate.
*/
double convertX(double x);
/**
Convert a Y coordinate.
@param y The coordinate to convert.
@return The converted coordinate.
*/
double convertY(double y);
}
package maps;
/**
A no-op coordinate conversion.
*/
public class IdentityConversion implements CoordinateConversion {
@Override
public double convertX(double x) {
return x;
}
@Override
public double convertY(double y) {
return y;
}
}
package maps;
/**
Top-level interface for all types of map.
*/
public interface Map {
}
\ No newline at end of file
package maps;
/**
Exceptions related to maps.
*/
public class MapException extends Exception {
/**
Construct a MapException with no error message.
*/
public MapException() {
super();
}
/**
Construct a MapException with an error message.
@param msg The error message.
*/
public MapException(String msg) {
super(msg);
}
/**
Construct a MapException with an underlying cause.
@param cause The cause.
*/
public MapException(Throwable cause) {
super(cause);
}
/**
Construct a MapException with an error message and underlying cause.
@param msg The error message.
@param cause The cause.
*/
public MapException(String msg, Throwable cause) {
super(msg, cause);
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment