View Javadoc
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&lt;T, ? extends DEVSSimulationInterface&lt;T&gt;&gt;; 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&lt;Double&gt;; the model for the control panel, to allow a reset of the model
232          * @param simulator DEVSSimulatorInterface&lt;Double&gt;; 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&lt;Float&gt;; the model for the control panel, to allow a reset of the model
266          * @param simulator DEVSSimulatorInterface&lt;Float&gt;; 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&lt;Long&gt;; the model for the control panel, to allow a reset of the model
300          * @param simulator DEVSSimulatorInterface&lt;Long&gt;; 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&lt;Duration&gt;; the model for the control panel, to allow a reset of the model
334          * @param simulator DEVSSimulatorInterface&lt;Duration&gt;; 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&lt;FloatDuration&gt;; the model for the control panel, to allow a reset of the model
368          * @param simulator DEVSSimulatorInterface&lt;FloatDuration&gt;; 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 }