Issue24859
This issue tracker has been migrated to GitHub,
and is currently read-only.
For more information,
see the GitHub FAQs in the Python's Developer Guide.
Created on 2015-08-13 19:20 by zeero, last changed 2022-04-11 14:58 by admin. This issue is now closed.
Files | ||||
---|---|---|---|---|
File name | Uploaded | Description | Edit | |
test_structure.py | zeero, 2015-08-13 19:20 | Good_ Bad_Test_Script |
Messages (6) | |||
---|---|---|---|
msg248536 - (view) | Author: (zeero) | Date: 2015-08-13 19:20 | |
I'm implementing a CAN SAEJ1939 stack in Python and convert CAN message ids in between integers and structures, tunneling them through a Union object with 4 bytes like what i would do in C. I was checking that particular function and the 3 priority bits were at the wrong position. Some trials later i concluded the bits are reversed and inverted the order in my structure. Now it works fine. I'm not sure if it's a bug but i would expect ctypes.Structure to store the fields in the order i provide and not the other way around. On the other hand the bytes are in the order i provided. The System I'm running on is 64Bit Ubuntu AMD A1046 but it shows the same behaviour an a 64bit Windows 7 Intel I5 |
|||
msg248552 - (view) | Author: Martin Panter (martin.panter) * | Date: 2015-08-14 02:38 | |
It would be helpful if you could trim down your example code a bit. Without studying the whole file, it is hard to see exactly what order you are seeing and what order you expect, since there are two versions with different orders in the code. My understanding of the “ctypes” module is that it is for interacting with the local OS, ABI, compiler, etc, which could use various layouts depending on the platform. According to the Linux x86-64 ABI <http:/www.x86-64.org/documentation/abi.pdf>, page 14, “bit-fields are allocated from right to left”, which I interpret to mean from least-significant to most-significant bit. Not so sure about Windows, but <https://msdn.microsoft.com/en-us/library/yszfawxh.aspx> suggests a similar story (LSB first). This behaviour agrees with my experiments on Linux and Wine: >>> class Bitfield(Structure): ... _fields_ = (("a", c_uint8, 4), ("b", c_uint8, 4)) ... >>> bytes(Bitfield(0xA, 0xB)) b'\xba' Does this agree with what you expect? Otherwise, what leads you to expect something different? Also: * bytes(saej1939_message_id) should copy the bytes directly; no need for a union. * struct.unpack() should also accept a “ctypes” object directly; no need for the copy. |
|||
msg248581 - (view) | Author: (zeero) | Date: 2015-08-14 11:00 | |
Sorry for the inconvenience. The format specification can be found in chapter 2.1 in http://vector.com/portal/medien/cmc/application_notes/AN-ION-1-3100_Introduction_to_J1939.pdf So I would write down the field contents in that order _fields_ = [('reserved',c_uint8,3), ('priority',c_uint8,3), ('extended_data_page',c_uint8,1), ('data_page',c_uint8,1) ... ] I expect the first Byte to be 0x1C when I set priority to 7 but it came out as 0x38, after tunneling it through the Union object. Reversing the order of the bit fields makes it work like expected. |
|||
msg248637 - (view) | Author: Eryk Sun (eryksun) * | Date: 2015-08-15 12:55 | |
It seems you want a BigEndianStructure: from ctypes import * class SAEJ1939MsgId(BigEndianStructure): _fields_ = (('reserved', c_uint8, 3), ('priority', c_uint8, 3), ('extended_data_page', c_uint8, 1), ('data_page', c_uint8, 1), ('pdu_format', c_uint8), ('pdu_specific', c_uint8), ('source_address', c_uint8)) def __init__(self, *args, pgn=0, **kwds): super().__init__(*args, **kwds) if pgn > 0: self.pgn = pgn def __int__(self): return int.from_bytes(self, 'big') @property def pgn(self): """pgn is an 18-bit number consisting of EDP, DP, PF, and PS""" return (int(self) >> 8) & 0x3FFFF @pgn.setter def pgn(self, value): value |= self.priority << 18 view = (c_char * 3).from_buffer(self) view[:] = value.to_bytes(3, 'big') @classmethod def from_bytes(cls, msg_id): return cls.from_buffer_copy(msg_id) @classmethod def from_integer(cls, msg_id): msg_id_bytes = msg_id.to_bytes(sizeof(cls), 'big') return cls.from_buffer_copy(msg_id_bytes) Example: >>> a = SAEJ1939MsgId(priority=7, source_address=3) >>> hex(int(a)) '0x1c000003' >>> b = SAEJ1939MsgId(pgn=0xf004, priority=7, source_address=3) >>> hex(int(b)) '0x1cf00403' >>> b.priority 7 >>> b.pdu_format 240 >>> b.pdu_specific 4 >>> b.source_address 3 >>> c = SAEJ1939MsgId.from_integer(int(b)) >>> hex(int(c)) '0x1cf00403' |
|||
msg248642 - (view) | Author: (zeero) | Date: 2015-08-15 16:53 | |
Thanks for the advise. I'll give it a try. So ctypes.Structure is always little endian regardless from the underlying architecture. I just checked on a raspberry pi 2 that should be a big endian device, and got the same results as before. I'm still not sure if it's right that ctypes.Structure swaps the order of the bit items but apparently it's the intended behaviour. I'll close the issue then. Thanks again. |
|||
msg248661 - (view) | Author: Eryk Sun (eryksun) * | Date: 2015-08-15 21:29 | |
No, ctypes.Structure should use native endianness. So on a little-endian it's the same as ctypes.LittleEndianStructure, and on a big-endian system it's the same as ctypes.BigEndianStructure. ARM processors can work in either mode. IIRC, the Raspberry Pi is built as a little-endian system. Check sys.byteorder to be sure. |
History | |||
---|---|---|---|
Date | User | Action | Args |
2022-04-11 14:58:19 | admin | set | github: 69047 |
2015-08-15 21:29:10 | eryksun | set | messages:
+ msg248661 stage: resolved |
2015-08-15 16:53:22 | zeero | set | status: open -> closed resolution: not a bug messages: + msg248642 |
2015-08-15 12:55:45 | eryksun | set | nosy:
+ eryksun messages: + msg248637 |
2015-08-14 11:00:05 | zeero | set | messages: + msg248581 |
2015-08-14 02:38:21 | martin.panter | set | nosy:
+ martin.panter messages: + msg248552 |
2015-08-13 19:20:46 | zeero | create |