import serial
import time
import threading
from config import cfg

READ_COM_RETRY_COUNT = 3           #retry count of read data from com interface, after send measure command
READ_COM_RETRY_INTERVAL = 0.1


_measurethreadsinglelock = False

class _measurethread(threading.Thread):
    def __init__(self, looptime):
        self.running = False
        self.looptime = looptime
                
        threading.Thread.__init__(self)
        
        
    def run(self):
        global _measurethreadsinglelock
        if _measurethreadsinglelock:
            return
        _measurethreadsinglelock = True
        self.running = True

        while self.running :
            start_time = time.time()

            d = Controller.Measure()
            Controller.SetDistance(d)
            
            end_time = time.time()
            jobduration = end_time - start_time
            if jobduration < self.looptime :
                waittime = self.looptime - jobduration
                time.sleep(waittime)
                
        self.running = False      # for break
        _measurethreadsinglelock = False

    
    def IsRunning(self):
        return _measurethreadsinglelock

    
    def StopThread(self):
        self.running = False
        while _measurethreadsinglelock:
            time.sleep(1)


class _soniccontroller():
    def __init__(self):
        self.SIGNAL = bytes.fromhex("00 00 00 01")
        
        self.ttyname = "/dev/%s" % cfg.get('relay','device')  # device can be find in folder '/dev/', like 'ttyUSB0'
        self.bps = cfg.getint('relay','bps')    # bits per second
        self.timeout = cfg.getint('relay','timeout')    # connect timeout
        self.engine = None
        self.distance = 0
        self.mthread = None
    
    
    def __del__(self):
        if self.engine is not None:
            if self.IsOpened():
                self.Close()

    
    def IsOpened(self):
        if self.engine is None:
            return False
            
        return self.engine.is_open

    
    def Open(self):
        if self.IsOpened():
            return True
        else:
            try:
                self.engine = serial.Serial(self.ttyname, self.bps, timeout=self.timeout)
                if self.IsOpened() :
                    return True
                    
                self.engine.open()
                return self.IsOpened()
                
            except Exception as e:
                print("Zhi Device Error:", e)
                return False


    def Close(self):
        if self.IsOpened():
            self.engine.close()
            return not self.IsOpened()
        else:
            return True

    
    def SetDistance(self, distance):
        self.distance = distance

    
    def GetDistance(self):
        return self.distance
        
    
    def Measure(self):
        if self.IsOpened():
            try:
                self.engine.read_all()    # clean buffer before write command and read data
                self.engine.write(self.SIGNAL)
                for i in range(0, READ_COM_RETRY_COUNT):
                    time.sleep(READ_COM_RETRY_INTERVAL)
                    data = self.engine.read(4)
                    if data :
                        d = (int)((data[1] << 8) | data[2])
                        return d
            except Execption as e:
                print("Zhi Device Error: ", e)
                
        return 0


    def MeasuresStart(self, looptime):
        if not self.IsOpened():
            return -1

        if self.mthread is None :
            self.mthread=_measurethread(looptime)
            self.mthread.start()
            return 0
        else:
            print("Zhi Error: Measure is bock when start it")
            return -2


    def MeasuresStop(self):
        if self.mthread is None:
            print("Zhi Error: Measure is not inited when stop it")
            return -1
            
        if not self.mthread.IsRunning():
            print("Zhi Error: Measure is not started when stop it")
            return -2
            
        self.mthread.StopThread()
        self.mthread = None
        return 0
        
        

Controller = _soniccontroller()


