diff options
Diffstat (limited to 'src/main/java/simulator')
10 files changed, 785 insertions, 3 deletions
diff --git a/src/main/java/simulator/VSSimulator.java b/src/main/java/simulator/VSSimulator.java index c6bdfad..2cf58c3 100644 --- a/src/main/java/simulator/VSSimulator.java +++ b/src/main/java/simulator/VSSimulator.java @@ -1352,7 +1352,10 @@ public class VSSimulator extends JPanel implements VSSerializable { menuItemStates.setPause(false); menuItemStates.setReset(true); menuItemStates.setReplay(true); - simulatorFrame.updateSimulatorMenu(); + // Update simulator menu only if running with GUI + if (simulatorFrame != null) { + simulatorFrame.updateSimulatorMenu(); + } } /** @@ -1415,7 +1418,9 @@ public class VSSimulator extends JPanel implements VSSerializable { localPIDComboBox.removeItemAt(index); processesComboBox.removeItemAt(index); - simulatorFrame.updateEditMenu(); + if (simulatorFrame != null) { + simulatorFrame.updateEditMenu(); + } updateTaskManagerTable(); } @@ -1436,7 +1441,10 @@ public class VSSimulator extends JPanel implements VSSerializable { globalPIDComboBox.insertItemAt("PID: " + processID, index); processesComboBox.insertItemAt(processString + " " + processID, index); - simulatorFrame.updateEditMenu(); + // Update edit menu only if running with GUI + if (simulatorFrame != null) { + simulatorFrame.updateEditMenu(); + } } /** diff --git a/src/main/java/simulator/VSSimulatorVisualization.java b/src/main/java/simulator/VSSimulatorVisualization.java index 2dc4a64..53cf391 100644 --- a/src/main/java/simulator/VSSimulatorVisualization.java +++ b/src/main/java/simulator/VSSimulatorVisualization.java @@ -3,6 +3,7 @@ package simulator; import java.awt.Canvas; import java.awt.Color; import java.awt.Graphics2D; +import java.awt.GraphicsEnvironment; import java.awt.RenderingHints; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -867,6 +868,13 @@ public class VSSimulatorVisualization extends Canvas * Paints the simulator. */ public void paint() { + // Skip painting in headless mode to avoid GUI errors + if (GraphicsEnvironment.isHeadless() || + Boolean.getBoolean("ds.sim.headless") || + !isDisplayable()) { + return; + } + while (getBufferStrategy() == null) { createBufferStrategy(3); strategy = getBufferStrategy(); diff --git a/src/main/java/simulator/engine/AbstractSimulationEngine.java b/src/main/java/simulator/engine/AbstractSimulationEngine.java new file mode 100644 index 0000000..21be5c7 --- /dev/null +++ b/src/main/java/simulator/engine/AbstractSimulationEngine.java @@ -0,0 +1,204 @@ +package simulator.engine; + +import core.*; +import prefs.VSPrefs; +import simulator.VSLogging; +import events.internal.VSMessageReceiveEvent; +import java.util.*; +import java.util.concurrent.CopyOnWriteArrayList; + +/** + * Abstract base implementation of SimulationEngine that provides common + * functionality for both headless and visual simulation engines. + */ +public abstract class AbstractSimulationEngine implements SimulationEngine { + + protected final VSPrefs prefs; + protected final List<VSInternalProcess> processes; + protected final List<SimulationVisualizer> visualizers; + protected final VSTaskManager taskManager; + protected VSLogging loging; + + protected long time; + protected boolean isPaused; + protected boolean isResetted; + protected boolean hasFinished; + + public AbstractSimulationEngine(VSPrefs prefs, VSLogging loging) { + this.prefs = prefs; + this.loging = loging; + this.processes = new ArrayList<>(); + this.visualizers = new CopyOnWriteArrayList<>(); + this.taskManager = new VSTaskManager(prefs, null); // We'll inject visualization later + this.time = 0; + this.isPaused = true; + this.isResetted = true; + this.hasFinished = false; + } + + @Override + public void sendMessage(VSMessage message) { + // Schedule message delivery to all processes (broadcast model) + scheduleMessageDelivery(message, time); + + // Notify visualizers + for (SimulationVisualizer visualizer : visualizers) { + visualizer.onMessageSent(message); + } + + // Log the message + if (loging != null) { + loging.log("Message sent; ID: " + message.getMessageID() + + "; Protocol: " + message.getName()); + } + } + + protected abstract long calculateDeliveryTime(VSMessage message); + + protected abstract void scheduleMessageDelivery(VSMessage message, long deliveryTime); + + @Override + public void addProcess(VSInternalProcess process) { + processes.add(process); + + // Notify visualizers + for (SimulationVisualizer visualizer : visualizers) { + visualizer.onProcessAdded(process); + } + } + + @Override + public void removeProcess(VSInternalProcess process) { + processes.remove(process); + + // Notify visualizers + for (SimulationVisualizer visualizer : visualizers) { + visualizer.onProcessRemoved(process); + } + } + + @Override + public List<VSInternalProcess> getProcesses() { + return new ArrayList<>(processes); + } + + @Override + public VSInternalProcess getProcess(int index) { + if (index >= 0 && index < processes.size()) { + return processes.get(index); + } + return null; + } + + @Override + public int getNumProcesses() { + return processes.size(); + } + + @Override + public VSTaskManager getTaskManager() { + return taskManager; + } + + @Override + public long getTime() { + return time; + } + + @Override + public void setTime(long time) { + this.time = time; + + // Notify visualizers + for (SimulationVisualizer visualizer : visualizers) { + visualizer.onTimeChanged(time); + } + } + + @Override + public void reset() { + // Reset state + isResetted = true; + isPaused = true; + hasFinished = false; + time = 0; + + // Reset all processes + for (VSInternalProcess process : processes) { + process.reset(); + } + + // Reset task manager + taskManager.reset(); + + // Notify visualizers + for (SimulationVisualizer visualizer : visualizers) { + visualizer.onSimulationReset(); + } + } + + @Override + public void play() { + isPaused = false; + isResetted = false; + + // Notify visualizers + for (SimulationVisualizer visualizer : visualizers) { + visualizer.onSimulationStarted(); + } + } + + @Override + public void pause() { + isPaused = true; + + // Notify visualizers + for (SimulationVisualizer visualizer : visualizers) { + visualizer.onSimulationPaused(); + } + } + + @Override + public boolean isPaused() { + return isPaused; + } + + @Override + public boolean isResetted() { + return isResetted; + } + + @Override + public boolean hasFinished() { + return hasFinished; + } + + @Override + public void setFinished(boolean finished) { + this.hasFinished = finished; + + if (finished) { + // Notify visualizers + for (SimulationVisualizer visualizer : visualizers) { + visualizer.onSimulationFinished(); + } + } + } + + @Override + public void addVisualizer(SimulationVisualizer visualizer) { + visualizers.add(visualizer); + } + + @Override + public void removeVisualizer(SimulationVisualizer visualizer) { + visualizers.remove(visualizer); + } + + /** + * Set the logging instance. + */ + public void setLogging(VSLogging loging) { + this.loging = loging; + } +}
\ No newline at end of file diff --git a/src/main/java/simulator/engine/HeadlessSimulationEngine.java b/src/main/java/simulator/engine/HeadlessSimulationEngine.java new file mode 100644 index 0000000..36a64a7 --- /dev/null +++ b/src/main/java/simulator/engine/HeadlessSimulationEngine.java @@ -0,0 +1,116 @@ +package simulator.engine; + +import core.*; +import prefs.VSPrefs; +import events.internal.VSMessageReceiveEvent; +import simulator.VSLogging; + +/** + * Headless implementation of the simulation engine that runs without any GUI dependencies. + * This engine focuses purely on simulation logic without any visualization concerns. + */ +public class HeadlessSimulationEngine extends AbstractSimulationEngine { + + public HeadlessSimulationEngine(VSPrefs prefs, VSLogging loging) { + super(prefs, loging); + } + + @Override + protected long calculateDeliveryTime(VSMessage message) { + // Get source process + VSInternalProcess source = (VSInternalProcess) message.getSendingProcess(); + + if (source == null) { + return time; // Deliver immediately if process not found + } + + // Calculate network delay + long networkDelay = prefs.getLong("sim.network.delay"); + long variability = prefs.getLong("sim.network.variability"); + + // Add random variability + if (variability > 0) { + long variance = (long)(Math.random() * variability * 2) - variability; + networkDelay += variance; + } + + // Ensure minimum delay + if (networkDelay < 0) { + networkDelay = 0; + } + + // Calculate delivery time based on source process time + return source.getTime() + networkDelay; + } + + @Override + protected void scheduleMessageDelivery(VSMessage message, long deliveryTime) { + // In DS-Sim, messages are broadcast to all processes + VSInternalProcess sendingProcess = (VSInternalProcess) message.getSendingProcess(); + boolean recvOwn = prefs.getBoolean("sim.message.own.recv"); + + // Schedule delivery to all processes + for (VSInternalProcess receiverProcess : processes) { + if (receiverProcess.equals(sendingProcess)) { + // Only deliver to self if configured + if (!recvOwn) { + continue; + } + } + + // Create receive event for this process + VSMessageReceiveEvent receiveEvent = new VSMessageReceiveEvent(message); + VSTask task = new VSTask(deliveryTime, receiverProcess, receiveEvent, VSTask.GLOBAL); + taskManager.addTask(task); + + if (loging != null) { + loging.log("Message scheduled for delivery to process " + + receiverProcess.getProcessNum() + "; ID: " + + message.getMessageID() + "; Time: " + deliveryTime); + } + } + } + + /** + * Run one simulation step. + * This method advances time and executes all tasks scheduled for the current time. + */ + public void runStep() { + if (isPaused || hasFinished) { + return; + } + + // Sync all process times + for (VSInternalProcess process : processes) { + process.syncTime(time); + } + + // Run tasks for current time + taskManager.runTasks(time, 0, time - 1); + + // Check if simulation has finished + // TODO: Implement proper finish detection + // For now, rely on external control or time limits + } + + /** + * Run the simulation for a specified duration. + * @param duration Duration in milliseconds + */ + public void runFor(long duration) { + long endTime = time + duration; + + while (time < endTime && !hasFinished) { + runStep(); + time++; + } + } + + /** + * Check if any protocols are still active. + * TODO: Implement this when protocol tracking is available + */ + private boolean hasActiveProtocols() { + return false; // For now, assume no active protocols + } +}
\ No newline at end of file diff --git a/src/main/java/simulator/engine/SimulationEngine.java b/src/main/java/simulator/engine/SimulationEngine.java new file mode 100644 index 0000000..d557aef --- /dev/null +++ b/src/main/java/simulator/engine/SimulationEngine.java @@ -0,0 +1,119 @@ +package simulator.engine; + +import core.VSInternalProcess; +import core.VSTaskManager; +import core.VSMessage; +import java.util.List; + +/** + * Core simulation engine interface that defines all simulation operations + * without any GUI dependencies. + */ +public interface SimulationEngine { + + /** + * Send a message between processes. + * @param message The message to send + */ + void sendMessage(VSMessage message); + + /** + * Add a process to the simulation. + * @param process The process to add + */ + void addProcess(VSInternalProcess process); + + /** + * Remove a process from the simulation. + * @param process The process to remove + */ + void removeProcess(VSInternalProcess process); + + /** + * Get all processes in the simulation. + * @return List of processes + */ + List<VSInternalProcess> getProcesses(); + + /** + * Get a specific process by index. + * @param index The process index + * @return The process or null if not found + */ + VSInternalProcess getProcess(int index); + + /** + * Get the number of processes. + * @return Process count + */ + int getNumProcesses(); + + /** + * Get the task manager. + * @return The task manager + */ + VSTaskManager getTaskManager(); + + /** + * Get the current simulation time. + * @return Current time in milliseconds + */ + long getTime(); + + /** + * Set the simulation time. + * @param time Time in milliseconds + */ + void setTime(long time); + + /** + * Reset the simulation to initial state. + */ + void reset(); + + /** + * Start or resume the simulation. + */ + void play(); + + /** + * Pause the simulation. + */ + void pause(); + + /** + * Check if simulation is paused. + * @return true if paused + */ + boolean isPaused(); + + /** + * Check if simulation has been reset. + * @return true if reset + */ + boolean isResetted(); + + /** + * Check if simulation has finished. + * @return true if finished + */ + boolean hasFinished(); + + /** + * Set finished state. + * @param finished The finished state + */ + void setFinished(boolean finished); + + /** + * Add a visualization observer. + * @param visualizer The visualizer to add + */ + void addVisualizer(SimulationVisualizer visualizer); + + /** + * Remove a visualization observer. + * @param visualizer The visualizer to remove + */ + void removeVisualizer(SimulationVisualizer visualizer); +}
\ No newline at end of file diff --git a/src/main/java/simulator/engine/SimulationVisualizer.java b/src/main/java/simulator/engine/SimulationVisualizer.java new file mode 100644 index 0000000..3151638 --- /dev/null +++ b/src/main/java/simulator/engine/SimulationVisualizer.java @@ -0,0 +1,61 @@ +package simulator.engine; + +import core.VSInternalProcess; +import core.VSMessage; + +/** + * Interface for visualization components that observe simulation events. + * Implementations can choose to display these events visually or ignore them. + */ +public interface SimulationVisualizer { + + /** + * Called when a message is sent in the simulation. + * @param message The message that was sent + */ + void onMessageSent(VSMessage message); + + /** + * Called when a process is added to the simulation. + * @param process The process that was added + */ + void onProcessAdded(VSInternalProcess process); + + /** + * Called when a process is removed from the simulation. + * @param process The process that was removed + */ + void onProcessRemoved(VSInternalProcess process); + + /** + * Called when the simulation time changes. + * @param time The new time value + */ + void onTimeChanged(long time); + + /** + * Called when the simulation is reset. + */ + void onSimulationReset(); + + /** + * Called when the simulation starts or resumes. + */ + void onSimulationStarted(); + + /** + * Called when the simulation is paused. + */ + void onSimulationPaused(); + + /** + * Called when the simulation finishes. + */ + void onSimulationFinished(); + + /** + * Called when a process state changes. + * @param process The process whose state changed + */ + void onProcessStateChanged(VSInternalProcess process); +}
\ No newline at end of file diff --git a/src/main/java/simulator/engine/VisualizationAdapter.java b/src/main/java/simulator/engine/VisualizationAdapter.java new file mode 100644 index 0000000..119596b --- /dev/null +++ b/src/main/java/simulator/engine/VisualizationAdapter.java @@ -0,0 +1,156 @@ +package simulator.engine; + +import simulator.*; +import core.*; +import prefs.VSPrefs; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +/** + * Adapter that allows VSSimulatorVisualization to work with the new SimulationEngine interface. + * This provides backward compatibility during the refactoring process. + */ +public class VisualizationAdapter { + + /** + * Inject a simulation engine into an existing VSSimulatorVisualization. + * This replaces direct simulation logic with delegated calls to the engine. + */ + public static void injectEngine(VSSimulatorVisualization viz, SimulationEngine engine) + throws Exception { + + // Replace sendMessage method behavior using a proxy + installSendMessageProxy(viz, engine); + + // Sync process list + syncProcessList(viz, engine); + + // Sync task manager + syncTaskManager(viz, engine); + } + + /** + * Install a proxy for the sendMessage method that delegates to the engine. + */ + private static void installSendMessageProxy(VSSimulatorVisualization viz, + SimulationEngine engine) throws Exception { + // This is complex with standard Java, so we'll use a different approach + // We'll override the behavior by setting a flag that the paint method checks + + // Set a flag indicating headless mode + Field headlessField = findOrCreateField(viz, "isHeadlessMode"); + headlessField.setAccessible(true); + headlessField.set(viz, true); + + // Store engine reference + Field engineField = findOrCreateField(viz, "simulationEngine"); + engineField.setAccessible(true); + engineField.set(viz, engine); + } + + /** + * Find a field or create it dynamically if possible. + */ + private static Field findOrCreateField(Object obj, String fieldName) { + try { + return obj.getClass().getDeclaredField(fieldName); + } catch (NoSuchFieldException e) { + // In real implementation, we'd need to use bytecode manipulation + // For now, we'll work with existing fields + return null; + } + } + + /** + * Sync the process list between visualization and engine. + */ + private static void syncProcessList(VSSimulatorVisualization viz, + SimulationEngine engine) throws Exception { + Field processesField = VSSimulatorVisualization.class.getDeclaredField("processes"); + processesField.setAccessible(true); + + // Get current processes from viz + java.util.ArrayList<VSInternalProcess> vizProcesses = + (java.util.ArrayList<VSInternalProcess>) processesField.get(viz); + + // Add all to engine + for (VSInternalProcess process : vizProcesses) { + engine.addProcess(process); + } + } + + /** + * Sync the task manager between visualization and engine. + */ + private static void syncTaskManager(VSSimulatorVisualization viz, + SimulationEngine engine) throws Exception { + Field taskManagerField = VSSimulatorVisualization.class.getDeclaredField("taskManager"); + taskManagerField.setAccessible(true); + + VSTaskManager vizTaskManager = (VSTaskManager) taskManagerField.get(viz); + VSTaskManager engineTaskManager = engine.getTaskManager(); + + // Copy tasks if needed + // This would require access to internal task manager state + } + + /** + * Create a headless wrapper for VSSimulatorVisualization that prevents paint operations. + */ + public static VSSimulatorVisualization createHeadlessWrapper( + final VSSimulatorVisualization original, + final SimulationEngine engine) { + + try { + // Get prefs, simulator, and loging via reflection + Field prefsField = VSSimulatorVisualization.class.getDeclaredField("prefs"); + prefsField.setAccessible(true); + VSPrefs prefs = (VSPrefs) prefsField.get(original); + + Field simulatorField = VSSimulatorVisualization.class.getDeclaredField("simulator"); + simulatorField.setAccessible(true); + VSSimulator simulator = (VSSimulator) simulatorField.get(original); + + Field logingField = VSSimulatorVisualization.class.getDeclaredField("loging"); + logingField.setAccessible(true); + VSLogging loging = (VSLogging) logingField.get(original); + + // Create a wrapper that intercepts paint calls + return new VSSimulatorVisualization(prefs, simulator, loging) { + + @Override + public void paint() { + // Do nothing - no painting in headless mode + } + + @Override + public void paint(java.awt.Graphics g) { + // Do nothing + } + + @Override + public void sendMessage(VSMessage message) { + // Delegate to engine instead of creating visual elements + engine.sendMessage(message); + } + + @Override + public void repaint() { + // Do nothing + } + + @Override + public java.awt.image.BufferStrategy getBufferStrategy() { + return null; // Prevent buffer strategy creation + } + + @Override + public void createBufferStrategy(int numBuffers) { + // Do nothing + } + }; + } catch (Exception e) { + throw new RuntimeException("Failed to create headless wrapper", e); + } + } +}
\ No newline at end of file diff --git a/src/main/java/simulator/messaging/HeadlessMessageHandler.java b/src/main/java/simulator/messaging/HeadlessMessageHandler.java new file mode 100644 index 0000000..fc2916e --- /dev/null +++ b/src/main/java/simulator/messaging/HeadlessMessageHandler.java @@ -0,0 +1,39 @@ +package simulator.messaging; + +import core.VSMessage; +import simulator.engine.SimulationEngine; + +/** + * Headless implementation of MessageHandler that processes messages + * without any GUI visualization. + */ +public class HeadlessMessageHandler implements MessageHandler { + private final SimulationEngine engine; + private long networkDelay = 100; + private long networkVariability = 0; + + public HeadlessMessageHandler(SimulationEngine engine) { + this.engine = engine; + } + + @Override + public void handleMessage(VSMessage message) { + // Just send to engine, no visualization + engine.sendMessage(message); + } + + @Override + public void visualizeMessage(VSMessage message) { + // No-op in headless mode + } + + @Override + public void setNetworkDelay(long delay) { + this.networkDelay = delay; + } + + @Override + public void setNetworkVariability(long variability) { + this.networkVariability = variability; + } +}
\ No newline at end of file diff --git a/src/main/java/simulator/messaging/MessageHandler.java b/src/main/java/simulator/messaging/MessageHandler.java new file mode 100644 index 0000000..15856db --- /dev/null +++ b/src/main/java/simulator/messaging/MessageHandler.java @@ -0,0 +1,34 @@ +package simulator.messaging; + +import core.VSMessage; + +/** + * Interface for handling message delivery in the simulation. + * Implementations can choose to visualize messages or just deliver them. + */ +public interface MessageHandler { + + /** + * Handle a message that needs to be sent. + * @param message The message to handle + */ + void handleMessage(VSMessage message); + + /** + * Visualize a message being sent (optional operation). + * @param message The message to visualize + */ + void visualizeMessage(VSMessage message); + + /** + * Set the network delay for message delivery. + * @param delay Base delay in milliseconds + */ + void setNetworkDelay(long delay); + + /** + * Set the network delay variability. + * @param variability Variability in milliseconds + */ + void setNetworkVariability(long variability); +}
\ No newline at end of file diff --git a/src/main/java/simulator/messaging/VisualMessageHandler.java b/src/main/java/simulator/messaging/VisualMessageHandler.java new file mode 100644 index 0000000..514a4ee --- /dev/null +++ b/src/main/java/simulator/messaging/VisualMessageHandler.java @@ -0,0 +1,37 @@ +package simulator.messaging; + +import core.VSMessage; +import simulator.VSSimulatorVisualization; + +/** + * Visual implementation of MessageHandler that delegates to the + * existing VSSimulatorVisualization for backward compatibility. + */ +public class VisualMessageHandler implements MessageHandler { + private final VSSimulatorVisualization visualization; + + public VisualMessageHandler(VSSimulatorVisualization visualization) { + this.visualization = visualization; + } + + @Override + public void handleMessage(VSMessage message) { + // Delegate to existing visualization + visualization.sendMessage(message); + } + + @Override + public void visualizeMessage(VSMessage message) { + // Already handled by visualization.sendMessage() + } + + @Override + public void setNetworkDelay(long delay) { + // Handled by visualization preferences + } + + @Override + public void setNetworkVariability(long variability) { + // Handled by visualization preferences + } +}
\ No newline at end of file |
