View Javadoc
1   package nl.tudelft.simulation.dsol.statistics;
2   
3   import java.rmi.RemoteException;
4   
5   import javax.naming.NamingException;
6   
7   import org.djutils.event.Event;
8   import org.djutils.event.EventProducer;
9   import org.djutils.event.EventType;
10  import org.djutils.event.TimedEvent;
11  import org.djutils.event.reference.ReferenceType;
12  import org.djutils.exceptions.Throw;
13  import org.djutils.logger.CategoryLogger;
14  import org.djutils.metadata.MetaData;
15  import org.djutils.metadata.ObjectDescriptor;
16  import org.djutils.stats.summarizers.event.EventBasedTally;
17  
18  import nl.tudelft.simulation.dsol.experiment.Replication;
19  import nl.tudelft.simulation.dsol.model.DsolModel;
20  import nl.tudelft.simulation.dsol.simulators.SimulatorInterface;
21  import nl.tudelft.simulation.naming.context.ContextInterface;
22  import nl.tudelft.simulation.naming.context.util.ContextUtil;
23  
24  /**
25   * The simulator aware Tally extends the djutils event-based tally and links it to the dsol framework.
26   * <p>
27   * Copyright (c) 2002-2024 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
28   * for project information <a href="https://simulation.tudelft.nl/" target="_blank"> https://simulation.tudelft.nl</a>. The DSOL
29   * project is distributed under a three-clause BSD-style license, which can be found at
30   * <a href="https://https://simulation.tudelft.nl/dsol/docs/latest/license.html" target="_blank">
31   * https://https://simulation.tudelft.nl/dsol/docs/latest/license.html</a>.
32   * </p>
33   * @author <a href="https://www.linkedin.com/in/peterhmjacobs">Peter Jacobs </a>
34   * @param <T> the absolute simulation time to use in the warmup event
35   */
36  public class SimTally<T extends Number & Comparable<T>> extends EventBasedTally implements SimulationStatistic<T>
37  {
38      /** */
39      private static final long serialVersionUID = 20140804L;
40  
41      /** the simulator. */
42      private SimulatorInterface<T> simulator = null;
43  
44      /** OBSERVATION_ADDED_EVENT is fired whenever an observation is processed. */
45      public static final EventType TIMED_OBSERVATION_ADDED_EVENT = new EventType(new MetaData("TIMED_OBSERVATION_ADDED_EVENT",
46              "observation added to Tally", new ObjectDescriptor("value", "Observation value", Double.class)));
47  
48      /** INITIALIZED_EVENT is fired whenever a Tally is (re-)initialized. */
49      public static final EventType TIMED_INITIALIZED_EVENT = new EventType(new MetaData("TIMED_INITIALIZED_EVENT",
50              "Tally initialized", new ObjectDescriptor("simTally", "Tally object", SimTally.class)));
51  
52      /**
53       * constructs a new SimTally.
54       * @param description String; refers to the description of this Tally.
55       * @param model DsolModel&lt;T, SimulatorInterface&lt;T&gt;&gt;; the model
56       */
57      public SimTally(final String description, final DsolModel<T, ? extends SimulatorInterface<T>> model)
58      {
59          super(description);
60          Throw.whenNull(model, "model cannot be null");
61          model.getOutputStatistics().add(this);
62          this.simulator = model.getSimulator();
63          try
64          {
65              // only if we are before the warmup time, subscribe to the warmul event
66              if (this.simulator.getSimulatorTime().compareTo(this.simulator.getReplication().getWarmupTime()) < 0)
67              {
68                  this.simulator.addListener(this, Replication.WARMUP_EVENT, ReferenceType.STRONG);
69              }
70              ContextInterface context =
71                      ContextUtil.lookupOrCreateSubContext(this.simulator.getReplication().getContext(), "statistics");
72              context.bindObject(this);
73          }
74          catch (NamingException | RemoteException exception)
75          {
76              this.simulator.getLogger().always().warn(exception, "<init>");
77          }
78      }
79  
80      /**
81       * constructs a new SimTally based on an eventType for which statistics are sampled.
82       * @param description String; the description of this tally.
83       * @param model DsolModel&lt;T, SimulatorInterface&lt;T&gt;&gt;; the model
84       * @param target EventProducer; the target on which to subscribe
85       * @param eventType EventType; the eventType for which statistics are sampled
86       */
87      public SimTally(final String description, final DsolModel<T, ? extends SimulatorInterface<T>> model,
88              final EventProducer target, final EventType eventType)
89      {
90          this(description, model);
91          try
92          {
93              target.addListener(this, eventType, ReferenceType.STRONG);
94          }
95          catch (RemoteException exception)
96          {
97              this.simulator.getLogger().always().warn(exception, "<init>");
98          }
99      }
100 
101     /** {@inheritDoc} */
102     @Override
103     public void initialize()
104     {
105         super.initialize();
106         // note that when initialize() is called from the (super) constructor, there cannot be listeners yet
107         if (this.simulator != null)
108         {
109             try
110             {
111                 fireTimedEvent(TIMED_INITIALIZED_EVENT, this, this.simulator.getSimulatorTime());
112             }
113             catch (RemoteException exception)
114             {
115                 this.simulator.getLogger().always().warn(exception, "initialize()");
116             }
117         }
118     }
119 
120     /** {@inheritDoc} */
121     @Override
122     @SuppressWarnings({"checkstyle:designforextension", "unchecked"})
123     public void notify(final Event event)
124     {
125         if (event.getType().equals(Replication.WARMUP_EVENT))
126         {
127             try
128             {
129                 this.simulator.removeListener(this, Replication.WARMUP_EVENT);
130             }
131             catch (RemoteException exception)
132             {
133                 CategoryLogger.always().warn(exception);
134             }
135             initialize();
136             return;
137         }
138         else if (event instanceof TimedEvent<?>)
139         {
140             TimedEvent<?> timedEvent = (TimedEvent<?>) event;
141             if (timedEvent.getTimeStamp() instanceof Number)
142             {
143                 // Tally can handle Number (and therefore also Time and Duration) and Calendar but not SimTime
144                 super.notify(new TimedEvent<T>(timedEvent.getType(), timedEvent.getContent(), ((T) timedEvent.getTimeStamp())));
145             }
146             else
147             {
148                 super.notify(event);
149             }
150         }
151         else
152         {
153             this.simulator.getLogger().always().warn("SimPersistent: event not a TimedEvent");
154         }
155     }
156 
157     /** {@inheritDoc} */
158     @Override
159     public double register(final double value)
160     {
161         super.register(value);
162         try
163         {
164             fireTimedEvent(TIMED_OBSERVATION_ADDED_EVENT, value, this.simulator.getSimulatorTime());
165         }
166         catch (RemoteException exception)
167         {
168             this.simulator.getLogger().always().warn(exception, "register()");
169         }
170         return value;
171     }
172 
173     /** {@inheritDoc} */
174     @Override
175     public SimulatorInterface<T> getSimulator()
176     {
177         return this.simulator;
178     }
179 
180 }