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 }