View Javadoc
1   package org.sim0mq.message.federationmanager;
2   
3   import java.util.HashMap;
4   import java.util.Map;
5   
6   import org.djunits.value.vdouble.scalar.Duration;
7   import org.djunits.value.vdouble.scalar.Time;
8   import org.djunits.value.vfloat.scalar.FloatDuration;
9   import org.djunits.value.vfloat.scalar.FloatTime;
10  import org.djutils.exceptions.Throw;
11  import org.djutils.serialization.SerializationException;
12  import org.sim0mq.Sim0MQException;
13  import org.sim0mq.message.MessageStatus;
14  import org.sim0mq.message.Sim0MQMessage;
15  import org.sim0mq.message.SimulationMessage;
16  import org.sim0mq.message.types.NumberDuration;
17  import org.sim0mq.message.types.NumberTime;
18  
19  /**
20   * SimRunControlMessage, FM.2. Message sent by the Federation Manager to the Model to initialize a simulation.
21   * <p>
22   * Copyright (c) 2016-2017 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
23   * BSD-style license. See <a href="http://sim0mq.org/docs/current/license.html">Sim0MQ License</a>.
24   * </p>
25   * $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
26   * initial version Apr 22, 2017 <br>
27   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
28   */
29  public class SimRunControlMessage extends Sim0MQMessage
30  {
31      /**
32       * Duration of the run of a single replication, including the warmup time, if present. The type is any numeric type (1-5) or
33       * Float or Double with Unit (25, 26) of type Duration (25).
34       */
35      private final NumberDuration runTime;
36  
37      /**
38       * Warmup time of the model in time units that the model uses. The type is any numeric type (1-5) or Float or Double with
39       * Unit (25, 26) of type Duration (25).
40       */
41      private final NumberDuration warmupTime;
42  
43      /**
44       * Offset of the time (e.g., a model time of 0 is the year 2016, or 1-1-2015). The type is any numeric type (1-5) or Float
45       * or Double with Unit (25, 26) of type Time (26).
46       */
47      private final NumberTime offsetTime;
48  
49      /** Speed as the number of times real-time the model should run; Double.INFINITY means as fast as possible. */
50      private final double speed;
51  
52      /** Number of replications for stochastic uncertainties in the model. */
53      private final int numberReplications;
54  
55      /** Number of random streams that follow. */
56      private final int numberRandomStreams;
57  
58      /** Random streams and seeds. */
59      private final Map<Object, Long> streamMap = new HashMap<>();
60  
61      /** the unique message id. */
62      private static final String MESSAGETYPE = "FM.2";
63  
64      /** */
65      private static final long serialVersionUID = 20170424L;
66  
67      /**
68       * @param simulationRunId the Simulation run ids can be provided in different types. Examples are two 64-bit longs
69       *            indicating a UUID, or a String with a UUID number, a String with meaningful identification, or a short or an
70       *            int with a simulation run number.
71       * @param senderId The sender id can be used to send back a message to the sender at some later time.
72       * @param receiverId The receiver id can be used to check whether the message is meant for us, or should be discarded (or an
73       *            error can be sent if we receive a message not meant for us).
74       * @param messageId The unique message number is meant to confirm with a callback that the message has been received
75       *            correctly. The number is unique for the sender, so not globally within the federation.
76       * @param runTime Duration of the run of a single replication, including the warmup time, if present. The type is any
77       *            numeric type (1-5) or Float or Double with Unit (25, 26) of type Duration (25).
78       * @param warmupTime Warmup time of the model in time units that the model uses. The type is any numeric type (1-5) or Float
79       *            or Double with Unit (25, 26) of type Duration (25).
80       * @param offsetTime Offset of the time (e.g., a model time of 0 is the year 2016, or 1-1-2015). The type is any numeric
81       *            type (1-5) or Float or Double with Unit (25, 26) of type Time (26).
82       * @param speed Speed as the number of times real-time the model should run; Double.INFINITY means as fast as possible.
83       * @param numberReplications Number of replications for stochastic uncertainties in the model.
84       * @param numberRandomStreams Number of random streams that follow.
85       * @param streamMap Random streams and seeds.
86       * @throws Sim0MQException on unknown data type
87       * @throws NullPointerException when one of the parameters is null
88       */
89      @SuppressWarnings("checkstyle:parameternumber")
90      public SimRunControlMessage(final Object simulationRunId, final Object senderId, final Object receiverId,
91              final long messageId, final Object runTime, final Object warmupTime, final Object offsetTime, final double speed,
92              final int numberReplications, final int numberRandomStreams, final Map<Object, Long> streamMap)
93              throws Sim0MQException, NullPointerException
94      {
95          super(simulationRunId, senderId, receiverId, MESSAGETYPE, messageId, MessageStatus.NEW);
96          Throw.whenNull(runTime, "runTime cannot be null");
97          Throw.whenNull(warmupTime, "warmupTime cannot be null");
98          Throw.whenNull(offsetTime, "offsetTime cannot be null");
99          Throw.whenNull(streamMap, "streamMap cannot be null");
100 
101         if (runTime instanceof Number)
102         {
103             this.runTime = new NumberDuration((Number) runTime);
104         }
105         else if (runTime instanceof Duration)
106         {
107             this.runTime = new NumberDuration((Duration) runTime);
108         }
109         else if (runTime instanceof FloatDuration)
110         {
111             this.runTime = new NumberDuration((FloatDuration) runTime);
112         }
113         else
114         {
115             throw new Sim0MQException("runTime should be Number, Duration or FloatDuration");
116         }
117 
118         if (warmupTime instanceof Number)
119         {
120             this.warmupTime = new NumberDuration((Number) warmupTime);
121         }
122         else if (warmupTime instanceof Duration)
123         {
124             this.warmupTime = new NumberDuration((Duration) warmupTime);
125         }
126         else if (warmupTime instanceof FloatDuration)
127         {
128             this.warmupTime = new NumberDuration((FloatDuration) warmupTime);
129         }
130         else
131         {
132             throw new Sim0MQException("warmupTime should be Number, Duration or FloatDuration");
133         }
134 
135         if (offsetTime instanceof Number)
136         {
137             this.offsetTime = new NumberTime((Number) offsetTime);
138         }
139         else if (offsetTime instanceof Time)
140         {
141             this.offsetTime = new NumberTime((Time) offsetTime);
142         }
143         else if (offsetTime instanceof FloatTime)
144         {
145             this.offsetTime = new NumberTime((FloatTime) offsetTime);
146         }
147         else
148         {
149             throw new Sim0MQException("offsetTime should be Number, Time or FloatTime");
150         }
151 
152         this.speed = speed;
153 
154         Throw.when(numberReplications <= 0, Sim0MQException.class, "numberReplications should be > 0");
155         this.numberReplications = numberReplications;
156 
157         Throw.when(numberRandomStreams < 0, Sim0MQException.class, "numberRandomStreams should be >= 0");
158         Throw.when(numberRandomStreams != streamMap.size(), Sim0MQException.class,
159                 "numberRandomStreams as given and in map are different");
160         this.numberRandomStreams = numberRandomStreams;
161         this.streamMap.putAll(streamMap);
162     }
163 
164     /**
165      * @return runTime
166      */
167     public final NumberDuration getRunTime()
168     {
169         return this.runTime;
170     }
171 
172     /**
173      * @return warmupTime
174      */
175     public final NumberDuration getWarmupTime()
176     {
177         return this.warmupTime;
178     }
179 
180     /**
181      * @return offsetTime
182      */
183     public final NumberTime getOffsetTime()
184     {
185         return this.offsetTime;
186     }
187 
188     /**
189      * @return speed
190      */
191     public final double getSpeed()
192     {
193         return this.speed;
194     }
195 
196     /**
197      * @return numberReplications
198      */
199     public final int getNumberReplications()
200     {
201         return this.numberReplications;
202     }
203 
204     /**
205      * @return numberRandomStreams
206      */
207     public final int getNumberRandomStreams()
208     {
209         return this.numberRandomStreams;
210     }
211 
212     /**
213      * @return streamMap
214      */
215     public final Map<Object, Long> getStreamMap()
216     {
217         return this.streamMap;
218     }
219 
220     /**
221      * @return messagetype
222      */
223     public static final String getMessageType()
224     {
225         return MESSAGETYPE;
226     }
227 
228     /** {@inheritDoc} */
229     @Override
230     public Object[] createObjectArray()
231     {
232         return new Object[] { getSimulationRunId(), getSenderId(), getReceiverId(), getMessageTypeId(), getMessageId(),
233                 getMessageStatus(), this.runTime, this.warmupTime, this.offsetTime, this.speed, this.numberReplications,
234                 this.numberRandomStreams, this.streamMap };
235     }
236 
237     /** {@inheritDoc} */
238     @Override
239     public byte[] createByteArray() throws Sim0MQException, SerializationException
240     {
241         return SimulationMessage.encodeUTF8(getSimulationRunId(), getSenderId(), getReceiverId(), getMessageTypeId(),
242                 getMessageId(), getMessageStatus(), this.runTime, this.warmupTime, this.offsetTime, this.speed,
243                 this.numberReplications, this.numberRandomStreams, this.streamMap);
244     }
245 
246     /**
247      * Build a message from an Object[] that was received.
248      * @param fields Object[]; the fields in the message
249      * @param intendedReceiverId id of the intended receiver
250      * @return a Sim0MQ message
251      * @throws Sim0MQException when number of fields is not correct
252      */
253     public static SimRunControlMessage createMessage(final Object[] fields, final Object intendedReceiverId)
254             throws Sim0MQException
255     {
256         Map<Object, Long> streams = new HashMap<>();
257         int numberStreams = ((Integer) fields[13]).intValue();
258         check(fields, 6 + 2 * numberStreams, MESSAGETYPE, intendedReceiverId);
259         for (int i = 14; i < 14 + 2 * numberStreams; i += 2)
260         {
261             Object streamId = fields[i];
262             Long seed = ((Long) fields[i + 1]).longValue();
263             streams.put(streamId, seed);
264         }
265         return new SimRunControlMessage(fields[1], fields[2], fields[3], ((Long) fields[5]).longValue(), fields[8], fields[9],
266                 fields[10], ((Double) fields[11]).doubleValue(), ((Integer) fields[12]).intValue(), numberStreams, streams);
267     }
268 
269     /**
270      * Builder for the SimRunControl Message. Can string setters together, and call build() at the end to build the actual
271      * message.
272      * <p>
273      * Copyright (c) 2016-2017 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
274      * <br>
275      * BSD-style license. See <a href="http://sim0mq.org/docs/current/license.html">Sim0MQ License</a>.
276      * </p>
277      * $LastChangedDate: 2015-07-24 02:58:59 +0200 (Fri, 24 Jul 2015) $, @version $Revision: 1147 $, by $Author: averbraeck $,
278      * initial version Apr 22, 2017 <br>
279      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
280      */
281     public static class Builder extends Sim0MQMessage.Builder<SimRunControlMessage.Builder>
282     {
283         /**
284          * Duration of the run of a single replication, including the warmup time, if present. The type is any numeric type
285          * (1-5) or Float or Double with Unit (25, 26) of type Duration (25).
286          */
287         private NumberDuration runTime;
288 
289         /**
290          * Warmup time of the model in time units that the model uses. The type is any numeric type (1-5) or Float or Double
291          * with Unit (25, 26) of type Duration (25).
292          */
293         private NumberDuration warmupTime;
294 
295         /**
296          * Offset of the time (e.g., a model time of 0 is the year 2016, or 1-1-2015). The type is any numeric type (1-5) or
297          * Float or Double with Unit (25, 26) of type Time (26).
298          */
299         private NumberTime offsetTime;
300 
301         /** Speed as the number of times real-time the model should run; Double.INFINITY means as fast as possible. */
302         private double speed;
303 
304         /** Number of replications for stochastic uncertainties in the model. */
305         private int numberReplications;
306 
307         /** Random streams and seeds. */
308         private Map<Object, Long> streamMap = new HashMap<>();
309 
310         /**
311          * Empty constructor.
312          */
313         public Builder()
314         {
315             // nothing to do.
316         }
317 
318         /**
319          * @param newRunTime set runTime
320          * @return the original object for chaining
321          */
322         public final Builder setRunTime(final Number newRunTime)
323         {
324             this.runTime = new NumberDuration(newRunTime);
325             return this;
326         }
327 
328         /**
329          * @param newRunTime set runTime
330          * @return the original object for chaining
331          */
332         public final Builder setRunTime(final Duration newRunTime)
333         {
334             this.runTime = new NumberDuration(newRunTime);
335             return this;
336         }
337 
338         /**
339          * @param newRunTime set runTime
340          * @return the original object for chaining
341          */
342         public final Builder setRunTime(final FloatDuration newRunTime)
343         {
344             this.runTime = new NumberDuration(newRunTime);
345             return this;
346         }
347 
348         /**
349          * @param newWarmupTime set warmupTime
350          * @return the original object for chaining
351          */
352         public final Builder setWarmupTime(final Number newWarmupTime)
353         {
354             this.warmupTime = new NumberDuration(newWarmupTime);
355             return this;
356         }
357 
358         /**
359          * @param newWarmupTime set warmupTime
360          * @return the original object for chaining
361          */
362         public final Builder setWarmupTime(final Duration newWarmupTime)
363         {
364             this.warmupTime = new NumberDuration(newWarmupTime);
365             return this;
366         }
367 
368         /**
369          * @param newWarmupTime set warmupTime
370          * @return the original object for chaining
371          */
372         public final Builder setWarmupTime(final FloatDuration newWarmupTime)
373         {
374             this.warmupTime = new NumberDuration(newWarmupTime);
375             return this;
376         }
377 
378         /**
379          * @param newOffsetTime set offsetTime
380          * @return the original object for chaining
381          */
382         public final Builder setOffsetTime(final Number newOffsetTime)
383         {
384             this.offsetTime = new NumberTime(newOffsetTime);
385             return this;
386         }
387 
388         /**
389          * @param newOffsetTime set offsetTime
390          * @return the original object for chaining
391          */
392         public final Builder setOffsetTime(final Duration newOffsetTime)
393         {
394             this.offsetTime = new NumberTime(newOffsetTime);
395             return this;
396         }
397 
398         /**
399          * @param newOffsetTime set offsetTime
400          * @return the original object for chaining
401          */
402         public final Builder setOffsetTime(final FloatDuration newOffsetTime)
403         {
404             this.offsetTime = new NumberTime(newOffsetTime);
405             return this;
406         }
407 
408         /**
409          * @param newSpeed set speed
410          * @return the original object for chaining
411          */
412         public final Builder setSpeed(final double newSpeed)
413         {
414             this.speed = newSpeed;
415             return this;
416         }
417 
418         /**
419          * @param newNumberReplications set numberReplications
420          * @return the original object for chaining
421          */
422         public final Builder setNumberReplications(final int newNumberReplications)
423         {
424             this.numberReplications = newNumberReplications;
425             return this;
426         }
427 
428         /**
429          * @param newStreamMap set streamMap
430          * @return the original object for chaining
431          */
432         public final Builder setStreamMap(final Map<Object, Long> newStreamMap)
433         {
434             this.streamMap = newStreamMap;
435             return this;
436         }
437 
438         /** {@inheritDoc} */
439         @Override
440         public Sim0MQMessage build() throws Sim0MQException, NullPointerException
441         {
442             return new SimRunControlMessage(this.simulationRunId, this.senderId, this.receiverId, this.messageId, this.runTime,
443                     this.warmupTime, this.offsetTime, this.speed, this.numberReplications, this.streamMap.size(),
444                     this.streamMap);
445         }
446 
447     }
448 }