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.sim0mq.Sim0MQException;
11 import org.sim0mq.message.MessageStatus;
12 import org.sim0mq.message.Sim0MQMessage;
13 import org.sim0mq.message.SimulationMessage;
14 import org.sim0mq.message.types.NumberDuration;
15 import org.sim0mq.message.types.NumberTime;
16
17 import nl.tudelft.simulation.language.Throw;
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
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 }