]> Pileus Git - ~andy/csm213a-hw/blobdiff - vis/device.py
Get things running
[~andy/csm213a-hw] / vis / device.py
index ba4678709df6b8872c022b805e9f490b5c79171d..82b8b4598b44e55757f79d1fbb543714fe92b875 100644 (file)
+import time
+
 from re       import compile
 from serial   import Serial
 from datetime import datetime
+from struct   import *
 
-class State:
-       acc   = [None]*3
-       mag   = [None]*3
-       touch = [None]*2
-       light = [None]*1
-       a2d   = [None]*6
-       time  = None
+class State:                                    #information stored
+       acc  = [None]*3
+       mag  = [None]*3
+       lgt  = [None]*1
+       tch  = [None]*2
+       a2d  = [None]*6
+       time = None
 
        def __init__(self):
                self.time = datetime.utcnow()
 
-class Device:
-       # Attributes
-       port = "/dev/ttyACM0"
+class Frame:
+       # Sensor types
+       ACC_SNS    = 0x00
+       MAG_SNS    = 0x01
+       LGT_SNS    = 0x02
+       TCH_SNS    = 0x03
+       A2D_SNS    = 0x04
+
+       SNS_NUM    = 0x05
+       SNS_SHIFT  = 4
+       SNS_MASK   = 0xF0
+
+       # Data types
+       INT        = 0
+       LONG       = 1
+       FLOAT      = 2
+       DOUBLE     = 3
+
+       TYP_NUM    = 4
+       TYP_SHIFT  = 0
+       TYP_MASK   = 0x0F
+
+       # Command codes
+       STOP       = 0
+       START      = 1
+       RATE       = 2
+
+       OPER_NUM   = 3
+       OPER_SHIFT = 0
+       OPER_MASK  = 0x0F
+
+       # Frame information
+       HEADER     = 0x02
+       TAIL       = 0x0A
+
+       HEADER_POS = 0
+       BITS_POS   = 1
+       COUNT_POS  = 2
+       DATA_POS   = 3
+
+       # Maps
+       snsMap   = {ACC_SNS: 'acc',
+                   MAG_SNS: 'mag',
+                   LGT_SNS: 'lgt',
+                   TCH_SNS: 'tch',
+                   A2D_SNS: 'a2d'}
+
+       cmdMap   = {START,   'start',
+                   STOP,    'stop',
+                   RATE,    'rate'}
+
+       sizeMap  = {INT:      2,
+                   LONG:     4,
+                   FLOAT:    4,
+                   DOUBLE:   8}
+
+       fmtMap   = {INT:     'h',
+                   LONG:    'i',
+                   FLOAT:   'f',
+                   DOUBLE:  'd'}
+
+       # Parser data
+       index    = 0   # read index
+       count    = 0   # number of items in frame
+       length   = 0   # length of frame (in bytes)
+       bits_sns = 0   # sensor type
+       bits_typ = 0   # data type
+       binary   = ""  # binary read-in
+       values   = []  # converted numeric data
+
+       # Constructor
+       def __init__(self):
+               pass
+
+       # Converters
+       @staticmethod
+       def findCode(dataMap, name):
+               for code in dataMap:
+                       if dataMap[code] == name:
+                               return code
+               print("[ERROR] No code found")
+
+       # Parse frame
+       def parse(self, byte):
+               # save current pos and increment read index
+               # if we have an error we cna reset index below
+               pos = self.index
+               self.index += 1
+
+               if pos == Frame.HEADER_POS:
+                       if ord(byte) != Frame.HEADER:
+                               self.index = 0
+                       #print('parse: header  %02x' % ord(byte))
+
+               elif pos == Frame.BITS_POS:
+                       self.bits_sns = (ord(byte) & Frame.SNS_MASK) >> Frame.SNS_SHIFT
+                       self.bits_typ = (ord(byte) & Frame.TYP_MASK) >> Frame.TYP_SHIFT
+                       if self.bits_sns >= Frame.SNS_NUM:
+                               self.index = 0
+                       if self.bits_typ >= Frame.TYP_NUM:
+                               self.index = 0
+                       #print('parse: bits    sns=%d typ=%d' %
+                       #       (self.bits_sns, self.bits_typ))
+
+               elif pos == Frame.COUNT_POS:
+                       wordsize    = Frame.sizeMap[self.bits_typ]
+                       self.count  = ord(byte)
+                       self.length = Frame.DATA_POS + self.count*wordsize + 1
+                       #print('parse: count   cnt=%d len=%d' %
+                       #       (self.count, self.length))
+
+               elif pos < self.length-1:
+                       self.binary += byte
+                       #print('parse: data    %02x @%d' %
+                       #       (ord(byte), pos-Frame.DATA_POS))
 
