-
Notifications
You must be signed in to change notification settings - Fork 207
Implement the remaining canopen datatypes #440
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
Changes from 4 commits
1205906
edfdf69
0bc5d1c
b4fdef6
a6e7ca3
e2e53e0
1a5bd8e
0164e28
9c2ec3d
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 |
---|---|---|
@@ -1,3 +1,4 @@ | ||
import struct | ||
|
||
BOOLEAN = 0x1 | ||
INTEGER8 = 0x2 | ||
|
@@ -10,16 +11,92 @@ | |
VISIBLE_STRING = 0x9 | ||
OCTET_STRING = 0xA | ||
UNICODE_STRING = 0xB | ||
TIME_OF_DAY = 0xC | ||
TIME_DIFFERENCE = 0xD | ||
DOMAIN = 0xF | ||
INTEGER24 = 0x10 | ||
REAL64 = 0x11 | ||
INTEGER40 = 0x12 | ||
INTEGER48 = 0x13 | ||
INTEGER56 = 0x14 | ||
INTEGER64 = 0x15 | ||
UNSIGNED24 = 0x16 | ||
UNSIGNED40 = 0x18 | ||
UNSIGNED48 = 0x19 | ||
UNSIGNED56 = 0x1A | ||
UNSIGNED64 = 0x1B | ||
PDO_COMMUNICATION_PARAMETER = 0x20 | ||
PDO_MAPPING = 0x21 | ||
SDO_PARAMETER = 0x22 | ||
IDENTITY = 0x23 | ||
|
||
SIGNED_TYPES = (INTEGER8, INTEGER16, INTEGER24, INTEGER32, INTEGER64) | ||
UNSIGNED_TYPES = (UNSIGNED8, UNSIGNED16, UNSIGNED24, UNSIGNED32, UNSIGNED64) | ||
SIGNED_TYPES = (INTEGER8, INTEGER16, INTEGER24, INTEGER32, INTEGER40, INTEGER48, INTEGER56, INTEGER64) | ||
UNSIGNED_TYPES = (UNSIGNED8, UNSIGNED16, UNSIGNED24, UNSIGNED32, UNSIGNED40, UNSIGNED48, UNSIGNED56, UNSIGNED64) | ||
INTEGER_TYPES = SIGNED_TYPES + UNSIGNED_TYPES | ||
FLOAT_TYPES = (REAL32, REAL64) | ||
NUMBER_TYPES = INTEGER_TYPES + FLOAT_TYPES | ||
DATA_TYPES = (VISIBLE_STRING, OCTET_STRING, UNICODE_STRING, DOMAIN) | ||
|
||
|
||
class UnsignedN: | ||
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. I just noticed that using these classes (just as the previous 24-bit only variants) would break with PDO that are not aligned to full bytes. That's because the code there actually examines the Let's add a read-only 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. What exactly is this test doing? It tests that the lower-case of the struct format string is not empty? So is it then a method to determine all types that are struct native, i.e. not 24, 48, 56 and so on? This code is very strange to me. The comment sais "Check if the variable is signed and if the data is negative prepend signedness". I think I need some enlightenment here. 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. The test hinges on the implementation detail that the struct mini-language uses uppercase codes (B, H, L, Q) for unsigned and lowercase (b, h, l, q) for signed types. It then goes on to check whether the value is in the upper half of the representable number space (with the specified number of bits), which indicates that it was a negative number being truncated. In that case, padding with 1-bits is added to the left to make it a truly negative integer (in two's complement) after converting back to the full-width integer type. This is needed for partially mapped signed integer variables. Judging from the name, the I think the easiest way to fix this is to actually inherit from 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. Ahhh. It sais I agree, let's implement some upper case letter for unsigned and lower case for signed. I think simply proxying 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. I really prefer the inheritance approach, as it allows us to also type-annotate the STRUCT_TYPES mapping as Should I take a stab at converting them? For example: class UnsignedN(struct.Struct):
"""Packing and unpacking unsigned integers of arbitrary width, like struct.Struct.
The width must be a multiple of 8 and must be between 8 and 64.
"""
def __init__(self, width: int):
self.width = width
if width % 8 != 0:
raise ValueError("Width must be a multiple of 8")
if width <= 0 or width > 64:
raise ValueError("Invalid width for UnsignedN")
elif width <= 8:
fmt = "B"
elif width <= 16:
fmt = "<H"
elif width <= 32:
fmt = "<L"
else:
fmt = "<Q"
super().__init__(fmt)
def unpack(self, buffer):
return super().unpack(buffer + b'\x00' * (super().size - self.size))
def pack(self, *v):
return super().pack(*v)[:self.size]
@property
def size(self) -> int:
return self.width // 8 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.
I see now that that page needs some care (yes, the triple question marks...) 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. FTR, the 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. Wow, it never crossed my mind that a Python class in the stdlib could NOT be subclassed. If such a thing exists, I hope there is a big fat runtime or compiler warning, besides being clearly stated in the documentation. 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'll get a Python 3.13.0b1 (v3.13.0b1:2268289a47, May 8 2024, 06:41:53) [Clang 13.0.0 (clang-1300.0.29.30)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import datetime
>>> class C(datetime.timezone): ...
...
Traceback (most recent call last):
File "<python-input-1>", line 1, in <module>
class C(datetime.timezone): ...
TypeError: type 'datetime.timezone' is not an acceptable base type
>>>
Wrong link, and more importantly, the docs do not mention it in this case. 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. Anyway (continuing the digression): most non-base types are not user visible. For example, the Python 3.13.0b1 (v3.13.0b1:2268289a47, May 8 2024, 06:41:53) [Clang 13.0.0 (clang-1300.0.29.30)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import struct
>>> tp = type(struct.iter_unpack("b", b'x'))
>>> tp.__name__
'unpack_iterator'
>>> class C(tp): ...
...
Traceback (most recent call last):
File "<python-input-3>", line 1, in <module>
class C(tp): ...
TypeError: type '_struct.unpack_iterator' is not an acceptable base type
>>> |
||
"""Packing and unpacking unsigned integers of arbitrary width, like struct.Struct. | ||
|
||
The width must be a multiple of 8 and must be between 8 and 64. | ||
""" | ||
def __init__(self, width: int): | ||
self.width = width | ||
if width % 8 != 0: | ||
raise ValueError("Width must be a multiple of 8") | ||
if width <= 0 or width > 64: | ||
raise ValueError("Invalid width for UnsignedN") | ||
elif width <= 8: | ||
self.struct = struct.Struct("B") | ||
elif width <= 16: | ||
self.struct = struct.Struct("<H") | ||
elif width <= 32: | ||
self.struct = struct.Struct("<L") | ||
else: | ||
self.struct = struct.Struct("<Q") | ||
|
||
def unpack(self, buffer): | ||
return self.struct.unpack(buffer + b'\x00' * (self.struct.size - self.size)) | ||
|
||
def pack(self, *v): | ||
return self.struct.pack(*v)[:self.size] | ||
|
||
@property | ||
def size(self): | ||
return self.width // 8 | ||
|
||
|
||
class IntegerN: | ||
"""Packing and unpacking integers of arbitrary width, like struct.Struct. | ||
|
||
The width must be a multiple of 8 and must be between 8 and 64. | ||
""" | ||
def __init__(self, width: int): | ||
self.width = width | ||
if width % 8 != 0: | ||
raise ValueError("Width must be a multiple of 8") | ||
if width <= 0 or width > 64: | ||
raise ValueError("Invalid width for IntegerN") | ||
elif width <= 8: | ||
self.struct = struct.Struct("b") | ||
elif width <= 16: | ||
self.struct = struct.Struct("<h") | ||
elif width <= 32: | ||
self.struct = struct.Struct("<l") | ||
else: | ||
self.struct = struct.Struct("<q") | ||
|
||
def unpack(self, buffer): | ||
mask = 0x80 | ||
neg = (buffer[self.size - 1] & mask) > 0 | ||
return self.struct.unpack(buffer + (b'\xff' if neg else b'\x00') * (self.struct.size - self.size)) | ||
|
||
def pack(self, *v): | ||
return self.struct.pack(*v)[:self.size] | ||
|
||
@property | ||
def size(self): | ||
return self.width // 8 |
This file was deleted.
Uh oh!
There was an error while loading. Please reload this page.