payloads-new.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 
00003 
00004 from collections import namedtuple
00005 
00006 from . import check
00007 from . import messages
00008 
00009 from .. import utils
00010 
00011 FieldMeta = namedtuple('FieldMeta', 'units format title')
00012 
00013 
00014 class Payload(check.Fields):
00015     def __init__(self, **kwargs):
00016         # Setting of field values has already happened in the 
00017         # tuple setup, in __new__. We just validate them here,
00018         # and raise any exceptions necessary. For received data,
00019         # any ValueErrors thrown will be captured by the Receiver
00020         # thread and assigned to Payload.error. For outbound messages
00021         # where the data is user-originated, Errors thrown will
00022         # bubble back to the user.
00023         # logger.debug("Payload: %s" % repr(self))
00024         self.validate()
00025 
00026     def __str__(self):
00027         """Prints a basic human-readable output, using formats and units for
00028         fields based on the self.meta information from the Payload.fields
00029         initializer."""
00030         lines = []
00031         for field in self._fields:
00032             meta = self.meta[field]
00033             value = getattr(self, field)
00034             #if isinstance(value, Fields):
00035                 # Special case for the Payload members of Message.
00036             #    value = str(value)
00037 
00038             one = lambda value: "%s %s" % (meta.format, meta.units) % value
00039             if isinstance(value, list):
00040                 if len(value) > 0:
00041                     valuestr = ', '.join(map(one, value))
00042                 else:
00043                     valuestr = '<none>'
00044             else:
00045                 valuestr = one(value)
00046 
00047             line = "%s: %s" % (meta.title, valuestr)
00048             lines.append(line)
00049 
00050         return "\n".join(lines)
00051 
00052     @staticmethod
00053     def fields(*fieldstrs):
00054         """Returns pointer to a SubPayload class which inherits from Payload, but also
00055         has a namedtuple mixin with the specified fields, and also a special
00056         meta attribute which provides hints to Payload about the fields and units
00057         and so-on. Usage:
00058         m = Payload.fields('field units format title')."""
00059         fieldmetas = {}
00060         fieldnames = []
00061         for fieldmeta in map(lambda s: s.split(' ', 4), fieldstrs):
00062             if len(fieldmeta) == 1: fieldmeta.append('')  # Default units
00063             if fieldmeta[1] == '-': fieldmeta[1] = ''
00064             if len(fieldmeta) == 2: fieldmeta.append("%.2f")  # Default format
00065             if len(fieldmeta) == 3: 
00066                 # Default title based on field name.
00067                 fieldmeta.append(fieldmeta[0].replace('_', ' ').title())
00068             fieldnames.append(fieldmeta[0])
00069             fieldmetas[fieldmeta[0]] = FieldMeta(*fieldmeta[1:])
00070         
00071         tuplename = "p%d_" % abs(hash(tuple(fieldnames)))
00072 
00073         class SubPayload(Payload, namedtuple(tuplename, fieldnames)):
00074             meta = fieldmetas
00075         return SubPayload
00076 
00077     @classmethod
00078     def parse(cls, data):
00079         '''Payloads which implement this may be received on the wire, and
00080         this method will parse the list of bytes and return a Payload object.''' 
00081         raise NotImplementedError("Payload does not have a parse method.")
00082 
00083     def data(self):
00084         '''Payloads which may be sent in outbound messages will implement this
00085         method, which produces a list of bytes. For most messages, the data()
00086         output will be identical to what is expected for parse(). However, not
00087         all the messages are symmetrical!''' 
00088         raise NotImplementedError("Payload does not have a data output method.")
00089 
00090     def validate(self):
00091         '''All payloads must implement validation, using Fields.check()'''
00092         raise NotImplementedError("Payloade does not have a validate method.")
00093 
00094 
00095 class DifferentialSpeed(Payload.fields('left_speed m/s', 'right_speed m/s',
00096                                        'left_accel m/s^2', 'right_accel m/s^2')):
00097 
00098     @classmethod
00099     def parse(cls, data):
00100         return cls(left_speed = utils.to_short(data[0:2], 100), 
00101                     right_speed = utils.to_short(data[2:4], 100),
00102                     left_accel = utils.to_short(data[4:6], 100), 
00103                     right_accel = utils.to_short(data[6:8], 100))
00104 
00105     def data(self):
00106         data = []
00107         data += utils.from_short(self.left_speed, 100)
00108         data += utils.from_short(self.right_speed, 100)
00109         data += utils.from_short(self.left_accel, 100)
00110         data += utils.from_short(self.right_accel, 100)
00111         return data;
00112 
00113     def validate(self):
00114         self.check('left_speed right_speed').range(-320, 320)
00115         self.check('left_accel right_accel').range(0, 320)
00116 
00117 
00118 class SystemStatus(Payload.fields('uptime sec %d', 'voltages V', 
00119                                   'currents A', 'temperatures C')):
00120 
00121     @classmethod
00122     def parse(cls, data):
00123         pass
00124     #    return cls(left_speed = utils.to_short(data[0:2], 100), 
00125     #                right_speed = utils.to_short(data[2:4], 100),
00126     #                left_accel = utils.to_short(data[4:6], 100), 
00127     #                right_accel = utils.to_short(data[6:8], 100))
00128 
00129     def validate(self):
00130         self.check('uptime').range(0, 4000000)
00131         self.check('voltages currents temperatures').each().range(0, 320)
00132 
00133 
00134 
00135 payload = DifferentialSpeed.parse([0x6E, 0x00, 0x88, 0xFF, 0xC8, 0x00, 0xC8, 0x00])
00136 print(payload)
00137 print(repr(payload))
00138 
00139 payload = DifferentialSpeed(left_speed = 0.8, right_speed = 0.8, left_accel = 0.2, right_accel = 0.2)
00140 print(payload.data())
00141 print(payload.hex())
00142 print(repr(payload))
00143 
00144 # payload = DifferentialSpeed(left_speed = 0.8, right_speed = 400, left_accel = 0.2, right_accel = 0.2)
00145 payload = SystemStatus(uptime=100, voltages=[1,200,3], currents=[100,101,120], temperatures=[])
00146 print(payload)
00147 print(repr(payload))
00148 
00149 msg = messages.Message(code=12, payload=payload, no_ack=0, timestamp=1000000)
00150 print(msg)
00151 print(repr(msg))
00152 


clearpath_base
Author(s): Mike Purvis
autogenerated on Sat Dec 28 2013 16:50:47