Skip to content

Commit 5fd76e3

Browse files
committed
chore: merge from main with conflict resolution
2 parents d483031 + de9ce6a commit 5fd76e3

File tree

12 files changed

+236
-81
lines changed

12 files changed

+236
-81
lines changed

common/clock.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
class Clock:
66
@staticmethod
77
def now() -> int:
8-
current_time_millis = time.time() * 1000
9-
local_timezone_offset_millis = datetime.now(timezone.utc).astimezone().utcoffset().total_seconds() * 1000
10-
current_time_micros = int((current_time_millis + local_timezone_offset_millis) * 1000)
8+
current_time_micros = time.time_ns() / 1e3
9+
local_timezone_offset_micros = datetime.now(timezone.utc).astimezone().utcoffset().total_seconds() * 1e6
10+
current_time_micros = int(current_time_micros + local_timezone_offset_micros)
1111
return current_time_micros

common/clock_resolution.py

Lines changed: 0 additions & 7 deletions
This file was deleted.

common/color/color.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,13 @@ class Color(Enum):
2020
# additional colors can be added, syntax is <<colorname>> = RGBAColor(r, g, b)
2121

2222
def get_red(self):
23-
return self.value.red
23+
return self.value.get_red()
2424

2525
def get_green(self):
26-
return self.value.green
26+
return self.value.get_green()
2727

2828
def get_blue(self):
29-
return self.value.blue
29+
return self.value.get_blue()
3030

3131
def get_alpha(self):
32-
return self.value.alpha
33-
34-
35-
36-
32+
return self.value.get_alpha()

common/color/rgbacolor.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,16 @@ def __init__(self, r: int, g: int, b: int, a: int = 0x00):
44
value = (a & 255) << 24 | (r & 255) << 16 | (g & 255) << 8 | (b & 255) << 0
55
self.__value = value
66

7-
@property
8-
def red(self):
7+
def get_red(self):
98
return self.__value >> 16 & 255
109

11-
@property
12-
def green(self):
10+
def get_green(self):
1311
return self.__value >> 8 & 255
1412

15-
@property
16-
def blue(self):
13+
def get_blue(self):
1714
return self.__value >> 0 & 255
1815

19-
@property
20-
def alpha(self):
16+
def get_alpha(self):
2117
return self.__value >> 24 & 255
2218

2319
@staticmethod

common/lookup_table.py

Lines changed: 58 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# Copyright (C) Code Partners Pty. Ltd. All rights reserved. #
2+
import typing
23
from typing import Optional
34

5+
from common.color.rgbacolor import RGBAColor
46
from common.file_rotate import FileRotate
57
from common.level import Level
68
from common.color.color import Color
@@ -27,6 +29,22 @@ class LookupTable:
2729
__KB_FACTOR = 1024
2830
__MB_FACTOR = __KB_FACTOR * 1024
2931
__GB_FACTOR = __MB_FACTOR * 1024
32+
__HEX_ID = (
33+
"0x", # C#, Java and Python
34+
"&H", # Visual Basic .NET
35+
"$", # Object Pascal
36+
)
37+
__HEX_TBL = (
38+
0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
39+
0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
40+
0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
41+
0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
42+
0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
43+
0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
44+
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
45+
0x08, 0x09, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f,
46+
0x7f, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x7f
47+
)
3048