+               elif pos == self.length-1:
+                       #print('parse: tail    %02x' % ord(byte))
+                       if ord(byte) == Frame.TAIL:
+                               state = self.convert()
+                       else:
+                               state = None
+                       self.binary = ""
+                       self.index  = 0
+                       return state
+
+               elif pos > self.length-1:
+                       print('Error parsing')
+
+       # Convert frame to state
+       def convert(self):
+               # Covnert data
+               fmt = Frame.fmtMap[self.bits_typ] * self.count
+               sns = Frame.snsMap[self.bits_sns]
+               self.values = unpack('<'+fmt, self.binary)
+               print('convert: %3s = \'%3s\'%%[%s] -> [%s]' %
+                       (sns, fmt, hexDump(self.binary), fltDump(self.values)))
+
+               # Create state
+               state = State()
+               setattr(state, sns, self.values)
+               return state
+
+class Device:
        # Constructors
        def __init__(self, config):
+               print('Defice.__init__')
                self.config = config
                self.serial = None
+               self.frame  = Frame()
 
        # Methods
        def connect(self):
+               print('Device.connect')
+               buildingFrame = 0
                try:
                        self.inbuf  = []
-                       self.serial = Serial(self.port,          \
+                       self.serial = Serial(self.config.device, \
                                baudrate = self.config.baudrate, \
                                parity   = self.config.parity,   \
                                bytesize = self.config.databits, \
                                stopbits = self.config.stopbits, \
                                timeout  = 0)
+                       for sns in self.config.rate:
+                               self.set_rate(sns, self.config.rate[sns])
+                       for sns in self.config.enable:
+                               self.set_enable(sns, self.config.enable[sns])
+                       self.serial.flushInput()
                except Exception as ex:
                        return str(ex)
 
        def disconnect(self):
+               print('Device.disconnect')
                if self.serial and self.serial.isOpen():
                        self.serial.close()
 
-       def running(self):
+       def running(self):                                      # isRunning
                if self.serial == None:
                        return False
                if self.serial.isOpen() == False:
                        return False
                return True
 
+       def set_rate(self, sensor, interval):
+               sns   = Frame.findCode(Frame.snsMap, sensor)
+               bits  = (sns        << Frame.SNS_SHIFT) | \
+                       (Frame.RATE << Frame.OPER_SHIFT)
+               self._write_binary('Bf', bits, interval)
+
+       def set_enable(self, sensor, enabled):
+               sns   = Frame.findCode(Frame.snsMap, sensor)
+               oper  = Frame.START if enabled else Frame.STOP
+               bits  = (sns  << Frame.SNS_SHIFT) | \
+                       (oper << Frame.OPER_SHIFT)
+               self._write_binary('B', bits)
+
        def process(self):
                items = []
                count = 0
-               limit = 100
+               limit = 1000
+
                if not self.running():
                        return items
+
                while self.serial.readable():
                        try:
-                               char = self.serial.read().decode()
+                               byte  = self.serial.read()
                        except Exception as ex:
-                               char = ''
-                       if len(char) == 0:
+                               # Not sure why this is excepting
+                               # if it says it's readable
                                break
-                       if char == '\r' or char == '\n':
-                               if len(self.inbuf) == 0:
-                                       continue
-                               line = "".join(self.inbuf)
-                               item = self._parse_ascii(line)
-                               items.append(item)
-                               self.inbuf = []
-                       else:
-                               self.inbuf.append(char)
+                       if len(byte) == 0:
+                               break
+
+                       state = self.frame.parse(byte)
+                       if state:
+                               items.append(state)
                        if count > limit:
-                               print("Eror: exceeded read limit")
+                               print('[ERROR] Exceeded Read Limit')
                                break
+
                        count += 1
+
                return items
 
+
+       # auxilary function
+       def frame_len(self, frame):
+               if len(frame) < 3:
+                       return -1
+               dataType_snsType = ord(frame[1])
+               dataNum  = ord(frame[2])
+               dataType = (dataType_snsType & Frame.TYP_MASK) >> Frame.TYP_SHIFT
+               snsType  = (dataType_snsType & Frame.SNS_MASK) >> Frame.SNS_SHIFT
+               dataSize = Frame.sizeMap[dataType]
+               return (dataSize*dataNum+4)
+
+       def printHex(self, frame):
+               frameLen = self.frame_len(frame)
+               i = 0
+               while i<frameLen:
+                       print(hex(ord(frame[i])))
+                       i+=1
+
        # Private methods
-       def _parse_ascii(self, line):
-               acc_re = compile("\[ACC\] accX=(.*) accY=(.*) accZ=(.*)")
-               mag_re = compile("\[MAG\] magX=(.*) magY=(.*) magZ=(.*)")
-               lgt_re = compile("\[LGT\] Light Intensity=(.*)")
-               tch_re = compile("\[TCH\] Force=(.*) Distance=(.*)")
-               a2d_re = compile("\[A2D\] ...")
-
-               acc_m = acc_re.match(line)
-               mag_m = mag_re.match(line)
-               lgt_m = lgt_re.match(line)
-               tch_m = tch_re.match(line)
-               a2d_m = a2d_re.match(line)
+       def _write_binary(self, fmt, *args):
+               #print('Device._write_binary')
+               if self.serial:
+                       fmt   = 'B' + fmt + 'B'
+                       args  = [Frame.HEADER] + list(args) + [Frame.TAIL]
+                       frame = pack('<'+fmt, *args)
+                       print('write: bin:[' + hexDump(frame) + ']')
+                       self.serial.write(frame)
+                       self.serial.flush()
+                       time.sleep(0.1)
 
-               state = State()
-               if acc_m:
-                       state.acc[0]   = float(acc_m.group(1))
-                       state.acc[1]   = float(acc_m.group(2))
-                       state.acc[2]   = float(acc_m.group(3))
-               if mag_m:              
-                       state.acc[0]   = float(mag_m.group(1))
-                       state.acc[1]   = float(mag_m.group(2))
-                       state.acc[2]   = float(mag_m.group(3))
-               if lgt_m:
-                       state.light[0] = float(lgt_m.group(1))
-               if tch_m:
-                       state.touch[0] = float(tch_m.group(1))
-               if a2d_m:
-                       state.a2d[0]   = float(tch_m.group(1))
-                       state.a2d[1]   = float(tch_m.group(2))
-                       state.a2d[2]   = float(tch_m.group(3))
-                       state.a2d[3]   = float(tch_m.group(4))
-                       state.a2d[4]   = float(tch_m.group(5))
-                       state.a2d[5]   = float(tch_m.group(6))
+def hexDump(frame):
+       digits = ['%02x' % ord(byte) for byte in frame]
+       return ' '.join(digits)
 
-               return state
+def fltDump(data):
+       digits = ['%5.2f' % flt for flt in data]
+       return ' '.join(digits)