Skip to content
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

Bug fixes, send/receive debug logs and Unsigned 24 support #267

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
__pycache__/
*.py[cod]
*$py.class
.idea/
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a separate section for IDE stuff down below, if such things must be collected here.

This change seems unrelated though, so let's keep it in a separate commit / PR.


# C extensions
*.so
Expand Down
12 changes: 12 additions & 0 deletions canopen/objectdictionary/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,17 @@ def add_member(self, variable):
self.subindices[variable.subindex] = variable
self.names[variable.name] = variable

class Unsigned24(struct.Struct):
def __init__(self, *args, **kwargs):
super().__init__("<I", *args, **kwargs)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As previously noted, use "<L" for consistency with the UNSIGNED32 type.


def unpack(self, data, *args, **kwargs):
if isinstance(data, bytearray):
while len(data) < 4:
data += b'\x00'
else:
logger.error(f"Unsigned24.unpack received wrong type - {type(data)}")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use %-style format string.

return super(Unsigned24, self).unpack(data, *args, **kwargs)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another super() thing.


class Variable(object):
"""Simple variable."""
Expand All @@ -231,6 +242,7 @@ class Variable(object):
INTEGER64: struct.Struct("<q"),
UNSIGNED8: struct.Struct("B"),
UNSIGNED16: struct.Struct("<H"),
UNSIGNED24: Unsigned24(),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be ordered by size?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

UNSIGNED32: struct.Struct("<L"),
UNSIGNED64: struct.Struct("<Q"),
REAL32: struct.Struct("<f"),
Expand Down
3 changes: 2 additions & 1 deletion canopen/objectdictionary/datatypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@
DOMAIN = 0xF
REAL64 = 0x11
INTEGER64 = 0x15
UNSIGNED24 = 0x16
UNSIGNED64 = 0x1B

SIGNED_TYPES = (INTEGER8, INTEGER16, INTEGER32, INTEGER64)
UNSIGNED_TYPES = (UNSIGNED8, UNSIGNED16, UNSIGNED32, UNSIGNED64)
UNSIGNED_TYPES = (UNSIGNED8, UNSIGNED16, UNSIGNED24, UNSIGNED32, UNSIGNED64)
INTEGER_TYPES = SIGNED_TYPES + UNSIGNED_TYPES
FLOAT_TYPES = (REAL32, REAL64)
NUMBER_TYPES = INTEGER_TYPES + FLOAT_TYPES
Expand Down
2 changes: 1 addition & 1 deletion canopen/pdo/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -562,7 +562,7 @@ def get_data(self):
data = data | (~((1 << self.length) - 1))
data = od_struct.pack(data)
else:
data = self.pdo_parent.data[byte_offset:byte_offset + len(self.od) // 8]
data = self.pdo_parent.data[byte_offset:byte_offset + self.length // 8]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using the cached value here seems like a good idea, but it does change the semantics. That should at least be explained in the commit message.

Why not apply the same treatment in set_data()? Is there maybe some intentional reason len(self.od) and self.length are both used in these functions?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't think of any reason len (self. OD) is used here.
Somehow len (self.od) changed during the program execution and wasn't equal to self.length when get_data was called. It made the returned data to be in the wrong size. Changing it to self.length fixed the problem.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, len(self.od) returns the number of bits (rounded up to full bytes) that the object consists of according to the node's object dictionary, which is constant. While self.length starts off with the same value, it might be overridden when adding the Variable to a PDO mapping, where you can e.g. specify to map only (the lower) 16 bits of a 32 bit unsigned object. That difference is adjusted in pdo.Map.add_variable().

I agree there are some corner cases in the code that don't deal well with mapping only some bits of an object. But to fix that properly, each use within get_data() and set_data() should be reconsidered individually. Just changing one instance might fix your problem, but cause issues for other users.

By the way, my (hacky) solution so far was to adjust the ObjectDictionary instance to artificially overwrite the data_type member when using partial objects in PDO mappings. That breaks SDO access though, and doesn't cope well with signed numbers (two's complement requires changing the higher bytes as well when crossing zero).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@acolomb This one


return data

Expand Down
4 changes: 4 additions & 0 deletions canopen/sdo/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ class SdoClient(SdoBase):
#: Seconds to wait before sending a request, for rate limiting
PAUSE_BEFORE_SEND = 0.0

# Seconds to wait before next read attempt for response in queue. For delayed responses.
RETRY_DELAY = 0.0

def __init__(self, rx_cobid, tx_cobid, od):
"""
:param int rx_cobid:
Expand Down Expand Up @@ -89,6 +92,7 @@ def request_response(self, sdo_request):
self.abort(0x5040000)
raise
logger.warning(str(e))
time.sleep(self.RETRY_DELAY)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
time.sleep(self.RETRY_DELAY)
if self.RETRY_DELAY:
time.sleep(self.RETRY_DELAY)


def abort(self, abort_code=0x08000000):
"""Abort current transfer."""
Expand Down
1 change: 1 addition & 0 deletions canopen/sdo/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ def segmented_download(self, command, request):
self.send_response(response)

def send_response(self, response):
logger.debug(f"Sending to {self.tx_cobid} data {response}.")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another overly verbose log message.

self.network.send_message(self.tx_cobid, response)

def abort(self, abort_code=0x08000000):
Expand Down