3149
def __init__(self):
3250
"""
@@ -193,10 +211,10 @@ def __is_valid_integer(cls, value: str) -> bool:
193211
pass
194212
return False
195213

196-
def get_color_value(self, key: str, default_value: Color) -> Color:
214+
def get_color_value(self, key: str, default_value: (Color, RGBAColor)) -> (RGBAColor, Color):
197215
"""
198-
Returns a Color value of an element for a given key.
199-
Returns either the value converted to a Color value for the given key
216+
Returns a Color/RGBAColor value of an element for a given key.
217+
Returns either the value converted to a Color/RGBAColor value for the given key
200218
if an element with the given key exists and the found value has a valid format or default_value otherwise.
201219
202220
The element value must be specified as a hexadecimal string. The hexadecimal value must represent
@@ -217,17 +235,31 @@ def get_color_value(self, key: str, default_value: Color) -> Color:
217235
found value has an invalid format.
218236
:raises: TypeError if the default_value is not Color type.
219237
"""
220-
if not isinstance(default_value, Color):
221-
raise TypeError("default_value must be a Color")
238+
if not (isinstance(default_value, Color) or isinstance(default_value, RGBAColor)):
239+
raise TypeError("default_value must be a Color or RGBAColor")
222240

223241
value = self.get_string_value(key, "")
224242

225243
if not value:
226244
return default_value
227-
228-
@staticmethod
229-
def __convert_hex_value(value: str):
230-
...
245+
else:
246+
b = self.__convert_hex_value(value.strip())
247+
if len(b) == 3:
248+
return RGBAColor(0xff & b[0],
249+
0xff & b[1],
250+
0xff & b[2])
251+
elif len(b) == 4:
252+
return RGBAColor(0xff & b[0],
253+
0xff & b[0],
254+
0xff & b[0],
255+
0xff & b[0])
256+
257+
def __convert_hex_value(self, value: str) -> typing.Optional[bytearray]:
258+
for prefix in self.__HEX_ID:
259+
if value.startswith(prefix):
260+
value = value[len(prefix):]
261+
return self.__convert_hex_string(value)
262+
return None
231263

232264
def get_boolean_value(self, key: str, default_value: bool) -> bool:
233265
"""
@@ -453,3 +485,20 @@ def __convert_unicode_value(value: str) -> Optional[bytes]:
453485
return value.encode('utf-8')
454486
except UnicodeEncodeError:
455487
return None
488+
489+
def __convert_hex_string(self, value: str) -> typing.Optional[bytes]:
490+
value = value.upper()
491+
if len(value) % 2 != 0: # Check if length is odd
492+
value += "0" # Append a '0' nibble
493+
494+
b = None
495+
if self.__is_valid_hex(value):
496+
b = bytes.fromhex(value)
497+
return b
498+
499+
def __is_valid_hex(self, value: str) -> bool:
500+
for char in value:
501+
code_point = ord(char)
502+
if code_point >= len(self.__HEX_TBL) or self.__HEX_TBL[code_point] > 0x0f:
503+
return False
504+
return True

configuration_timer.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import datetime
2+
import os
3+
import threading
4+
import typing
5+
6+
from smartinspect import SmartInspect
7+
8+
9+
class RepetitiveTimer(threading.Timer):
10+
"""
11+
This class is used internally in SmartInspect ConfigurationTimer and is not intended to be used otherwise.
12+
It overrides the basic threading.Timer behaviour to run a task in a separate thread repeatedly until
13+
stopped by Timer.cancel() method.
14+
"""
15+
16+
def __init__(self, interval, function):
17+
super().__init__(interval, function)
18+
19+
def run(self):
20+
"""
21+
Overriden from threading.Timer (which only runs a task once) to run the task repeatedly with
22+
given intervals until stopped with .cancel() method
23+
"""
24+
while not self.finished.is_set():
25+
self.finished.wait(self.interval)
26+
self.function(*self.args, **self.kwargs)
27+
28+
29+
class ConfigurationTimer:
30+
"""
31+
A configurable timer for monitoring and reloading SmartInspect configuration files on changes.
32+
Use this class to monitor and automatically reload SmartInspect configuration files.
33+
This timer periodically checks if the related configuration file has changed (by comparing
34+
the last write time) and automatically tries to reload the configuration properties.
35+
You can pass the SmartInspect object to configure, the name of the configuration file
36+
to monitor and the interval in which this timer should check for changes.
37+
For information about SmartInspect configuration files, please refer to the
38+
documentation of the SmartInspect.load_configuration() method.
39+
This class is fully thread-safe.
40+
"""
41+
42+
def __init__(self, smartinspect: SmartInspect, filename: str, period: int):
43+
"""
44+
Initializes a new ConfigurationTimer object.
45+
46+
:param smartinspect: The SmartInspect instance to configure.
47+
:param filename: The name of the configuration file to monitor.
48+
:param period: The seconds interval in which this timer should check for changes.
49+
50+
:raises TypeError: If the smartinspect is not SmartInspect type
51+
:raises TypeError: If the filename is not str type
52+
:raises TypeError: If the period is not int type
53+
:raises ValueError: If the period is not a positive integer
54+
"""
55+
if not isinstance(smartinspect, SmartInspect):
56+
raise TypeError("smartinspect must be a SmartInspect type")
57+
if not isinstance(filename, str):
58+
raise TypeError("filename must be an str")
59+
if not isinstance(period, int):
60+
raise TypeError("period must be an int")
61+
if period <= 0:
62+
raise ValueError("period must be a positive integer")
63+
64+
self._filename: str = filename
65+
self._si: SmartInspect = smartinspect
66+
self._last_update = self._get_file_age(self._filename)
67+
self._timer: typing.Optional[RepetitiveTimer] = RepetitiveTimer(period, self._reload_configuration)
68+
self._timer.start()
69+
70+
def _reload_configuration(self) -> None:
71+
last_update = self._get_file_age(self._filename)
72+
if last_update is None or last_update <= self._last_update:
73+
return
74+
self._last_update = last_update
75+
self._si.load_configuration(self._filename)
76+
77+
@staticmethod
78+
def _get_file_age(filename: str) -> typing.Optional[datetime.datetime]:
79+
result = None
80+
try:
81+
file = os.path.abspath(filename)
82+
last_update = os.path.getmtime(file)
83+
if last_update != 0:
84+
result = datetime.datetime.fromtimestamp(last_update)
85+
except OSError:
86+
pass
87+
88+
return result
89+
90+
def dispose(self):
91+
"""
92+
Releases all resources of this ConfigurationTimer object
93+
and stops monitoring the SmartInspect configuration file for
94+
changes.
95+
"""
96+
if self._timer:
97+
self._timer.cancel()
98+
self._timer = None

