1 package nl.tudelft.simulation.dsol.swing.gui.control;
2
3 import java.awt.event.ActionEvent;
4 import java.rmi.RemoteException;
5
6 import javax.swing.JButton;
7 import javax.swing.SwingUtilities;
8
9 import org.djunits.value.vdouble.scalar.Duration;
10 import org.djunits.value.vfloat.scalar.FloatDuration;
11
12 import nl.tudelft.simulation.dsol.SimRuntimeException;
13 import nl.tudelft.simulation.dsol.formalisms.eventscheduling.SimEvent;
14 import nl.tudelft.simulation.dsol.model.DSOLModel;
15 import nl.tudelft.simulation.dsol.simulators.DevsSimulatorInterface;
16 import nl.tudelft.simulation.dsol.simulators.RunState;
17
18 /**
19 * ControlPanel container for the a DEVS simulator, with clocks for different time units.
20 * <p>
21 * Copyright (c) 2020-2023 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
22 * for project information <a href="https://simulation.tudelft.nl/dsol/manual/" target="_blank">DSOL Manual</a>. The DSOL
23 * project is distributed under a three-clause BSD-style license, which can be found at
24 * <a href="https://https://simulation.tudelft.nl/dsol/docs/latest/license.html" target="_blank">DSOL License</a>.
25 * </p>
26 * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
27 * @param <T> the extended type itself to be able to implement a comparator on the simulation time.
28 * @param <S> the simulator type to use
29 */
30 public class DEVSControlPanel<T extends Number & Comparable<T>, S extends DevsSimulatorInterface<T>>
31 extends AbstractControlPanel<T, S>
32 {
33 /** */
34 private static final long serialVersionUID = 20201227L;
35
36 /** The currently registered stop at event. */
37 private SimEvent<T> stopAtEvent = null;
38
39 /**
40 * Generic control panel with a different set of control buttons. The control panel assumes a DEVSSimulator that can be
41 * paused, but it does not assume animation.
42 * @param model DSOLModel<T, ? extends DEVSSimulationInterface<T>>; the model for the control panel, to allow a
43 * reset of the model
44 * @param simulator S; the simulator. Specified separately, because the model can have been specified with a superclass of
45 * the simulator that the ControlPanel actually needs (e.g., model has been specified with a DEVSAnimator,
46 * whereas the panel needs a RealTimeControlAnimator)
47 * @throws RemoteException when simulator cannot be accessed for listener attachment
48 */
49 public DEVSControlPanel(final DSOLModel<T, ? extends DevsSimulatorInterface<T>> model, final S simulator)
50 throws RemoteException
51 {
52 super(model, simulator);
53
54 // add the buttons to step the simulation
55 getControlButtonsPanel().add(makeButton("stepButton", "/resources/Step.png", "Step", "Execute one event", true));
56 getControlButtonsPanel().add(makeButton("nextTimeButton", "/resources/StepTime.png", "NextTime",
57 "Execute all events scheduled for the current time", true));
58 }
59
60 /** {@inheritDoc} */
61 @Override
62 public void actionPerformed(final ActionEvent actionEvent)
63 {
64 String actionCommand = actionEvent.getActionCommand();
65 try
66 {
67 if (actionCommand.equals("Step"))
68 {
69 if (getSimulator().isStartingOrRunning())
70 {
71 getSimulator().stop();
72 }
73 getSimulator().step();
74 }
75 if (actionCommand.equals("NextTime"))
76 {
77 if (getSimulator().isStartingOrRunning())
78 {
79 getSimulator().stop();
80 }
81 T now = getSimulator().getSimulatorTime();
82 try
83 {
84 this.stopAtEvent =
85 new SimEvent<T>(now, SimEvent.MIN_PRIORITY, this,"autoPauseSimulator", null);
86 getSimulator().scheduleEvent(this.stopAtEvent);
87 }
88 catch (SimRuntimeException exception)
89 {
90 getSimulator().getLogger().always()
91 .error("Caught an exception while trying to schedule an autoPauseSimulator event "
92 + "at the current simulator time");
93 }
94 getSimulator().start();
95 }
96 }
97 catch (Exception exception)
98 {
99 exception.printStackTrace();
100 }
101 super.actionPerformed(actionEvent); // includes fixButtons()
102 }
103
104 /** {@inheritDoc} */
105 @Override
106 protected void fixButtons()
107 {
108 final boolean moreWorkToDo = getSimulator().getRunState() != RunState.ENDED;
109 for (JButton button : getControlButtons())
110 {
111 final String actionCommand = button.getActionCommand();
112 if (actionCommand.equals("Step"))
113 {
114 button.setEnabled(moreWorkToDo && isControlButtonsEnabled());
115 }
116 else if (actionCommand.equals("NextTime"))
117 {
118 button.setEnabled(moreWorkToDo && isControlButtonsEnabled());
119 }
120 }
121 super.fixButtons(); // handles the start/stop button
122 }
123
124 /** {@inheritDoc} */
125 @Override
126 protected void invalidateButtons()
127 {
128 for (JButton button : getControlButtons())
129 {
130 final String actionCommand = button.getActionCommand();
131 if (actionCommand.equals("Step"))
132 {
133 button.setEnabled(false);
134 }
135 else if (actionCommand.equals("NextTime"))
136 {
137 button.setEnabled(false);
138 }
139 }
140 super.invalidateButtons(); // handles the start/stop button
141 }
142
143 /**
144 * Pause the simulator.
145 */
146 public void autoPauseSimulator()
147 {
148 if (getSimulator().isStartingOrRunning())
149 {
150 try
151 {
152 getSimulator().stop();
153 }
154 catch (SimRuntimeException exception1)
155 {
156 exception1.printStackTrace();
157 }
158 T currentTick = getSimulator().getSimulatorTime();
159 T nextTick = getSimulator().getEventList().first().getAbsoluteExecutionTime();
160 if (nextTick.compareTo(currentTick) > 0)
161 {
162 // The clock is now just beyond where it was when the user requested the NextTime operation
163 // Insert another autoPauseSimulator event just before what is now the time of the next event
164 // and let the simulator time increment to that time
165 try
166 {
167 this.stopAtEvent =
168 new SimEvent<T>(nextTick, SimEvent.MAX_PRIORITY, this,"autoPauseSimulator", null);
169 getSimulator().scheduleEvent(this.stopAtEvent);
170 getSimulator().start();
171 }
172 catch (SimRuntimeException exception)
173 {
174 getSimulator().getLogger().always()
175 .error("Caught an exception while trying to re-schedule an autoPauseEvent at the next real event");
176 }
177 }
178 else
179 {
180 if (SwingUtilities.isEventDispatchThread())
181 {
182 fixButtons();
183 }
184 else
185 {
186 try
187 {
188 SwingUtilities.invokeAndWait(new Runnable()
189 {
190 @Override
191 public void run()
192 {
193 fixButtons();
194 }
195 });
196 }
197 catch (Exception e)
198 {
199 if (e instanceof InterruptedException)
200 {
201 System.out.println("Caught " + e);
202 }
203 else
204 {
205 e.printStackTrace();
206 }
207 }
208 }
209 }
210 }
211 }
212
213 /**
214 * DEVS ControlPanel for a Double timeunit.
215 * <p>
216 * Copyright (c) 2020-2023 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved.
217 * See for project information <a href="https://simulation.tudelft.nl/dsol/manual/" target="_blank">DSOL Manual</a>. The
218 * DSOL project is distributed under a three-clause BSD-style license, which can be found at
219 * <a href="https://https://simulation.tudelft.nl/dsol/docs/latest/license.html" target="_blank">DSOL License</a>.
220 * </p>
221 * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
222 */
223 public static class TimeDouble extends DEVSControlPanel<Double, DevsSimulatorInterface<Double>>
224 {
225 /** */
226 private static final long serialVersionUID = 20201227L;
227
228 /**
229 * Construct a DEVS control panel for a Double time unit, with a different set of control buttons. The control panel
230 * assumes a DEVSSimulator, but not animation.
231 * @param model DSOLModel<Double>; the model for the control panel, to allow a reset of the model
232 * @param simulator DEVSSimulatorInterface<Double>; the simulator. Specified separately, because the model can have been
233 * specified with a superclass of the simulator that the ControlPanel actually needs (e.g., model has been
234 * specified with a DEVSAnimator, whereas the panel needs a RealTimeControlAnimator)
235 * @throws RemoteException when simulator cannot be accessed for listener attachment
236 */
237 public TimeDouble(final DSOLModel<Double, ? extends DevsSimulatorInterface<Double>> model,
238 final DevsSimulatorInterface<Double> simulator) throws RemoteException
239 {
240 super(model, simulator);
241 setClockPanel(new ClockPanel.TimeDouble(getSimulator()));
242 setSpeedPanel(new SpeedPanel.TimeDouble(getSimulator()));
243 setRunUntilPanel(new RunUntilPanel.TimeDouble(getSimulator()));
244 }
245 }
246
247 /**
248 * DEVS ControlPanel for a Float timeunit.
249 * <p>
250 * Copyright (c) 2020-2023 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved.
251 * See for project information <a href="https://simulation.tudelft.nl/dsol/manual/" target="_blank">DSOL Manual</a>. The
252 * DSOL project is distributed under a three-clause BSD-style license, which can be found at
253 * <a href="https://https://simulation.tudelft.nl/dsol/docs/latest/license.html" target="_blank">DSOL License</a>.
254 * </p>
255 * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
256 */
257 public static class TimeFloat extends DEVSControlPanel<Float, DevsSimulatorInterface<Float>>
258 {
259 /** */
260 private static final long serialVersionUID = 20201227L;
261
262 /**
263 * Construct a DEVS control panel for a Float time unit, with a different set of control buttons. The control panel
264 * assumes a DEVSSimulator, but not animation.
265 * @param model DSOLModel<Float>; the model for the control panel, to allow a reset of the model
266 * @param simulator DEVSSimulatorInterface<Float>; the simulator. Specified separately, because the model can have been
267 * specified with a superclass of the simulator that the ControlPanel actually needs (e.g., model has been
268 * specified with a DEVSAnimator, whereas the panel needs a RealTimeControlAnimator)
269 * @throws RemoteException when simulator cannot be accessed for listener attachment
270 */
271 public TimeFloat(final DSOLModel<Float, ? extends DevsSimulatorInterface<Float>> model,
272 final DevsSimulatorInterface<Float> simulator) throws RemoteException
273 {
274 super(model, simulator);
275 setClockPanel(new ClockPanel.TimeFloat(getSimulator()));
276 setSpeedPanel(new SpeedPanel.TimeFloat(getSimulator()));
277 setRunUntilPanel(new RunUntilPanel.TimeFloat(getSimulator()));
278 }
279 }
280
281 /**
282 * DEVS ControlPanel for a Long timeunit.
283 * <p>
284 * Copyright (c) 2020-2023 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved.
285 * See for project information <a href="https://simulation.tudelft.nl/dsol/manual/" target="_blank">DSOL Manual</a>. The
286 * DSOL project is distributed under a three-clause BSD-style license, which can be found at
287 * <a href="https://https://simulation.tudelft.nl/dsol/docs/latest/license.html" target="_blank">DSOL License</a>.
288 * </p>
289 * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
290 */
291 public static class TimeLong extends DEVSControlPanel<Long, DevsSimulatorInterface<Long>>
292 {
293 /** */
294 private static final long serialVersionUID = 20201227L;
295
296 /**
297 * Construct a DEVS control panel for a Long time unit, with a different set of control buttons. The control panel
298 * assumes a DEVSSimulator, but not animation.
299 * @param model DSOLModel<Long>; the model for the control panel, to allow a reset of the model
300 * @param simulator DEVSSimulatorInterface<Long>; the simulator. Specified separately, because the model can have been
301 * specified with a superclass of the simulator that the ControlPanel actually needs (e.g., model has been
302 * specified with a DEVSAnimator, whereas the panel needs a RealTimeControlAnimator)
303 * @throws RemoteException when simulator cannot be accessed for listener attachment
304 */
305 public TimeLong(final DSOLModel<Long, ? extends DevsSimulatorInterface<Long>> model,
306 final DevsSimulatorInterface<Long> simulator) throws RemoteException
307 {
308 super(model, simulator);
309 setClockPanel(new ClockPanel.TimeLong(getSimulator()));
310 setSpeedPanel(new SpeedPanel.TimeLong(getSimulator()));
311 setRunUntilPanel(new RunUntilPanel.TimeLong(getSimulator()));
312 }
313 }
314
315 /**
316 * DEVS ControlPanel for a djunits double timeunit.
317 * <p>
318 * Copyright (c) 2020-2023 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved.
319 * See for project information <a href="https://simulation.tudelft.nl/dsol/manual/" target="_blank">DSOL Manual</a>. The
320 * DSOL project is distributed under a three-clause BSD-style license, which can be found at
321 * <a href="https://https://simulation.tudelft.nl/dsol/docs/latest/license.html" target="_blank">DSOL License</a>.
322 * </p>
323 * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
324 */
325 public static class TimeDoubleUnit extends DEVSControlPanel<Duration, DevsSimulatorInterface<Duration>>
326 {
327 /** */
328 private static final long serialVersionUID = 20201227L;
329
330 /**
331 * Construct a DEVS control panel for a djunits double time unit, with a different set of control buttons. The control
332 * panel assumes a DEVSSimulator, but not animation.
333 * @param model DSOLModel<Duration>; the model for the control panel, to allow a reset of the model
334 * @param simulator DEVSSimulatorInterface<Duration>; the simulator. Specified separately, because the model can have
335 * been specified with a superclass of the simulator that the ControlPanel actually needs (e.g., model has
336 * been specified with a DEVSAnimator, whereas the panel needs a RealTimeControlAnimator)
337 * @throws RemoteException when simulator cannot be accessed for listener attachment
338 */
339 public TimeDoubleUnit(final DSOLModel<Duration, ? extends DevsSimulatorInterface<Duration>> model,
340 final DevsSimulatorInterface<Duration> simulator) throws RemoteException
341 {
342 super(model, simulator);
343 setClockPanel(new ClockPanel.TimeDoubleUnit(getSimulator()));
344 setSpeedPanel(new SpeedPanel.TimeDoubleUnit(getSimulator()));
345 setRunUntilPanel(new RunUntilPanel.TimeDoubleUnit(getSimulator()));
346 }
347 }
348
349 /**
350 * DEVS ControlPanel for a djunits float timeunit.
351 * <p>
352 * Copyright (c) 2020-2023 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved.
353 * See for project information <a href="https://simulation.tudelft.nl/dsol/manual/" target="_blank">DSOL Manual</a>. The
354 * DSOL project is distributed under a three-clause BSD-style license, which can be found at
355 * <a href="https://https://simulation.tudelft.nl/dsol/docs/latest/license.html" target="_blank">DSOL License</a>.
356 * </p>
357 * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
358 */
359 public static class TimeFloatUnit extends DEVSControlPanel<FloatDuration, DevsSimulatorInterface<FloatDuration>>
360 {
361 /** */
362 private static final long serialVersionUID = 20201227L;
363
364 /**
365 * Construct a DEVS control panel for a djunits float time unit, with a different set of control buttons. The control
366 * panel assumes a DEVSSimulator, but not animation.
367 * @param model DSOLModel<FloatDuration>; the model for the control panel, to allow a reset of the model
368 * @param simulator DEVSSimulatorInterface<FloatDuration>; the simulator. Specified separately, because the model can
369 * have been specified with a superclass of the simulator that the ControlPanel actually needs (e.g., model
370 * has been specified with a DEVSAnimator, whereas the panel needs a RealTimeControlAnimator)
371 * @throws RemoteException when simulator cannot be accessed for listener attachment
372 */
373 public TimeFloatUnit(final DSOLModel<FloatDuration, ? extends DevsSimulatorInterface<FloatDuration>> model,
374 final DevsSimulatorInterface<FloatDuration> simulator) throws RemoteException
375 {
376 super(model, simulator);
377 setClockPanel(new ClockPanel.TimeFloatUnit(getSimulator()));
378 setSpeedPanel(new SpeedPanel.TimeFloatUnit(getSimulator()));
379 setRunUntilPanel(new RunUntilPanel.TimeFloatUnit(getSimulator()));
380 }
381 }
382
383 }