vlcSim 0.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- vlcsim/__init__.py +25 -0
- vlcsim/controller.py +743 -0
- vlcsim/scene.py +1793 -0
- vlcsim/simulator.py +797 -0
- vlcsim-0.4.0.dist-info/METADATA +164 -0
- vlcsim-0.4.0.dist-info/RECORD +8 -0
- vlcsim-0.4.0.dist-info/WHEEL +4 -0
- vlcsim-0.4.0.dist-info/licenses/LICENSE.md +17 -0
vlcsim/simulator.py
ADDED
|
@@ -0,0 +1,797 @@
|
|
|
1
|
+
"""
|
|
2
|
+
The Module **Simulator** has all the Simulation Dynamics.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .controller import *
|
|
6
|
+
from .scene import *
|
|
7
|
+
import numpy as np
|
|
8
|
+
from enum import Enum
|
|
9
|
+
from typing import Optional, List, Any
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Event:
|
|
13
|
+
"""
|
|
14
|
+
The Event class has the information about the event, containing the time when the event occurs, the type of event, and the connection ID.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
event = Enum("event", "ARRIVE DEPARTURE PAUSE RESUME NEXT_CONNECTION_TRY")
|
|
18
|
+
"""
|
|
19
|
+
There are five event types. The **ARRIVE** event is used when a connection arrives to the scenario. **DEPARTURE** event when the connection leaves. **PAUSE** and **RESUME** is related to a connection that is currently served by an AP, but because of the frame-slot behavior is paused and resumed multiple times. Finally, **NEXT_CONNECTION_TRY** is used when a connection couldn't be stablished because capacity, and waits a random time and need to be served in future·
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(
|
|
23
|
+
self, type: Optional[event] = None, time: float = -1, id_connection: int = -1
|
|
24
|
+
) -> None:
|
|
25
|
+
self.__time: float = time
|
|
26
|
+
self.__id_connection: int = id_connection
|
|
27
|
+
if type is None:
|
|
28
|
+
self.__type: Event.event = Event.event.ARRIVE
|
|
29
|
+
else:
|
|
30
|
+
self.__type = type
|
|
31
|
+
self.__connection: Optional[Connection] = None
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def type(self) -> event:
|
|
35
|
+
"""
|
|
36
|
+
The connection type. Could be one of the fives types described before.
|
|
37
|
+
|
|
38
|
+
:return: Connection type
|
|
39
|
+
:rtype: event
|
|
40
|
+
"""
|
|
41
|
+
return self.__type
|
|
42
|
+
|
|
43
|
+
@property
|
|
44
|
+
def time(self) -> float:
|
|
45
|
+
"""
|
|
46
|
+
The instant when this event occurs.
|
|
47
|
+
|
|
48
|
+
:return: Instant time of this event
|
|
49
|
+
:rtype: float
|
|
50
|
+
"""
|
|
51
|
+
return self.__time
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def id_connection(self) -> int:
|
|
55
|
+
"""
|
|
56
|
+
Connection ID of the connection related to this event
|
|
57
|
+
|
|
58
|
+
:return: Connection ID
|
|
59
|
+
:rtype: int
|
|
60
|
+
"""
|
|
61
|
+
return self.__id_connection
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def connection(self) -> Optional[Connection]:
|
|
65
|
+
"""
|
|
66
|
+
Connection object related to this event.
|
|
67
|
+
|
|
68
|
+
:return: Connection object
|
|
69
|
+
:rtype: Connection
|
|
70
|
+
"""
|
|
71
|
+
return self.__connection
|
|
72
|
+
|
|
73
|
+
@connection.setter
|
|
74
|
+
def connection(self, value: Connection):
|
|
75
|
+
self.__connection = value
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class Simulator:
|
|
79
|
+
"""
|
|
80
|
+
Simulator class. In chanrge of randomness and the clock routine.
|
|
81
|
+
"""
|
|
82
|
+
|
|
83
|
+
def __init__(self, x: float, y: float, z: float, nGrids: int, rho: float) -> None:
|
|
84
|
+
"""
|
|
85
|
+
Simulator object constructor
|
|
86
|
+
|
|
87
|
+
:param x: Length of the scenario
|
|
88
|
+
:type x: float
|
|
89
|
+
:param y: Width of the scenario
|
|
90
|
+
:type y: float
|
|
91
|
+
:param z: Height of the scenario
|
|
92
|
+
:type z: float
|
|
93
|
+
:param nGrids: Number of segments on each dimension. Usefull for precision.
|
|
94
|
+
:type nGrids: int
|
|
95
|
+
:param rho: reflexion coefficient. A floating number between 0 and 1.
|
|
96
|
+
:type rho: float
|
|
97
|
+
"""
|
|
98
|
+
self.__controller: Controller = Controller(x, y, z, nGrids, rho)
|
|
99
|
+
self.__events: List[Event] = []
|
|
100
|
+
self.__current_event: Optional[Event] = None
|
|
101
|
+
|
|
102
|
+
self.__initReady: Optional[bool] = None
|
|
103
|
+
self.__lambdaS: Optional[float] = None
|
|
104
|
+
self.__mu: Optional[float] = None
|
|
105
|
+
self.__seedArrive: Optional[int] = None
|
|
106
|
+
self.__seedDeparture: Optional[int] = None
|
|
107
|
+
self.__seedX: Optional[int] = None
|
|
108
|
+
self.__seedY: Optional[int] = None
|
|
109
|
+
self.__seedZ: Optional[int] = None
|
|
110
|
+
self.__seedRandomWait: Optional[int] = None
|
|
111
|
+
self.__seedCapacityRequired: Optional[int] = None
|
|
112
|
+
self.__numberOfConnections: int = 0
|
|
113
|
+
self.__goalConnections: int = 10000
|
|
114
|
+
self.__arrival_variable: Any = None
|
|
115
|
+
self.__departure_variable: Any = None
|
|
116
|
+
self.__x_variable: Any = None
|
|
117
|
+
self.__y_variable: Any = None
|
|
118
|
+
self.__z_variable: Any = None
|
|
119
|
+
self.__random_wait_variable: Any = None
|
|
120
|
+
self.__capacity_required_variable: Any = None
|
|
121
|
+
self.__rtn_allocation: Optional[Any] = None
|
|
122
|
+
|
|
123
|
+
self.__allocatedConnections: int = 0
|
|
124
|
+
# time
|
|
125
|
+
self.__clock: float = 0.0
|
|
126
|
+
self.__time_duration: Optional[float] = None
|
|
127
|
+
|
|
128
|
+
self.__upper_random_wait: Optional[float] = None
|
|
129
|
+
self.__lower_random_wait: Optional[float] = None
|
|
130
|
+
|
|
131
|
+
self.__upper_capacity_required: Optional[float] = None
|
|
132
|
+
self.__lower_capacity_required: Optional[float] = None
|
|
133
|
+
|
|
134
|
+
self.__users_by_vlc: List[int] = []
|
|
135
|
+
self.__users_by_rf: List[int] = []
|
|
136
|
+
|
|
137
|
+
self.default_values()
|
|
138
|
+
|
|
139
|
+
def default_values(self):
|
|
140
|
+
"""
|
|
141
|
+
Set the default simulation values, that are:
|
|
142
|
+
|
|
143
|
+
* lambdaS = 3
|
|
144
|
+
* mu = 10
|
|
145
|
+
* seedArrive = 12345
|
|
146
|
+
* seedDeparture = 1234
|
|
147
|
+
* seedX = 1235
|
|
148
|
+
* seedY = 1245
|
|
149
|
+
* seedZ = 1345
|
|
150
|
+
* seedRandomWait = 1345
|
|
151
|
+
* seedCapacityRequired = 1345
|
|
152
|
+
* goalConnections = 10000
|
|
153
|
+
* lower_random_wait = 5
|
|
154
|
+
* upper_random_wait = 15
|
|
155
|
+
"""
|
|
156
|
+
self.__initReady = False
|
|
157
|
+
self.__lambdaS = 3
|
|
158
|
+
self.__mu = 10
|
|
159
|
+
self.__seedArrive = 12345
|
|
160
|
+
self.__seedDeparture = 1234
|
|
161
|
+
self.__seedX = 1235
|
|
162
|
+
self.__seedY = 1245
|
|
163
|
+
self.__seedZ = 1345
|
|
164
|
+
self.__seedRandomWait = 1345
|
|
165
|
+
self.__seedCapacityRequired = 1345
|
|
166
|
+
|
|
167
|
+
self.__numberOfConnections = 0
|
|
168
|
+
self.__goalConnections: int = 10000
|
|
169
|
+
self.__lower_random_wait = 5
|
|
170
|
+
self.__upper_random_wait = 15
|
|
171
|
+
self.__allocatedConnections = 0
|
|
172
|
+
self.__controller.allocator = Controller.default_alloc
|
|
173
|
+
|
|
174
|
+
@property
|
|
175
|
+
def upper_capacity_required(self) -> Optional[float]:
|
|
176
|
+
"""
|
|
177
|
+
The upper possible capacity required
|
|
178
|
+
|
|
179
|
+
:return: Upper capacity required
|
|
180
|
+
:rtype: float
|
|
181
|
+
"""
|
|
182
|
+
return self.__upper_capacity_required
|
|
183
|
+
|
|
184
|
+
@upper_capacity_required.setter
|
|
185
|
+
def upper_capacity_required(self, value: float) -> None:
|
|
186
|
+
self.__upper_capacity_required = value
|
|
187
|
+
|
|
188
|
+
@property
|
|
189
|
+
def lower_capacity_required(self) -> Optional[float]:
|
|
190
|
+
"""
|
|
191
|
+
The lower possible capacity required
|
|
192
|
+
|
|
193
|
+
:return: Lower capacity required
|
|
194
|
+
:rtype: float
|
|
195
|
+
"""
|
|
196
|
+
return self.__lower_capacity_required
|
|
197
|
+
|
|
198
|
+
@lower_capacity_required.setter
|
|
199
|
+
def lower_capacity_required(self, value: float) -> None:
|
|
200
|
+
self.__lower_capacity_required = value
|
|
201
|
+
|
|
202
|
+
@property
|
|
203
|
+
def seedCapacityRequired(self) -> Optional[int]:
|
|
204
|
+
"""
|
|
205
|
+
Seed of capacity required random generator
|
|
206
|
+
|
|
207
|
+
:return: random generator seed
|
|
208
|
+
:rtype: int
|
|
209
|
+
"""
|
|
210
|
+
return self.__seedCapacityRequired
|
|
211
|
+
|
|
212
|
+
@seedCapacityRequired.setter
|
|
213
|
+
def seedCapacityRequired(self, value: int):
|
|
214
|
+
self.__seedCapacityRequired = value
|
|
215
|
+
|
|
216
|
+
@property
|
|
217
|
+
def upper_random_wait(self) -> Optional[float]:
|
|
218
|
+
"""
|
|
219
|
+
the upper possible random wait
|
|
220
|
+
|
|
221
|
+
:return: upper waiting time
|
|
222
|
+
:rtype: float
|
|
223
|
+
"""
|
|
224
|
+
return self.__upper_random_wait
|
|
225
|
+
|
|
226
|
+
@upper_random_wait.setter
|
|
227
|
+
def upper_random_wait(self, value: float) -> None:
|
|
228
|
+
self.__upper_random_wait = value
|
|
229
|
+
|
|
230
|
+
@property
|
|
231
|
+
def lower_random_wait(self) -> Optional[float]:
|
|
232
|
+
"""
|
|
233
|
+
The lower possible random wait
|
|
234
|
+
|
|
235
|
+
:return: lower waiting time
|
|
236
|
+
:rtype: float
|
|
237
|
+
"""
|
|
238
|
+
return self.__lower_random_wait
|
|
239
|
+
|
|
240
|
+
@lower_random_wait.setter
|
|
241
|
+
def lower_random_wait(self, value: float) -> None:
|
|
242
|
+
self.__lower_random_wait = value
|
|
243
|
+
|
|
244
|
+
@property
|
|
245
|
+
def lambdaS(self) -> Optional[float]:
|
|
246
|
+
"""
|
|
247
|
+
Get or set the attribute lambda.
|
|
248
|
+
|
|
249
|
+
:return: lambda attribute
|
|
250
|
+
:rtype: float
|
|
251
|
+
"""
|
|
252
|
+
return self.__lambdaS
|
|
253
|
+
|
|
254
|
+
@lambdaS.setter
|
|
255
|
+
def lambdaS(self, lambdaS: float) -> None:
|
|
256
|
+
if self.__initReady:
|
|
257
|
+
print(
|
|
258
|
+
"You can not set mu parameter AFTER calling init simulator " "method."
|
|
259
|
+
)
|
|
260
|
+
return
|
|
261
|
+
self.__lambdaS = lambdaS
|
|
262
|
+
|
|
263
|
+
@property
|
|
264
|
+
def mu(self) -> Optional[float]:
|
|
265
|
+
"""
|
|
266
|
+
Get or set the attribute mu.
|
|
267
|
+
|
|
268
|
+
:return: mu attribute
|
|
269
|
+
:rtype: float
|
|
270
|
+
"""
|
|
271
|
+
return self.__mu
|
|
272
|
+
|
|
273
|
+
@mu.setter
|
|
274
|
+
def mu(self, mu: float) -> None:
|
|
275
|
+
if self.__initReady:
|
|
276
|
+
print(
|
|
277
|
+
"You can not set mu parameter AFTER calling init simulator " "method."
|
|
278
|
+
)
|
|
279
|
+
return
|
|
280
|
+
self.__mu = mu
|
|
281
|
+
|
|
282
|
+
@property
|
|
283
|
+
def seedX(self) -> Optional[int]:
|
|
284
|
+
"""
|
|
285
|
+
Get or set the attribute seedX.
|
|
286
|
+
|
|
287
|
+
:return: the seed of random X generator
|
|
288
|
+
:rtype: int
|
|
289
|
+
"""
|
|
290
|
+
return self.__seedX
|
|
291
|
+
|
|
292
|
+
@seedX.setter
|
|
293
|
+
def seedX(self, seedX: int) -> None:
|
|
294
|
+
if self.__initReady:
|
|
295
|
+
print(
|
|
296
|
+
"You can not set mu parameter AFTER calling init simulator " "method."
|
|
297
|
+
)
|
|
298
|
+
return
|
|
299
|
+
self.__seedX = seedX
|
|
300
|
+
|
|
301
|
+
@property
|
|
302
|
+
def seedY(self) -> Optional[int]:
|
|
303
|
+
"""
|
|
304
|
+
Get or set the attribute seedY.
|
|
305
|
+
|
|
306
|
+
:return: the seed of random Y generator
|
|
307
|
+
:rtype: int
|
|
308
|
+
"""
|
|
309
|
+
return self.__seedY
|
|
310
|
+
|
|
311
|
+
@seedY.setter
|
|
312
|
+
def seedY(self, seedY: int) -> None:
|
|
313
|
+
if self.__initReady:
|
|
314
|
+
print(
|
|
315
|
+
"You can not set mu parameter AFTER calling init simulator " "method."
|
|
316
|
+
)
|
|
317
|
+
return
|
|
318
|
+
self.__seedY = seedY
|
|
319
|
+
|
|
320
|
+
@property
|
|
321
|
+
def seedZ(self) -> Optional[int]:
|
|
322
|
+
"""
|
|
323
|
+
Get or set the attribute seedZ.
|
|
324
|
+
|
|
325
|
+
:return: the seed of random Z generator
|
|
326
|
+
:rtype: int
|
|
327
|
+
"""
|
|
328
|
+
return self.__seedZ
|
|
329
|
+
|
|
330
|
+
@seedZ.setter
|
|
331
|
+
def seedZ(self, seedZ: int) -> None:
|
|
332
|
+
if self.__initReady:
|
|
333
|
+
print(
|
|
334
|
+
"You can not set mu parameter AFTER calling init simulator " "method."
|
|
335
|
+
)
|
|
336
|
+
return
|
|
337
|
+
self.__seedZ = seedZ
|
|
338
|
+
|
|
339
|
+
@property
|
|
340
|
+
def seedRandomWait(self) -> Optional[int]:
|
|
341
|
+
"""
|
|
342
|
+
Get or set the attribute seedRandomWait.
|
|
343
|
+
|
|
344
|
+
:return: the seed of random random-wait generator
|
|
345
|
+
:rtype: int
|
|
346
|
+
"""
|
|
347
|
+
return self.__seedRandomWait
|
|
348
|
+
|
|
349
|
+
@seedRandomWait.setter
|
|
350
|
+
def seedRandomWait(self, seedRandomWait: int) -> None:
|
|
351
|
+
if self.__initReady:
|
|
352
|
+
print(
|
|
353
|
+
"You can not set mu parameter AFTER calling init simulator " "method."
|
|
354
|
+
)
|
|
355
|
+
return
|
|
356
|
+
self.__seedRandomWait = seedRandomWait
|
|
357
|
+
|
|
358
|
+
@property
|
|
359
|
+
def goalConnections(self) -> Optional[int]:
|
|
360
|
+
"""
|
|
361
|
+
Get or set the attribute goalConnections.
|
|
362
|
+
|
|
363
|
+
:return: The goal connections
|
|
364
|
+
:rtype: int
|
|
365
|
+
"""
|
|
366
|
+
return self.__goalConnections
|
|
367
|
+
|
|
368
|
+
@goalConnections.setter
|
|
369
|
+
def goalConnections(self, goalConnections: int) -> None:
|
|
370
|
+
if self.__initReady:
|
|
371
|
+
print(
|
|
372
|
+
"You can not set mu parameter AFTER calling init simulator " "method."
|
|
373
|
+
)
|
|
374
|
+
return
|
|
375
|
+
self.__goalConnections = goalConnections
|
|
376
|
+
|
|
377
|
+
def print_initial_info(self):
|
|
378
|
+
"""
|
|
379
|
+
Prints the initial info of simulation.
|
|
380
|
+
"""
|
|
381
|
+
print(
|
|
382
|
+
"Scenario:\t %s x %s x %s"
|
|
383
|
+
% (
|
|
384
|
+
self.__controller.scenario.length,
|
|
385
|
+
self.__controller.scenario.width,
|
|
386
|
+
self.__controller.scenario.height,
|
|
387
|
+
)
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
print(f"Number of VLeds: {self.__controller.scenario.numberOfVLeds}")
|
|
391
|
+
print("Positions:")
|
|
392
|
+
print(" ID | X | Y | Z |")
|
|
393
|
+
print("-" * 47)
|
|
394
|
+
for vled in self.__controller.scenario.vleds:
|
|
395
|
+
print(
|
|
396
|
+
"{:>10} | {:8.4f} | {:8.4f} | {:8.4f} |".format(
|
|
397
|
+
vled.ID, vled.x, vled.y, vled.z
|
|
398
|
+
)
|
|
399
|
+
)
|
|
400
|
+
print()
|
|
401
|
+
print(f"Number of RFs: {self.__controller.scenario.numberOfRFs}")
|
|
402
|
+
print("Positions:")
|
|
403
|
+
print(" ID | X | Y | Z |")
|
|
404
|
+
print("-" * 47)
|
|
405
|
+
for rf in self.__controller.scenario.rfs:
|
|
406
|
+
print(
|
|
407
|
+
"{:>10} | {:8.4f} | {:8.4f} | {:8.4f} |".format(
|
|
408
|
+
rf.ID, rf.x, rf.y, rf.z
|
|
409
|
+
)
|
|
410
|
+
)
|
|
411
|
+
print()
|
|
412
|
+
|
|
413
|
+
print("=" * 146)
|
|
414
|
+
print("| Time ", end="")
|
|
415
|
+
print("| Event ", end="")
|
|
416
|
+
print("| Receiver ", end="")
|
|
417
|
+
print("| X ", end="")
|
|
418
|
+
print("| Y ", end="")
|
|
419
|
+
print("| Z ", end="")
|
|
420
|
+
print("| Access Point (A.C.) ", end="")
|
|
421
|
+
print("| Goal time ", end="")
|
|
422
|
+
print("| Elapsed time ", end="")
|
|
423
|
+
print("| SNR ", end="")
|
|
424
|
+
print("| Req. Cap. |")
|
|
425
|
+
print("=" * 146)
|
|
426
|
+
|
|
427
|
+
def print_row(self, event: Event) -> None:
|
|
428
|
+
"""
|
|
429
|
+
print every simulation row
|
|
430
|
+
|
|
431
|
+
:param event: The event to print_
|
|
432
|
+
:type event: Event
|
|
433
|
+
"""
|
|
434
|
+
text = ""
|
|
435
|
+
text = "{:9.4f}".format(event.time) + "|"
|
|
436
|
+
if event.type == Event.event.ARRIVE:
|
|
437
|
+
text += " ARRIVE |"
|
|
438
|
+
elif event.type == Event.event.RESUME:
|
|
439
|
+
text += " RESUME |"
|
|
440
|
+
elif event.type == Event.event.PAUSE:
|
|
441
|
+
text += " PAUSE |"
|
|
442
|
+
elif event.type == Event.event.DEPARTURE:
|
|
443
|
+
text += " DEPARTURE |"
|
|
444
|
+
elif event.type == Event.event.NEXT_CONNECTION_TRY:
|
|
445
|
+
text += " RETRYING |"
|
|
446
|
+
text += "{:>10}".format(event.id_connection) + " |"
|
|
447
|
+
if event.connection:
|
|
448
|
+
text += "{:8.4f}".format(event.connection.receiver.x) + " |"
|
|
449
|
+
text += "{:8.4f}".format(event.connection.receiver.y) + " |"
|
|
450
|
+
text += "{:8.4f}".format(event.connection.receiver.z) + " |"
|
|
451
|
+
else:
|
|
452
|
+
text += " N/A | N/A | N/A |"
|
|
453
|
+
if event.connection and isinstance(event.connection.AP, VLed):
|
|
454
|
+
text += (
|
|
455
|
+
" VLed: {:>5}".format(event.connection.AP.ID)
|
|
456
|
+
+ " ({:^5})".format(
|
|
457
|
+
self.__controller.numberOfActiveConnections(event.connection.AP)
|
|
458
|
+
)
|
|
459
|
+
+ " |"
|
|
460
|
+
)
|
|
461
|
+
elif event.connection and isinstance(event.connection.AP, RF):
|
|
462
|
+
text += (
|
|
463
|
+
" RF: {:>5}".format(event.connection.AP.ID)
|
|
464
|
+
+ " ({:^5})".format(
|
|
465
|
+
self.__controller.numberOfActiveConnections(event.connection.AP)
|
|
466
|
+
)
|
|
467
|
+
+ " |"
|
|
468
|
+
)
|
|
469
|
+
else:
|
|
470
|
+
text += " NOT_SELECTED |"
|
|
471
|
+
if event.connection and event.connection.allocated:
|
|
472
|
+
text += "{:10.4f}".format(event.connection.receiver.goalTime) + " |"
|
|
473
|
+
else:
|
|
474
|
+
text += " NOT_ALLOC |"
|
|
475
|
+
if event.connection:
|
|
476
|
+
text += "{:12.4f}".format(event.connection.receiver.timeActive) + " |"
|
|
477
|
+
else:
|
|
478
|
+
text += " N/A |"
|
|
479
|
+
# if event.connection != None:
|
|
480
|
+
# print(
|
|
481
|
+
# event.time,
|
|
482
|
+
# event.type,
|
|
483
|
+
# event.id_connection,
|
|
484
|
+
# event.connection.receiver.goalTime,
|
|
485
|
+
# event.connection.receiver.timeActive,
|
|
486
|
+
# )
|
|
487
|
+
# else:
|
|
488
|
+
# print(
|
|
489
|
+
# event.time,
|
|
490
|
+
# event.type,
|
|
491
|
+
# event.id_connection,
|
|
492
|
+
# )
|
|
493
|
+
if event.connection:
|
|
494
|
+
text += "{:10.2e}".format(event.connection.snr) + " |"
|
|
495
|
+
text += "{:10.2e}".format(event.connection.capacityRequired) + " |"
|
|
496
|
+
else:
|
|
497
|
+
text += " N/A | N/A |"
|
|
498
|
+
print(text)
|
|
499
|
+
|
|
500
|
+
def event_routine(self):
|
|
501
|
+
"""
|
|
502
|
+
The event routine. This function selects the event and run the associated routine.
|
|
503
|
+
"""
|
|
504
|
+
self.__current_event = self.__events[0]
|
|
505
|
+
self.__rtn_allocation = None
|
|
506
|
+
self.__clock = self.__current_event.time
|
|
507
|
+
# print()
|
|
508
|
+
# print(self.__controller.activeConnections[0])
|
|
509
|
+
# print(self.__controller.activeConnections[1])
|
|
510
|
+
# print(self.__controller.activeConnections[2])
|
|
511
|
+
# print(self.__controller.activeConnections[3])
|
|
512
|
+
# print(self.__controller.activeConnections[4])
|
|
513
|
+
if self.__current_event.type == Event.event.ARRIVE:
|
|
514
|
+
next_event_time = self.__clock + self.__arrival_variable.exponential(
|
|
515
|
+
self.__lambdaS
|
|
516
|
+
)
|
|
517
|
+
for pos in range(len(self.__events) - 1, -1, -1):
|
|
518
|
+
if self.__events[pos].time < next_event_time:
|
|
519
|
+
self.__events.insert(
|
|
520
|
+
pos + 1,
|
|
521
|
+
Event(
|
|
522
|
+
Event.event.ARRIVE,
|
|
523
|
+
next_event_time,
|
|
524
|
+
self.__numberOfConnections,
|
|
525
|
+
),
|
|
526
|
+
)
|
|
527
|
+
self.__numberOfConnections += 1
|
|
528
|
+
break
|
|
529
|
+
|
|
530
|
+
self.__x = self.__x_variable.uniform(
|
|
531
|
+
low=self.__controller.scenario.start_x,
|
|
532
|
+
high=self.__controller.scenario.end_x,
|
|
533
|
+
)
|
|
534
|
+
self.__y = self.__y_variable.uniform(
|
|
535
|
+
low=self.__controller.scenario.start_y,
|
|
536
|
+
high=self.__controller.scenario.end_y,
|
|
537
|
+
)
|
|
538
|
+
self.__z = self.__z_variable.uniform(
|
|
539
|
+
low=0,
|
|
540
|
+
high=self.__controller.scenario.height,
|
|
541
|
+
)
|
|
542
|
+
receiver = Receiver(self.__x, self.__y, self.__z, 1e-4, 1.0, 1.5, 70.0)
|
|
543
|
+
connection = Connection(
|
|
544
|
+
self.__current_event.id_connection, receiver, self.__clock
|
|
545
|
+
)
|
|
546
|
+
connection.capacityRequired = self.__capacity_required_variable.uniform(
|
|
547
|
+
self.__lower_capacity_required, self.__upper_capacity_required
|
|
548
|
+
)
|
|
549
|
+
# connection.receiver.goalTime = connection.goalTime
|
|
550
|
+
next_status, time, connection = self.__controller.assignConnection(
|
|
551
|
+
connection, self.__clock
|
|
552
|
+
)
|
|
553
|
+
if (
|
|
554
|
+
next_status == Controller.nextStatus.RESUME
|
|
555
|
+
or next_status == Controller.nextStatus.PAUSE
|
|
556
|
+
):
|
|
557
|
+
if type(connection.AP) == VLed and connection.AP.ID is not None:
|
|
558
|
+
self.__users_by_vlc[connection.AP.ID] += 1
|
|
559
|
+
elif type(connection.AP) == RF and connection.AP.ID is not None:
|
|
560
|
+
self.__users_by_rf[connection.AP.ID] += 1
|
|
561
|
+
self.__current_event.connection = connection
|
|
562
|
+
if next_status == Controller.nextStatus.RESUME:
|
|
563
|
+
for pos in range(len(self.__events) - 1, -1, -1):
|
|
564
|
+
if self.__events[pos].time <= time:
|
|
565
|
+
e = Event(
|
|
566
|
+
Event.event.RESUME,
|
|
567
|
+
time,
|
|
568
|
+
connection.id,
|
|
569
|
+
)
|
|
570
|
+
e.connection = connection
|
|
571
|
+
self.__events.insert(pos + 1, e)
|
|
572
|
+
break
|
|
573
|
+
self.__allocatedConnections += 1
|
|
574
|
+
elif next_status == Controller.nextStatus.RND_WAIT:
|
|
575
|
+
self.__current_event.connection = connection
|
|
576
|
+
next_event_time = self.__clock + self.__random_wait_variable.uniform(
|
|
577
|
+
low=self.__lower_random_wait,
|
|
578
|
+
high=self.upper_random_wait,
|
|
579
|
+
)
|
|
580
|
+
next_event = Event(
|
|
581
|
+
Event.event.NEXT_CONNECTION_TRY,
|
|
582
|
+
next_event_time,
|
|
583
|
+
connection.id,
|
|
584
|
+
)
|
|
585
|
+
next_event.connection = connection
|
|
586
|
+
connection.allocated = False
|
|
587
|
+
for pos in range(len(self.__events) - 1, -1, -1):
|
|
588
|
+
if self.__events[pos].time < next_event_time:
|
|
589
|
+
self.__events.insert(pos + 1, next_event)
|
|
590
|
+
break
|
|
591
|
+
elif self.__current_event.type == Event.event.NEXT_CONNECTION_TRY:
|
|
592
|
+
if not self.__current_event.connection:
|
|
593
|
+
return
|
|
594
|
+
next_status, time, connection = self.__controller.assignConnection(
|
|
595
|
+
self.__current_event.connection, self.__clock
|
|
596
|
+
)
|
|
597
|
+
if (
|
|
598
|
+
next_status == Controller.nextStatus.RESUME
|
|
599
|
+
or next_status == Controller.nextStatus.PAUSE
|
|
600
|
+
):
|
|
601
|
+
connection.receiver.goalTime = self.__departure_variable.exponential(
|
|
602
|
+
self.__mu
|
|
603
|
+
)
|
|
604
|
+
connection.allocated = True
|
|
605
|
+
if next_status == Controller.nextStatus.RESUME:
|
|
606
|
+
for pos in range(len(self.__events) - 1, -1, -1):
|
|
607
|
+
if self.__events[pos].time <= time:
|
|
608
|
+
e = Event(
|
|
609
|
+
Event.event.RESUME,
|
|
610
|
+
time,
|
|
611
|
+
connection.id,
|
|
612
|
+
)
|
|
613
|
+
e.connection = connection
|
|
614
|
+
self.__events.insert(pos + 1, e)
|
|
615
|
+
break
|
|
616
|
+
self.__allocatedConnections += 1
|
|
617
|
+
elif next_status == Controller.nextStatus.RND_WAIT:
|
|
618
|
+
next_event_time = self.__clock + self.__random_wait_variable.uniform(
|
|
619
|
+
low=self.__lower_random_wait,
|
|
620
|
+
high=self.upper_random_wait,
|
|
621
|
+
)
|
|
622
|
+
next_event = Event(
|
|
623
|
+
Event.event.NEXT_CONNECTION_TRY,
|
|
624
|
+
next_event_time,
|
|
625
|
+
connection.id,
|
|
626
|
+
)
|
|
627
|
+
next_event.connection = connection
|
|
628
|
+
for pos in range(len(self.__events) - 1, -1, -1):
|
|
629
|
+
if self.__events[pos].time < next_event_time:
|
|
630
|
+
self.__events.insert(pos + 1, next_event)
|
|
631
|
+
break
|
|
632
|
+
elif self.__current_event.type == Event.event.PAUSE:
|
|
633
|
+
if not self.__current_event.connection:
|
|
634
|
+
return
|
|
635
|
+
next_status, time, connection = self.__controller.pauseConnection(
|
|
636
|
+
self.__current_event.connection, self.__clock
|
|
637
|
+
)
|
|
638
|
+
for pos in range(len(self.__events) - 1, -1, -1):
|
|
639
|
+
if self.__events[pos].time <= time:
|
|
640
|
+
e = Event(
|
|
641
|
+
Event.event.RESUME,
|
|
642
|
+
time,
|
|
643
|
+
connection.id,
|
|
644
|
+
)
|
|
645
|
+
e.connection = connection
|
|
646
|
+
self.__events.insert(pos + 1, e)
|
|
647
|
+
break
|
|
648
|
+
elif self.__current_event.type == Event.event.RESUME:
|
|
649
|
+
if not self.__current_event.connection:
|
|
650
|
+
return
|
|
651
|
+
next_status, time, connection = self.__controller.resumeConnection(
|
|
652
|
+
self.__current_event.connection, self.__clock
|
|
653
|
+
)
|
|
654
|
+
for pos in range(len(self.__events) - 1, -1, -1):
|
|
655
|
+
if self.__events[pos].time <= time:
|
|
656
|
+
e = None
|
|
657
|
+
if next_status == Controller.nextStatus.PAUSE:
|
|
658
|
+
e = Event(
|
|
659
|
+
Event.event.PAUSE,
|
|
660
|
+
time,
|
|
661
|
+
connection.id,
|
|
662
|
+
)
|
|
663
|
+
else:
|
|
664
|
+
e = Event(
|
|
665
|
+
Event.event.DEPARTURE,
|
|
666
|
+
time,
|
|
667
|
+
connection.id,
|
|
668
|
+
)
|
|
669
|
+
e.connection = connection
|
|
670
|
+
self.__events.insert(pos + 1, e)
|
|
671
|
+
break
|
|
672
|
+
elif self.__current_event.type == Event.event.DEPARTURE:
|
|
673
|
+
if not self.__current_event.connection:
|
|
674
|
+
return
|
|
675
|
+
next_status, time, connection = self.__controller.unassignConnection(
|
|
676
|
+
self.__current_event.connection, self.__clock
|
|
677
|
+
)
|
|
678
|
+
# if next_status == Controller.nextStatus.RESUME:
|
|
679
|
+
# for pos in range(len(self.__events) - 1, -1, -1):
|
|
680
|
+
# if self.__events[pos].time <= time:
|
|
681
|
+
# e = Event(
|
|
682
|
+
# Event.event.RESUME,
|
|
683
|
+
# time,
|
|
684
|
+
# connection.id,
|
|
685
|
+
# )
|
|
686
|
+
# e.connection = connection
|
|
687
|
+
# self.__events.insert(pos + 1, e)
|
|
688
|
+
# break
|
|
689
|
+
self.__events.pop(0)
|
|
690
|
+
|
|
691
|
+
return self.__rtn_allocation
|
|
692
|
+
|
|
693
|
+
def init(self):
|
|
694
|
+
"""
|
|
695
|
+
Simulation init. Initialize every simulation parameter.
|
|
696
|
+
"""
|
|
697
|
+
self.__initReady = True
|
|
698
|
+
self.__clock = 0
|
|
699
|
+
self.__arrival_variable = np.random.default_rng(self.__seedArrive)
|
|
700
|
+
self.__departure_variable = np.random.default_rng(self.__seedDeparture)
|
|
701
|
+
self.__x_variable = np.random.default_rng(self.__seedX)
|
|
702
|
+
self.__y_variable = np.random.default_rng(self.__seedY)
|
|
703
|
+
self.__z_variable = np.random.default_rng(self.__seedZ)
|
|
704
|
+
self.__random_wait_variable = np.random.default_rng(self.__seedZ)
|
|
705
|
+
self.__capacity_required_variable = np.random.default_rng(self.__seedZ)
|
|
706
|
+
self.__events.append(
|
|
707
|
+
Event(
|
|
708
|
+
Event.event.ARRIVE,
|
|
709
|
+
self.__arrival_variable.exponential(self.__lambdaS),
|
|
710
|
+
self.__numberOfConnections,
|
|
711
|
+
)
|
|
712
|
+
)
|
|
713
|
+
self.__numberOfConnections += 1
|
|
714
|
+
self.__allocatedConnections = 0
|
|
715
|
+
self.__controller.init()
|
|
716
|
+
if (
|
|
717
|
+
self.__controller.scenario.numberOfVLeds == 0
|
|
718
|
+
and self.__controller.scenario.numberOfRFs == 0
|
|
719
|
+
):
|
|
720
|
+
raise ValueError("The scenario does not have any Vleds or RFs")
|
|
721
|
+
for _ in range(self.__controller.scenario.numberOfVLeds):
|
|
722
|
+
self.__users_by_vlc.append(0)
|
|
723
|
+
for _ in range(self.__controller.scenario.numberOfRFs):
|
|
724
|
+
self.__users_by_rf.append(0)
|
|
725
|
+
return
|
|
726
|
+
|
|
727
|
+
def run(self):
|
|
728
|
+
"""
|
|
729
|
+
Run this simulation with the parameters.
|
|
730
|
+
"""
|
|
731
|
+
self.print_initial_info()
|
|
732
|
+
while self.__numberOfConnections <= self.__goalConnections:
|
|
733
|
+
# for i in range(self.__goalConnections):
|
|
734
|
+
self.event_routine()
|
|
735
|
+
if self.__current_event:
|
|
736
|
+
self.print_row(self.__current_event)
|
|
737
|
+
self.aggregated_metrics()
|
|
738
|
+
|
|
739
|
+
def time_duration(self) -> float:
|
|
740
|
+
"""
|
|
741
|
+
Duration time
|
|
742
|
+
|
|
743
|
+
:return: duration time
|
|
744
|
+
:rtype: float
|
|
745
|
+
"""
|
|
746
|
+
return self.__time_duration if self.__time_duration is not None else 0.0
|
|
747
|
+
|
|
748
|
+
def get_Blocking_Probability(self) -> float:
|
|
749
|
+
"""
|
|
750
|
+
Blocking probability
|
|
751
|
+
|
|
752
|
+
:return: blocking probability
|
|
753
|
+
:rtype: float
|
|
754
|
+
"""
|
|
755
|
+
blocking = round(
|
|
756
|
+
1 - self.__allocatedConnections / self.__numberOfConnections, 2
|
|
757
|
+
)
|
|
758
|
+
return blocking
|
|
759
|
+
|
|
760
|
+
def set_allocation_algorithm(
|
|
761
|
+
self,
|
|
762
|
+
alloc_alg: Callable[
|
|
763
|
+
[Receiver, Connection, Any, Controller], tuple[Any, Connection]
|
|
764
|
+
],
|
|
765
|
+
) -> None:
|
|
766
|
+
"""
|
|
767
|
+
Set allocation algorithm.
|
|
768
|
+
|
|
769
|
+
:param alloc_alg: Allocation algorithm
|
|
770
|
+
"""
|
|
771
|
+
self.__controller.allocator = alloc_alg
|
|
772
|
+
|
|
773
|
+
@property
|
|
774
|
+
def scenario(self):
|
|
775
|
+
"""
|
|
776
|
+
Scenario used in this simulation
|
|
777
|
+
|
|
778
|
+
:return: Scenario
|
|
779
|
+
:rtype: Scenario
|
|
780
|
+
"""
|
|
781
|
+
return self.__controller.scenario
|
|
782
|
+
|
|
783
|
+
def aggregated_metrics(self):
|
|
784
|
+
"""
|
|
785
|
+
Number of users connected to each AP
|
|
786
|
+
"""
|
|
787
|
+
print("Number of users connected to each VLed")
|
|
788
|
+
for i in range(self.__controller.scenario.numberOfVLeds):
|
|
789
|
+
id = self.__controller.scenario.vleds[i].ID
|
|
790
|
+
if id is not None:
|
|
791
|
+
print(f"VLed {id}: {self.__users_by_vlc[id]}")
|
|
792
|
+
|
|
793
|
+
print("Number of users connected to each RF")
|
|
794
|
+
for i in range(self.__controller.scenario.numberOfRFs):
|
|
795
|
+
id = self.__controller.scenario.rfs[i].ID
|
|
796
|
+
if id is not None:
|
|
797
|
+
print(f"RF {id}: {self.__users_by_rf[id]}")
|