protocols/cloud/chunk.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,4 @@ def milliseconds_since_the_first_packet(self) -> int:
6464

6565
@staticmethod
6666
def _get_nano_time() -> int:
67-
return int(time.perf_counter() * 1e9)
67+
return int(time.perf_counter_ns())

protocols/protocol.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,7 @@ def _reset(self):
435435
try:
436436
self._internal_disconnect()
437437
finally:
438-
self.__reconnect_tick_count = time.time() * 1000
438+
self.__reconnect_tick_count = time.time_ns() / 1e6
439439

440440
def _impl_disconnect(self):
441441
if self._connected:
@@ -735,7 +735,7 @@ def __forward_packet(self, packet: Packet, disconnect: bool) -> None:
735735

736736
def __do_reconnect(self) -> None:
737737
if self.__reconnect_interval > 0:
738-
tick_count = time.time() * 1000
738+
tick_count = time.time_ns() / 1e6
739739
if tick_count - self.__reconnect_tick_count < self.__reconnect_interval:
740740
return
741741

session/session.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from typing import Optional, Union
1010

1111
from common.color.color import Color
12+
from common.color.rgbacolor import RGBAColor
1213
from common.context.binary_context import BinaryContext
1314
from common.context.binary_viewer_context import BinaryViewerContext
1415
from common.context.data_viewer_context import DataViewerContext
@@ -147,13 +148,13 @@ def color(self):
147148
return self.__color
148149

149150
@color.setter
150-
def color(self, color: Color) -> None:
151+
def color(self, color: (Color, RGBAColor)) -> None:
151152
"""
152153
Sets the background color in the SmartInspect Console of this session.
153154
The session color helps you to identify Log Entries from different sessions
154155
in the SmartInspect Console by changing the background color.
155156
"""
156-
if isinstance(color, Color):
157+
if isinstance(color, Color) or isinstance(color, RGBAColor):
157158
self.__color = color
158159

159160
@property

session/session_defaults.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from common.color.color import Color
2+
from common.color.rgbacolor import RGBAColor
23
from session.session import Session
34
from common.level import Level
45

@@ -22,9 +23,9 @@ def __init__(self):
2223
self.__level: Level = Level.DEBUG
2324

2425
def assign(self, session: Session) -> None:
25-
self.__active = session.active
26-
self.__color = session.color
27-
self.__level = session.level
26+
session.active = self.__active
27+
session.color = self.__color
28+
session.level = self.__level
2829

2930
def is_active(self) -> bool:
3031
"""
@@ -40,18 +41,18 @@ def set_active(self, active: bool) -> None:
4041
raise TypeError("Active must be a boolean")
4142
self.__active = active
4243

43-
def get_color(self) -> Color:
44+
def get_color(self) -> (Color, RGBAColor):
4445
"""
45-
Returns the Color property for created sessions.
46+
Returns the color property for created sessions.
4647
"""
4748
return self.__color
4849

49-
def set_color(self, color: Color) -> None:
50+
def set_color(self, color: (Color, RGBAColor)) -> None:
5051
"""
51-
Specifies the default Color property for newly created sessions.
52+
Specifies the default color property for newly created sessions.
5253
"""
53-
if not isinstance(color, Color):
54-
raise TypeError("Color must be a Color")
54+
if not isinstance(color, Color) and not isinstance(color, RGBAColor):
55+
raise TypeError("Color must be a Color or RGBAColor")
5556
self.__color = color
5657

5758
def get_level(self) -> Level:

0 commit comments

Comments
 (0)