Source code for calpack.models.fields

"""
A collection of built-in :code:`Field`'s for creating packets.  This module also contains the building blocks for 
creating custom FieldTypes as well.  
"""

import ctypes

from calpack.models.utils import typed_property

__all__ = ['Field', 'IntField', 'ArrayField', 'PacketField']

[docs]class Field(object): """ A Super class that all other fields inherit from. This class is NOT intended for direct use. Custom Fields MUST inherit from this class. When creating a custom field you MUST define the :code:`c_type` property with a valid :code:`ctypes` data class. """ c_type = None field_name = None creation_counter = 0 bit_len = typed_property('bit_len', int, 16) def __init__(self, default_val=None): super(Field, self).__init__() self.default_val = default_val self.creation_counter = Field.creation_counter Field.creation_counter += 1 def __get__(self, instance, cls): if instance is None: return self return self.c_to_py(instance._get_c_field(self.field_name)) def __set__(self, instance, val): c_val = self.py_to_c(val) instance._set_c_field(self.field_name, c_val)
[docs] def py_to_c(self, val): """ py_to_c - A function used to convert a python object into a valid ctypes assignable object. As a default this function simply returns :code:`val`. It's up to the other :code:`Field`'s to define this if further formating is reuqired in order to set the internal structure of the packet. :param val: the value the user is attempting to set the packet field to. This can be any python object. """ return val
[docs] def c_to_py(self, c_field): """ c_to_py - A function used to convert the cytpes object into a python object. As a default this function simply returns :code:`c_field` directly from the ctypes.Structure object. It's up to the other :code:`Field`'s to define this if further formating is required in order to turn the cyptes value into something user friendly. :param c_field: a ctypes object from the packet's internal :code:`ctypes.Structure` object """ return c_field
[docs] def create_field_c_tuple(self): """ create_field_c_tuple - A function used to create the required an field in the :code:`ctypes.Structure._fields_` tuple. This must return a tuple that is acceptable for one of the items in the :code:`_fields_` list of the :code:`cytpes.Structure`. The first value in the tuple MUST be :code:`self.field_name` as this is used to access the internal c structure. """ return (self.field_name, self.c_type)
[docs]class IntField(Field): """ An Integer field. This field can be configured to be signed or unsinged. It's bit length can also be set, however the max bit length for this field is 64. :param int bit_len: the length in bits of the integer. Max value of 64. (default 16) :param bool signed: whether to treat the int as an signed integer or unsigned integer (default unsigned) :param int default_val: the default value of the field (default 0) :raises ValueError: if the :code:`bit_len` is less than or equal to 0 or greater than 64 """ signed = typed_property('signed', bool, False) # TODO: Implement endianess processing little_endian = typed_property('little_endian', bool) def __init__(self, bit_len=16, signed=False, default_val=0, little_endian=False): super(IntField, self).__init__(default_val) if bit_len <= 0 or bit_len > 64: raise ValueError("bit_len must be between 1 and 64") self.default_val = default_val self.bit_len = bit_len self.little_endian = little_endian self.signed = signed if self.signed: self.c_type = ctypes.c_int64 else: self.c_type = ctypes.c_uint64 def py_to_c(self, val): if not self.signed and val < 0: raise TypeError("Signed valued cannot be set for an unsiged IntField!") return val def create_field_c_tuple(self, name): return (name, self.c_type, self.bit_len)
[docs]class PacketField(Field): """ A custom Field for handling another packet as a field. :param packet_cls: A :code:`calpack.models.Packet` subclass that represents another packet """ packet_cls = None def __init__(self, packet_cls): super(PacketField, self).__init__() self.packet_cls = packet_cls self.packet = packet_cls() self.bit_len = self.packet_cls.bit_len self.c_type = self.packet._Packet__c_struct def create_field_c_tuple(self, name): return (name, self.packet_cls._Packet__c_struct) def __setattr__(self, arg, value): if self.packet_cls is not None and arg in self.packet_cls.fields_order: setattr(self.packet_cls, arg, value) else: super(PacketField, self).__setattr__(arg, value) def py_to_c(self, val): if not isinstance(val, self.packet_cls): raise TypeError("Must be of type {p}".format(type(p=self.packet_cls))) return val.c_pkt
[docs]class ArrayField(Field, list): """ A custom field for handling an array of fields :param array_cls: a :code:`calpack.models.Field` subclass **object** that represent the Field the array will be filled with. :param int array_size: the length of the array. """ def __init__(self, array_cls, array_size, default_val=None): super(ArrayField, self).__init__(default_val) self.array_cls = array_cls self.array_size = array_size self.c_type = (self.array_cls.c_type * self.array_size) self.bit_len = self.array_cls.bit_len * self.array_size def c_to_py(self, c_field): return c_field[:] def py_to_c(self, val): if not isinstance(val, ArrayField) and not isinstance(val, list): raise TypeError("Must be of type ArrayField or list") return self.c_type(*val) def create_field_c_tuple(self, name): return (name, self.array_cls.c_type * self.array_size)