salabim 24.0.17__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.17 → salabim-25.0.0}/PKG-INFO +2 -2
- {salabim-24.0.17 → salabim-25.0.0}/pyproject.toml +36 -30
- {salabim-24.0.17 → salabim-25.0.0}/salabim/LICENSE.txt +1 -1
- {salabim-24.0.17 → salabim-25.0.0}/salabim/salabim.py +373 -140
- {salabim-24.0.17 → salabim-25.0.0}/salabim.egg-info/PKG-INFO +2 -2
- {salabim-24.0.17 → salabim-25.0.0}/README.md +0 -0
- {salabim-24.0.17 → salabim-25.0.0}/salabim/DejaVuSansMono.ttf +0 -0
- {salabim-24.0.17 → salabim-25.0.0}/salabim/__init__.py +0 -0
- {salabim-24.0.17 → salabim-25.0.0}/salabim/calibri.ttf +0 -0
- {salabim-24.0.17 → salabim-25.0.0}/salabim/mplus-1m-regular.ttf +0 -0
- {salabim-24.0.17 → salabim-25.0.0}/salabim.egg-info/SOURCES.txt +0 -0
- {salabim-24.0.17 → salabim-25.0.0}/salabim.egg-info/dependency_links.txt +0 -0
- {salabim-24.0.17 → salabim-25.0.0}/salabim.egg-info/top_level.txt +0 -0
- {salabim-24.0.17 → salabim-25.0.0}/setup.cfg +0 -0
- {salabim-24.0.17 → salabim-25.0.0}/tests/test salabim.py +0 -0
- {salabim-24.0.17 → salabim-25.0.0}/tests/test_cap_now.py +0 -0
- {salabim-24.0.17 → salabim-25.0.0}/tests/test_componentgenerator.py +0 -0
- {salabim-24.0.17 → salabim-25.0.0}/tests/test_datetime.py +0 -0
- {salabim-24.0.17 → salabim-25.0.0}/tests/test_distributions.py +0 -0
- {salabim-24.0.17 → salabim-25.0.0}/tests/test_misc.py +0 -0
- {salabim-24.0.17 → salabim-25.0.0}/tests/test_monitor.py +0 -0
- {salabim-24.0.17 → salabim-25.0.0}/tests/test_process.py +0 -0
- {salabim-24.0.17 → salabim-25.0.0}/tests/test_queue.py +0 -0
- {salabim-24.0.17 → salabim-25.0.0}/tests/test_state.py +0 -0
- {salabim-24.0.17 → salabim-25.0.0}/tests/test_store.py +0 -0
- {salabim-24.0.17 → 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
|
@@ -31,7 +30,6 @@ import subprocess
|
|
31
30
|
import tempfile
|
32
31
|
import struct
|
33
32
|
import binascii
|
34
|
-
import operator
|
35
33
|
import copy
|
36
34
|
import numbers
|
37
35
|
import platform
|
@@ -43,8 +41,12 @@ import urllib.request
|
|
43
41
|
import urllib.error
|
44
42
|
import base64
|
45
43
|
import zipfile
|
46
|
-
|
47
44
|
from pathlib import Path
|
45
|
+
try:
|
46
|
+
import peek
|
47
|
+
except ModuleNotFoundError:
|
48
|
+
...
|
49
|
+
|
48
50
|
|
49
51
|
from typing import Any, Union, Iterable, Tuple, List, Callable, TextIO, Dict, Set, Type, Hashable, Optional
|
50
52
|
|
@@ -62,6 +64,46 @@ Chromebook = "penguin" in platform.uname()
|
|
62
64
|
PythonInExcel = not ("__file__" in globals())
|
63
65
|
AnacondaCode = sys.platform == "emscripten"
|
64
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())
|
65
107
|
|
66
108
|
def a_log(*args):
|
67
109
|
if not hasattr(a_log, "a_logfile_name"):
|
@@ -4867,7 +4909,7 @@ class Queue:
|
|
4867
4909
|
"""
|
4868
4910
|
return getattr(self, "_sequence_number", 1)
|
4869
4911
|
|
4870
|
-
def add(self, component: "Component") -> "Queue":
|
4912
|
+
def add(self, component: "Component", priority: float = None) -> "Queue":
|
4871
4913
|
"""
|
4872
4914
|
adds a component to the tail of a queue
|
4873
4915
|
|
@@ -4878,18 +4920,23 @@ class Queue:
|
|
4878
4920
|
|
4879
4921
|
may not be member of the queue yet
|
4880
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
|
+
|
4881
4928
|
Note
|
4882
4929
|
----
|
4883
|
-
the priority will be set to
|
4930
|
+
if prioority is None, the priority will be set to
|
4884
4931
|
the priority of the tail of the queue, if any
|
4885
4932
|
or 0 if queue is empty
|
4886
4933
|
|
4887
4934
|
This method is equivalent to append()
|
4888
4935
|
"""
|
4889
|
-
component.enter(self)
|
4936
|
+
component.enter(self, priority)
|
4890
4937
|
return self
|
4891
4938
|
|
4892
|
-
def append(self, component: "Component") -> "Queue":
|
4939
|
+
def append(self, component: "Component", priority: float = None) -> "Queue":
|
4893
4940
|
"""
|
4894
4941
|
appends a component to the tail of a queue
|
4895
4942
|
|
@@ -4900,9 +4947,14 @@ class Queue:
|
|
4900
4947
|
|
4901
4948
|
may not be member of the queue yet
|
4902
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
|
+
|
4903
4955
|
Note
|
4904
4956
|
----
|
4905
|
-
the priority will be set to
|
4957
|
+
if priority is None, the priority will be set to
|
4906
4958
|
the priority of the tail of the queue, if any
|
4907
4959
|
or 0 if queue is empty
|
4908
4960
|
|
@@ -5014,7 +5066,7 @@ class Queue:
|
|
5014
5066
|
component.enter_behind(self, poscomponent)
|
5015
5067
|
return self
|
5016
5068
|
|
5017
|
-
def add_sorted(self, component: "Component", priority:
|
5069
|
+
def add_sorted(self, component: "Component", priority: float) -> "Queue":
|
5018
5070
|
"""
|
5019
5071
|
adds a component to a queue, according to the priority
|
5020
5072
|
|
@@ -5025,7 +5077,7 @@ class Queue:
|
|
5025
5077
|
|
5026
5078
|
may not be member of the queue yet
|
5027
5079
|
|
5028
|
-
priority:
|
5080
|
+
priority: float
|
5029
5081
|
priority in the queue
|
5030
5082
|
|
5031
5083
|
Note
|
@@ -7317,6 +7369,29 @@ by adding at the end:
|
|
7317
7369
|
def __repr__(self):
|
7318
7370
|
return object_to_str(self) + " (" + self.name() + ")"
|
7319
7371
|
|
7372
|
+
def reset_monitors(self, monitor: bool = None, stats_only: bool = None) -> None:
|
7373
|
+
"""
|
7374
|
+
resets the monitor for the component's status and mode monitors
|
7375
|
+
|
7376
|
+
Parameters
|
7377
|
+
----------
|
7378
|
+
monitor : bool
|
7379
|
+
if True, monitoring will be on.
|
7380
|
+
|
7381
|
+
if False, monitoring is disabled
|
7382
|
+
|
7383
|
+
if omitted, no change of monitoring state
|
7384
|
+
|
7385
|
+
stats_only : bool
|
7386
|
+
if True, only statistics will be collected (using less memory, but also less functionality)
|
7387
|
+
|
7388
|
+
if False, full functionality
|
7389
|
+
|
7390
|
+
if omittted, no change of stats_only
|
7391
|
+
"""
|
7392
|
+
self.status.reset(monitor=monitor, stats_only=stats_only)
|
7393
|
+
self.mode.reset(monitor=monitor, stats_only=stats_only)
|
7394
|
+
|
7320
7395
|
def register(self, registry: List) -> "Component":
|
7321
7396
|
"""
|
7322
7397
|
registers the component in the registry
|
@@ -8050,6 +8125,7 @@ by adding:
|
|
8050
8125
|
self,
|
8051
8126
|
store: Union["Store", Iterable],
|
8052
8127
|
filter: Callable = lambda c: True,
|
8128
|
+
request_priority: Any = 0,
|
8053
8129
|
fail_priority: float = 0,
|
8054
8130
|
urgent: bool = True,
|
8055
8131
|
fail_at: float = None,
|
@@ -8073,6 +8149,9 @@ by adding:
|
|
8073
8149
|
|
8074
8150
|
default: lambda c: True (i.e. always return True)
|
8075
8151
|
|
8152
|
+
request_priority: float
|
8153
|
+
put component in to_store_requesters according to the given priority (default 0)
|
8154
|
+
|
8076
8155
|
fail_priority : float
|
8077
8156
|
priority of the fail event
|
8078
8157
|
|
@@ -8213,7 +8292,7 @@ by adding:
|
|
8213
8292
|
|
8214
8293
|
self._from_stores = from_stores
|
8215
8294
|
for store in from_stores:
|
8216
|
-
self.
|
8295
|
+
self.enter_sorted(store._from_store_requesters, priority=request_priority)
|
8217
8296
|
self.status._value = requesting
|
8218
8297
|
self._from_store_item = None
|
8219
8298
|
self._from_store_filter = filter
|
@@ -8226,6 +8305,7 @@ by adding:
|
|
8226
8305
|
self,
|
8227
8306
|
store: Union["Store", Iterable],
|
8228
8307
|
item: "Component",
|
8308
|
+
request_priority: float = None,
|
8229
8309
|
priority: float = 0,
|
8230
8310
|
fail_priority: float = 0,
|
8231
8311
|
urgent: bool = True,
|
@@ -8245,6 +8325,9 @@ by adding:
|
|
8245
8325
|
item: Component
|
8246
8326
|
component to put to store
|
8247
8327
|
|
8328
|
+
request_priority: float
|
8329
|
+
put component in to_store_requesters according to the given priority (default 0)
|
8330
|
+
|
8248
8331
|
fail_priority : float
|
8249
8332
|
priority of the fail event
|
8250
8333
|
|
@@ -8256,11 +8339,11 @@ by adding:
|
|
8256
8339
|
urgent : bool
|
8257
8340
|
urgency indicator
|
8258
8341
|
|
8259
|
-
if False (default), the
|
8342
|
+
if False (default), the fail event will be scheduled
|
8260
8343
|
behind all other components scheduled
|
8261
8344
|
for the same time and priority
|
8262
8345
|
|
8263
|
-
if True, the
|
8346
|
+
if True, the fail event will be scheduled
|
8264
8347
|
in front of all components scheduled
|
8265
8348
|
for the same time and priority
|
8266
8349
|
|
@@ -8358,7 +8441,7 @@ by adding:
|
|
8358
8441
|
return
|
8359
8442
|
|
8360
8443
|
for store in to_stores:
|
8361
|
-
self.
|
8444
|
+
self.enter_sorted(store._to_store_requesters, priority=request_priority)
|
8362
8445
|
self.status._value = requesting
|
8363
8446
|
self._to_store_item = item
|
8364
8447
|
self._to_store_priority = priority
|
@@ -8384,7 +8467,19 @@ by adding:
|
|
8384
8467
|
for store in self._from_stores:
|
8385
8468
|
store.rescan()
|
8386
8469
|
|
8387
|
-
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:
|
8388
8483
|
"""
|
8389
8484
|
request from a resource or resources
|
8390
8485
|
|
@@ -8394,9 +8489,12 @@ by adding:
|
|
8394
8489
|
- resource, where quantity=1, priority=tail of requesters queue
|
8395
8490
|
- tuples/list containing a resource, a quantity and optionally a priority.
|
8396
8491
|
if the priority is not specified, the request
|
8397
|
-
for the resource be added to the
|
8398
|
-
|
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)
|
8399
8496
|
|
8497
|
+
put component requesters according to the given priority (default 0)
|
8400
8498
|
|
8401
8499
|
priority : float
|
8402
8500
|
priority of the fail event
|
@@ -8499,17 +8597,7 @@ by adding:
|
|
8499
8597
|
--> requests 1 from r1, r2 or r3
|
8500
8598
|
|
8501
8599
|
"""
|
8502
|
-
fail_at = kwargs.pop("fail_at", None)
|
8503
|
-
fail_delay = kwargs.pop("fail_delay", None)
|
8504
|
-
mode = kwargs.pop("mode", None)
|
8505
|
-
urgent = kwargs.pop("urgent", False)
|
8506
|
-
schedule_priority = kwargs.pop("priority", 0)
|
8507
|
-
cap_now = kwargs.pop("cap_now", None)
|
8508
|
-
oneof = kwargs.pop("oneof", False)
|
8509
|
-
called_from = kwargs.pop("called_from", "request")
|
8510
8600
|
self.oneof_request = oneof
|
8511
|
-
if kwargs:
|
8512
|
-
raise TypeError(called_from + "() got an unexpected keyword argument '" + tuple(kwargs)[0] + "'")
|
8513
8601
|
|
8514
8602
|
if self.status.value != current:
|
8515
8603
|
self._checkisnotdata()
|
@@ -8542,7 +8630,7 @@ by adding:
|
|
8542
8630
|
return
|
8543
8631
|
for arg in args:
|
8544
8632
|
q = 1
|
8545
|
-
priority =
|
8633
|
+
priority = request_priority
|
8546
8634
|
if isinstance(arg, Resource):
|
8547
8635
|
r = arg
|
8548
8636
|
elif isinstance(arg, (tuple, list)):
|
@@ -8580,7 +8668,7 @@ by adding:
|
|
8580
8668
|
if self.oneof_request:
|
8581
8669
|
addstring += " (oneof)"
|
8582
8670
|
|
8583
|
-
self.
|
8671
|
+
self.enter(r._requesters, priority)
|
8584
8672
|
if self.env._trace:
|
8585
8673
|
self.env.print_trace("", "", self.name(), req_text + r.name() + addstring)
|
8586
8674
|
|
@@ -8829,7 +8917,144 @@ by adding:
|
|
8829
8917
|
for r in list(self._claims):
|
8830
8918
|
self._release(r)
|
8831
8919
|
|
8832
|
-
def
|
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):
|
8921
|
+
schedule_priority = priority
|
8922
|
+
"""
|
8923
|
+
wait for any or all of the given state values are met
|
8924
|
+
|
8925
|
+
Parameters
|
8926
|
+
----------
|
8927
|
+
cond : callable
|
8928
|
+
parameterless function that return True if wait is over
|
8929
|
+
|
8930
|
+
states : iterable
|
8931
|
+
specicies which states should trigger the cond to be checked
|
8932
|
+
|
8933
|
+
request_priority : float
|
8934
|
+
put component in waiters queue according to the given priority (deafult 0)
|
8935
|
+
|
8936
|
+
priority : float
|
8937
|
+
priority of the fail event
|
8938
|
+
|
8939
|
+
default: 0
|
8940
|
+
|
8941
|
+
if a component has the same time on the event list, this component is sorted accoring to
|
8942
|
+
the priority.
|
8943
|
+
|
8944
|
+
urgent : bool
|
8945
|
+
urgency indicator
|
8946
|
+
|
8947
|
+
if False (default), the component will be scheduled
|
8948
|
+
behind all other components scheduled
|
8949
|
+
for the same time and priority
|
8950
|
+
|
8951
|
+
if True, the component will be scheduled
|
8952
|
+
in front of all components scheduled
|
8953
|
+
for the same time and priority
|
8954
|
+
|
8955
|
+
fail_at : float or distribution
|
8956
|
+
time out
|
8957
|
+
|
8958
|
+
if the wait is not honored before fail_at,
|
8959
|
+
the wait will be cancelled and the
|
8960
|
+
parameter failed will be set.
|
8961
|
+
|
8962
|
+
if not specified, the wait will not time out.
|
8963
|
+
|
8964
|
+
if distribution, the distribution is sampled
|
8965
|
+
|
8966
|
+
fail_delay : float or distribution
|
8967
|
+
time out
|
8968
|
+
|
8969
|
+
if the wait is not honored before now+fail_delay,
|
8970
|
+
the request will be cancelled and the
|
8971
|
+
parameter failed will be set.
|
8972
|
+
|
8973
|
+
if not specified, the wait will not time out.
|
8974
|
+
|
8975
|
+
if distribution, the distribution is sampled
|
8976
|
+
|
8977
|
+
mode : str preferred
|
8978
|
+
mode
|
8979
|
+
|
8980
|
+
will be used in trace and can be used in animations
|
8981
|
+
|
8982
|
+
if nothing specified, the mode will be unchanged.
|
8983
|
+
|
8984
|
+
also mode_time will be set to now, if mode is set.
|
8985
|
+
|
8986
|
+
cap_now : bool
|
8987
|
+
indicator whether times (fail_at, fail_duration) in the past are allowed. If, so now() will be used.
|
8988
|
+
default: sys.default_cap_now(), usualy False
|
8989
|
+
|
8990
|
+
Note
|
8991
|
+
----
|
8992
|
+
Not allowed for data components or main.
|
8993
|
+
|
8994
|
+
Only if yieldless is False: If to be used for the current component
|
8995
|
+
(which will be nearly always the case),
|
8996
|
+
use ``yield self.wait(...)``.
|
8997
|
+
|
8998
|
+
"""
|
8999
|
+
if self.status.value != current:
|
9000
|
+
self._checkisnotdata()
|
9001
|
+
self._checkisnotmain()
|
9002
|
+
self._remove()
|
9003
|
+
self._check_fail()
|
9004
|
+
|
9005
|
+
self._failed = False
|
9006
|
+
|
9007
|
+
if fail_at is None:
|
9008
|
+
if fail_delay is None:
|
9009
|
+
scheduled_time = inf
|
9010
|
+
else:
|
9011
|
+
if fail_delay == inf:
|
9012
|
+
scheduled_time = inf
|
9013
|
+
else:
|
9014
|
+
fail_delay = self.env.spec_to_duration(fail_delay)
|
9015
|
+
scheduled_time = self.env._now + fail_delay
|
9016
|
+
else:
|
9017
|
+
if fail_delay is None:
|
9018
|
+
fail_at = self.env.spec_to_time(fail_at)
|
9019
|
+
scheduled_time = fail_at + self.env._offset
|
9020
|
+
else:
|
9021
|
+
raise ValueError("both fail_at and fail_delay specified")
|
9022
|
+
|
9023
|
+
self.set_mode(mode)
|
9024
|
+
|
9025
|
+
self._cond = cond # add test ***
|
9026
|
+
for state in states:
|
9027
|
+
self._waits.append((state, None, None))
|
9028
|
+
if priority is None:
|
9029
|
+
self.enter(state._waiters)
|
9030
|
+
else:
|
9031
|
+
self.enter_sorted(state._waiters, priority)
|
9032
|
+
|
9033
|
+
if not self._waits:
|
9034
|
+
raise TypeError("no states specified")
|
9035
|
+
|
9036
|
+
self._remaining_duration = scheduled_time - self.env._now
|
9037
|
+
|
9038
|
+
self._trywait()
|
9039
|
+
|
9040
|
+
if self._waits:
|
9041
|
+
self.status._value = waiting
|
9042
|
+
self._reschedule(scheduled_time, schedule_priority, urgent, "wait_for", cap_now)
|
9043
|
+
else:
|
9044
|
+
return
|
9045
|
+
|
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:
|
8833
9058
|
"""
|
8834
9059
|
wait for any or all of the given state values are met
|
8835
9060
|
|
@@ -8845,6 +9070,8 @@ by adding:
|
|
8845
9070
|
be added to the tail of
|
8846
9071
|
the waiters queue
|
8847
9072
|
|
9073
|
+
request_priority : float
|
9074
|
+
put component in waiters queue according to the given priority (default 0)
|
8848
9075
|
|
8849
9076
|
priority : float
|
8850
9077
|
priority of the fail event
|
@@ -8970,16 +9197,7 @@ by adding:
|
|
8970
9197
|
--> waits for s1.value()==True and s2.value==True
|
8971
9198
|
|
8972
9199
|
"""
|
8973
|
-
|
8974
|
-
fail_delay = kwargs.pop("fail_delay", None)
|
8975
|
-
all = kwargs.pop("all", False)
|
8976
|
-
mode = kwargs.pop("mode", None)
|
8977
|
-
urgent = kwargs.pop("urgent", False)
|
8978
|
-
schedule_priority = kwargs.pop("priority", 0)
|
8979
|
-
cap_now = kwargs.pop("cap_now", None)
|
8980
|
-
|
8981
|
-
if kwargs:
|
8982
|
-
raise TypeError("wait() got an unexpected keyword argument '" + tuple(kwargs)[0] + "'")
|
9200
|
+
self._cond = None
|
8983
9201
|
|
8984
9202
|
if self.status.value != current:
|
8985
9203
|
self._checkisnotdata()
|
@@ -9010,7 +9228,7 @@ by adding:
|
|
9010
9228
|
|
9011
9229
|
for arg in args:
|
9012
9230
|
value = True
|
9013
|
-
priority =
|
9231
|
+
priority = request_priority
|
9014
9232
|
if isinstance(arg, State):
|
9015
9233
|
state = arg
|
9016
9234
|
elif isinstance(arg, (tuple, list)):
|
@@ -9018,10 +9236,8 @@ by adding:
|
|
9018
9236
|
if not isinstance(state, State):
|
9019
9237
|
raise TypeError("incorrect specifier", arg)
|
9020
9238
|
if len(arg) >= 2:
|
9021
|
-
|
9239
|
+
priority = arg[1]
|
9022
9240
|
if len(arg) >= 3:
|
9023
|
-
priority = arg[2]
|
9024
|
-
if len(arg) >= 4:
|
9025
9241
|
raise TypeError("incorrect specifier", arg)
|
9026
9242
|
else:
|
9027
9243
|
raise TypeError("incorrect specifier", arg)
|
@@ -9057,37 +9273,40 @@ by adding:
|
|
9057
9273
|
def _trywait(self):
|
9058
9274
|
if self.status.value == interrupted:
|
9059
9275
|
return False
|
9060
|
-
if self.
|
9061
|
-
honored =
|
9062
|
-
for state, value, valuetype in self._waits:
|
9063
|
-
if valuetype == 0:
|
9064
|
-
if value != state._value:
|
9065
|
-
honored = False
|
9066
|
-
break
|
9067
|
-
elif valuetype == 1:
|
9068
|
-
if not eval(value.replace("$", "state._value")):
|
9069
|
-
honored = False
|
9070
|
-
break
|
9071
|
-
elif valuetype == 2:
|
9072
|
-
if not value(state._value, self, state):
|
9073
|
-
honored = False
|
9074
|
-
break
|
9075
|
-
|
9276
|
+
if self._cond:
|
9277
|
+
honored = self._cond()
|
9076
9278
|
else:
|
9077
|
-
|
9078
|
-
|
9079
|
-
|
9080
|
-
if
|
9081
|
-
|
9082
|
-
|
9083
|
-
|
9084
|
-
|
9085
|
-
|
9086
|
-
|
9087
|
-
|
9088
|
-
|
9089
|
-
|
9090
|
-
|
9279
|
+
if self._wait_all:
|
9280
|
+
honored = True
|
9281
|
+
for state, value, valuetype in self._waits:
|
9282
|
+
if valuetype == 0:
|
9283
|
+
if value != state._value:
|
9284
|
+
honored = False
|
9285
|
+
break
|
9286
|
+
elif valuetype == 1:
|
9287
|
+
if not eval(value.replace("$", "state._value")):
|
9288
|
+
honored = False
|
9289
|
+
break
|
9290
|
+
elif valuetype == 2:
|
9291
|
+
if not value(state._value, self, state):
|
9292
|
+
honored = False
|
9293
|
+
break
|
9294
|
+
|
9295
|
+
else:
|
9296
|
+
honored = False
|
9297
|
+
for state, value, valuetype in self._waits:
|
9298
|
+
if valuetype == 0:
|
9299
|
+
if value == state._value:
|
9300
|
+
honored = True
|
9301
|
+
break
|
9302
|
+
elif valuetype == 1:
|
9303
|
+
if eval(value.replace("$", str(state._value))):
|
9304
|
+
honored = True
|
9305
|
+
break
|
9306
|
+
elif valuetype == 2:
|
9307
|
+
if value(state._value, self, state):
|
9308
|
+
honored = True
|
9309
|
+
break
|
9091
9310
|
|
9092
9311
|
if honored:
|
9093
9312
|
for s, _, _ in self._waits:
|
@@ -9446,7 +9665,7 @@ by adding:
|
|
9446
9665
|
index += 1
|
9447
9666
|
return index
|
9448
9667
|
|
9449
|
-
def enter(self, q: "Queue") -> "Component":
|
9668
|
+
def enter(self, q: "Queue", priority: float = None) -> "Component":
|
9450
9669
|
"""
|
9451
9670
|
enters a queue at the tail
|
9452
9671
|
|
@@ -9462,8 +9681,17 @@ by adding:
|
|
9462
9681
|
or 0 if queue is empty
|
9463
9682
|
"""
|
9464
9683
|
self._checknotinqueue(q)
|
9465
|
-
priority
|
9466
|
-
|
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)
|
9467
9695
|
return self
|
9468
9696
|
|
9469
9697
|
def enter_at_head(self, q: "Queue") -> "Component":
|
@@ -9542,22 +9770,14 @@ by adding:
|
|
9542
9770
|
q : Queue
|
9543
9771
|
queue to enter
|
9544
9772
|
|
9545
|
-
priority:
|
9773
|
+
priority: float
|
9546
9774
|
priority in the queue
|
9547
9775
|
|
9548
9776
|
Note
|
9549
9777
|
----
|
9550
9778
|
The component is placed just before the first component with a priority > given priority
|
9551
9779
|
"""
|
9552
|
-
self.
|
9553
|
-
if q._length >= 1 and priority < q._head.successor.priority: # direct enter component that's smaller than the rest
|
9554
|
-
m2 = q._head.successor
|
9555
|
-
else:
|
9556
|
-
m2 = q._tail
|
9557
|
-
while (m2.predecessor != q._head) and (m2.predecessor.priority > priority):
|
9558
|
-
m2 = m2.predecessor
|
9559
|
-
Qmember().insert_in_front_of(m2, self, q, priority)
|
9560
|
-
return self
|
9780
|
+
return self.enter(q, priority)
|
9561
9781
|
|
9562
9782
|
def leave(self, q: "Queue" = None) -> "Component":
|
9563
9783
|
"""
|
@@ -9623,7 +9843,7 @@ by adding:
|
|
9623
9843
|
q : Queue
|
9624
9844
|
queue where the component belongs to
|
9625
9845
|
|
9626
|
-
priority :
|
9846
|
+
priority : float
|
9627
9847
|
priority in queue
|
9628
9848
|
|
9629
9849
|
if omitted, no change
|
@@ -10765,8 +10985,8 @@ class Environment:
|
|
10765
10985
|
s = "view("
|
10766
10986
|
items = []
|
10767
10987
|
for prop in props.split():
|
10768
|
-
items.append(f"{getattr(self.view,prop)(t):.4f}")
|
10769
|
-
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}")
|
10770
10990
|
|
10771
10991
|
def _bind(self, tkinter_event, func):
|
10772
10992
|
self.root.bind(tkinter_event, func)
|
@@ -10882,7 +11102,7 @@ class Environment:
|
|
10882
11102
|
ao.label = "fovy" if prop == "field_of_view_y" else prop
|
10883
11103
|
|
10884
11104
|
ao = AnimateText(
|
10885
|
-
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}",
|
10886
11106
|
x=5 + i * 80 + 70,
|
10887
11107
|
y=top,
|
10888
11108
|
font="calibri",
|
@@ -11724,7 +11944,7 @@ class Environment:
|
|
11724
11944
|
format="GIF",
|
11725
11945
|
)
|
11726
11946
|
else:
|
11727
|
-
for _ in range(2):
|
11947
|
+
for _ in range(2): # normally runs only once
|
11728
11948
|
try:
|
11729
11949
|
self._images[0].save(
|
11730
11950
|
self._video_name,
|
@@ -11736,7 +11956,7 @@ class Environment:
|
|
11736
11956
|
)
|
11737
11957
|
break
|
11738
11958
|
except ValueError: # prevent bug in Python 3.13
|
11739
|
-
self._images=[image.convert("RGB") for image in self._images]
|
11959
|
+
self._images = [image.convert("RGB") for image in self._images]
|
11740
11960
|
|
11741
11961
|
else:
|
11742
11962
|
if PythonInExcel or AnacondaCode:
|
@@ -11752,9 +11972,8 @@ class Environment:
|
|
11752
11972
|
format="GIF",
|
11753
11973
|
)
|
11754
11974
|
else:
|
11755
|
-
for _ in range(2):
|
11975
|
+
for _ in range(2): # normally runs only once
|
11756
11976
|
try:
|
11757
|
-
|
11758
11977
|
self._images[0].save(
|
11759
11978
|
self._video_name,
|
11760
11979
|
disposal=2,
|
@@ -11765,7 +11984,7 @@ class Environment:
|
|
11765
11984
|
optimize=False,
|
11766
11985
|
)
|
11767
11986
|
except ValueError: # prevent bug in Python 3.13
|
11768
|
-
self._images=[image.convert("RGB") for image in self._images]
|
11987
|
+
self._images = [image.convert("RGB") for image in self._images]
|
11769
11988
|
|
11770
11989
|
del self._images
|
11771
11990
|
elif self._video_out == "png":
|
@@ -11829,7 +12048,6 @@ class Environment:
|
|
11829
12048
|
Number of 1/30 second long frames to be inserted
|
11830
12049
|
"""
|
11831
12050
|
|
11832
|
-
|
11833
12051
|
if self._video_out is None:
|
11834
12052
|
raise ValueError("video not set")
|
11835
12053
|
if isinstance(image, (Path, str)):
|
@@ -11855,7 +12073,6 @@ class Environment:
|
|
11855
12073
|
open_cv_image = cv2.cvtColor(numpy.array(image), cv2.COLOR_RGB2BGR)
|
11856
12074
|
self._video_out.write(open_cv_image)
|
11857
12075
|
|
11858
|
-
|
11859
12076
|
def _save_frame(self):
|
11860
12077
|
self._exclude_from_animation = "not in video"
|
11861
12078
|
image = self._capture_image("RGBA", self._video_mode)
|
@@ -12790,7 +13007,7 @@ class Environment:
|
|
12790
13007
|
self.animation_parameters(synced=value, animate=None)
|
12791
13008
|
return self._synced
|
12792
13009
|
|
12793
|
-
def minimized(self, value: bool=None)-> bool:
|
13010
|
+
def minimized(self, value: bool = None) -> bool:
|
12794
13011
|
"""
|
12795
13012
|
minimized
|
12796
13013
|
|
@@ -13058,7 +13275,7 @@ class Environment:
|
|
13058
13275
|
"""
|
13059
13276
|
return self._current_component
|
13060
13277
|
|
13061
|
-
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):
|
13062
13279
|
"""
|
13063
13280
|
start execution of the simulation
|
13064
13281
|
|
@@ -13198,7 +13415,6 @@ class Environment:
|
|
13198
13415
|
self._t = self.animation_start_time
|
13199
13416
|
else:
|
13200
13417
|
self._t = self.animation_start_time + ((time.time() - self.animation_start_clocktime) * self._speed)
|
13201
|
-
|
13202
13418
|
while self.peek() < self._t:
|
13203
13419
|
self.step()
|
13204
13420
|
if not (self.running and self._animate):
|
@@ -15004,7 +15220,7 @@ class Environment:
|
|
15004
15220
|
self.pause_at = inf
|
15005
15221
|
|
15006
15222
|
if self.pause_at is None:
|
15007
|
-
sg.popup(
|
15223
|
+
sg.popup("Pause not valid")
|
15008
15224
|
else:
|
15009
15225
|
if "-PAUSE-EACH-" in self._ui_keys:
|
15010
15226
|
pause_each_str = values["-PAUSE-EACH-"]
|
@@ -15023,7 +15239,7 @@ class Environment:
|
|
15023
15239
|
self.pause_at = None
|
15024
15240
|
|
15025
15241
|
if self.pause_at is None:
|
15026
|
-
sg.popup(
|
15242
|
+
sg.popup("Pause interval not valid")
|
15027
15243
|
else:
|
15028
15244
|
if self.pause_at > self.t() * 0.99999999:
|
15029
15245
|
if "-ANIMATE-" in self._ui_keys:
|
@@ -15626,29 +15842,49 @@ class Animate2dBase(DynamicClass):
|
|
15626
15842
|
im = Image.new("RGBA", (int(totwidth + 0.1 * fontsize), int(totheight)), (0, 0, 0, 0))
|
15627
15843
|
imwidth, imheight = im.size
|
15628
15844
|
draw = ImageDraw.Draw(im)
|
15629
|
-
|
15845
|
+
ypos = 0
|
15846
|
+
now_color = textcolor
|
15630
15847
|
for line, width in zip(lines, widths):
|
15631
15848
|
if line:
|
15632
|
-
|
15633
|
-
|
15634
|
-
|
15635
|
-
|
15636
|
-
|
15637
|
-
|
15638
|
-
|
15639
|
-
|
15640
|
-
|
15641
|
-
|
15642
|
-
|
15643
|
-
|
15644
|
-
|
15645
|
-
|
15646
|
-
|
15647
|
-
|
15648
|
-
|
15649
|
-
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:]
|
15650
15866
|
|
15651
|
-
|
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
|
15652
15888
|
|
15653
15889
|
self.imwidth, self.imheight = im.size
|
15654
15890
|
self.heightA = heightA
|
@@ -23508,7 +23744,7 @@ class State:
|
|
23508
23744
|
self.value.tally(value_after)
|
23509
23745
|
self._trywait()
|
23510
23746
|
|
23511
|
-
def _trywait(self, max=inf):
|
23747
|
+
def _trywait(self, max=inf): # this _trywait of a state
|
23512
23748
|
mx = self._waiters._head.successor
|
23513
23749
|
while mx != self._waiters._tail:
|
23514
23750
|
c = mx.component
|
@@ -25426,7 +25662,6 @@ class Animate3dObj(Animate3dBase):
|
|
25426
25662
|
global visualization
|
25427
25663
|
global pyglet
|
25428
25664
|
|
25429
|
-
|
25430
25665
|
self.x = x
|
25431
25666
|
self.y = y
|
25432
25667
|
self.z = z
|
@@ -25451,12 +25686,12 @@ class Animate3dObj(Animate3dBase):
|
|
25451
25686
|
try:
|
25452
25687
|
import pywavefront
|
25453
25688
|
except ImportError:
|
25454
|
-
pywavefront=None
|
25455
|
-
|
25689
|
+
pywavefront = None
|
25690
|
+
|
25456
25691
|
try:
|
25457
|
-
import pyglet
|
25692
|
+
import pyglet # this is a requirement for visualization!
|
25458
25693
|
except ImportError:
|
25459
|
-
pyglet=None
|
25694
|
+
pyglet = None
|
25460
25695
|
|
25461
25696
|
from pywavefront import visualization
|
25462
25697
|
|
@@ -25466,7 +25701,7 @@ class Animate3dObj(Animate3dBase):
|
|
25466
25701
|
global pyglet
|
25467
25702
|
if pywavefront is None:
|
25468
25703
|
raise ImportError("Animate3dObj requires pywavefront. Not found")
|
25469
|
-
if pyglet is None:
|
25704
|
+
if pyglet is None:
|
25470
25705
|
raise ImportError("Animate3dObj requires pyglet. Not found")
|
25471
25706
|
|
25472
25707
|
obj_filename = Path(self.filename(t))
|
@@ -26976,8 +27211,6 @@ def getfont(fontname, fontsize):
|
|
26976
27211
|
return getfont.lookup[(fontname, fontsize)]
|
26977
27212
|
else:
|
26978
27213
|
getfont.lookup = {}
|
26979
|
-
if fontname == "":
|
26980
|
-
a = 1
|
26981
27214
|
if isinstance(fontname, str):
|
26982
27215
|
fontlist1 = [fontname]
|
26983
27216
|
else:
|
@@ -27222,7 +27455,7 @@ def can_animate3d(try_only: bool = True) -> bool:
|
|
27222
27455
|
glut.glutInit()
|
27223
27456
|
except OpenGL.error.NullFunctionError:
|
27224
27457
|
raise ImportError("Installed OpenGL does not support glut. Try 'pip install OpenGL-glut' or see the salabim documentation")
|
27225
|
-
|
27458
|
+
|
27226
27459
|
return True
|
27227
27460
|
else:
|
27228
27461
|
if try_only:
|
@@ -27585,7 +27818,7 @@ def set_environment_aliases():
|
|
27585
27818
|
return # do not set when using Sphinx build!
|
27586
27819
|
|
27587
27820
|
for name, obj in list(globals().items()):
|
27588
|
-
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):
|
27589
27822
|
if inspect.isclass(obj) and obj.__module__ == Environment.__module__:
|
27590
27823
|
if issubclass(obj, Exception):
|
27591
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
|