summaryrefslogtreecommitdiff
path: root/src/main/java/simulator/builder/SimulationBuilder.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/simulator/builder/SimulationBuilder.java')
-rw-r--r--src/main/java/simulator/builder/SimulationBuilder.java362
1 files changed, 362 insertions, 0 deletions
diff --git a/src/main/java/simulator/builder/SimulationBuilder.java b/src/main/java/simulator/builder/SimulationBuilder.java
new file mode 100644
index 0000000..8ac5d04
--- /dev/null
+++ b/src/main/java/simulator/builder/SimulationBuilder.java
@@ -0,0 +1,362 @@
+package simulator.builder;
+
+import simulator.*;
+import core.*;
+import prefs.*;
+import events.*;
+import events.internal.*;
+import serialize.VSSerialize;
+import java.io.*;
+import java.lang.reflect.*;
+import java.util.*;
+import java.util.ArrayList;
+
+/**
+ * Builder for creating DS-Sim simulations programmatically without GUI.
+ *
+ * Example usage:
+ * <pre>
+ * SimulationBuilder builder = new SimulationBuilder()
+ * .withProcesses(5)
+ * .withProtocol("protocols.implementations.VSRaftProtocol")
+ * .activateServers(0, 1, 2)
+ * .activateClients(3, 4)
+ * .addCrashEvent(0, 2000)
+ * .addRecoveryEvent(0, 3000)
+ * .save("saved-simulations/my-raft.dat");
+ * </pre>
+ */
+public class SimulationBuilder {
+
+ private VSDefaultPrefs prefs;
+ private VSSimulator simulator;
+ private VSSimulatorVisualization visualization;
+ private VSTaskManager taskManager;
+ private String protocolClass;
+ private int numProcesses = 3; // default
+ private List<ScheduledTask> scheduledTasks = new ArrayList<>();
+ private int simulationDuration = 10000; // default 10 seconds
+
+ /**
+ * Internal class to hold task scheduling information
+ */
+ private static class ScheduledTask {
+ long time;
+ int processId;
+ VSAbstractEvent event;
+ boolean isGlobalTimed;
+
+ ScheduledTask(long time, int processId, VSAbstractEvent event, boolean isGlobalTimed) {
+ this.time = time;
+ this.processId = processId;
+ this.event = event;
+ this.isGlobalTimed = isGlobalTimed;
+ }
+ }
+
+ /**
+ * Initialize the builder with default preferences
+ */
+ public SimulationBuilder() throws Exception {
+ // Initialize preferences
+ prefs = new VSDefaultPrefs();
+ prefs.fillWithDefaults();
+
+ // Initialize registered events
+ VSRegisteredEvents.init(prefs);
+ }
+
+ /**
+ * Set the simulation duration in milliseconds
+ */
+ public SimulationBuilder withDuration(int durationMs) {
+ this.simulationDuration = durationMs;
+ return this;
+ }
+
+ /**
+ * Set the number of processes in the simulation
+ */
+ public SimulationBuilder withProcesses(int count) {
+ this.numProcesses = count;
+ return this;
+ }
+
+ /**
+ * Set the protocol class to use
+ */
+ public SimulationBuilder withProtocol(String protocolClassName) {
+ this.protocolClass = protocolClassName;
+ return this;
+ }
+
+ /**
+ * Activate protocol as server on specified processes
+ */
+ public SimulationBuilder activateServers(int... processIds) {
+ for (int pid : processIds) {
+ VSProtocolEvent event = new VSProtocolEvent();
+ event.onInit(); // Initialize the event first
+ setProtocolClassname(event, protocolClass);
+ setIsServer(event, true);
+
+ scheduledTasks.add(new ScheduledTask(0, pid, event, true));
+ }
+ return this;
+ }
+
+ /**
+ * Activate protocol as client on specified processes
+ */
+ public SimulationBuilder activateClients(int... processIds) {
+ return activateClients(500, processIds); // default delay
+ }
+
+ /**
+ * Activate protocol as client on specified processes with custom start time
+ */
+ public SimulationBuilder activateClients(long startTime, int... processIds) {
+ for (int i = 0; i < processIds.length; i++) {
+ VSProtocolEvent event = new VSProtocolEvent();
+ event.onInit(); // Initialize the event first
+ setProtocolClassname(event, protocolClass);
+ setIsServer(event, false);
+
+ // Stagger client starts
+ long time = startTime + (i * 200);
+ scheduledTasks.add(new ScheduledTask(time, processIds[i], event, true));
+ }
+ return this;
+ }
+
+ /**
+ * Add a process crash event
+ */
+ public SimulationBuilder addCrashEvent(int processId, long time) {
+ try {
+ // Use reflection to create crash event
+ Class<?> crashClass = Class.forName("events.implementations.VSProcessCrashEvent");
+ VSAbstractEvent crashEvent = (VSAbstractEvent) crashClass.getDeclaredConstructor().newInstance();
+ scheduledTasks.add(new ScheduledTask(time, processId, crashEvent, true));
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to create crash event", e);
+ }
+ return this;
+ }
+
+ /**
+ * Add a process recovery event
+ */
+ public SimulationBuilder addRecoveryEvent(int processId, long time) {
+ try {
+ // Use reflection to create recovery event
+ Class<?> recoverClass = Class.forName("events.implementations.VSProcessRecoverEvent");
+ VSAbstractEvent recoverEvent = (VSAbstractEvent) recoverClass.getDeclaredConstructor().newInstance();
+ scheduledTasks.add(new ScheduledTask(time, processId, recoverEvent, true));
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to create recovery event", e);
+ }
+ return this;
+ }
+
+ /**
+ * Add a custom event
+ */
+ public SimulationBuilder addEvent(String eventClassName, int processId, long time) {
+ try {
+ Class<?> eventClass = Class.forName(eventClassName);
+ VSAbstractEvent event = (VSAbstractEvent) eventClass.getDeclaredConstructor().newInstance();
+ scheduledTasks.add(new ScheduledTask(time, processId, event, true));
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to create event: " + eventClassName, e);
+ }
+ return this;
+ }
+
+ /**
+ * Set protocol classname using reflection (since field is private)
+ */
+ private void setProtocolClassname(VSProtocolEvent event, String classname) {
+ try {
+ Field field = VSProtocolEvent.class.getDeclaredField("protocolClassname");
+ field.setAccessible(true);
+ field.set(event, classname);
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to set protocol classname", e);
+ }
+ }
+
+ /**
+ * Set isServer flag using reflection (field is called isClientProtocol)
+ */
+ private void setIsServer(VSProtocolEvent event, boolean isServer) {
+ try {
+ // The field is actually called isClientProtocol, and server = !client
+ Field field = VSProtocolEvent.class.getDeclaredField("isClientProtocol");
+ field.setAccessible(true);
+ field.set(event, !isServer); // Invert: server means NOT client
+
+ // Also set protocol activation to true
+ Field activationField = VSProtocolEvent.class.getDeclaredField("isProtocolActivation");
+ activationField.setAccessible(true);
+ activationField.set(event, true);
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to set protocol flags", e);
+ }
+ }
+
+ /**
+ * Build the simulation (must be called before save)
+ */
+ private void build() throws Exception {
+ if (simulator != null) {
+ return; // Already built
+ }
+
+ // Set simulation duration
+ prefs.setInteger("sim.seconds", simulationDuration / 1000);
+
+ // Set network delay parameters for message delivery
+ prefs.setInteger("process.msg.delay.min", 10); // 10ms minimum delay
+ prefs.setInteger("process.msg.delay.max", 50); // 50ms maximum delay
+
+ // Create simulator without frame for headless
+ simulator = new VSSimulator(prefs, null);
+
+ // Create visualization without GUI
+ VSLogging logging = new VSLogging();
+ visualization = new VSSimulatorVisualization(prefs, simulator, logging);
+
+ // Set visualization in simulator using reflection
+ Field vizField = VSSimulator.class.getDeclaredField("simulatorVisualization");
+ vizField.setAccessible(true);
+ vizField.set(simulator, visualization);
+
+ // Add processes if needed (default is 3)
+ Method addProcessMethod = VSSimulatorVisualization.class.getDeclaredMethod("addProcess");
+ addProcessMethod.setAccessible(true);
+
+ // Remove default processes if we want fewer
+ if (numProcesses < 3) {
+ Field processesField = VSSimulatorVisualization.class.getDeclaredField("processes");
+ processesField.setAccessible(true);
+ ArrayList<VSInternalProcess> processes = (ArrayList<VSInternalProcess>) processesField.get(visualization);
+ while (processes.size() > numProcesses) {
+ processes.remove(processes.size() - 1);
+ }
+ }
+
+ // Add more processes if needed
+ for (int i = 3; i < numProcesses; i++) {
+ addProcessMethod.invoke(visualization);
+ }
+
+ // Get task manager
+ taskManager = visualization.getTaskManager();
+
+ // Initialize all events with their processes
+ for (ScheduledTask st : scheduledTasks) {
+ VSInternalProcess process = visualization.getProcess(st.processId);
+ st.event.init(process);
+
+ // For protocol events, update the shortname after init
+ if (st.event instanceof VSProtocolEvent) {
+ VSProtocolEvent protocolEvent = (VSProtocolEvent) st.event;
+ // Force shortname update by calling the method via reflection
+ try {
+ Method createShortname = VSProtocolEvent.class.getDeclaredMethod("createShortname", String.class);
+ createShortname.setAccessible(true);
+ String shortname = (String) createShortname.invoke(protocolEvent, (String)null);
+ protocolEvent.setShortname(shortname);
+ } catch (Exception e) {
+ // Ignore
+ }
+ }
+
+ // Create task
+ VSTask task = new VSTask(st.time, process, st.event,
+ st.isGlobalTimed ? VSTask.GLOBAL : VSTask.LOCAL);
+ taskManager.addTask(task);
+ }
+ }
+
+ /**
+ * Save the simulation to a file
+ */
+ public SimulationBuilder save(String filename) throws Exception {
+ build();
+
+ File outputFile = new File(filename);
+ outputFile.getParentFile().mkdirs();
+
+ VSSerialize serialize = new VSSerialize();
+
+ // Save using the serializer
+ try {
+ FileOutputStream fos = new FileOutputStream(outputFile);
+ ObjectOutputStream oos = new ObjectOutputStream(fos);
+
+ // Create serializable prefs from our prefs
+ VSSerializablePrefs serializablePrefs = new VSSerializablePrefs();
+
+ // Copy all preferences
+ for (String key : prefs.getIntegerKeySet()) {
+ serializablePrefs.initInteger(key, prefs.getInteger(key));
+ }
+ for (String key : prefs.getBooleanKeySet()) {
+ serializablePrefs.initBoolean(key, prefs.getBoolean(key));
+ }
+ for (String key : prefs.getStringKeySet()) {
+ serializablePrefs.initString(key, prefs.getString(key));
+ }
+ for (String key : prefs.getFloatKeySet()) {
+ serializablePrefs.initFloat(key, prefs.getFloat(key));
+ }
+ for (String key : prefs.getColorKeySet()) {
+ serializablePrefs.initColor(key, prefs.getColor(key));
+ }
+ for (String key : prefs.getVectorKeySet()) {
+ serializablePrefs.initVector(key, prefs.getVector(key));
+ }
+ for (String key : prefs.getLongKeySet()) {
+ serializablePrefs.initLong(key, prefs.getLong(key));
+ }
+
+ // Serialize preferences first
+ serializablePrefs.serialize(serialize, oos);
+
+ // Then serialize simulator
+ simulator.serialize(serialize, oos);
+
+ oos.close();
+ fos.close();
+
+ System.out.println("Simulation saved to: " + outputFile.getAbsolutePath());
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to save simulation", e);
+ }
+
+ return this;
+ }
+
+ /**
+ * Get the built simulator (for testing/verification)
+ */
+ public VSSimulator getSimulator() throws Exception {
+ build();
+ return simulator;
+ }
+
+ /**
+ * Fluent API for common protocol setups
+ */
+ public static class Protocols {
+ public static final String RAFT = "protocols.implementations.VSRaftProtocol";
+ public static final String PING_PONG = "protocols.implementations.VSPingPongProtocol";
+ public static final String BERKLEY_TIME = "protocols.implementations.VSBerkelyTimeProtocol";
+ public static final String BROADCAST = "protocols.implementations.VSBroadcastProtocol";
+ public static final String ONE_PHASE_COMMIT = "protocols.implementations.VSOnePhaseCommitProtocol";
+ public static final String TWO_PHASE_COMMIT = "protocols.implementations.VSTwoPhaseCommitProtocol";
+ public static final String RELIABLE_MULTICAST = "protocols.implementations.VSReliableMulticastProtocol";
+ }
+} \ No newline at end of file