cgse-common 2024.1.1__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.
@@ -0,0 +1,64 @@
1
+ Metadata-Version: 2.4
2
+ Name: cgse-common
3
+ Version: 2024.1.1
4
+ Summary: Software framework to support hardware testing
5
+ Author: IVS KU Leuven
6
+ Maintainer-email: Rik Huygen <rik.huygen@kuleuven.be>, Sara Regibo <sara.regibo@kuleuven.be>
7
+ License-Expression: MIT
8
+ Keywords: CGSE,Common-EGSE,hardware testing,software framework
9
+ Requires-Python: >=3.9
10
+ Requires-Dist: click
11
+ Requires-Dist: deepdiff
12
+ Requires-Dist: distro
13
+ Requires-Dist: gitpython
14
+ Requires-Dist: numpy==1.22.4
15
+ Requires-Dist: pandas
16
+ Requires-Dist: pip>=24.3.1
17
+ Requires-Dist: prometheus-client
18
+ Requires-Dist: psutil
19
+ Requires-Dist: pyyaml
20
+ Requires-Dist: pyzmq==23.2.1
21
+ Requires-Dist: rich
22
+ Provides-Extra: dev
23
+ Requires-Dist: pipdeptree; extra == 'dev'
24
+ Requires-Dist: ruff; extra == 'dev'
25
+ Requires-Dist: tomlkit; extra == 'dev'
26
+ Provides-Extra: test
27
+ Requires-Dist: pytest; extra == 'test'
28
+ Requires-Dist: pytest-cov; extra == 'test'
29
+ Requires-Dist: pytest-mock; extra == 'test'
30
+ Description-Content-Type: text/markdown
31
+
32
+ # Generic Functionality used in the Common-EGSE
33
+
34
+ This package 'cgse-common' contains functionality that is used by all `cgse` sub-packages, but it is designed to be a stand-alone generic package that can be used also in any other project.
35
+
36
+
37
+ ## Installation
38
+
39
+ Install the package using pip:
40
+
41
+ $ pip install cgse-common
42
+
43
+
44
+ ## Usage
45
+
46
+ All functionality resides in the package `egse`. As an example, if you need a standard way to format your timestamp, use the `format_datetime()` function from `egse.system`:
47
+
48
+ >>> from egse.system import format_datetime
49
+ >>> print(format_datetime())
50
+ 2023-10-10T08:41:51.937+0000
51
+
52
+
53
+ ## Included Functionality
54
+
55
+ A non-comprehensive list of available functionality:
56
+
57
+ ### Functionality of General Use
58
+
59
+ * **egse.bits**: contains a number of convenience functions to work with bits, bytes and integers
60
+ * **egse.decorators**: a collection of useful decorator functions
61
+ * **egse.reload**: a slightly better approach to reloading modules and function than the standard importlib.reload() function.
62
+ * **egse.resource**: provides convenience functions to use resources in your code without the need to specify an absolute path
63
+ * **egse.system**: defines convenience functions that provide information on system specific functionality like, file system interactions, timing, operating system interactions, etc.
64
+ * **egse.version**: functionality to retrieve the package version information
@@ -0,0 +1,32 @@
1
+ egse/bits.py,sha256=6oNKp6BrCjwMlZ6lDwMPuZXTT6NzRW4Y04jLd5ALUGc,10235
2
+ egse/command.py,sha256=PEPivQQu_FPerhEveVri_qiUkFnrjFA2nIkgo9qwXNg,23112
3
+ egse/config.py,sha256=xWYs5PRV-LAtcc1ZaNXA3bhJnVjJD0u_RZWbLCHcWnM,9626
4
+ egse/control.py,sha256=v3uby6VzHumC1hXj48UhU0YGv8UcXryvqWzD1IYghKA,15181
5
+ egse/decorators.py,sha256=M0PavrPcTwhWS6yLlEo-mOS20lcy90Mym7HUVzPNmhs,13404
6
+ egse/device.py,sha256=NGsjVELxMFFKJd1w-Bs1dFsAIpeistdv1H5xARdj1O4,8385
7
+ egse/env.py,sha256=styV3-01Hd-Ug9Ulwa60mXIReSaRTzxrb9z4pBt9DmM,9930
8
+ egse/exceptions.py,sha256=Tc1xqUrWxV6SXxc9xB9viK_O-GVa_SpzqZUVEhZkwzA,1801
9
+ egse/mixin.py,sha256=J3yu5lPnm0grJqIm5LiacBUCZujJsdcKBNRaOQcMnNo,17345
10
+ egse/monitoring.py,sha256=-pwXqPoiNKzQYKQSpKddFlaPkCTJZYdxvG1d2MBN3l0,3033
11
+ egse/observer.py,sha256=6faaLHqgpOQs_oEvdBygQ5HF7mGneKJEfyQEFUFA5VY,1069
12
+ egse/obsid.py,sha256=-HPuHApZrr3Nj1J2-qqnIiE814C-gm4FSHdM2afKdRY,5883
13
+ egse/persistence.py,sha256=Lx6LMJ1-dh8N43XF7tTM6RwD0sSETiGQ9DNqug-G-zQ,2160
14
+ egse/plugin.py,sha256=Fd_QnABm33_dLjaj1zDgEZr3RKy-N88U5Hz5OZm9Jj0,2684
15
+ egse/process.py,sha256=mQ2ojeL_9oE_QkMJlQDPd1290z0j2mOrGXrlrWtOtzI,16615
16
+ egse/protocol.py,sha256=Psy0iOLPTgARn1VqeKtPCSKepHr_S2KW58UYwjOA6J0,23827
17
+ egse/proxy.py,sha256=pMKdnF62SXm0quLoKfgvK9GFkH2mLMB5fWNrZenfqQQ,18100
18
+ egse/reload.py,sha256=rDT0bC6RFeRhW38wSgFcxr30h8FvaKkoGp_OE-AwBB4,4388
19
+ egse/resource.py,sha256=VoB7BVrQULT_SJ1XioDzB59-uH47nUcN-KNVLvFxiFE,15163
20
+ egse/services.py,sha256=ZgkF0Rx_PykOVHAOV1cKduJdUhuY6A4DgwjPJWRGj3U,7642
21
+ egse/services.yaml,sha256=p8QBF56zLI21iJ9skt65VlNz4rIqRoFfBTZxOIUZCZ4,1853
22
+ egse/settings.py,sha256=t0Zzb1W40fF6cnJ2xvOdt0ZYnoE8i22wdS_co9SbTOI,13389
23
+ egse/settings.yaml,sha256=Kfi8sMtrzwQkYuIqmutdsSxODWNdPfcen4_QMdFpXD4,48649
24
+ egse/setup.py,sha256=XZN5vPf7kcgkIVP2c2gE3DXhJQApIRoeKndXVhZwpS4,41676
25
+ egse/state.py,sha256=ekcCZu_DZKkKYn-5iWG7ij7Aif2WYMNVs5h3cia-cVc,5352
26
+ egse/system.py,sha256=yvADB0ukpKFEd64tfx50TpG4y22UrnXOwqbko9UK2SY,48100
27
+ egse/version.py,sha256=EigdH05E8pNtSQznUfqM_RxlOjuAVp3Oe6S6MM5xGIM,6132
28
+ egse/zmq_ser.py,sha256=2-nwVUBWZ3vvosKNmlWobHJrIJA2HlM3V5a63Gz2JY0,1819
29
+ cgse_common-2024.1.1.dist-info/METADATA,sha256=RFlw_ox9b_vDly8YhXvPMMcQcbebUsdlsocCN1b-JcQ,2377
30
+ cgse_common-2024.1.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
31
+ cgse_common-2024.1.1.dist-info/entry_points.txt,sha256=E-KaQ9NGWAP1XvLHncNxq5oa22EAf9sOpBZWphXcxiE,34
32
+ cgse_common-2024.1.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.27.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [cgse.version]
2
+ cgse-common = egse
egse/bits.py ADDED
@@ -0,0 +1,318 @@
1
+ """
2
+ This module contains a number of convenience functions to work with bits, bytes and integers.
3
+ """
4
+ import ctypes
5
+ from typing import Union
6
+
7
+
8
+ def set_bit(value: int, bit) -> int:
9
+ """
10
+ Set bit to 1 for the given value.
11
+
12
+ Args:
13
+ value (int): the integer value that needs a bit set or unset
14
+ bit (int): the index of the bit to set/unset, starting from 0 at the LSB
15
+
16
+ Returns:
17
+ the changed value.
18
+ """
19
+ return value | (1 << bit)
20
+
21
+
22
+ def set_bits(value: int, bits: tuple) -> int:
23
+ """
24
+ Set the given bits in value to 1.
25
+ Args:
26
+ value (int): the value where the given bits shall be changed
27
+ bits (tuple): a tuple with start and stop bits
28
+ Returns:
29
+ the changed value
30
+ """
31
+ for i in range(bits[0], bits[1]):
32
+ value |= 1 << i
33
+ return value
34
+
35
+
36
+ def clear_bit(value: int, bit) -> int:
37
+ """
38
+ Set bit to 0 for the given value.
39
+
40
+ Args:
41
+ value (int): the integer value that needs a bit set or unset
42
+ bit (int): the index of the bit to set/unset, starting from 0 at the LSB
43
+
44
+ Returns:
45
+ the changed value.
46
+ """
47
+ return value & ~(1 << bit)
48
+
49
+
50
+ def clear_bits(value: int, bits: tuple) -> int:
51
+ """
52
+ Set the given bits in value to 0.
53
+ Args:
54
+ value (int): the value where the given bits shall be changed
55
+ bits (tuple): a tuple with start and stop bits
56
+ Returns:
57
+ the changed value
58
+ """
59
+ for i in range(bits[0], bits[1]):
60
+ value &= ~(1 << i)
61
+ return value
62
+
63
+
64
+ def toggle_bit(value: int, bit) -> int:
65
+ """
66
+ Toggle the bit in the given value.
67
+
68
+ Args:
69
+ value (int): the integer value that needs a bit toggled
70
+ bit (int): the index of the bit to toggle, starting from 0 at the LSB
71
+
72
+ Returns:
73
+ the changed value.
74
+ """
75
+ return value ^ (1 << bit)
76
+
77
+
78
+ def bit_set(value: int, bit) -> bool:
79
+ """
80
+ Return True if the bit is set.
81
+
82
+ Args:
83
+ value (int): the value to check
84
+ bit (int): the index of the bit to check, starting from 0 at the LSB
85
+
86
+ Returns:
87
+ True if the bit is set (1).
88
+ """
89
+ bit_value = 1 << bit
90
+ return value & (bit_value) == bit_value
91
+
92
+
93
+ def bits_set(value: int, *args) -> bool:
94
+ """
95
+ Return True if all the bits are set.
96
+
97
+ Args:
98
+ value (int): the value to check
99
+ args: a set of indices of the bits to check, starting from 0 at the LSB
100
+ Returns:
101
+ True if all the bits are set (1).
102
+
103
+ For example:
104
+ >>> assert bits_set(0b0101_0000_1011, [0, 1, 3, 8, 10])
105
+ >>> assert bits_set(0b0101_0000_1011, [3, 8])
106
+ >>> assert not bits_set(0b0101_0000_1011, [1, 2, 3])
107
+ """
108
+
109
+ if len(args) == 1 and isinstance(args[0], list):
110
+ args = args[0]
111
+ return all([bit_set(value, bit) for bit in args])
112
+
113
+
114
+ def beautify_binary(value: int, sep: str = ' ', group: int = 8, prefix: str = '', size: int = 0):
115
+ """
116
+ Returns a binary representation of the given value. The bits are presented
117
+ in groups of 8 bits for clarity by default (can be changed with the `group` keyword).
118
+
119
+ Args:
120
+ value (int): the value to beautify
121
+ sep (str): the separator character to be used, default is a space
122
+ group (int): the number of bits to group together, default is 8
123
+ prefix (str): a string to prefix the result, default is ''
124
+ size (int): number of digits
125
+
126
+ Returns:
127
+ a binary string representation.
128
+
129
+ For example:
130
+ >>> status = 2**14 + 2**7
131
+ >>> assert beautify_binary(status) == "01000000 10000000"
132
+ """
133
+
134
+ if size == 0:
135
+ size = 8
136
+ while value > 2**size - 1:
137
+ size += 8
138
+
139
+ b_str = f'{value:0{size}b}'
140
+
141
+ return prefix + sep.join([b_str[i:i + group] for i in range(0, len(b_str), group)])
142
+
143
+
144
+ def humanize_bytes(n: int, base: Union[int, str] = 2, precision: int = 3) -> str:
145
+ """
146
+ Represents the size `n` in human readable form, i.e. as byte, KiB, MiB, GiB, ...
147
+
148
+ Please note that, by default, I use the IEC standard (International Engineering Consortium)
149
+ which is in `base=2` (binary), i.e. 1024 bytes = 1.0 KiB. If you need SI units (International
150
+ System of Units), you need to specify `base=10` (decimal), i.e. 1000 bytes = 1.0 kB.
151
+
152
+ Args:
153
+ n (int): number of byte
154
+ base (int, str): binary (2) or decimal (10)
155
+ precision (int): the number of decimal places [default=3]
156
+ Returns:
157
+ a human readable size, like 512 byte or 2.300 TiB
158
+ Raises:
159
+ ValueError when base is different from 2 (binary) or 10 (decimal).
160
+
161
+ Examples:
162
+ >>> assert humanize_bytes(55) == "55 bytes"
163
+ >>> assert humanize_bytes(1024) == "1.000 KiB"
164
+ >>> assert humanize_bytes(1000, base=10) == "1.000 kB"
165
+ >>> assert humanize_bytes(1000000000) == '953.674 MiB'
166
+ >>> assert humanize_bytes(1000000000, base=10) == '1.000 GB'
167
+ >>> assert humanize_bytes(1073741824) == '1.000 GiB'
168
+ >>> assert humanize_bytes(1024**5 - 1, precision=0) == '1024 TiB'
169
+ """
170
+
171
+ if base not in [2, 10, "binary", "decimal"]:
172
+ raise ValueError(f"Only base 2 (binary) and 10 (decimal) are supported, got {base}.")
173
+
174
+ # By default we assume base == 2 or base == "binary"
175
+
176
+ one_kilo = 1024
177
+ units = ['KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']
178
+
179
+ if base == 10 or base == 'decimal':
180
+ one_kilo = 1000
181
+ units = ['kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
182
+
183
+ _n = n
184
+ if _n < one_kilo:
185
+ return f"{_n} byte{'' if n == 1 else 's'}"
186
+
187
+ for dim in units:
188
+ _n /= one_kilo
189
+ if _n < one_kilo:
190
+ return f"{_n:.{precision}f} {dim}"
191
+
192
+ return f"{n} byte{'' if n == 1 else 's'}"
193
+
194
+
195
+ CRC_TABLE = [
196
+ 0x00, 0x91, 0xe3, 0x72, 0x07, 0x96, 0xe4, 0x75,
197
+ 0x0e, 0x9f, 0xed, 0x7c, 0x09, 0x98, 0xea, 0x7b,
198
+ 0x1c, 0x8d, 0xff, 0x6e, 0x1b, 0x8a, 0xf8, 0x69,
199
+ 0x12, 0x83, 0xf1, 0x60, 0x15, 0x84, 0xf6, 0x67,
200
+ 0x38, 0xa9, 0xdb, 0x4a, 0x3f, 0xae, 0xdc, 0x4d,
201
+ 0x36, 0xa7, 0xd5, 0x44, 0x31, 0xa0, 0xd2, 0x43,
202
+ 0x24, 0xb5, 0xc7, 0x56, 0x23, 0xb2, 0xc0, 0x51,
203
+ 0x2a, 0xbb, 0xc9, 0x58, 0x2d, 0xbc, 0xce, 0x5f,
204
+ 0x70, 0xe1, 0x93, 0x02, 0x77, 0xe6, 0x94, 0x05,
205
+ 0x7e, 0xef, 0x9d, 0x0c, 0x79, 0xe8, 0x9a, 0x0b,
206
+ 0x6c, 0xfd, 0x8f, 0x1e, 0x6b, 0xfa, 0x88, 0x19,
207
+ 0x62, 0xf3, 0x81, 0x10, 0x65, 0xf4, 0x86, 0x17,
208
+ 0x48, 0xd9, 0xab, 0x3a, 0x4f, 0xde, 0xac, 0x3d,
209
+ 0x46, 0xd7, 0xa5, 0x34, 0x41, 0xd0, 0xa2, 0x33,
210
+ 0x54, 0xc5, 0xb7, 0x26, 0x53, 0xc2, 0xb0, 0x21,
211
+ 0x5a, 0xcb, 0xb9, 0x28, 0x5d, 0xcc, 0xbe, 0x2f,
212
+ 0xe0, 0x71, 0x03, 0x92, 0xe7, 0x76, 0x04, 0x95,
213
+ 0xee, 0x7f, 0x0d, 0x9c, 0xe9, 0x78, 0x0a, 0x9b,
214
+ 0xfc, 0x6d, 0x1f, 0x8e, 0xfb, 0x6a, 0x18, 0x89,
215
+ 0xf2, 0x63, 0x11, 0x80, 0xf5, 0x64, 0x16, 0x87,
216
+ 0xd8, 0x49, 0x3b, 0xaa, 0xdf, 0x4e, 0x3c, 0xad,
217
+ 0xd6, 0x47, 0x35, 0xa4, 0xd1, 0x40, 0x32, 0xa3,
218
+ 0xc4, 0x55, 0x27, 0xb6, 0xc3, 0x52, 0x20, 0xb1,
219
+ 0xca, 0x5b, 0x29, 0xb8, 0xcd, 0x5c, 0x2e, 0xbf,
220
+ 0x90, 0x01, 0x73, 0xe2, 0x97, 0x06, 0x74, 0xe5,
221
+ 0x9e, 0x0f, 0x7d, 0xec, 0x99, 0x08, 0x7a, 0xeb,
222
+ 0x8c, 0x1d, 0x6f, 0xfe, 0x8b, 0x1a, 0x68, 0xf9,
223
+ 0x82, 0x13, 0x61, 0xf0, 0x85, 0x14, 0x66, 0xf7,
224
+ 0xa8, 0x39, 0x4b, 0xda, 0xaf, 0x3e, 0x4c, 0xdd,
225
+ 0xa6, 0x37, 0x45, 0xd4, 0xa1, 0x30, 0x42, 0xd3,
226
+ 0xb4, 0x25, 0x57, 0xc6, 0xb3, 0x22, 0x50, 0xc1,
227
+ 0xba, 0x2b, 0x59, 0xc8, 0xbd, 0x2c, 0x5e, 0xcf,
228
+ ]
229
+
230
+
231
+ def crc_calc(data, start: int, len: int) -> int:
232
+ """
233
+ Calculate the checksum for (part of) the data.
234
+
235
+ Reference:
236
+ The description of the CRC calculation for RMAP is given in the ECSS document
237
+ _Space Engineering: SpaceWire - Remote Memory Access Protocol_, section A.3
238
+ on page 80 [ECSS‐E‐ST‐50‐52C].
239
+
240
+ Args:
241
+ data: the data for which the checksum needs to be calculated
242
+ start: offset into the data array [byte]
243
+ len: number of bytes to incorporate into the calculation
244
+
245
+ Returns:
246
+ the calculated checksum.
247
+ """
248
+ crc: int = 0
249
+
250
+ # The check below is needed because we can pass data that is of type ctypes.c_char_Array
251
+ # and the individual elements have then type 'bytes'.
252
+
253
+ if isinstance(data[0], bytes):
254
+ for idx in range(start, start+len):
255
+ crc = CRC_TABLE[crc ^ (int.from_bytes(data[idx], byteorder='big') & 0xFF)]
256
+ elif isinstance(data[0], int):
257
+ for idx in range(start, start+len):
258
+ crc = CRC_TABLE[crc ^ (data[idx] & 0xFF)]
259
+
260
+ return crc
261
+
262
+
263
+ def s16(value: int):
264
+ """
265
+ Return the signed equivalent of a hex or binary number.
266
+
267
+ Since integers in Python are objects and stored in a variable number of bits, Python doesn't
268
+ know the concept of twos-complement for negative integers. For example, this 16-bit number
269
+
270
+ >>> 0b1000_0000_0001_0001
271
+ 32785
272
+
273
+ which in twos-complement is actually a negative value:
274
+
275
+ >>> s16(0b1000_0000_0001_0001)
276
+ -32751
277
+
278
+ The 'bin()' fuction will return a strange representation of this number:
279
+
280
+ >>> bin(-32751)
281
+ '-0b111111111101111'
282
+
283
+ when we however mask the value we get:
284
+
285
+ >>> bin(-32751 & 0b1111_1111_1111_1111)
286
+ '0b1000000000010001'
287
+
288
+ See:
289
+ https://stackoverflow.com/questions/1604464/twos-complement-in-python and
290
+ https://stackoverflow.com/questions/46993519/python-representation-of-negative-integers and
291
+ https://stackoverflow.com/questions/25096755/signed-equivalent-of-a-2s-complement-hex-value
292
+ and https://stackoverflow.com/a/32262478/4609203
293
+
294
+ Returns:
295
+ The negative equivalent of a twos-complement binary number.
296
+ """
297
+ return ctypes.c_int16(value).value
298
+
299
+
300
+ def s32(value: int):
301
+ """
302
+ Return the signed equivalent of a hex or binary number.
303
+
304
+ Since integers in Python are objects and stored in a variable number of bits, Python doesn't
305
+ know the concept of twos-complement for negative integers. For example, this 32-bit number
306
+
307
+ >>> 0b1000_0000_0000_0000_0000_0000_0001_0001
308
+ 2147483665
309
+
310
+ which in twos-complement is actually a negative value:
311
+
312
+ >>> s32(0b1000_0000_0000_0000_0000_0000_0001_0001)
313
+ -2147483631
314
+
315
+ Returns:
316
+ The negative equivalent of a twos-complement binary number.
317
+ """
318
+ return ctypes.c_int32(value).value