Creating a Custom Field
¶
Creating a custom Field
is done by first subclassing the Field
class from
calpack.models
. There are a few things to consider before creating a custom Field:
- There are specific properties that must be defined within the class
- Any methods defined must be a class method and cannot be used as an instance method
- You must have a basic understanding of the
ctypes
module specifically how to use thectypes.Structure
class.
It is recommended that you study the way that Structures are created if you are looking to create a custom Field.
With that said, defining a custom Field is actually quite simple. The following properties
must be defined at either the class level or in a custom __init__
:
- c_type - a
ctypes
class to be used within the internalctypes.Structure
- instance.
Field Function Overrides¶
Within your custom Field
you can customize the following functions which are further
defined in subsequent sections:
- create_field_c_tuple
- py_to_c
- c_to_py
create_field_c_tuple
¶
This function is used to create the tuple that will go into the _fields_
property of the
Packet’s internal ctypes.Structure
. It’s also important to note that the first elements in
the tuple must be the property self.field_name
. CalPack will automatically populate
this property for the Field class.
As a default, CalPack’s Field
super class defines create_field_c_tuple
as the
following:
def create_field_c_tuple(self):
return (self.field_name, ctypes.c_int)
However this can be customized to suit the needs of the custom field. Since this will be directly
used to create the ctypes.Structure._fields_
anything that is appropriate in creating the
structure can be used here:
def create_field_c_tuple(self):
# return a c_int with only 4 bits as a field tuple. Note: this is similar to how
# we set the bit length of the IntField classes.
return (self.field_name, ctypes.c_int, 4)
def create_field_c_tuple(self):
# return a c_int Array as a field tuple. Note: this is similar to how we set the
# array size of the ArrayField class.
return (self.field_name, ctypes.c_int * 10)
Warning
If the first element in the field tuple is not self.field_name
then access
to the internal c structure will be broken and the packets will not be accessible properly.
py_to_c
¶
When setting a packet field to a value, that python object must be converted to a value that can be
set for the internal ctypes.Structure
object of the packet. Most occasions the value of
the python object is already appropriate. By default this function does exactly that:
def py_to_c(self, val):
return val
However in certain cases additional formatting, transformation or validation might be required. Use this function to override the behavior as needed.
c_to_py
¶
Similar to going from python objects to c, the reverse of going from c to python might need to be configured properly. Most occasions the value of the c object is already appropriate. By default this function does exactly that:
def c_to_py(self, c_field):
return c_field
However in certain cases additional formatting, transformation or validation might be required. Use this function to override the behavior as needed.
Full Example of Creating a Custom Field¶
The following is a quick example of how to create a custom Field within CalPack:
from calpack import models
import ctypes
class UInt30(models.Field):
c_type = ctypes.c_uint32
bit_len = 30
def __init__(self):
super(UInt30, self).__init__()
self.max_size = (2 << self.bit_len) - 1
def create_field_c_tuple(self):
return (self.field_name, self.c_type, self.bit_len)
def py_to_c(self, val):
if val < 0:
raise ValueError("UInt30 must be a positive number!")
if val > self.max_size:
raise ValueError("UInt30 cannot be greater than {}".format(self.max_size))
return val