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