Go to the documentation of this file.00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00018 
00019 
00020 
00021 
00022 
00023 
00024 
00025 
00026 
00027 
00028 
00029 
00030 
00031 
00032 
00033 
00034 
00035 
00036 
00037 
00038 
00039 
00040 
00041 
00042 
00043 
00044 
00045 
00046 from .. import utils            
00047 from .  import codes            
00048 from .  import check
00049 
00050 
00051 import logging                  
00052 
00053 
00054 
00055 __version__  = "1.0"
00056 __revision__ = "$Revision: 916 $"
00057 
00058 
00059 
00060 logger = logging.getLogger('clearpath.horizon.messages')
00061 """Horizon Messages Module Log"""
00062 logger.setLevel(logging.DEBUG)
00063 logger.addHandler(utils.NullLoggingHandler())
00064 logger.propagate = False
00065 logger.debug("Loading clearpath.horizon.messages ...")
00066 
00067 
00068 
00069 
00070 
00071 
00072 class Message(check.Fields, check.namedtuple('Message_', 'code payload no_ack timestamp')):
00073     """Horizon Protocol Message"""
00074     
00075     
00076     SOH = 0xAA
00077     STX = 0x55
00078  
00079     @classmethod
00080     def command(cls, name, args={}, timestamp=0, no_ack=False):
00081         code = codes.codes[name]
00082         return cls(code=code.set(), payload=code.data_payload(**args), 
00083                    no_ack=no_ack, timestamp=timestamp)
00084 
00085     @classmethod
00086     def request(cls, name, args={}, timestamp=0):
00087         code = codes.codes[name]
00088         return cls(code=code.request(), payload=code.request_payload(**args), 
00089                    no_ack=False, timestamp=timestamp)
00090 
00091     @classmethod
00092     def parse(cls, raw):
00093         checksum = utils.to_unsigned_short(raw[-2:])
00094         if utils.ccitt_checksum(raw[:-2]) != checksum:
00095             raise utils.ChecksumError("Bad Checksum")
00096             
00097         if raw[0] != Message.SOH:
00098             raise ValueError("Invalid SOH")
00099             
00100         if raw[1] != 0xFF & (~raw[2]):
00101             raise ValueError("Invalid length complement")
00102       
00103         timestamp = utils.to_unsigned_int(raw[4:8])
00104                 
00105         if utils.to_byte(raw[8:9]) == 1:
00106             no_ack = True
00107         elif utils.to_byte(raw[8:9]) == 0:
00108             no_ack = False
00109         else:
00110             raise ValueError("Invalid flags")
00111  
00112         code = utils.to_unsigned_short(raw[9:11])
00113             
00114         if raw[11] != Message.STX:
00115             raise ValueError("Invalid STX")
00116  
00117         
00118         
00119         payload_cls = codes.codes[codes.names[code]].data_payload
00120         if code < 0x8000:
00121             payload_cls = codes.payloads.Ack
00122 
00123         
00124         payload = payload_cls(raw = raw[12:-2])   
00125         return cls(code=code, payload=payload, no_ack=no_ack, timestamp=timestamp)
00126 
00127    
00128     def __init__(self, **kwargs):
00129         
00130         self.validate()
00131 
00132 
00133     def __str__(self):
00134         lines = []
00135         lines.append("Code: 0x%X" % self.code)
00136         lines.append("No Ack: %s" % bool(self.no_ack))
00137         lines.append("Timestamp: %d" % self.timestamp)
00138         lines.append("Payload:\n  %s" % (str(self.payload).replace("\n", "\n  ")))
00139         return "\n".join(lines)
00140 
00141 
00142     def copy(self, timestamp = None):
00143         """Return a copy of this message, with a new timestamp. This is for
00144         resending messages."""
00145         if timestamp == None:
00146             timestamp = self.timestamp
00147         return self._replace(timestamp=timestamp)
00148 
00149 
00150     def validate(self):
00151         self.check('timestamp').range(0, 4294967295)
00152         self.check('code').range(0, 65535)
00153 
00154         
00155     def data(self):
00156         data = []
00157         data += utils.from_byte(codes.VERSION_BYTE)
00158         data += utils.from_unsigned_int(self.timestamp)
00159         data += utils.from_byte(1 if self.no_ack else 0)
00160         data += utils.from_unsigned_short(self.code)
00161         data += [Message.STX]
00162         data += self.payload.data   
00163         
00164         length = len(data) + 2  
00165         data = [Message.SOH] + utils.from_byte(length) + utils.from_byte(0xFF&(~(0xFF&length))) + data
00166             
00167         
00168         checksum = utils.ccitt_checksum(data)
00169         data += utils.from_unsigned_short(checksum)
00170         return data
00171 
00172     
00173 logger.debug("... clearpath.horizon.messages loaded.")