-
Notifications
You must be signed in to change notification settings - Fork 0
/
MODES.mo
executable file
·4351 lines (3902 loc) · 245 KB
/
MODES.mo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
package MODES
annotation(
Documentation(info = "<HTML>
<p>MODES (MOdelica Discrete Event Systems) is a Modelica library designed to run discrete events simulations in OpenModelica environment. It is composed by the following subpackages:
<ul>
<li><b>Atomic_PKG</b>: package containing the Atomic block from which to start creating DEVS atomic models.</li>
<li><b>Template_PKG</b>: providing a starting point to extend the Atomic partial block from <b>Atomic_PKG</b>.</li>
<li><b>EventsLib</b>: a library to perform discrete event simulations with blocks based on the parallel DEVS formalism.</li>
</ul>
Additionally, in this distribution the following packages can be found:
<ul>
<li><b>I40Lab</b>: package containing blocks to simulate the plant installed in I4.0 Lab, an activity present in the thesis work describing this library.</li>
<li><b>ServerExample_PKG</b>: a package in which a basic Server block is defined, used as an example in the thesis work describing this library, at chapter 3.1.3.</li>
</ul>
</p>
<p>To simulate discrete event systems it resorts to the Parallel DEVS formalism, invented by Bernard P. Ziegler.<br>
To simulate parallel DEVS atomic models this package divides the transition procedure in 5 event iteration steps.
<ol>
<li>Detect internal events, when internal transitions are scheduled to happen. </li>
<li>Send and detect messages as external events. </li>
<li>Acquire the messages content and decide for every atomic models which transition shall be executed. </li>
<li>Execute the transition, updating the state. </li>
<li>Schedule new internal events based on new state </li>
</ol>
Below a Petri Net showing the five steps procedure can be seen.
</p>
<img width=\"500\" src=\"modelica://MODES/Resources/Images/Petri-1.png\">
</HTML>"));
package Atomic_PKG
annotation(
Documentation(info = "<HTML>
<p>This package contains components that can be used to define Parallel Atomic DEVS models:
<ul>
<li>3 records that define the variables that can be extended to characterize the DEVS:
<ul>
<li><b>State</b>: containing state variables, is updated at transitions.</li>
<li><b>InputVariables_X</b>: Input variables, used to store informations from external events, to be used by external transitions. Representative of the <i>Bag</i> from parallel DEVS specification.</li>
<li><b>OutputVariables_Y</b>: To be updated before every internal event, its variables represent the result of the lambda output function on the given state.</li>
</ul>
<br>
<li>A parial block called <b>Atomic</b>, containing records instanciations and variables used to handle transiotions. Can be extended to model atomic DEVS.
</ul>
<p>
<h3><font color=\"#008000\">State</font></h3>
Aimed at containing state variables, should be updated only by transitions.<br>
</p>
<p>
<h3><font color=\"#008000\">InputVariables_X</font></h3>
Aimed at containing informations coming from input ports after every external event.<br>
Can be used in the transition execution algorithm section for external and confluent transitions to update the state.<br>
<p>
<h3><font color=\"#008000\">OutputVariables_Y</font></h3>
Aimed at containing output informations generated using state variables.<br>
Has to be updated right before every internal event triggers a transition.<br>
</p>
</HTML>"));
record State
end State;
record InputVariables_X
end InputVariables_X;
record OutputVariables_Y
end OutputVariables_Y;
partial block Atomic
annotation(
Documentation(info = "<HTML>
<p>This partial block is aimed at managing the transition triggers to run parallel DEVS simulations.<br>
To achieve that it contains the following elements:
<ul>
<li>3 replaceable record declarations that define the parallel DEVS' variables. In extending DEVS blocks they can be redeclared to be substitued with the proper records representing the desired atomic model.
<ul>
<li> A State instantiation <b>S</b>: containing state variables, can be updated by inheriting parallel DEVS blocks at transitions using an algorithm in the form:<br>
<pre><i>when pre(confluent_transtion) then
//S.some_variable := S.some_variable + some_variable;
elsewhen pre(external_transition) then
//S.some_variable := S.some_variable + some_variable;
elsewhen pre(internal_transition) then
//S.some_variable := S.some_variable + some_variable;
end when; </i></pre>
</li>
<li> An InputVariables_X instantiation <b>X</b>: input variables, used to store informations from external events, to be used by external transitions. Representative of the <i>Bag</i> from parallel DEVS specification. Shall be updated by extending DEVS blocks using input connectors' values inside a pre(), by an algorithm in the form:
<pre><i>when pre(external_event) then
//X.some_variable := pre(CONNECTOR.some_variable);
end when; </i></pre>
</li>
<li> An OutputVariables_Y instantiation <b>Y</b>: to be updated before every internal event, its variables represent the result of the lambda output function on the given state. Its values should be updated using state variables before every internal or confluent transition is triggered, by an algorithm section in the form:
<pre><i>when pre(internal_transition_planned[1]) then
//Y.some_variable := S.some_variable;
end when; </i></pre>
</li>
</ul>
<br>
<li>A set of boolean variables managed by the <b>Atomic</b> partial block, used to trigger transitions:
<ul>
<li><b>internal_transition</b></li>
<li><b>external_transition</b></li>
<li><b>confluent_transition</b></li>
</ul>
</li>
<li>A variable <b>next_TS</b>, which stand for next Scheduled Transition, that is used to set the time instant of the next internal event. Should be updated in inheriting DEVS blocks, by an algorithm section representing for <i>ta(s)</i> functions from DEVS specification formalism:
<pre><i>when pre(transition_happened) then
//next_TS := S.some_variable;
end when; </i></pre>
</li>
<li> A boolean <b>internal_event</b> equal to <i>time > <b>next_TS</b></i>, used to trigger the beginning of an event chain leading to an internal or a confluent transtion.
</li>
<li> A boolean array <b>external_event[:]</b> with undefined size, shall be redeclared in all extending DEVS blocks with a dimension suitable for the number of possible external events that may happen. Its members should be assigned so as to be triggered right when an external event happens, so that the DEVS block is advised of the presence of an external event and will behave consequently.
</li>
<li> Two protected undeclared integer parameters <b>n_inputs</b> and <b>n_outputs</b> that by default can be used to account for the number of input and output ports of the system.
</li>
A real variable <b>elapsed_time</b> that can be used in external and confluent transitions to determine the new state.
</li>
</ul>
<p>
</HTML>"));
replaceable State S;
replaceable InputVariables_X X;
replaceable OutputVariables_Y Y;
discrete Real next_TS "Variable defining the time instant of the next internal transition";
discrete Real last_transition_time (start = 0) "accounting for the time instant of last transtion";
Real elapsed_time = time - last_transition_time "amount of time elapsed since last transition";
Boolean transition_happened = pre(confluent_transition) or pre(external_transition) or pre(internal_transition) "boolean that uses S.transition_number to account for the happening of a transition";
//set of variables representing the number of transition happened
//for every possible kind of transition
//have no use in system's modeling, are just left for simulation evaluation
Integer n_of_internal_transitions_happened(start = 0);
Integer n_of_external_transitions_happened(start = 0);
Integer n_of_confluent_transitions_happened(start = 0);
protected
parameter Integer n_inputs;
parameter Integer n_outputs;
Boolean internal_event = time > next_TS;
Boolean external_event[:] "Array to be redeclared in every block, used to catch changes in external variables and so to acknowledge the arrival of an external event";
Boolean internal_transition (start = false) "Boolean used to trigger internal state transitions";
Boolean external_transition (start = false) "Boolean used to trigger external state transitions";
Boolean confluent_transition (start = false) "Boolean used to trigger confluent state transitions";
Boolean internal_transition_planned[2] (start = {false, false}) "Booleans used to plan internal transitions: whenever an internal event happens the first if gets flagged. The second is set to be the pre() of the first, so that internal transitions are retarded, thus being simultaneous to external ones";
Boolean external_transition_planned (start = false) "When an external event happens this boolean is flagged to track that an external transition needs to be triggered, confluent if an internal event is happening at the same time";
algorithm
when pre(transition_happened) then
last_transition_time := time;
end when;
algorithm
when cat(1, {change(next_TS), internal_event, pre(internal_transition_planned[1])}, external_event) then //Whenever an event happens or there was an internal event at the previous time step this when is entered
if pre(internal_transition_planned[1]) then
internal_transition_planned[2] := true "At first, if the internal event was at the previous time step update the second flag";
elseif internal_event then
internal_transition_planned[1] := true "Otherwise, in case of internal event, fire the first flag";
end if;
if max(external_event) then
external_transition_planned := true "In presence of an external event";
end if;
internal_transition := false;
external_transition := false;
confluent_transition := false "In case an event is triggered but a transition occurred at the previous iteration step, set to false all transition triggers";
elsewhen not max(external_event) and (pre(internal_transition_planned[2]) or pre(external_transition_planned)) then //When no events are arriving and transitions are schedeled this when is entered
if external_transition_planned and internal_transition_planned[2] then
confluent_transition := true "in case both an internal and an external event happened use the confluent transition";
elseif external_transition_planned then
external_transition := true;
elseif internal_transition_planned[2] then
internal_transition := true;
end if "An if else tree decides which transition should be triggered";
internal_transition_planned := {false, false};
external_transition_planned := false "At the end, all transition planning variables are put to false";
elsewhen pre(internal_transition) or pre(external_transition) or pre(confluent_transition) then
internal_transition := false;
external_transition := false;
confluent_transition := false "In case nothing happened, but at the previous step there has been a transition, reset the triggers";
end when;
algorithm
//algorithm aimed at updating the number of transition happened of every possible kind
when pre(confluent_transition) then
n_of_confluent_transitions_happened := n_of_confluent_transitions_happened + 1;
elsewhen pre(external_transition) then
n_of_external_transitions_happened := n_of_external_transitions_happened + 1;
elsewhen pre(internal_transition) then
n_of_internal_transitions_happened := n_of_internal_transitions_happened + 1;
end when;
end Atomic;
end Atomic_PKG;
package Template_PKG
annotation(
Documentation(info = "<HTML>
Starting block from which is possible to start to define new atomic blocks.
</HTML>"));
record StateTmp
extends Atomic_PKG.State;
end StateTmp;
record InputVariables_XTmp
extends Atomic_PKG.InputVariables_X;
end InputVariables_XTmp;
record OutputVariables_YTmp
extends Atomic_PKG.OutputVariables_Y;
end OutputVariables_YTmp;
block Template
extends Atomic_PKG.Atomic(redeclare StateTmp S, redeclare InputVariables_XTmp X, redeclare OutputVariables_YTmp Y, n_inputs = number_inputs, n_outputs = number_outputs, redeclare Boolean external_event[n_external_events]);
parameter Integer n_external_events = 1 "example number used to express the dimension of redeclared external_event array";
parameter Integer number_inputs;
parameter Integer number_outputs;
initial algorithm
//INITIALIZATION PHASE FOR VARIABLES
algorithm
//UPDATING next_TS
when pre(transition_happened) then
//next_TS := S.value;
end when;
algorithm
when pre(internal_transition_planned[1]) then
//Y.value := S.value;
end when;
///////////////////////////////////////////////////////////////////////
// INPUT RECORD X UPDATE AT EVENT ARRIVAL //
///////////////////////////////////////////////////////////////////////
//To avoid algebraic loops the when should be activated by pre(external_event) and X variables should be assigned with pre() of connectors' variables
algorithm
when pre(external_event) then
for i in 1:n_outputs loop
//X.input_value := pre(CONNECTOR.input_value);
end for;
end when;
///////////////////////////////////////////////////////////////////////
// END OF INPUT RECORD X UPDATE AT EVENT ARRIVAL //
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
// TRANSITIONS CODE //
///////////////////////////////////////////////////////////////////////
algorithm
when pre(confluent_transition) then
//CONFLUENT TRANSITION CODE
//S.state_variable := S.state_variable + 1 "Updating the number of transitions";
elsewhen pre(external_transition) then
//EXTERNAL TRANSITION CODE
//S.state_variable := S.state_variable + 1 "Updating the number of transitions";
elsewhen pre(internal_transition) then
//INTERNAL TRANSITION CODE
//S.state_variable := S.state_variable + 1 "Updating the number of transitions";
end when;
///////////////////////////////////////////////////////////////////////
// END OF TRANSITIONS CODE //
///////////////////////////////////////////////////////////////////////
equation
for i in 1:n_external_events loop
//external_event[i] = change(CONNECTOR.input_value);
//CONNECTOR.ouput_value = Y.value;
end for;
end Template;
end Template_PKG;
package EventsLib
annotation(
Documentation(info = "<HTML>
<p>EventsLib is a package providing blocks to perform discrete events simulation. Its functionalities are inspired by the SimEvents library, provided in the SIMULINK environment.<br>
All the active blocks are developed starting from the Atomic partial block, and they have been modeled and designed following the DEVS specification formalism.<br>
The basic unit that is transmitted from one block to the other is the event, which can represent discrete uniform entities or other components flowing inside the system, depending on the needs of the user. Every augmentation of value 1 in the blocks’ connectors indicates that an event
moves from output to input port. An augmentation with value <i>n</i> corresponds
the transfer of <i>n</i> events from output to input port.
A block can have multiple input and output ports, but all the possible connections
must be one-to-one. The following subpackages can be found:
<ul>
<li><b>Blocks</b>: a package containing the library's blocks.</li>
<li><b>Interfaces</b>: package containing the two connectors used as ports by the blocks.</li>
<li><b>Functions</b>.</li>
<li><b>RNG</b>: package containing a block for performing random number generation.</li>
<li><b>BlocksPackages</b>: packae containg DEVS blocks source code.</li>
</ul>
</p>
</HTML>"));
package Blocks
block Generator = BlocksPackages.Generator_PKG.Generator;
block Queue = BlocksPackages.Queue_PKG.Queue;
block Server = BlocksPackages.Server_PKG.Server;
block Delay = BlocksPackages.Delay_PKG.Delay;
block RandomSwitch = BlocksPackages.RandomSwitch_PKG.RandomSwitch;
block Combiner = BlocksPackages.Combiner_PKG.Combiner;
block Displacer = BlocksPackages.Displacer_PKG.Displacer;
package AuxiliaryBlocks
annotation(
Documentation(info = "<HTML>
<b>AuxiliaryBlocks</b><br>
<br>
A set of auxiliary blocks is provided to eventually manage connections between components. Even though many blocks provide for multiple connections, the user can choose to pick a block to merge a pair of ports or other simple operations. They are not modeled following the DEVS formalism, thus they do not require any event iteration step to accomplish their operations, mantaining the DEVS synchronized.
<ul>
<li> <b>EventMultiplier</b>: takes events from input and output and multiplies by a constant. If we desire a block to output an event from its first port and concurrently two events out of the second port, an EventMultiplier can be placed at the end of this last port to multiply its output.<br>
The user can also choose to multiply the request value.
</li><li> <b>EventDuplicate</b>: for every event received transmits an event through both its two output ports. The user if the request value the input will set is going the minimum or maximum of the two output ports.
</li><li> <b>EventSum</b>: acquires events from two input ports and redirects them through one input port by summing them. The request value from the output ports is directly propagated at its input ports.
</ul>
</HTML>"));
block EventMultiplier "Block that multiplies the events from input to output by a parameter"
EventsLib.Interfaces.In_Port IN annotation(
Placement(visible = true, transformation(origin = {-100, 0}, extent = {{-20, -20}, {20, 20}}, rotation = 0), iconTransformation(origin = {-100, 3.55271e-15}, extent = {{-20, -20}, {20, 20}}, rotation = 0)));
EventsLib.Interfaces.Out_Port OUT annotation(
Placement(visible = true, transformation(origin = {100, 0}, extent = {{-20, -20}, {20, 20}}, rotation = 0), iconTransformation(origin = {100, 3.55271e-15}, extent = {{-20, -20}, {20, 20}}, rotation = 0)));
parameter Integer event_multiplier = 1;
parameter Integer request_multiplier = 1;
algorithm
when change(IN.event_signal) then
OUT.event_signal := IN.event_signal * event_multiplier;
end when;
when change(OUT.event_request_signal) then
IN.event_request_signal := OUT.event_request_signal * request_multiplier;
end when;
end EventMultiplier;
block EventDuplicate "Block that duplicates the events received in input tp its two output ports"
EventsLib.Interfaces.In_Port IN annotation(
Placement(visible = true, transformation(origin = {-100, 0}, extent = {{-20, -20}, {20, 20}}, rotation = 0), iconTransformation(origin = {-100, 3.55271e-15}, extent = {{-20, -20}, {20, 20}}, rotation = 0)));
EventsLib.Interfaces.Out_Port OUT1 annotation(
Placement(visible = true, transformation(origin = {100, 50}, extent = {{-20, -20}, {20, 20}}, rotation = 0), iconTransformation(origin = {100, 45}, extent = {{-20, -20}, {20, 20}}, rotation = 0)));
EventsLib.Interfaces.Out_Port OUT2 annotation(
Placement(visible = true, transformation(origin = {100, -50}, extent = {{-20, -20}, {20, 20}}, rotation = 0), iconTransformation(origin = {100, -45}, extent = {{-20, -20}, {20, 20}}, rotation = 0)));
parameter Boolean max_request = true "if true, request given in input is the maximum of the two, otherwise the min";
algorithm
when change(IN.event_signal) then
OUT1.event_signal := IN.event_signal;
OUT2.event_signal := IN.event_signal;
end when;
when {change(OUT1.event_request_signal), change(OUT2.event_request_signal)} then
if max_request then
IN.event_request_signal := max(OUT1.event_request_signal, OUT2.event_request_signal);
else
IN.event_request_signal := min(OUT1.event_request_signal, OUT2.event_request_signal);
end if;
end when;
end EventDuplicate;
block EventSum "Block that sums events coming from multiple inputs to one output"
EventsLib.Interfaces.In_Port IN1 annotation(
Placement(visible = true, transformation(origin = {-100, 0}, extent = {{-20, -20}, {20, 20}}, rotation = 0), iconTransformation(origin = {-100, -45}, extent = {{-20, -20}, {20, 20}}, rotation = 0)));
EventsLib.Interfaces.In_Port IN2 annotation(
Placement(visible = true, transformation(origin = {-100, 0}, extent = {{-20, -20}, {20, 20}}, rotation = 0), iconTransformation(origin = {-100, 45}, extent = {{-20, -20}, {20, 20}}, rotation = 0)));
EventsLib.Interfaces.Out_Port OUT annotation(
Placement(visible = true, transformation(origin = {100, 0}, extent = {{-20, -20}, {20, 20}}, rotation = 0), iconTransformation(origin = {100, 3.55271e-15}, extent = {{-20, -20}, {20, 20}}, rotation = 0)));
algorithm
when {change(IN1.event_signal), change(IN1.event_signal)} then
OUT.event_signal := IN1.event_signal + IN2.event_signal;
end when;
when change(OUT.event_request_signal) then
IN1.event_request_signal := OUT.event_request_signal;
IN2.event_request_signal := OUT.event_request_signal;
end when;
end EventSum;
end AuxiliaryBlocks;
end Blocks;
package Interfaces
connector In_Port
input Integer event_signal "Event arrival signal";
output Integer event_request_signal "Channel to let the block require events";
annotation(
Icon(graphics = {Rectangle(origin = {0, -1}, fillColor = {255, 255, 255}, fillPattern = FillPattern.Solid, extent = {{-100, 101}, {100, -99}}), Line(origin = {4.49296, -0.303664}, points = {{-35.6464, 72}, {36.3536, 0}, {-35.6464, -72}, {-35.6464, -72}}, color = {0, 0, 255}, thickness = 3)}, coordinateSystem(initialScale = 0.1)));
end In_Port;
connector Out_Port
output Integer event_signal "Signal used to send signals";
input Integer event_request_signal "Events communicated from blocks connected in output";
annotation(
Icon(graphics = {Rectangle(origin = {0, -1}, fillPattern = FillPattern.Solid, extent = {{-100, 101}, {100, -99}}, fillColor = {0, 0, 255}), Line(origin = {4.49296, -0.303664}, points = {{-35.6464, 72}, {36.3536, 0}, {-35.6464, -72}, {-35.6464, -72}}, color = {255, 255, 255}, thickness = 3)}));
end Out_Port;
end Interfaces;
package Functions
function ImmediateInternalTransition "function used to decrement next internal transition time to trigger consecutive internal transitions"
input Real next_TS_in;
output Real next_TS_out;
algorithm
if next_TS_in >= 0 then
next_TS_out := -1;
else
next_TS_out := next_TS_in - 1;
end if;
end ImmediateInternalTransition;
end Functions;
package RNG
block RandomGenerator
annotation(
Documentation(info = "<HTML>
In this block, the implementation of the Xorshift1024* algorithm contained in the Modelica Standard Library is used to generate random numbers for selecting statuses lifetimes.\par
Its operations have been wrapped inside a block named <b>RandomGenerator</b>, that uses this generator to obtain uniform and exponential distributions for different models. Blocks entitled to follow stochastic behaviour have these parameters to select the desired behaviour:
<ul>
<li> <b>localSeed</b> and <b>globalSeed</b> are the seeds employed to generate the RNG's state, and will have to be set by the user.
</li><li> <b>distributionSelected</b> is an integer that can be used to select the desired distribution. A constant distribution is related to the value 0, uniform distribution to value 1, exponential distribution to value 2 and a gaussian to value 3. If another integer is set, by default the constant distribution is selected.
</li><li> <b>distributionParameter1</b> and <b>distributionParameter2</b> are the parameters used to characterize the distribution.\\
In case constant distribution is chosen, the block will deterministically return <b>distributionParameter1</b>, otherwise the distributions will take the following forms:
<ul>
<li>
Uniform: <b>U</b>(a, b)
</li>
<li>
Exponential: <b>E</b>(<i>lambda</i> = a)
</li>
<li>
Gaussiam: <b>N</b>(<i>mu</i> = a, <i>sigma</i> = b)
</li>
with
<li>
a = <b>distributionParameter1</b>, b = <b>distributionParameter2</b>
</li>
</ul>
In case a constant distribution is required and <b>distributionParameter1</b> < <b>distributionParameter2</b> the numbers generated will be constant and equal to <b>distributionParameter1</b>.</li>
</ul>
</HTML>"));
parameter Integer localSeed = 10 "localSeed of the RNG";
parameter Integer globalSeed = 7 "globalSeed of the RNG";
parameter Integer distributionSelected = 0 "0 = constant; 1 = uniform; 2 = exponential";
parameter Real distributionParameter1 = 1;
parameter Real distributionParameter2 = 0 "used only in uniform distrib";
discrete Real randomValue;
input Integer trigger;
Boolean number_request = change(trigger);
parameter Real lower_limit = 0.00001;
protected
Integer RNGState[33];
initial algorithm
RNGState := Modelica.Math.Random.Generators.Xorshift1024star.initialState(localSeed, globalSeed);
if distributionSelected == 1 then
(randomValue, RNGState) := Modelica.Math.Random.Generators.Xorshift1024star.random(RNGState);
randomValue := distributionParameter1 + randomValue * max(0, distributionParameter2 - distributionParameter1);
elseif distributionSelected == 2 then
(randomValue, RNGState) := Modelica.Math.Random.Generators.Xorshift1024star.random(RNGState);
randomValue := -Modelica.Math.log(randomValue) / distributionParameter1;
elseif distributionSelected == 3 then
(randomValue, RNGState) := Modelica.Math.Random.Generators.Xorshift1024star.random(RNGState);
randomValue := Modelica.Math.Distributions.Normal.quantile(randomValue, distributionParameter1, distributionParameter2);
else
randomValue := distributionParameter1;
end if;
if randomValue < lower_limit then
randomValue := lower_limit;
end if;
algorithm
when pre(number_request) then
if distributionSelected == 1 then
(randomValue, RNGState) := Modelica.Math.Random.Generators.Xorshift1024star.random(RNGState);
randomValue := distributionParameter1 + randomValue * max(0, distributionParameter2 - distributionParameter1);
elseif distributionSelected == 2 then
(randomValue, RNGState) := Modelica.Math.Random.Generators.Xorshift1024star.random(RNGState);
randomValue := -Modelica.Math.log(randomValue) / distributionParameter1;
elseif distributionSelected == 3 then
(randomValue, RNGState) := Modelica.Math.Random.Generators.Xorshift1024star.random(RNGState);
randomValue := Modelica.Math.Distributions.Normal.quantile(randomValue, distributionParameter1, distributionParameter2);
else
randomValue := distributionParameter1;
end if;
if randomValue < lower_limit then
randomValue := lower_limit;
end if;
end when;
end RandomGenerator;
end RNG;
package BlocksPackages "Containing packages models' records and implementation blocks"
/////////////////////////////////////////////////////////////////////////
// GENERATOR PACKAGE //
/////////////////////////////////////////////////////////////////////////
package Generator_PKG
record StateGen
extends Atomic_PKG.State;
discrete Real next_TS "Effective time instant of next internal transition";
discrete Real next_scheduled_generation "Since next_TS is affected by blocking of ports, this variable stores the completion time instant";
Integer events_already_sent "Events that the block had already sent through its output ports";
Boolean blocked_output "true when OUT.event_request_signal differs from zero, thus output is blocked";
Integer events_in_batch "Events belonging to the last generated batch still to be sent";
Integer events_being_sent "Events that are going to be sent at the next internal event";
Boolean immediate_transition_required "boolean set to true everytime that the next status will have 0 lifetime";
end StateGen;
record InputVariables_XGen
extends Atomic_PKG.InputVariables_X;
parameter Integer n_outputs;
Boolean portBlocked[n_outputs] "Array that stores information about blockage of output ports (an output port is considered blocked when its request value -1)";
end InputVariables_XGen;
record OutputVariables_YGen
extends Atomic_PKG.OutputVariables_Y;
Integer eventsSent "Elements generated in output";
end OutputVariables_YGen;
block Generator
annotation(
Documentation(info = "<HTML>
<b>Generator</b><br>
<br>
This block cyclically introduces events in the system. It is possible to send multiple batches of events simultaneously by using more than one output port.<br>
In case any of its ports happen to be blocked, it continues to prepare the generation, but doesn't send events once generated. In case a port is blocked and unlocked between two generation time instants, no visible effect can be seen in the inputs and outputs of the block.<br>
It starts a new generation only once all the already generated events have been successfully sent.<br>
To tune it, it is possible to act on its random distribution for selecting the timeadvance and the following parameters:
<ul>
<li> <b>outputBatchDimension</b>: amount of events that are going to be created at every new generation.
</li><li> <b>sendEventsSingularly</b>: if true, batches of events will be sent one by one, to let the receiver some transition steps to close its ports if necessary. If the system being modeled has no serious constraints on capacity, this parameter can be set to false so that all generated events will be sent together in the space of a unique transition. Having less transitions to inspect can be more convenient when simulation events are analyzed.
</li><li> <b>generateOnStart</b> can be used to determine if the first event generation will happen at the beginning of the simulation or after a certain amount of time.
</ul>
</HTML>"));
extends Atomic_PKG.Atomic(redeclare StateGen S, redeclare InputVariables_XGen X(n_outputs = n_outputs), redeclare OutputVariables_YGen Y, n_inputs = inputsNumber, n_outputs = outputsNumber, redeclare Boolean external_event[n_outputs]);
parameter Integer outputBatchDimension = 1 "Elements produced at every generation";
constant Integer inputsNumber = 0 "number of input ports of the system";
parameter Integer outputsNumber = 1 "number of output ports of the system";
//Random number generation components
RNG.RandomGenerator RNGBlock(localSeed = localSeed, globalSeed = globalSeed, distributionSelected = distributionSelected, distributionParameter1 = distributionParameter1, distributionParameter2 = distributionParameter2);
parameter Integer localSeed = 10 "localSeed of the RNG";
parameter Integer globalSeed = 7 "globalSeed of the RNG";
parameter Integer distributionSelected = 0 "0 = constant; 1 = uniform; 2 = exponential";
parameter Real distributionParameter1 = 1;
parameter Real distributionParameter2 = 0 "used only in uniform distribution to set the bias of the random number";
parameter Boolean sendEventsSingularly = true "if true, events ready to be sent are sent in a unique batch, otherwise multiple sendings are executed for all the available events. In case the block at the end has a certain capacity, it is advised to keep this value true to avoid events being discarded. Otherwise, if no such strictness is required, transmissions of batches of events can be activated by setting to false this parameters. Calculation performances would increase";
parameter Boolean generateOnStart = true "if true an element will be generated at the start of the generation, otherwise a time cycle will occur before the first output propagation";
//Block's ports
EventsLib.Interfaces.Out_Port OUT[n_outputs] annotation(
Placement(visible = true, transformation(origin = {100, 0}, extent = {{-20, -20}, {20, 20}}, rotation = 0), iconTransformation(origin = {100, 3.55271e-15}, extent = {{-20, -20}, {20, 20}}, rotation = 0)));
initial algorithm
if generateOnStart then
S.next_scheduled_generation := 0 "in case a generation on start is required the next internal transtion will happen at zero time";
else
S.next_scheduled_generation := RNGBlock.randomValue "otherwise the next internal transition will happen around a certain time instant given by the random number generator";
RNGBlock.trigger := RNGBlock.trigger + 1 "augmenting the RNG trigger to require the generation of a new random number";
end if ;
S.next_TS := S.next_scheduled_generation;
S.events_already_sent := 0 "The generator starts with an element already produced";
if sendEventsSingularly then
S.events_in_batch := outputBatchDimension - 1;
S.events_being_sent := 1;
else
S.events_in_batch := 0;
S.events_being_sent := outputBatchDimension;
end if "prepare the first sending";
S.blocked_output := false "by default the output ports are considered open";
for i in 1:n_outputs loop
X.portBlocked[i] := pre(OUT[i].event_request_signal) == -1;
end for;
Y.eventsSent := 0;
next_TS := S.next_TS;
algorithm
//UPDATING next_TS
when pre(transition_happened) then
next_TS := S.next_TS "After every transition next_TS equals the value set into the state";
end when;
algorithm
when pre(internal_transition_planned[1]) then
Y.eventsSent := S.events_already_sent + S.events_being_sent "At transition time the number of outputs sent is equal to the value of events already sent plus the events set at this new lambda output. The transition will thus update the state number of elements already sent";
end when;
/*elsewhen internal_transition_planned[2] then
for i in 1:n_outputs loop
OUT[i].event_signal := Y.eventsSent;
end for;
end when;*/
///////////////////////////////////////////////////////////////////////
// INPUT RECORD X UPDATE AT EVENT ARRIVAL //
///////////////////////////////////////////////////////////////////////
//To avoid algebraic loops the when should be activated by pre(external_event) and X variables should be assigned with pre() of connectors' variables
algorithm
when pre(external_event) then
for i in 1:n_outputs loop
X.portBlocked[i] := pre(OUT[i].event_request_signal) == -1 "If the output port's request value is -1, the port is considered blocked";
end for;
end when;
///////////////////////////////////////////////////////////////////////
// END OF INPUT RECORD X UPDATE AT EVENT ARRIVAL //
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
// TRANSITIONS CODE //
///////////////////////////////////////////////////////////////////////
algorithm
when pre(confluent_transition) then
//CONFLUENT TRANSITION CODE
S.blocked_output := false "At the beginning of every transition it is assumed that the output is not blocked, to then check for every port";
for i in 1:n_outputs loop
if X.portBlocked[i] then
S.blocked_output := true "If any port is found blocked the whole system considers the output blocked";
end if;
end for "loop aimed at checking if any of the output ports is considered blocked";
S.immediate_transition_required := false "at the beginning of every transition we assume no immediate transition will be needed, to then check if it is over the course of the function";
if S.events_being_sent > 0 then
S.events_already_sent := S.events_already_sent + S.events_being_sent "if an event had been sent during the last transition, add it to the already sent events";
if S.events_in_batch > 0 then
S.events_being_sent := 0;
if not S.blocked_output then
S.immediate_transition_required := true "if an event has been sent but there are still and the ports are open schedule a transition with no outputs";
end if;
else //if no other events are left to be sent, schedule a new generation
S.next_scheduled_generation := time + RNGBlock.randomValue "when a new generation is started, the next internal event is scheduled to be in advance by an amount of time set by the RNG";
RNGBlock.trigger := RNGBlock.trigger + 1 "augmenting the RNG trigger to require the generation of a new random number";
if sendEventsSingularly then
S.events_in_batch := outputBatchDimension - 1;
S.events_being_sent := 1;
else
S.events_in_batch := 0;
S.events_being_sent := outputBatchDimension;
end if "if events have to be sent one by one, send one, otherwise send entire batch";
end if "if there are still events left to send, schedule an idle transition, otherwise start a new generation";
else //if no events have been sent before this transition
if S.events_in_batch > 0 then
if not S.blocked_output then
S.immediate_transition_required := true "if the last transition did not output anything, there are still elements to be sent and the outputs are open, send immediately an event";
if sendEventsSingularly then
S.events_in_batch := S.events_in_batch - 1;
S.events_being_sent := 1;
else
S.events_being_sent := S.events_in_batch;
S.events_in_batch := 0 ;
end if "if events have to be sent one by one, send one, otherwise send entire batch";
end if;
else //if no events have been sent but the batch is somehow empty, schedule a new generation. This case is almost impossible, but still it is treated
S.next_scheduled_generation := time + RNGBlock.randomValue "next generation is scheduled at a timedvance in advance to the actual time";
RNGBlock.trigger := RNGBlock.trigger + 1 "augmenting the RNG trigger to require the generation of a new random number";
if sendEventsSingularly then
S.events_in_batch := outputBatchDimension - 1;
S.events_being_sent := 1;
else
S.events_in_batch := 0;
S.events_being_sent := outputBatchDimension;
end if "if events have to be sent one by one, send one, otherwise send entire batch";
end if;
end if "if branch used to decide what to do right after an internal event depending on the events sent";
if S.blocked_output then
S.next_TS := Modelica.Constants.inf "until output is blocked next transition is scheduled at infinite time";
elseif S.immediate_transition_required then
S.next_TS := Functions.ImmediateInternalTransition(S.next_TS) "if an immediate transition is required, this function changes next_TS to a negative number lesser than the actual one";
else
S.next_TS := S.next_scheduled_generation "otherwise the next internal event will be when normally scheduled";
end if;
elsewhen pre(external_transition) then
//EXTERNAL TRANSITION CODE
S.blocked_output := false "At the beginning of every transition it is assumed that the output is not blocked, to then check for every port";
for i in 1:n_outputs loop
if X.portBlocked[i] then
S.blocked_output := true "If any port is found blocked the whole system considers the output blocked";
end if;
end for "loop aimed at checking if any of the output ports is considered blocked";
S.immediate_transition_required := false "for safety, immediate transition is always set false at the beginning of a transition";
if S.blocked_output then
S.next_TS := Modelica.Constants.inf "until output is blocked next transition is scheduled at infinite time";
elseif S.immediate_transition_required then
S.next_TS := Functions.ImmediateInternalTransition(S.next_TS) "if an immediate transition is required, this function changes next_TS to a negative number lesser than the actual one";
else
S.next_TS := S.next_scheduled_generation "otherwise the next internal event will be when normally scheduled";
end if "if any of the output ports are blocked, then the next internal transition is set to infinite, otherwise, it is just the next scheduled one";
elsewhen pre(internal_transition) then
//INTERNAL TRANSITION CODE
S.immediate_transition_required := false "at the beginning of every transition we assume no immediate transition will be needed, to then check if it is over the course of the function";
if S.events_being_sent > 0 then
S.events_already_sent := S.events_already_sent + S.events_being_sent "if an event had been sent during the last transition, add it to the already sent events";
if S.events_in_batch > 0 then
S.events_being_sent := 0;
if not S.blocked_output then
S.immediate_transition_required := true "if an event has been sent but there are still and the ports are open schedule a transition with no outputs";
end if;
else
S.next_scheduled_generation := time + RNGBlock.randomValue "when a new generation is started, the next internal event is scheduled to be in advance by an amount of time set by the RNG";
RNGBlock.trigger := RNGBlock.trigger + 1 "augmenting the RNG trigger to require the generation of a new random number";
if sendEventsSingularly then
S.events_in_batch := outputBatchDimension - 1;
S.events_being_sent := 1;
else
S.events_in_batch := 0;
S.events_being_sent := outputBatchDimension;
end if "if events have to be sent one by one, send one, otherwise send entire batch";
end if "if there are still events left to send, schedule an idle transition, otherwise start a new generation";
else
if S.events_in_batch > 0 then
if not S.blocked_output then
S.immediate_transition_required := true "if the last transition did not output anything, there are still elements to be sent and the outputs are open, send immediately an event";
if sendEventsSingularly then
S.events_in_batch := S.events_in_batch - 1;
S.events_being_sent := 1;
else
S.events_being_sent := S.events_in_batch;
S.events_in_batch := 0;
end if "if events have to be sent one by one, send one, otherwise send entire batch";
end if;
else //if no events have been sent but the batch is somehow empty, schedule a new generation. This case is almost impossible, but still it is treated
S.next_scheduled_generation := time + RNGBlock.randomValue "when a new generation is started, the next internal event is scheduled to be in advance by an amount of time set by the RNG";
RNGBlock.trigger := RNGBlock.trigger + 1 "augmenting the RNG trigger to require the generation of a new random number";
if sendEventsSingularly then
S.events_in_batch := outputBatchDimension - 1;
S.events_being_sent := 1;
else
S.events_in_batch := 0;
S.events_being_sent := outputBatchDimension;
end if "if events have to be sent one by one, send one, otherwise send entire batch";
end if;
end if;
if S.blocked_output then
S.next_TS := Modelica.Constants.inf "until output is blocked next transition is scheduled at infinite time";
elseif S.immediate_transition_required then
S.next_TS := Functions.ImmediateInternalTransition(S.next_TS) "if an immediate transition is required, this function changes next_TS to a negative number lesser than the actual one";
else
S.next_TS := S.next_scheduled_generation "otherwise the next internal event will be when normally scheduled";
end if;
end when;
///////////////////////////////////////////////////////////////////////
// END OF TRANSITIONS CODE //
///////////////////////////////////////////////////////////////////////
equation
for i in 1:n_outputs loop
OUT[i].event_signal = Y.eventsSent;
external_event[i] = change(OUT[i].event_request_signal);
end for;
end Generator;
end Generator_PKG;
/////////////////////////////////////////////////////////////////////////
// END OF GENERATOR PACKAGE //
/////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////
// QUEUE PACKAGE //
/////////////////////////////////////////////////////////////////////////
package Queue_PKG
record StateQue
extends Atomic_PKG.State;
parameter Integer n_inputs;
parameter Integer n_outputs;
Boolean immediate_transition_required "boolean set to true everytime that the next status will have 0 lifetime";
discrete Real next_TS "time instant of next internal transition";
Integer events_in_queue "number of elements in queue";
Integer acquired_events[n_inputs] "value of elements arrived at the last transition from input ports";
Integer events_sent[n_outputs] "elements to be sent out of output ports";
Integer requests_acquired[n_outputs] "value of requests arrived at the last transition from input ports";
Integer pending_requests[n_outputs] "requests awaiting to be satisfied for every input port";
Boolean blocking_input_ports "If true then the queue reached maximum capacity and so input ports need to be blocked";
Integer eventsDiscarded "amount of events arrived that could not be retained because of capacity issues and that will be discarded from the system";
end StateQue;
record InputVariables_XQue
extends Atomic_PKG.InputVariables_X;
parameter Integer n_inputs;
parameter Integer n_outputs;
Integer eventsArrived[n_inputs] "number of elements arrived in input";
Integer requestsArrived[n_outputs] "number of requests arrived from outputs";
end InputVariables_XQue;
record OutputVariables_YQue
extends Atomic_PKG.OutputVariables_Y;
parameter Integer n_outputs;
Integer eventsSent[n_outputs] "elements sent as output to requesting blocks";
Integer inputPortsRequestValue "Value used to eventually block input ports";
end OutputVariables_YQue;
block Queue
annotation(
Documentation(info = "<HTML>
<b>Queue</b><br>
<br>
A queue receives events from inputs and stores them. At the same time, it receives requests from server blocks through its output ports, and will send events to the querying port if it has enough of them stored to satisfy the request. The priority with which requests will be satisfied is given by the index of the connector.<br>
The maximum amount of events that a queue can be stored can be defined by its capacity. If a queue has a set of pending requests, it will first satisfy them with the events arrived, and only after will check if the storing limit has been reached. In case, it blocks its output ports. Its behaviour can be defined with the following parameters.
<ul>
<li> <b>capacity</b> defines the maximum amount of events that the queue is supposed to store. If no unlimited capacity is desired, this value can be set to -1.
</li><li> <b>fragmentedService</b> allows to choose between two policies for pending requests satisfaction. If true, the queue is allowed to partially satisfy pending requests. For example, if a server required two events, when an event arrives it is directly sent to that server, even if it doesn't satisfy completely the request.<br>
When false, a queue will satisfy a pending request only if it has enough events to serve it entirely.
</li><li> <b>allowEventsInExcess</b> can be used to specify whether or not events surpassing the capacity limit will be kept or discarded.
</li><li> <b>startingEventsInQueue</b> specifies the amount of events which the queue initially possesses, usually 0.
</ul>
</HTML>"));
extends Atomic_PKG.Atomic(redeclare StateQue S(n_inputs = n_inputs, n_outputs = n_outputs), redeclare OutputVariables_YQue Y(n_outputs = n_outputs), redeclare InputVariables_XQue X(n_inputs = n_inputs, n_outputs = n_outputs), n_inputs = inputsNumber, redeclare Boolean external_event[n_inputs + n_outputs], n_outputs = outputsNumber);
parameter Integer inputsNumber = 1 "number of input ports of the system";
parameter Integer outputsNumber = 1 "number of output ports of the system";
parameter Integer capacity = -1;
parameter Boolean fragmentedService = false "Fragmented service true: pending requests with more than one element may be partially served; false: pending requests will be satisfied only when whole pending request will be satisfiable";
parameter Boolean allowEventsInExcess = true "If true, when elements surpassing the capacity arrive, they are retained if less than the twice the number of inputs. If false, elements in excess are discarded";
parameter Integer startingEventsInQueue = 0 "number of elements in the queue at the beginning of the simulation";
EventsLib.Interfaces.In_Port IN[n_inputs] annotation(
Placement(visible = true, transformation(origin = {-100, 0}, extent = {{-20, -20}, {20, 20}}, rotation = 0), iconTransformation(origin = {-100, 3.55271e-15}, extent = {{-20, -20}, {20, 20}}, rotation = 0)));
EventsLib.Interfaces.Out_Port OUT[n_outputs] annotation(
Placement(visible = true, transformation(origin = {100, 0}, extent = {{-20, -20}, {20, 20}}, rotation = 0), iconTransformation(origin = {100, 3.55271e-15}, extent = {{-20, -20}, {20, 20}}, rotation = 0)));
initial algorithm
S.events_in_queue := startingEventsInQueue;
for i in 1:n_inputs loop
S.acquired_events[i] := 0;
end for;
S.next_TS := Modelica.Constants.inf "At start no internal transition is scheduled";
S.blocking_input_ports := S.events_in_queue >= capacity and capacity >= 0;
S.immediate_transition_required := S.blocking_input_ports;
for i in 1:n_outputs loop
S.pending_requests[i] := 0;
S.requests_acquired[i] := 0;
S.events_sent[i] := 0;
X.requestsArrived[i] := 0;
Y.eventsSent[i] := S.events_sent[i];
end for;
for i in 1:n_inputs loop
X.eventsArrived[i] := 0;
end for;
if S.blocking_input_ports then
Y.inputPortsRequestValue := -1;
else
Y.inputPortsRequestValue := 0;
end if;
next_TS := S.next_TS;
algorithm
//UPDATING next_TS
when pre(transition_happened) then
next_TS := S.next_TS "After every transition next_TS equals the value set into the state";
end when;
algorithm
when pre(internal_transition_planned[1]) then
for i in 1:n_outputs loop
Y.eventsSent[i] := S.events_sent[i];
end for;
if S.blocking_input_ports then
Y.inputPortsRequestValue := -1;
else
Y.inputPortsRequestValue := 0;
end if "If input ports are set ot be blocked then request value is set to -1, otherwise 0";
end when;
///////////////////////////////////////////////////////////////////////
// TRANSITIONS CODE //
///////////////////////////////////////////////////////////////////////
algorithm
when pre(confluent_transition) then
//CONFLUENT TRANSITION CODE
//After an external event a queue has to: 1. check if new elements have arrived and add them to the queue 2. check if new requests arrived and memorize the as pending requests 3. check if elements can be sent to satisfy requests 4. set the output ports in relation to capacity and check if an internal transition to transmit outputs has to be fired
for i in 1:n_inputs loop
S.events_in_queue := S.events_in_queue + X.eventsArrived[i] - S.acquired_events[i]"update the number of elements in the queue considering the difference between actual and previous inputs";
S.acquired_events[i] := X.eventsArrived[i] "at the next transition the previous inputs will be equal to the actual ones";
end for "Cicle aimed at updating the events_in_queue value";
for i in 1:n_outputs loop
if X.requestsArrived[i] - S.requests_acquired[i] > 0 then
S.pending_requests[i] := S.pending_requests[i] + X.requestsArrived[i] - S.requests_acquired[i] "number of previous requests is updated according to the difference between actual and previous requests";
S.requests_acquired[i] := X.requestsArrived[i] "at the next transition the previous requests will be equal to the actual ones";
end if;
end for "Cicle aimed at updating the pending_requests";
S.immediate_transition_required := false "At the beginning of requests evaluations we assume no response is needed";
for i in 1:n_outputs loop //check requests from every output port
if S.pending_requests[i] > 0 then //in case a pending request from port i is present
if S.events_in_queue >= S.pending_requests[i] then
S.events_sent[i] := S.events_sent[i] + S.pending_requests[i] "elements sent at output i is augmented enough to satisfy the request";
S.events_in_queue := S.events_in_queue - S.pending_requests[i] "elements are effectively prelevated from the queue";
S.pending_requests[i] := 0 "No more requests are left pending after satisfaction";
S.immediate_transition_required := true "we flag that we will need an internal transition to output the elements to be sent";
elseif fragmentedService and S.events_in_queue > 0 then //In case the queue doesn't have enough elements to satisfy the request, in case of unitary service the request is fulfilled partially
S.events_sent[i] := S.events_sent[i] + S.events_in_queue "All elements in the queue are sent in output";
S.pending_requests[i] := S.pending_requests[i] - S.events_in_queue "the pending request is reduced by the number of elements the queue contained";
S.events_in_queue := 0 "the queue is emptied";
S.immediate_transition_required := true "we flag that we will need an internal transition";
end if;
end if;
end for;
if S.events_in_queue > capacity and capacity > -1 and not allowEventsInExcess then
S.eventsDiscarded := S.eventsDiscarded + S.events_in_queue - capacity;
S.events_in_queue := capacity;
end if "If the queue has a certain capacity, after having satisfied requests, events in excess are removed";
if capacity > -1 and S.events_in_queue >= capacity and not S.blocking_input_ports then //In case the queue contains more elements than allowed and the input ports are not blocked, then block them immediately by setting immediate_transition_required to true
S.blocking_input_ports := true;
S.immediate_transition_required := true;
elseif S.events_in_queue < capacity and S.blocking_input_ports then //In case the port was locked but there is room for elements, unlock the inputs immediately by setting immediate_transition_required to true
S.blocking_input_ports := false;
S.immediate_transition_required := true;
end if "if branch used to handle the blocking of input ports";
if S.immediate_transition_required then
S.next_TS := Functions.ImmediateInternalTransition(S.next_TS) "In case the queue has to send elements or inputs need to blocked, next internal transition needs to be immediate";
else
S.next_TS := Modelica.Constants.inf "in case no elements need to be sent and ports are unlocked, the queue becomes passive and sets to infity its next internal event";
end if;
elsewhen pre(external_transition) then
//EXTERNAL TRANSITION CODE
//After an external event a queue has to: 1. check if new elements have arrived and add them to the queue 2. check if new requests arrived and memorize the as pending requests 3. check if elements can be sent to satisfy requests 4. set the output ports in relation to capacity and check if an internal transition to transmit outputs has to be fired
for i in 1:n_inputs loop
S.events_in_queue := S.events_in_queue + X.eventsArrived[i] - S.acquired_events[i]"update the number of elements in the queue considering the difference between actual and previous inputs";
S.acquired_events[i] := X.eventsArrived[i] "at the next transition the previous inputs will be equal to the actual ones";
end for "Cicle aimed at updating the events_in_queue value";
for i in 1:n_outputs loop
if X.requestsArrived[i] - S.requests_acquired[i] > 0 then
S.pending_requests[i] := S.pending_requests[i] + X.requestsArrived[i] - S.requests_acquired[i] "number of previous requests is updated according to the difference between actual and previous requests";
S.requests_acquired[i] := X.requestsArrived[i] "at the next transition the previous requests will be equal to the actual ones";
end if;
end for "Cicle aimed at updating the pending_requests";
S.immediate_transition_required := false "At the beginning of requests evaluations we assume no response is needed";
for i in 1:n_outputs loop //check requests from every output port
if S.pending_requests[i] > 0 then //in case a pending request from port i is present
if S.events_in_queue >= S.pending_requests[i] then
S.events_sent[i] := S.events_sent[i] + S.pending_requests[i] "elements sent at output i is augmented enough to satisfy the request";
S.events_in_queue := S.events_in_queue - S.pending_requests[i] "elements are effectively prelevated from the queue";
S.pending_requests[i] := 0 "No more requests are left pending after satisfaction";
S.immediate_transition_required := true "we flag that we will need an internal transition to output the elements to be sent";
elseif fragmentedService and S.events_in_queue > 0 then //In case the queue doesn't have enough elements to satisfy the request, in case of unitary service the request is fulfilled partially
S.events_sent[i] := S.events_sent[i] + S.events_in_queue "All elements in the queue are sent in output";
S.pending_requests[i] := S.pending_requests[i] - S.events_in_queue "the pending request is reduced by the number of elements the queue contained";
S.events_in_queue := 0 "the queue is emptied";
S.immediate_transition_required := true "we flag that we will need an internal transition";
end if;
end if;
end for;