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 import applanix_msgs.msg as msg
00044
00045
00046 from translator import Translator
00047
00048
00049 import threading
00050 import socket
00051 import struct
00052 from cStringIO import StringIO
00053
00054
00055 class Port(threading.Thread):
00056 """ Common base class for DataPort and ControlPort. Provides functionality to
00057 recv/send Applanix-formatted packets from the socket. Could in future
00058 support LoggingPort and DisplayPort."""
00059 checksum_struct = struct.Struct("<hh")
00060
00061 def __init__(self, sock, **opts):
00062 super(Port, self).__init__()
00063 self.sock = sock
00064 self.opts = opts
00065 self.daemon = False
00066 self.finish = threading.Event()
00067
00068
00069 self.header = msg.CommonHeader()
00070 self.footer = msg.CommonFooter()
00071
00072 def recv(self, d=False):
00073 """ Receive a packet from the port's socket.
00074 Returns (pkt_id, pkt_str), where pkt_id is ("$GRP"|"$MSG", num)
00075 Returns None, None when no data. """
00076 try:
00077 header_str = self.sock.recv(self.header.translator().size)
00078 except socket.timeout:
00079 return None, None
00080
00081 header_data = StringIO(header_str)
00082 self.header.translator().deserialize(header_data)
00083 pkt_id = (str(self.header.start).encode('string_escape'), self.header.id)
00084
00085
00086 if pkt_id[0] not in (msg.CommonHeader.START_GROUP, msg.CommonHeader.START_MESSAGE):
00087 raise ValueError("Bad header %s.%d" % pkt_id)
00088
00089
00090 if pkt_id == ("$GRP", 20015):
00091 self.sock.recv(135)
00092 return None, None
00093
00094
00095 pkt_str = self.sock.recv(self.header.length)
00096
00097
00098 footer_data = StringIO(pkt_str[-self.footer.translator().size:])
00099 self.footer.translator().deserialize(footer_data)
00100 if str(self.footer.end) != msg.CommonFooter.END:
00101 raise("Bad footer from pkt %s.%d" % pkt_id)
00102
00103
00104 if self._checksum(StringIO(header_str + pkt_str)) != 0:
00105 raise("Bad checksum from pkt %s.%d: %%d" % pkt_id % checksum)
00106
00107 return pkt_id, pkt_str
00108
00109 def send(self, header, message):
00110 """ Sends a header/msg/footer out the socket. Takes care of computing
00111 length field for header and checksum field for footer. """
00112 msg_buff = StringIO()
00113 message.translator().preserialize()
00114 message.translator().serialize(msg_buff)
00115 pad_count = -msg_buff.tell() % 4
00116 msg_buff.write("\x00" * pad_count)
00117
00118 footer = msg.CommonFooter(end=msg.CommonFooter.END)
00119 header.length = msg_buff.tell() + footer.translator().size
00120
00121
00122 buff = StringIO()
00123 header.translator().serialize(buff)
00124 buff.write(msg_buff.getvalue())
00125
00126
00127 footer_start = buff.tell()
00128 footer.translator().serialize(buff)
00129
00130
00131 buff.seek(0)
00132 footer.checksum = 65536 - self._checksum(buff)
00133
00134
00135 buff.seek(footer_start)
00136 footer.translator().serialize(buff)
00137
00138
00139 self.sock.send(buff.getvalue())
00140
00141 @classmethod
00142 def _checksum(cls, buff):
00143 """ Compute Applanix checksum. Expects a StringIO with a
00144 size that is a multiple of four bytes. """
00145 checksum = 0
00146 while True:
00147 data = buff.read(cls.checksum_struct.size)
00148 if len(data) == 0:
00149 break
00150 if len(data) < 4:
00151 raise ValueError("Checksum data length is not a multiple of 4.")
00152 c1, c2 = cls.checksum_struct.unpack(data)
00153 checksum += c1 + c2
00154 return checksum % 65536
00155