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