Open hecko opened 3 years ago
Hi Marcel,
StructDef class has the method _elementnames() that can be used to get all field names. It is a hidden method (starts with ), but it can be used. However the method might be removed or changed in future releases without this is indicated by the pycstruct version number.
pycstruct was not intended for documenting C header files but it could be used for it. Also, checkout following projects that might suit you needs even more:
Best regards, Joel
Well, I gave it a go with pycstruct anyways and yeah - the pasring is slightly inconsistent in output:
For example:
#define MEASUREMENT_BUFFER_SIZE 5
typedef struct __attribute__((packed)) {
uint8_t packet_type : 4; /**< \sa MEASUREMENT_PACKET_TYPES */
uint8_t packet_version : 4; /**< packet version \sa PACKET_VERSIONS */
uint8_t measurement_type : 4; /**< \sa MEASUREMENT_TYPES */
uint8_t measurement_unit : 4; /**< \sa MEASUREMENT_UNIT_TYPES */
uint16_t location_id; /**< Location ID */
uint32_t sensor_id; /**< Sensor ID */
uint8_t manufacturer_id; /**< Manufacturer ID */
uint16_t measurement_interval; /**< Measurement interval in seconds. */
uint16_t measurement_amount; /**< Number of measurements in this packet */
uint8_t packet_number; /**< Packets of this type sent since boot */
float sensor_measurement_range_min;
float sensor_measurement_range_max;
Measurement measurements[MEASUREMENT_BUFFER_SIZE]; /**< Actual measurements */
} AnalogPacket;
/** Actual analog reading / measurement from sensor */
typedef struct __attribute__((packed)) {
uint16_t reading : 12; /**< Measured range expressed as 0-4095 range value. */
uint16_t overflowed : 1; /**< Set to 1 if the measurement is more or less than min or max value for the
sensor. */
uint16_t sensor_error : 1; /**< Set to 1 if sensor has error detected. */
uint16_t reserved_3 : 1; /**< Reserved - unused */
uint16_t reserved_4 : 1; /**< Reserved - unused */
} Measurement;
Is parsed as:
Field name: packet_type
<class 'NoneType'>
---
Field name: packet_version
<class 'NoneType'>
---
Field name: measurement_type
<class 'NoneType'>
---
Field name: measurement_unit
<class 'NoneType'>
---
Field name: location_id
<class 'pycstruct.pycstruct.BasicTypeDef'>
Field type:
uint16
Field size: 2
---
Field name: sensor_id
<class 'pycstruct.pycstruct.BasicTypeDef'>
Field type:
uint32
Field size: 4
---
Field name: manufacturer_id
<class 'pycstruct.pycstruct.BasicTypeDef'>
Field type:
uint8
Field size: 1
---
Field name: measurement_interval
<class 'pycstruct.pycstruct.BasicTypeDef'>
Field type:
uint16
Field size: 2
---
Field name: measurement_amount
<class 'pycstruct.pycstruct.BasicTypeDef'>
Field type:
uint16
Field size: 2
---
Field name: packet_number
<class 'pycstruct.pycstruct.BasicTypeDef'>
Field type:
uint8
Field size: 1
---
Field name: sensor_measurement_range_min
<class 'pycstruct.pycstruct.BasicTypeDef'>
Field type:
float32
Field size: 4
---
Field name: sensor_measurement_range_max
<class 'pycstruct.pycstruct.BasicTypeDef'>
Field type:
float32
Field size: 4
---
Field name: measurements
<class 'pycstruct.pycstruct.ArrayDef'>
Field type:
Name Bits Offset Signed
reading 12 0 -
overflowed 1 12 -
sensor_error 1 13 -
reserved_3 1 14 -
reserved_4 1 15 -
Field size: 10
---
It kind of makes sense (the last "Field size" is probably 10, because MEASUREMENT_BUFFER_SIZE = 5), but the bitfields are interpreted as None types. On the other hand I really like the output of the Field type for the 'measurements' substruct - we can actually see the size of the field in bits - something like this output would be great for packet_type, packet_version etc fields.
The mix of "bitfield elements" and "other elements" in a struct is sort of hacked into pycstruct. Originally a struct was either without any bitfield members (StructDef) or only bitfield members (BitfieldDef). To solve this issue the pycparser checks which elements next to each other which are bitfields and create a BitfieldDef which is set added with same_level = True in the StructDef, i.e. the "subtype" BitfieldDef members are moved up to the parent StructDef so that it looks like all members are on the same level.
In you example above packet_type, packet_version, measurement_type and measurement_unit will together form a BitfieldDef. Which is added to the AnalogPacket struct def.
If you do this:
types = pycstruct.parse_str("your source code")
AnalogPacket = types["AnalogPacket"]
print(AnalogPacket)
You will get output:
Name Type Size Length Offset Largest type
__auto_bitfield_0 bitfield 2 0 2
location_id uint16 2 2 2
sensor_id uint32 4 4 4
manufacturer_id uint8 1 8 1
measurement_interval uint16 2 9 2
measurement_amount uint16 2 11 2
packet_number uint8 1 13 1
sensor_measurement_range_min float32 4 14 4
sensor_measurement_range_max float32 4 18 4
measurements bitfield 2 5 22 2
As you see above your bitfield members will be part of __auto_bitfield_0.
You can print that one:
print(AnalogPacket._element_type("__auto_bitfield_0"))
Which will result in:
Name Bits Offset Signed
packet_type 4 0 -
packet_version 4 4 -
measurement_type 4 8 -
measurement_unit 4 12 -
I can see that I can get get_field_type of a specific field, however is there a method available to list the available fields?
Im trying to create a dynamic documentation tool to document C header file with struct defs, but im struggling with the use of this module. Am I missing something - is the pycstruct really not suitable for this kind of thing?
Marcel