salabim 24.0.18__tar.gz → 25.0.0__tar.gz
Sign up to get free protection for your applications and to get access to all the features.
- {salabim-24.0.18 → salabim-25.0.0}/PKG-INFO +2 -2
- {salabim-24.0.18 → salabim-25.0.0}/pyproject.toml +36 -30
- {salabim-24.0.18 → salabim-25.0.0}/salabim/LICENSE.txt +1 -1
- {salabim-24.0.18 → salabim-25.0.0}/salabim/salabim.py +195 -114
- {salabim-24.0.18 → salabim-25.0.0}/salabim.egg-info/PKG-INFO +2 -2
- {salabim-24.0.18 → salabim-25.0.0}/README.md +0 -0
- {salabim-24.0.18 → salabim-25.0.0}/salabim/DejaVuSansMono.ttf +0 -0
- {salabim-24.0.18 → salabim-25.0.0}/salabim/__init__.py +0 -0
- {salabim-24.0.18 → salabim-25.0.0}/salabim/calibri.ttf +0 -0
- {salabim-24.0.18 → salabim-25.0.0}/salabim/mplus-1m-regular.ttf +0 -0
- {salabim-24.0.18 → salabim-25.0.0}/salabim.egg-info/SOURCES.txt +0 -0
- {salabim-24.0.18 → salabim-25.0.0}/salabim.egg-info/dependency_links.txt +0 -0
- {salabim-24.0.18 → salabim-25.0.0}/salabim.egg-info/top_level.txt +0 -0
- {salabim-24.0.18 → salabim-25.0.0}/setup.cfg +0 -0
- {salabim-24.0.18 → salabim-25.0.0}/tests/test salabim.py +0 -0
- {salabim-24.0.18 → salabim-25.0.0}/tests/test_cap_now.py +0 -0
- {salabim-24.0.18 → salabim-25.0.0}/tests/test_componentgenerator.py +0 -0
- {salabim-24.0.18 → salabim-25.0.0}/tests/test_datetime.py +0 -0
- {salabim-24.0.18 → salabim-25.0.0}/tests/test_distributions.py +0 -0
- {salabim-24.0.18 → salabim-25.0.0}/tests/test_misc.py +0 -0
- {salabim-24.0.18 → salabim-25.0.0}/tests/test_monitor.py +0 -0
- {salabim-24.0.18 → salabim-25.0.0}/tests/test_process.py +0 -0
- {salabim-24.0.18 → salabim-25.0.0}/tests/test_queue.py +0 -0
- {salabim-24.0.18 → salabim-25.0.0}/tests/test_state.py +0 -0
- {salabim-24.0.18 → salabim-25.0.0}/tests/test_store.py +0 -0
- {salabim-24.0.18 → salabim-25.0.0}/tests/test_timeunit.py +0 -0
@@ -1,30 +1,36 @@
|
|
1
|
-
[build-system]
|
2
|
-
requires = [
|
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
|
-
|
1
|
+
[build-system]
|
2
|
+
requires = [
|
3
|
+
"setuptools",
|
4
|
+
]
|
5
|
+
build-backend = "setuptools.build_meta"
|
6
|
+
|
7
|
+
[project]
|
8
|
+
name = "salabim"
|
9
|
+
authors = [
|
10
|
+
{ name = "Ruud van der Ham", email = "rt.van.der.ham@gmail.com" },
|
11
|
+
]
|
12
|
+
description = "salabim - discrete event simulation in Python"
|
13
|
+
version = "25.0.0"
|
14
|
+
readme = "README.md"
|
15
|
+
requires-python = ">=3.7"
|
16
|
+
dependencies = []
|
17
|
+
classifiers = [
|
18
|
+
"Development Status :: 5 - Production/Stable",
|
19
|
+
"License :: OSI Approved :: MIT License",
|
20
|
+
"Programming Language :: Python :: 3 :: Only",
|
21
|
+
]
|
22
|
+
|
23
|
+
[project.urls]
|
24
|
+
Homepage = "https://salabim.org"
|
25
|
+
Repository = "https://github.com/salabim/salabim"
|
26
|
+
|
27
|
+
[tool.setuptools]
|
28
|
+
packages = [
|
29
|
+
"salabim",
|
30
|
+
]
|
31
|
+
|
32
|
+
[tool.setuptools.package-data]
|
33
|
+
"*" = [
|
34
|
+
"*.ttf",
|
35
|
+
"*.txt",
|
36
|
+
]
|
@@ -1,6 +1,6 @@
|
|
1
1
|
The MIT License (MIT)
|
2
2
|
|
3
|
-
Copyright (C) 2017-
|
3
|
+
Copyright (C) 2017-2025 Ruud van der Ham, ruud@salabim.org
|
4
4
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
6
|
this software and associated documentation files (the "Software"), to deal in
|
@@ -1,14 +1,13 @@
|
|
1
|
-
# _ _ _ ____
|
2
|
-
# ___ __ _ | | __ _ | |__ (_) _ __ ___ |___ \ |
|
3
|
-
# / __| / _` || | / _` || '_ \ | || '_ ` _ \ __) ||
|
4
|
-
# \__ \| (_| || || (_| || |_) || || | | | | | / __/ |
|
5
|
-
# |___/ \__,_||_| \__,_||_.__/ |_||_| |_| |_| |_____
|
1
|
+
# _ _ _ ____ ____ ___ ___
|
2
|
+
# ___ __ _ | | __ _ | |__ (_) _ __ ___ |___ \ | ___| / _ \ / _ \
|
3
|
+
# / __| / _` || | / _` || '_ \ | || '_ ` _ \ __) ||___ \ | | | | | | | |
|
4
|
+
# \__ \| (_| || || (_| || |_) || || | | | | | / __/ ___) | _ | |_| | _ | |_| |
|
5
|
+
# |___/ \__,_||_| \__,_||_.__/ |_||_| |_| |_| |_____||____/ (_) \___/ (_) \___/
|
6
6
|
# discrete event simulation
|
7
7
|
#
|
8
8
|
# see www.salabim.org for more information, the documentation and license information
|
9
9
|
|
10
|
-
__version__ = "
|
11
|
-
|
10
|
+
__version__ = "25.0.0"
|
12
11
|
import heapq
|
13
12
|
import random
|
14
13
|
import time
|
@@ -42,8 +41,12 @@ import urllib.request
|
|
42
41
|
import urllib.error
|
43
42
|
import base64
|
44
43
|
import zipfile
|
45
|
-
|
46
44
|
from pathlib import Path
|
45
|
+
try:
|
46
|
+
import peek
|
47
|
+
except ModuleNotFoundError:
|
48
|
+
...
|
49
|
+
|
47
50
|
|
48
51
|
from typing import Any, Union, Iterable, Tuple, List, Callable, TextIO, Dict, Set, Type, Hashable, Optional
|
49
52
|
|
@@ -61,6 +64,46 @@ Chromebook = "penguin" in platform.uname()
|
|
61
64
|
PythonInExcel = not ("__file__" in globals())
|
62
65
|
AnacondaCode = sys.platform == "emscripten"
|
63
66
|
|
67
|
+
_color_name_to_ANSI = dict(
|
68
|
+
dark_black="\033[0;30m",
|
69
|
+
dark_red="\033[0;31m",
|
70
|
+
dark_green="\033[0;32m",
|
71
|
+
dark_yellow="\033[0;33m",
|
72
|
+
dark_blue="\033[0;34m",
|
73
|
+
dark_magenta="\033[0;35m",
|
74
|
+
dark_cyan="\033[0;36m",
|
75
|
+
dark_white="\033[0;37m",
|
76
|
+
black="\033[1;30m",
|
77
|
+
red="\033[1;31m",
|
78
|
+
green="\033[1;32m",
|
79
|
+
yellow="\033[1;33m",
|
80
|
+
blue="\033[1;34m",
|
81
|
+
magenta="\033[1;35m",
|
82
|
+
cyan="\033[1;36m",
|
83
|
+
white="\033[1;37m",
|
84
|
+
reset="\033[0m",
|
85
|
+
)
|
86
|
+
_ANSI_to_rgb = {
|
87
|
+
"\033[1;30m": (51, 51, 51),
|
88
|
+
"\033[1;31m": (255, 0, 0),
|
89
|
+
"\033[1;32m": (0, 255, 0),
|
90
|
+
"\033[1;33m": (255, 255, 0),
|
91
|
+
"\033[1;34m": (0, 178, 255),
|
92
|
+
"\033[1;35m": (255, 0, 255),
|
93
|
+
"\033[1;36m": (0, 255, 255),
|
94
|
+
"\033[1;37m": (255, 255, 255),
|
95
|
+
"\033[0;30m": (76, 76, 76),
|
96
|
+
"\033[0;31m": (178, 0, 0),
|
97
|
+
"\033[0;32m": (0, 178, 0),
|
98
|
+
"\033[0;33m": (178, 178, 0),
|
99
|
+
"\033[0;34m": (0, 89, 255),
|
100
|
+
"\033[0;35m": (178, 0, 178),
|
101
|
+
"\033[0;36m": (0, 178, 178),
|
102
|
+
"\033[0;37m": (178, 178, 178),
|
103
|
+
"\033[0m": (),
|
104
|
+
}
|
105
|
+
|
106
|
+
ANSI=types.SimpleNamespace(_color_name_to_ANSI.items())
|
64
107
|
|
65
108
|
def a_log(*args):
|
66
109
|
if not hasattr(a_log, "a_logfile_name"):
|
@@ -4866,7 +4909,7 @@ class Queue:
|
|
4866
4909
|
"""
|
4867
4910
|
return getattr(self, "_sequence_number", 1)
|
4868
4911
|
|
4869
|
-
def add(self, component: "Component") -> "Queue":
|
4912
|
+
def add(self, component: "Component", priority: float = None) -> "Queue":
|
4870
4913
|
"""
|
4871
4914
|
adds a component to the tail of a queue
|
4872
4915
|
|
@@ -4877,18 +4920,23 @@ class Queue:
|
|
4877
4920
|
|
4878
4921
|
may not be member of the queue yet
|
4879
4922
|
|
4923
|
+
priority : float
|
4924
|
+
if None (default), add to the tail of the queue
|
4925
|
+
|
4926
|
+
otherwise, put after the last component with the same priority
|
4927
|
+
|
4880
4928
|
Note
|
4881
4929
|
----
|
4882
|
-
the priority will be set to
|
4930
|
+
if prioority is None, the priority will be set to
|
4883
4931
|
the priority of the tail of the queue, if any
|
4884
4932
|
or 0 if queue is empty
|
4885
4933
|
|
4886
4934
|
This method is equivalent to append()
|
4887
4935
|
"""
|
4888
|
-
component.enter(self)
|
4936
|
+
component.enter(self, priority)
|
4889
4937
|
return self
|
4890
4938
|
|
4891
|
-
def append(self, component: "Component") -> "Queue":
|
4939
|
+
def append(self, component: "Component", priority: float = None) -> "Queue":
|
4892
4940
|
"""
|
4893
4941
|
appends a component to the tail of a queue
|
4894
4942
|
|
@@ -4899,9 +4947,14 @@ class Queue:
|
|
4899
4947
|
|
4900
4948
|
may not be member of the queue yet
|
4901
4949
|
|
4950
|
+
priority : float
|
4951
|
+
if None (default), add to the tail of the queue
|
4952
|
+
|
4953
|
+
otherwise, put after the last component with the same priority
|
4954
|
+
|
4902
4955
|
Note
|
4903
4956
|
----
|
4904
|
-
the priority will be set to
|
4957
|
+
if priority is None, the priority will be set to
|
4905
4958
|
the priority of the tail of the queue, if any
|
4906
4959
|
or 0 if queue is empty
|
4907
4960
|
|
@@ -5013,7 +5066,7 @@ class Queue:
|
|
5013
5066
|
component.enter_behind(self, poscomponent)
|
5014
5067
|
return self
|
5015
5068
|
|
5016
|
-
def add_sorted(self, component: "Component", priority:
|
5069
|
+
def add_sorted(self, component: "Component", priority: float) -> "Queue":
|
5017
5070
|
"""
|
5018
5071
|
adds a component to a queue, according to the priority
|
5019
5072
|
|
@@ -5024,7 +5077,7 @@ class Queue:
|
|
5024
5077
|
|
5025
5078
|
may not be member of the queue yet
|
5026
5079
|
|
5027
|
-
priority:
|
5080
|
+
priority: float
|
5028
5081
|
priority in the queue
|
5029
5082
|
|
5030
5083
|
Note
|
@@ -7339,7 +7392,6 @@ by adding at the end:
|
|
7339
7392
|
self.status.reset(monitor=monitor, stats_only=stats_only)
|
7340
7393
|
self.mode.reset(monitor=monitor, stats_only=stats_only)
|
7341
7394
|
|
7342
|
-
|
7343
7395
|
def register(self, registry: List) -> "Component":
|
7344
7396
|
"""
|
7345
7397
|
registers the component in the registry
|
@@ -8073,6 +8125,7 @@ by adding:
|
|
8073
8125
|
self,
|
8074
8126
|
store: Union["Store", Iterable],
|
8075
8127
|
filter: Callable = lambda c: True,
|
8128
|
+
request_priority: Any = 0,
|
8076
8129
|
fail_priority: float = 0,
|
8077
8130
|
urgent: bool = True,
|
8078
8131
|
fail_at: float = None,
|
@@ -8096,6 +8149,9 @@ by adding:
|
|
8096
8149
|
|
8097
8150
|
default: lambda c: True (i.e. always return True)
|
8098
8151
|
|
8152
|
+
request_priority: float
|
8153
|
+
put component in to_store_requesters according to the given priority (default 0)
|
8154
|
+
|
8099
8155
|
fail_priority : float
|
8100
8156
|
priority of the fail event
|
8101
8157
|
|
@@ -8236,7 +8292,7 @@ by adding:
|
|
8236
8292
|
|
8237
8293
|
self._from_stores = from_stores
|
8238
8294
|
for store in from_stores:
|
8239
|
-
self.
|
8295
|
+
self.enter_sorted(store._from_store_requesters, priority=request_priority)
|
8240
8296
|
self.status._value = requesting
|
8241
8297
|
self._from_store_item = None
|
8242
8298
|
self._from_store_filter = filter
|
@@ -8249,6 +8305,7 @@ by adding:
|
|
8249
8305
|
self,
|
8250
8306
|
store: Union["Store", Iterable],
|
8251
8307
|
item: "Component",
|
8308
|
+
request_priority: float = None,
|
8252
8309
|
priority: float = 0,
|
8253
8310
|
fail_priority: float = 0,
|
8254
8311
|
urgent: bool = True,
|
@@ -8268,6 +8325,9 @@ by adding:
|
|
8268
8325
|
item: Component
|
8269
8326
|
component to put to store
|
8270
8327
|
|
8328
|
+
request_priority: float
|
8329
|
+
put component in to_store_requesters according to the given priority (default 0)
|
8330
|
+
|
8271
8331
|
fail_priority : float
|
8272
8332
|
priority of the fail event
|
8273
8333
|
|
@@ -8279,11 +8339,11 @@ by adding:
|
|
8279
8339
|
urgent : bool
|
8280
8340
|
urgency indicator
|
8281
8341
|
|
8282
|
-
if False (default), the
|
8342
|
+
if False (default), the fail event will be scheduled
|
8283
8343
|
behind all other components scheduled
|
8284
8344
|
for the same time and priority
|
8285
8345
|
|
8286
|
-
if True, the
|
8346
|
+
if True, the fail event will be scheduled
|
8287
8347
|
in front of all components scheduled
|
8288
8348
|
for the same time and priority
|
8289
8349
|
|
@@ -8381,7 +8441,7 @@ by adding:
|
|
8381
8441
|
return
|
8382
8442
|
|
8383
8443
|
for store in to_stores:
|
8384
|
-
self.
|
8444
|
+
self.enter_sorted(store._to_store_requesters, priority=request_priority)
|
8385
8445
|
self.status._value = requesting
|
8386
8446
|
self._to_store_item = item
|
8387
8447
|
self._to_store_priority = priority
|
@@ -8407,7 +8467,19 @@ by adding:
|
|
8407
8467
|
for store in self._from_stores:
|
8408
8468
|
store.rescan()
|
8409
8469
|
|
8410
|
-
def request(
|
8470
|
+
def request(
|
8471
|
+
self,
|
8472
|
+
*args,
|
8473
|
+
fail_at: float = None,
|
8474
|
+
fail_delay: float = None,
|
8475
|
+
mode: Any = None,
|
8476
|
+
urgent: bool = False,
|
8477
|
+
request_priority: float = 0,
|
8478
|
+
schedule_priority: float = 0,
|
8479
|
+
cap_now: bool = None,
|
8480
|
+
oneof: bool = False,
|
8481
|
+
called_from: str = "request",
|
8482
|
+
) -> None:
|
8411
8483
|
"""
|
8412
8484
|
request from a resource or resources
|
8413
8485
|
|
@@ -8417,9 +8489,12 @@ by adding:
|
|
8417
8489
|
- resource, where quantity=1, priority=tail of requesters queue
|
8418
8490
|
- tuples/list containing a resource, a quantity and optionally a priority.
|
8419
8491
|
if the priority is not specified, the request
|
8420
|
-
for the resource be added to the
|
8421
|
-
|
8492
|
+
for the resource will be added according to the request_priority parameter
|
8493
|
+
|
8494
|
+
request_priority: float
|
8495
|
+
(may be overridden by the priority parameter in the arg sequence)
|
8422
8496
|
|
8497
|
+
put component requesters according to the given priority (default 0)
|
8423
8498
|
|
8424
8499
|
priority : float
|
8425
8500
|
priority of the fail event
|
@@ -8522,17 +8597,7 @@ by adding:
|
|
8522
8597
|
--> requests 1 from r1, r2 or r3
|
8523
8598
|
|
8524
8599
|
"""
|
8525
|
-
fail_at = kwargs.pop("fail_at", None)
|
8526
|
-
fail_delay = kwargs.pop("fail_delay", None)
|
8527
|
-
mode = kwargs.pop("mode", None)
|
8528
|
-
urgent = kwargs.pop("urgent", False)
|
8529
|
-
schedule_priority = kwargs.pop("priority", 0)
|
8530
|
-
cap_now = kwargs.pop("cap_now", None)
|
8531
|
-
oneof = kwargs.pop("oneof", False)
|
8532
|
-
called_from = kwargs.pop("called_from", "request")
|
8533
8600
|
self.oneof_request = oneof
|
8534
|
-
if kwargs:
|
8535
|
-
raise TypeError(called_from + "() got an unexpected keyword argument '" + tuple(kwargs)[0] + "'")
|
8536
8601
|
|
8537
8602
|
if self.status.value != current:
|
8538
8603
|
self._checkisnotdata()
|
@@ -8565,7 +8630,7 @@ by adding:
|
|
8565
8630
|
return
|
8566
8631
|
for arg in args:
|
8567
8632
|
q = 1
|
8568
|
-
priority =
|
8633
|
+
priority = request_priority
|
8569
8634
|
if isinstance(arg, Resource):
|
8570
8635
|
r = arg
|
8571
8636
|
elif isinstance(arg, (tuple, list)):
|
@@ -8603,7 +8668,7 @@ by adding:
|
|
8603
8668
|
if self.oneof_request:
|
8604
8669
|
addstring += " (oneof)"
|
8605
8670
|
|
8606
|
-
self.
|
8671
|
+
self.enter(r._requesters, priority)
|
8607
8672
|
if self.env._trace:
|
8608
8673
|
self.env.print_trace("", "", self.name(), req_text + r.name() + addstring)
|
8609
8674
|
|
@@ -8852,7 +8917,7 @@ by adding:
|
|
8852
8917
|
for r in list(self._claims):
|
8853
8918
|
self._release(r)
|
8854
8919
|
|
8855
|
-
def wait_for(self,cond, states, priority=0, urgent=False, mode=None,fail_delay=None, fail_at=None, cap_now=None):
|
8920
|
+
def wait_for(self, cond, states, request_priority=0, priority=0, urgent=False, mode=None, fail_delay=None, fail_at=None, cap_now=None):
|
8856
8921
|
schedule_priority = priority
|
8857
8922
|
"""
|
8858
8923
|
wait for any or all of the given state values are met
|
@@ -8865,6 +8930,9 @@ by adding:
|
|
8865
8930
|
states : iterable
|
8866
8931
|
specicies which states should trigger the cond to be checked
|
8867
8932
|
|
8933
|
+
request_priority : float
|
8934
|
+
put component in waiters queue according to the given priority (deafult 0)
|
8935
|
+
|
8868
8936
|
priority : float
|
8869
8937
|
priority of the fail event
|
8870
8938
|
|
@@ -8954,9 +9022,9 @@ by adding:
|
|
8954
9022
|
|
8955
9023
|
self.set_mode(mode)
|
8956
9024
|
|
8957
|
-
self._cond=cond # add test ***
|
9025
|
+
self._cond = cond # add test ***
|
8958
9026
|
for state in states:
|
8959
|
-
self._waits.append((state, None,None))
|
9027
|
+
self._waits.append((state, None, None))
|
8960
9028
|
if priority is None:
|
8961
9029
|
self.enter(state._waiters)
|
8962
9030
|
else:
|
@@ -8975,9 +9043,18 @@ by adding:
|
|
8975
9043
|
else:
|
8976
9044
|
return
|
8977
9045
|
|
8978
|
-
|
8979
|
-
|
8980
|
-
|
9046
|
+
def wait(
|
9047
|
+
self,
|
9048
|
+
*args,
|
9049
|
+
fail_at: float = None,
|
9050
|
+
fail_delay: float = None,
|
9051
|
+
all: bool = False,
|
9052
|
+
mode: Any = None,
|
9053
|
+
urgent: bool = False,
|
9054
|
+
request_priority: float = 0,
|
9055
|
+
schedule_priority: float = 0,
|
9056
|
+
cap_now: bool = None,
|
9057
|
+
) -> None:
|
8981
9058
|
"""
|
8982
9059
|
wait for any or all of the given state values are met
|
8983
9060
|
|
@@ -8993,6 +9070,8 @@ by adding:
|
|
8993
9070
|
be added to the tail of
|
8994
9071
|
the waiters queue
|
8995
9072
|
|
9073
|
+
request_priority : float
|
9074
|
+
put component in waiters queue according to the given priority (default 0)
|
8996
9075
|
|
8997
9076
|
priority : float
|
8998
9077
|
priority of the fail event
|
@@ -9118,18 +9197,7 @@ by adding:
|
|
9118
9197
|
--> waits for s1.value()==True and s2.value==True
|
9119
9198
|
|
9120
9199
|
"""
|
9121
|
-
|
9122
|
-
fail_delay = kwargs.pop("fail_delay", None)
|
9123
|
-
all = kwargs.pop("all", False)
|
9124
|
-
mode = kwargs.pop("mode", None)
|
9125
|
-
urgent = kwargs.pop("urgent", False)
|
9126
|
-
schedule_priority = kwargs.pop("priority", 0)
|
9127
|
-
cap_now = kwargs.pop("cap_now", None)
|
9128
|
-
|
9129
|
-
self._cond=None
|
9130
|
-
|
9131
|
-
if kwargs:
|
9132
|
-
raise TypeError("wait() got an unexpected keyword argument '" + tuple(kwargs)[0] + "'")
|
9200
|
+
self._cond = None
|
9133
9201
|
|
9134
9202
|
if self.status.value != current:
|
9135
9203
|
self._checkisnotdata()
|
@@ -9160,7 +9228,7 @@ by adding:
|
|
9160
9228
|
|
9161
9229
|
for arg in args:
|
9162
9230
|
value = True
|
9163
|
-
priority =
|
9231
|
+
priority = request_priority
|
9164
9232
|
if isinstance(arg, State):
|
9165
9233
|
state = arg
|
9166
9234
|
elif isinstance(arg, (tuple, list)):
|
@@ -9168,10 +9236,8 @@ by adding:
|
|
9168
9236
|
if not isinstance(state, State):
|
9169
9237
|
raise TypeError("incorrect specifier", arg)
|
9170
9238
|
if len(arg) >= 2:
|
9171
|
-
|
9239
|
+
priority = arg[1]
|
9172
9240
|
if len(arg) >= 3:
|
9173
|
-
priority = arg[2]
|
9174
|
-
if len(arg) >= 4:
|
9175
9241
|
raise TypeError("incorrect specifier", arg)
|
9176
9242
|
else:
|
9177
9243
|
raise TypeError("incorrect specifier", arg)
|
@@ -9205,7 +9271,6 @@ by adding:
|
|
9205
9271
|
return
|
9206
9272
|
|
9207
9273
|
def _trywait(self):
|
9208
|
-
|
9209
9274
|
if self.status.value == interrupted:
|
9210
9275
|
return False
|
9211
9276
|
if self._cond:
|
@@ -9600,7 +9665,7 @@ by adding:
|
|
9600
9665
|
index += 1
|
9601
9666
|
return index
|
9602
9667
|
|
9603
|
-
def enter(self, q: "Queue") -> "Component":
|
9668
|
+
def enter(self, q: "Queue", priority: float = None) -> "Component":
|
9604
9669
|
"""
|
9605
9670
|
enters a queue at the tail
|
9606
9671
|
|
@@ -9616,8 +9681,17 @@ by adding:
|
|
9616
9681
|
or 0 if queue is empty
|
9617
9682
|
"""
|
9618
9683
|
self._checknotinqueue(q)
|
9619
|
-
priority
|
9620
|
-
|
9684
|
+
if priority is None:
|
9685
|
+
priority = q._tail.predecessor.priority
|
9686
|
+
Qmember().insert_in_front_of(q._tail, self, q, priority)
|
9687
|
+
else:
|
9688
|
+
if q._length >= 1 and priority < q._head.successor.priority: # direct enter component that's smaller than the rest
|
9689
|
+
m2 = q._head.successor
|
9690
|
+
else:
|
9691
|
+
m2 = q._tail
|
9692
|
+
while (m2.predecessor != q._head) and (m2.predecessor.priority > priority):
|
9693
|
+
m2 = m2.predecessor
|
9694
|
+
Qmember().insert_in_front_of(m2, self, q, priority)
|
9621
9695
|
return self
|
9622
9696
|
|
9623
9697
|
def enter_at_head(self, q: "Queue") -> "Component":
|
@@ -9696,22 +9770,14 @@ by adding:
|
|
9696
9770
|
q : Queue
|
9697
9771
|
queue to enter
|
9698
9772
|
|
9699
|
-
priority:
|
9773
|
+
priority: float
|
9700
9774
|
priority in the queue
|
9701
9775
|
|
9702
9776
|
Note
|
9703
9777
|
----
|
9704
9778
|
The component is placed just before the first component with a priority > given priority
|
9705
9779
|
"""
|
9706
|
-
self.
|
9707
|
-
if q._length >= 1 and priority < q._head.successor.priority: # direct enter component that's smaller than the rest
|
9708
|
-
m2 = q._head.successor
|
9709
|
-
else:
|
9710
|
-
m2 = q._tail
|
9711
|
-
while (m2.predecessor != q._head) and (m2.predecessor.priority > priority):
|
9712
|
-
m2 = m2.predecessor
|
9713
|
-
Qmember().insert_in_front_of(m2, self, q, priority)
|
9714
|
-
return self
|
9780
|
+
return self.enter(q, priority)
|
9715
9781
|
|
9716
9782
|
def leave(self, q: "Queue" = None) -> "Component":
|
9717
9783
|
"""
|
@@ -9777,7 +9843,7 @@ by adding:
|
|
9777
9843
|
q : Queue
|
9778
9844
|
queue where the component belongs to
|
9779
9845
|
|
9780
|
-
priority :
|
9846
|
+
priority : float
|
9781
9847
|
priority in queue
|
9782
9848
|
|
9783
9849
|
if omitted, no change
|
@@ -10919,8 +10985,8 @@ class Environment:
|
|
10919
10985
|
s = "view("
|
10920
10986
|
items = []
|
10921
10987
|
for prop in props.split():
|
10922
|
-
items.append(f"{getattr(self.view,prop)(t):.4f}")
|
10923
|
-
print("view(" + (",".join(f"{prop}={getattr(self.view,prop)(t):.4f}" for prop in props.split())) + f") # t={t:.4f}")
|
10988
|
+
items.append(f"{getattr(self.view, prop)(t):.4f}")
|
10989
|
+
print("view(" + (",".join(f"{prop}={getattr(self.view, prop)(t):.4f}" for prop in props.split())) + f") # t={t:.4f}")
|
10924
10990
|
|
10925
10991
|
def _bind(self, tkinter_event, func):
|
10926
10992
|
self.root.bind(tkinter_event, func)
|
@@ -11036,7 +11102,7 @@ class Environment:
|
|
11036
11102
|
ao.label = "fovy" if prop == "field_of_view_y" else prop
|
11037
11103
|
|
11038
11104
|
ao = AnimateText(
|
11039
|
-
text=lambda arg, t: f"{getattr(self.view,arg.prop)(t):11.3f}",
|
11105
|
+
text=lambda arg, t: f"{getattr(self.view, arg.prop)(t):11.3f}",
|
11040
11106
|
x=5 + i * 80 + 70,
|
11041
11107
|
y=top,
|
11042
11108
|
font="calibri",
|
@@ -11878,7 +11944,7 @@ class Environment:
|
|
11878
11944
|
format="GIF",
|
11879
11945
|
)
|
11880
11946
|
else:
|
11881
|
-
for _ in range(2):
|
11947
|
+
for _ in range(2): # normally runs only once
|
11882
11948
|
try:
|
11883
11949
|
self._images[0].save(
|
11884
11950
|
self._video_name,
|
@@ -11890,7 +11956,7 @@ class Environment:
|
|
11890
11956
|
)
|
11891
11957
|
break
|
11892
11958
|
except ValueError: # prevent bug in Python 3.13
|
11893
|
-
self._images=[image.convert("RGB") for image in self._images]
|
11959
|
+
self._images = [image.convert("RGB") for image in self._images]
|
11894
11960
|
|
11895
11961
|
else:
|
11896
11962
|
if PythonInExcel or AnacondaCode:
|
@@ -11906,9 +11972,8 @@ class Environment:
|
|
11906
11972
|
format="GIF",
|
11907
11973
|
)
|
11908
11974
|
else:
|
11909
|
-
for _ in range(2):
|
11975
|
+
for _ in range(2): # normally runs only once
|
11910
11976
|
try:
|
11911
|
-
|
11912
11977
|
self._images[0].save(
|
11913
11978
|
self._video_name,
|
11914
11979
|
disposal=2,
|
@@ -11919,7 +11984,7 @@ class Environment:
|
|
11919
11984
|
optimize=False,
|
11920
11985
|
)
|
11921
11986
|
except ValueError: # prevent bug in Python 3.13
|
11922
|
-
self._images=[image.convert("RGB") for image in self._images]
|
11987
|
+
self._images = [image.convert("RGB") for image in self._images]
|
11923
11988
|
|
11924
11989
|
del self._images
|
11925
11990
|
elif self._video_out == "png":
|
@@ -11983,7 +12048,6 @@ class Environment:
|
|
11983
12048
|
Number of 1/30 second long frames to be inserted
|
11984
12049
|
"""
|
11985
12050
|
|
11986
|
-
|
11987
12051
|
if self._video_out is None:
|
11988
12052
|
raise ValueError("video not set")
|
11989
12053
|
if isinstance(image, (Path, str)):
|
@@ -12009,7 +12073,6 @@ class Environment:
|
|
12009
12073
|
open_cv_image = cv2.cvtColor(numpy.array(image), cv2.COLOR_RGB2BGR)
|
12010
12074
|
self._video_out.write(open_cv_image)
|
12011
12075
|
|
12012
|
-
|
12013
12076
|
def _save_frame(self):
|
12014
12077
|
self._exclude_from_animation = "not in video"
|
12015
12078
|
image = self._capture_image("RGBA", self._video_mode)
|
@@ -12944,7 +13007,7 @@ class Environment:
|
|
12944
13007
|
self.animation_parameters(synced=value, animate=None)
|
12945
13008
|
return self._synced
|
12946
13009
|
|
12947
|
-
def minimized(self, value: bool=None)-> bool:
|
13010
|
+
def minimized(self, value: bool = None) -> bool:
|
12948
13011
|
"""
|
12949
13012
|
minimized
|
12950
13013
|
|
@@ -13212,7 +13275,7 @@ class Environment:
|
|
13212
13275
|
"""
|
13213
13276
|
return self._current_component
|
13214
13277
|
|
13215
|
-
def run(self, duration: float = None, till: float = None, priority:
|
13278
|
+
def run(self, duration: float = None, till: float = None, priority: float = inf, urgent: bool = False, cap_now: bool = None):
|
13216
13279
|
"""
|
13217
13280
|
start execution of the simulation
|
13218
13281
|
|
@@ -13352,7 +13415,6 @@ class Environment:
|
|
13352
13415
|
self._t = self.animation_start_time
|
13353
13416
|
else:
|
13354
13417
|
self._t = self.animation_start_time + ((time.time() - self.animation_start_clocktime) * self._speed)
|
13355
|
-
|
13356
13418
|
while self.peek() < self._t:
|
13357
13419
|
self.step()
|
13358
13420
|
if not (self.running and self._animate):
|
@@ -15780,29 +15842,49 @@ class Animate2dBase(DynamicClass):
|
|
15780
15842
|
im = Image.new("RGBA", (int(totwidth + 0.1 * fontsize), int(totheight)), (0, 0, 0, 0))
|
15781
15843
|
imwidth, imheight = im.size
|
15782
15844
|
draw = ImageDraw.Draw(im)
|
15783
|
-
|
15845
|
+
ypos = 0
|
15846
|
+
now_color = textcolor
|
15784
15847
|
for line, width in zip(lines, widths):
|
15785
15848
|
if line:
|
15786
|
-
|
15787
|
-
|
15788
|
-
|
15789
|
-
|
15790
|
-
|
15791
|
-
|
15792
|
-
|
15793
|
-
|
15794
|
-
|
15795
|
-
|
15796
|
-
|
15797
|
-
|
15798
|
-
|
15799
|
-
|
15800
|
-
|
15801
|
-
|
15802
|
-
|
15803
|
-
pix[x, y] = (textcolor[0], textcolor[1], textcolor[2], pix[x, y][3])
|
15849
|
+
if "\033[" in line: # ANSI
|
15850
|
+
xpos = 0.1 * fontsize
|
15851
|
+
while line:
|
15852
|
+
for ansi, rgb in _ANSI_to_rgb.items():
|
15853
|
+
if line.startswith(ansi):
|
15854
|
+
if rgb:
|
15855
|
+
now_color = rgb
|
15856
|
+
else:
|
15857
|
+
now_color = textcolor
|
15858
|
+
line = line[len(ansi) :]
|
15859
|
+
break
|
15860
|
+
else:
|
15861
|
+
c = line[0]
|
15862
|
+
draw.text(xy=(xpos, ypos), text=c, font=font, fill=now_color)
|
15863
|
+
charwidth = font.getbbox(c)[2]
|
15864
|
+
xpos += charwidth
|
15865
|
+
line = line[1:]
|
15804
15866
|
|
15805
|
-
|
15867
|
+
else:
|
15868
|
+
draw.text(xy=(0.1 * fontsize, ypos), text=line, font=font, fill=now_color)
|
15869
|
+
|
15870
|
+
ypos += lineheight
|
15871
|
+
# # this code is to correct a bug in the rendering of text,
|
15872
|
+
# # leaving a kind of shadow around the text
|
15873
|
+
# del draw
|
15874
|
+
# if textcolor[:3] != (0, 0, 0): # black is ok
|
15875
|
+
# if False and has_numpy():
|
15876
|
+
# arr = numpy.asarray(im).copy()
|
15877
|
+
# arr[:, :, 0] = textcolor[0]
|
15878
|
+
# arr[:, :, 1] = textcolor[1]
|
15879
|
+
# arr[:, :, 2] = textcolor[2]
|
15880
|
+
# im = Image.fromarray(numpy.uint8(arr))
|
15881
|
+
# else:
|
15882
|
+
# pix = im.load()
|
15883
|
+
# for y in range(imheight):
|
15884
|
+
# for x in range(imwidth):
|
15885
|
+
# pix[x, y] = (textcolor[0], textcolor[1], textcolor[2], pix[x, y][3])
|
15886
|
+
|
15887
|
+
# # end of code to correct bug
|
15806
15888
|
|
15807
15889
|
self.imwidth, self.imheight = im.size
|
15808
15890
|
self.heightA = heightA
|
@@ -23662,7 +23744,7 @@ class State:
|
|
23662
23744
|
self.value.tally(value_after)
|
23663
23745
|
self._trywait()
|
23664
23746
|
|
23665
|
-
def _trywait(self, max=inf):
|
23747
|
+
def _trywait(self, max=inf): # this _trywait of a state
|
23666
23748
|
mx = self._waiters._head.successor
|
23667
23749
|
while mx != self._waiters._tail:
|
23668
23750
|
c = mx.component
|
@@ -25580,7 +25662,6 @@ class Animate3dObj(Animate3dBase):
|
|
25580
25662
|
global visualization
|
25581
25663
|
global pyglet
|
25582
25664
|
|
25583
|
-
|
25584
25665
|
self.x = x
|
25585
25666
|
self.y = y
|
25586
25667
|
self.z = z
|
@@ -25605,12 +25686,12 @@ class Animate3dObj(Animate3dBase):
|
|
25605
25686
|
try:
|
25606
25687
|
import pywavefront
|
25607
25688
|
except ImportError:
|
25608
|
-
pywavefront=None
|
25609
|
-
|
25689
|
+
pywavefront = None
|
25690
|
+
|
25610
25691
|
try:
|
25611
|
-
import pyglet
|
25692
|
+
import pyglet # this is a requirement for visualization!
|
25612
25693
|
except ImportError:
|
25613
|
-
pyglet=None
|
25694
|
+
pyglet = None
|
25614
25695
|
|
25615
25696
|
from pywavefront import visualization
|
25616
25697
|
|
@@ -25620,7 +25701,7 @@ class Animate3dObj(Animate3dBase):
|
|
25620
25701
|
global pyglet
|
25621
25702
|
if pywavefront is None:
|
25622
25703
|
raise ImportError("Animate3dObj requires pywavefront. Not found")
|
25623
|
-
if pyglet is None:
|
25704
|
+
if pyglet is None:
|
25624
25705
|
raise ImportError("Animate3dObj requires pyglet. Not found")
|
25625
25706
|
|
25626
25707
|
obj_filename = Path(self.filename(t))
|
@@ -27374,7 +27455,7 @@ def can_animate3d(try_only: bool = True) -> bool:
|
|
27374
27455
|
glut.glutInit()
|
27375
27456
|
except OpenGL.error.NullFunctionError:
|
27376
27457
|
raise ImportError("Installed OpenGL does not support glut. Try 'pip install OpenGL-glut' or see the salabim documentation")
|
27377
|
-
|
27458
|
+
|
27378
27459
|
return True
|
27379
27460
|
else:
|
27380
27461
|
if try_only:
|
@@ -27737,7 +27818,7 @@ def set_environment_aliases():
|
|
27737
27818
|
return # do not set when using Sphinx build!
|
27738
27819
|
|
27739
27820
|
for name, obj in list(globals().items()):
|
27740
|
-
if (not name.startswith("_") or name in ("_Trajectory", "_Distribution")) and name != "yieldless" and name != "Environment":
|
27821
|
+
if (not name.startswith("_") or name in ("_Trajectory", "_Distribution")) and name != "yieldless" and name != "Environment" and not hasattr(Environment,name):
|
27741
27822
|
if inspect.isclass(obj) and obj.__module__ == Environment.__module__:
|
27742
27823
|
if issubclass(obj, Exception):
|
27743
27824
|
exec(f"Environment.{name}={name}")
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|