View Javadoc
1   package org.djutils.stats.summarizers.event;
2   
3   import java.rmi.RemoteException;
4   
5   import org.djutils.event.Event;
6   import org.djutils.event.EventListener;
7   import org.djutils.event.EventListenerMap;
8   import org.djutils.event.EventProducer;
9   import org.djutils.event.LocalEventProducer;
10  import org.djutils.exceptions.Throw;
11  import org.djutils.stats.summarizers.Tally;
12  import org.djutils.stats.summarizers.quantileaccumulator.NoStorageAccumulator;
13  import org.djutils.stats.summarizers.quantileaccumulator.QuantileAccumulator;
14  
15  /**
16   * The EventBasedTally class registers a series of values and provides mean, standard deviation, etc. of the registered values.
17   * It embeds an EventProducer so it can keep listeners informed about new observations, and it listens to external events to be
18   * able to receive observations, in addition to the register(...) method.
19   * <p>
20   * Copyright (c) 2002-2024 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
21   * for project information <a href="https://simulation.tudelft.nl/" target="_blank"> https://simulation.tudelft.nl</a>. The DSOL
22   * project is distributed under a three-clause BSD-style license, which can be found at
23   * <a href="https://simulation.tudelft.nl/dsol/3.0/license.html" target="_blank">
24   * https://simulation.tudelft.nl/dsol/3.0/license.html</a>. <br>
25   * @author <a href="https://www.tudelft.nl/averbraeck" target="_blank"> Alexander Verbraeck</a>
26   * @author <a href="https://www.linkedin.com/in/peterhmjacobs">Peter Jacobs </a>
27   * @author <a href="https://www.tudelft.nl/staff/p.knoppers/">Peter Knoppers</a>
28   */
29  public class EventBasedTally extends Tally implements EventProducer, EventListener
30  {
31      /** */
32      private static final long serialVersionUID = 20200228L;
33  
34      /** The embedded EventProducer. */
35      private EventProducer eventProducer = null;
36  
37      /**
38       * Constructs a new EventBasedTally.
39       * @param description String; the description of this tally
40       * @param quantileAccumulator QuantileAccumulator; the input series accumulator that can approximate or compute quantiles.
41       */
42      public EventBasedTally(final String description, final QuantileAccumulator quantileAccumulator)
43      {
44          this(description, quantileAccumulator, new LocalEventProducer());
45      }
46  
47      /**
48       * Convenience constructor that uses a NoStorageAccumulator to estimate quantiles.
49       * @param description String; the description of this tally
50       */
51      public EventBasedTally(final String description)
52      {
53          this(description, new NoStorageAccumulator());
54      }
55  
56      /**
57       * Construct a new EventBasedCounter with a specific EventProducer, e.g. a remote one. The Tally uses uses a
58       * NoStorageAccumulator to estimate quantiles.
59       * @param description String; the description for this counter
60       * @param eventProducer EventProducer; the EventProducer to embed and use in this statistic
61       */
62      public EventBasedTally(final String description, final EventProducer eventProducer)
63      {
64          this(description, new NoStorageAccumulator(), eventProducer);
65      }
66  
67      /**
68       * Construct a new EventBasedCounter with a specific EventProducer, e.g. a remote one.
69       * @param description String; the description for this counter
70       * @param quantileAccumulator QuantileAccumulator; the input series accumulator that can approximate or compute quantiles.
71       * @param eventProducer EventProducer; the EventProducer to embed and use in this statistic
72       */
73      public EventBasedTally(final String description, final QuantileAccumulator quantileAccumulator,
74              final EventProducer eventProducer)
75      {
76          super(description, quantileAccumulator);
77          Throw.whenNull(eventProducer, "eventProducer cannot be null");
78          this.eventProducer = eventProducer;
79      }
80  
81      /** {@inheritDoc} */
82      @Override
83      public EventListenerMap getEventListenerMap() throws RemoteException
84      {
85          return this.eventProducer.getEventListenerMap();
86      }
87  
88      /** {@inheritDoc} */
89      @Override
90      public void initialize()
91      {
92          super.initialize();
93          if (this.eventProducer != null)
94          {
95              try
96              {
97                  this.eventProducer.fireEvent(StatisticsEvents.INITIALIZED_EVENT);
98              }
99              catch (RemoteException exception)
100             {
101                 throw new RuntimeException(exception);
102             }
103         }
104     }
105 
106     /** {@inheritDoc} */
107     @Override
108     @SuppressWarnings("checkstyle:designforextension")
109     public void notify(final Event event)
110     {
111         if (!(event.getContent() instanceof Number))
112         {
113             throw new IllegalArgumentException("Tally does not accept " + event);
114         }
115         double value = ((Number) event.getContent()).doubleValue();
116         register(value);
117     }
118 
119     /** {@inheritDoc} */
120     @Override
121     public double register(final double value)
122     {
123         super.register(value);
124         try
125         {
126             if (hasListeners())
127             {
128                 this.eventProducer.fireEvent(StatisticsEvents.OBSERVATION_ADDED_EVENT, value);
129                 fireEvents();
130             }
131         }
132         catch (RemoteException exception)
133         {
134             throw new RuntimeException(exception);
135         }
136         return value;
137     }
138 
139     /**
140      * Method that can be overridden to fire own events or additional events when registering an observation.
141      * @throws RemoteException on network error
142      */
143     protected void fireEvents() throws RemoteException
144     {
145         this.eventProducer.fireEvent(StatisticsEvents.N_EVENT, getN());
146         this.eventProducer.fireEvent(StatisticsEvents.MIN_EVENT, getMin());
147         this.eventProducer.fireEvent(StatisticsEvents.MAX_EVENT, getMax());
148         this.eventProducer.fireEvent(StatisticsEvents.POPULATION_MEAN_EVENT, getPopulationMean());
149         this.eventProducer.fireEvent(StatisticsEvents.POPULATION_VARIANCE_EVENT, getPopulationVariance());
150         this.eventProducer.fireEvent(StatisticsEvents.POPULATION_SKEWNESS_EVENT, getPopulationSkewness());
151         this.eventProducer.fireEvent(StatisticsEvents.POPULATION_KURTOSIS_EVENT, getPopulationKurtosis());
152         this.eventProducer.fireEvent(StatisticsEvents.POPULATION_EXCESS_KURTOSIS_EVENT, getPopulationExcessKurtosis());
153         this.eventProducer.fireEvent(StatisticsEvents.POPULATION_STDEV_EVENT, getPopulationStDev());
154         this.eventProducer.fireEvent(StatisticsEvents.SUM_EVENT, getSum());
155         this.eventProducer.fireEvent(StatisticsEvents.SAMPLE_MEAN_EVENT, getSampleMean());
156         this.eventProducer.fireEvent(StatisticsEvents.SAMPLE_VARIANCE_EVENT, getSampleVariance());
157         this.eventProducer.fireEvent(StatisticsEvents.SAMPLE_SKEWNESS_EVENT, getSampleSkewness());
158         this.eventProducer.fireEvent(StatisticsEvents.SAMPLE_KURTOSIS_EVENT, getSampleKurtosis());
159         this.eventProducer.fireEvent(StatisticsEvents.SAMPLE_EXCESS_KURTOSIS_EVENT, getSampleExcessKurtosis());
160         this.eventProducer.fireEvent(StatisticsEvents.SAMPLE_STDEV_EVENT, getSampleStDev());
161     }
162 
163     /** {@inheritDoc} */
164     @Override
165     @SuppressWarnings("checkstyle:designforextension")
166     public String toString()
167     {
168         return "EventBasedTally" + super.toString().substring(5);
169     }
170 
171 }