salabim 24.0.18__py3-none-any.whl → 25.0.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.
- salabim/LICENSE.txt +1 -1
- salabim/salabim.py +195 -114
- {salabim-24.0.18.dist-info → salabim-25.0.0.dist-info}/METADATA +2 -2
- salabim-25.0.0.dist-info/RECORD +10 -0
- {salabim-24.0.18.dist-info → salabim-25.0.0.dist-info}/WHEEL +1 -1
- salabim-24.0.18.dist-info/RECORD +0 -10
- {salabim-24.0.18.dist-info → salabim-25.0.0.dist-info}/top_level.txt +0 -0
salabim/LICENSE.txt
CHANGED
@@ -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
|
salabim/salabim.py
CHANGED
@@ -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}")
|
@@ -0,0 +1,10 @@
|
|
1
|
+
salabim/DejaVuSansMono.ttf,sha256=Z_oIXp5yp1Zaw2y2p3vaxwHhjHpG0MFbmwhxSh4aIEI,335068
|
2
|
+
salabim/LICENSE.txt,sha256=eTPlcDJz4G0096Qv-wfMjm1Wxbd4ilDlsYg5rN4HjWQ,1106
|
3
|
+
salabim/__init__.py,sha256=r7qPLvlmX0dkZDyjuTo8Jo3ex3sD1L4pmK6K5ib9vyw,56
|
4
|
+
salabim/calibri.ttf,sha256=RWpf8Uo31RfvGGNaSt9-2sXSuN87AVE_NFMRsV3LhBk,1330156
|
5
|
+
salabim/mplus-1m-regular.ttf,sha256=EuFHr90BJjuAn_r5MleJFN-WfkeWJ4tf7DweI5zr8tU,289812
|
6
|
+
salabim/salabim.py,sha256=G7w62bzTMLNDN8paZkvYUA3MmvfPhwouL2nc7SLNO2A,1123563
|
7
|
+
salabim-25.0.0.dist-info/METADATA,sha256=m_Yl7oewus-EsdjjsC1OpAwGJy7UE5UCNFBgOa4jfO4,3457
|
8
|
+
salabim-25.0.0.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
9
|
+
salabim-25.0.0.dist-info/top_level.txt,sha256=UE6zVlbi3F6T5ma1a_5TrojMaF21GYKDt9svvm0U4cQ,8
|
10
|
+
salabim-25.0.0.dist-info/RECORD,,
|
salabim-24.0.18.dist-info/RECORD
DELETED
@@ -1,10 +0,0 @@
|
|
1
|
-
salabim/DejaVuSansMono.ttf,sha256=Z_oIXp5yp1Zaw2y2p3vaxwHhjHpG0MFbmwhxSh4aIEI,335068
|
2
|
-
salabim/LICENSE.txt,sha256=qHlBa-POyexatCxDTjSKMlYtkBFQDn9lu-YV_1L6V0U,1106
|
3
|
-
salabim/__init__.py,sha256=r7qPLvlmX0dkZDyjuTo8Jo3ex3sD1L4pmK6K5ib9vyw,56
|
4
|
-
salabim/calibri.ttf,sha256=RWpf8Uo31RfvGGNaSt9-2sXSuN87AVE_NFMRsV3LhBk,1330156
|
5
|
-
salabim/mplus-1m-regular.ttf,sha256=EuFHr90BJjuAn_r5MleJFN-WfkeWJ4tf7DweI5zr8tU,289812
|
6
|
-
salabim/salabim.py,sha256=Ic3A6isSW-Ybu3nvB8judAXxfqp9mh1iCYBx-ACKBPY,1120439
|
7
|
-
salabim-24.0.18.dist-info/METADATA,sha256=oVc1cFiKv3eqzVQbIH78pnnMu1afHe1S2K-glVtqnyU,3458
|
8
|
-
salabim-24.0.18.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
9
|
-
salabim-24.0.18.dist-info/top_level.txt,sha256=UE6zVlbi3F6T5ma1a_5TrojMaF21GYKDt9svvm0U4cQ,8
|
10
|
-
salabim-24.0.18.dist-info/RECORD,,
|
File without changes
|