I am connecting to a fanuc cnc machine with Python using a C library. I learned C++ about 20 years ago (haven't used it since) and am not super strong in Python so I'm struggling with data types.
I have data coming from the c library in the following format
I am using the following code to read it:
cnc_ids = (ctypes.c_uint32 * 11)()
ret = focas.cnc_rddynamic2(libh, 1, 44, cnc_ids)
if ret != 0:
raise Exception(f"Failed to read cnc id! ({ret})")
for i in range(11):
print(f"{i}: {cnc_ids[i]}")
If all of the pieces of data were a length of 4 bytes this would be easy, but the first two are only 2 bytes each.
I imagine I could just split the first 4 byte group into two with some extra code, but I will be interfacing about 300 different functions, all with similar structures so it will be a recurring issue. Some of these functions will have several different lengths of data that need to be processed.
What is the best way to process this data?
My end goal is to output the data in a json format that will be used for an api within flask.
Additional info - The typedef is also provided. If there is just a way I can use that directly that would be awesome. Below is the example they give for this function.
typedef struct odbdy2 {
short dummy ; /* not used */
short axis ; /* axis number */
long alarm ; /* alarm status */
long prgnum ; /* current program number */
long prgmnum ; /* main program number */
long seqnum ; /* current sequence number */
long actf ; /* actual feedrate */
long acts ; /* actual spindle speed */
union {
struct {
long absolute[MAX_AXIS] ; /* absolute */
long machine[MAX_AXIS] ; /* machine */
long relative[MAX_AXIS] ; /* relative */
long distance[MAX_AXIS] ; /* distance to go */
} faxis ; /* In case of all axes */
struct {
long absolute ; /* absolute */
long machine ; /* machine */
long relative ; /* relative */
long distance ; /* distance to go */
} oaxis ; /* In case of 1 axis */
} pos ;
} ODBDY2 ; /* MAX_AXIS is the maximum controlled axes. */
With ctypes
, the structure can be declared and used directly. If you receive the data as a buffer of 32-bit values, you can cast that buffer into the structure as shown below:
import ctypes as ct
MAX_AXIS = 3 # Not provided by OP, guess...
class FAXIS(ct.Structure):
_fields_ = (('absolute', ct.c_long * MAX_AXIS),
('machine', ct.c_long * MAX_AXIS),
('relative', ct.c_long * MAX_AXIS),
('distance', ct.c_long * MAX_AXIS))
def __repr__(self):
return f'FAXIS({list(self.absolute)}, {list(self.machine)}, {list(self.relative)}, {list(self.distance)})'
class OAXIS(ct.Structure):
_fields_ = (('absolute', ct.c_long),
('machine', ct.c_long),
('relative', ct.c_long),
('distance', ct.c_long))
def __repr__(self):
return f'OAXIS({self.absolute}, {self.machine}, {self.relative}, {self.distance})'
class POS(ct.Union):
_fields_ = (('faxis', FAXIS),
('oaxis', OAXIS))
def __repr__(self):
return f'POS({self.faxis!r}, {self.oaxis!r})'
class ODBDY2(ct.Structure):
_fields_ = (('dummy', ct.c_short),
('axis', ct.c_short),
('alarm', ct.c_long),
('prgnum', ct.c_long),
('prgmnum', ct.c_long),
('seqnum', ct.c_long),
('actf', ct.c_long),
('acts', ct.c_long),
('pos', POS))
def __repr__(self):
return f'ODBDY2({self.dummy}, {self.axis}, {self.alarm}, {self.prgnum}, {self.prgmnum}, {self.seqnum}, {self.actf}, {self.acts}, {self.pos!r})'
cnc_one_axis = (ct.c_uint32 * 11)(1,2,3,4,5,6,7,8,9,10,11)
cnc_all_axis = (ct.c_uint32 * 19)(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19)
p = ct.pointer(cnc_all_axis)
data = ct.cast(p, ct.POINTER(ODBDY2))
print(data.contents)
p = ct.pointer(cnc_one_axis)
data = ct.cast(p, ct.POINTER(ODBDY2)) # Notice only OAXIS has valid data
print(data.contents)
Output:
ODBDY2(1, 0, 2, 3, 4, 5, 6, 7, POS(FAXIS([8, 9, 10], [11, 12, 13], [14, 15, 16], [17, 18, 19]), OAXIS(8, 9, 10, 11)))
ODBDY2(1, 0, 2, 3, 4, 5, 6, 7, POS(FAXIS([8, 9, 10], [11, 0, 1], [0, -1838440016, 32762], [1, 0, -1]), OAXIS(8, 9, 10, 11)))