-
-
Notifications
You must be signed in to change notification settings - Fork 247
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add get_time and set_time in util #308
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -586,8 +586,85 @@ def get_dt(bytearray_: bytearray, byte_index: int) -> str: | |
return date_and_time | ||
|
||
|
||
def get_time(bytearray_: bytearray, byte_index: int) -> str: | ||
"""Get time value from bytearray. | ||
|
||
Notes: | ||
Datatype `time` consists in 4 bytes in the PLC. | ||
Maximum possible value is T#24D_20H_31M_23S_647MS(2147483647). | ||
Lower posible value is T#-24D_20H_31M_23S_648MS(-2147483648). | ||
|
||
Args: | ||
bytearray_: buffer to read. | ||
byte_index: byte index from where to start reading. | ||
|
||
Returns: | ||
Value read. | ||
|
||
Examples: | ||
>>> import struct | ||
>>> data = bytearray(4) | ||
>>> data[:] = struct.pack(">i", 2147483647) | ||
>>> snap7.util.get_time(data, 0) | ||
'24:20:31:23:647' | ||
""" | ||
data_bytearray = bytearray_[byte_index:byte_index + 4] | ||
bits = 32 | ||
sign = 1 | ||
byte_str = data_bytearray.hex() | ||
val = int(byte_str, 16) | ||
if (val & (1 << (bits - 1))) != 0: | ||
sign = -1 # if sign bit is set e.g., 8bit: 128-255 | ||
val = val - (1 << bits) # compute negative value | ||
val = val * sign | ||
|
||
milli_seconds = val % 1000 | ||
seconds = val // 1000 | ||
minutes = seconds // 60 | ||
hours = minutes // 60 | ||
days = hours // 24 | ||
time_str = str(days * sign) + ":" + str(hours % 24) + ":" + str(minutes % 60) + ":" + str(seconds % 60) + "." + str(milli_seconds) | ||
return time_str | ||
|
||
|
||
def set_time(bytearray_: bytearray, byte_index: int, time_string: str) -> bytearray: | ||
"""Set value in bytearray to time | ||
|
||
Notes: | ||
Datatype `time` consists in 4 bytes in the PLC. | ||
Maximum possible value is T#24D_20H_31M_23S_647MS(2147483647). | ||
Lower posible value is T#-24D_20H_31M_23S_648MS(-2147483648). | ||
|
||
Args: | ||
bytearray_: buffer to write. | ||
byte_index: byte index from where to start writing. | ||
time_string: time value in string | ||
|
||
Examples: | ||
>>> data = bytearray(4) | ||
>>> snap7.util.set_dint(data, 0, '-22:3:57:28.192') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Typo. Wrong method. |
||
>>> data | ||
bytearray(b'\x8d\xda\xaf\x00') | ||
""" | ||
import re | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Good catch. I didn't notice that issue. I will modify this and make a new pull request. |
||
sign = 1 | ||
bits = 32 | ||
data_list = re.split('[: .]', time_string) | ||
days, hours, minutes, seconds, milli_seconds = [int(x) for x in data_list] | ||
if days < 0: | ||
sign = -1 | ||
time_int = ((days * sign * 3600 * 24 + (hours % 24) * 3600 + (minutes % 60) * 60 + seconds % 60) * 1000 + milli_seconds) * sign | ||
if sign < 0: | ||
time_int = (1 << bits) + time_int | ||
formatstring = '{:0%ib}' % bits | ||
byte_hex = hex(int(formatstring.format(time_int), 2)).split('x')[1] | ||
bytes_array = bytes.fromhex(byte_hex) | ||
Comment on lines
+659
to
+661
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Are these conversions necessary? Int has a method .to_bytes() that does the same. Or it might be better to use a struct. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You approach is more elegant. Thank you. |
||
bytearray_[byte_index:byte_index + 4] = bytes_array | ||
return bytearray_ | ||
|
||
|
||
def set_usint(bytearray_: bytearray, byte_index: int, _int: int) -> bytearray: | ||
"""set unsigned small int | ||
"""Set unsigned small int | ||
|
||
Notes: | ||
Datatype `usint` (Unsigned small int) consists on 1 byte in the PLC. | ||
|
@@ -749,7 +826,8 @@ class DB: | |
|
||
def __init__(self, db_number: int, bytearray_: bytearray, | ||
specification: str, row_size: int, size: int, id_field: Optional[str] = None, | ||
db_offset: Optional[int] = 0, layout_offset: Optional[int] = 0, row_offset: Optional[int] = 0, area: Optional[Areas] = Areas.DB): | ||
db_offset: Optional[int] = 0, layout_offset: Optional[int] = 0, row_offset: Optional[int] = 0, | ||
area: Optional[Areas] = Areas.DB): | ||
""" Creates a new instance of the `Row` class. | ||
|
||
Args: | ||
|
@@ -844,14 +922,14 @@ class DB_Row: | |
_specification: OrderedDict = OrderedDict() # row specification | ||
|
||
def __init__( | ||
self, | ||
bytearray_: bytearray, | ||
_specification: str, | ||
row_size: Optional[int] = 0, | ||
db_offset: int = 0, | ||
layout_offset: int = 0, | ||
row_offset: Optional[int] = 0, | ||
area: Optional[Areas] = Areas.DB | ||
self, | ||
bytearray_: bytearray, | ||
_specification: str, | ||
row_size: Optional[int] = 0, | ||
db_offset: int = 0, | ||
layout_offset: int = 0, | ||
row_offset: Optional[int] = 0, | ||
area: Optional[Areas] = Areas.DB | ||
): | ||
"""Creates a new instance of the `DB_Row` class. | ||
|
||
|
@@ -1012,7 +1090,7 @@ def get_value(self, byte_index: Union[str, int], type_: str) -> Union[ValueError | |
# add these three not implemented data typ to avoid | ||
# 'Unable to get repr for class<snap7.util.DB_ROW>' error | ||
elif type_ == 'TIME': | ||
return 'read TIME not implemented' | ||
return get_time(bytearray_, byte_index) | ||
|
||
elif type_ == 'DATE': | ||
return 'read DATE not implemented' | ||
|
@@ -1081,6 +1159,9 @@ def set_value(self, byte_index: Union[str, int], type: str, value: Union[bool, s | |
if type == 'SINT' and isinstance(value, int): | ||
return set_sint(bytearray_, byte_index, value) | ||
|
||
if type == 'TIME' and isinstance(value, str): | ||
return set_time(bytearray_, byte_index, value) | ||
|
||
raise ValueError | ||
|
||
def write(self, client: Client) -> None: | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This string concatination style still works, but is a bit outdated (but not wrong). An f-string equivalent would be sth. like this:
time_str = f"{days * sign!s}):{hours % 24!s}:{minutes % 60}:{seconds % 60!s}.{milli_seconds!s}")