import sdtcloudpubsub
import uuid
from pymodbus.client import ModbusSerialClient as ModbusClient
import os, json, sys, time
import threading

sdtcloud = sdtcloudpubsub.sdtcloudpubsub()
sdtcloud.setClient(f"device-app-{uuid.uuid1()}") # parameter is client ID(string)

def get_modbus(evt, serial_obj):
    global isThread, g_zero_point, pub_dict, a_col, d_col, t_col, p_col, S_col
    
    ctl_mode = 'None'
    cmd_status = [0, 0, 0, 0]
    cmd_sub_status = 'None'
    inverter_status = [0] * 6
    valve_status = [False] * 8

    while isThread:
        evt.wait()
        evt.clear()

        # with open(os.path.join(file_path, 'control.json'), 'r') as f:
        with open('./control.json', 'r') as f:
            control_data = json.load(f)
            pre_dict = control_data.copy()

        # CWT-TM-320s
        try:
            tank_temperature = serial_obj.read_holding_registers(address=32, count=32, slave=2)
            # print(f'tank Temp: {tank_temperature.registers}')
            for i, j in enumerate(t_col):
                pub_dict[j] = tank_temperature.registers[i] * 0.1
        except Exception as e:
            print(f'Tank Sensor Error: {e}')
            pass
        
        # CTT-MB307D
        try:
            cdu_analog = serial_obj.read_holding_registers(address=50, count=8, slave=5)
            cdu_digital = serial_obj.read_discrete_inputs(address=0, count=8, slave=5)
            # print(f'cdu_analog:{cdu_analog.registers}')
            # print(f'cdu_digital:{cdu_digital.bits}')
        
            if control_data['set_zero_temperature'] == 'y':
                # with open(os.path.join(file_path, 'config.json'), 'r') as f:
                with open('./config.json', 'r') as f:
                    config_data = json.load(f)
        
                for i, j in enumerate(a_col):
                    if j == 'oilInFlowRate' or j == 'waterInFlowRate':
                        config_data['ref_zero_point'][j] = cdu_analog.registers[i]
                
                control_data['set_zero_temperature'] = 'n'
                
                # with open(os.path.join(path, 'config.json'), 'w') as f:
                with open('./config.json', 'w') as f:
                    json.dump(config_data, f, indent=4)
                
                g_zero_point[0] = config_data['ref_zero_point']['oilInFlowRate']
                g_zero_point[1] = config_data['ref_zero_point']['waterInFlowRate']

            for i, j in enumerate(a_col):
                if j == 'oilInFlowRate': 
                    pub_dict[j] = ((cdu_analog.registers[i] - g_zero_point[0]) / 16000) * 300
                elif j == 'waterInFlowRate':
                    pub_dict[j] = ((cdu_analog.registers[i] - g_zero_point[1]) / 16000) * 300
                elif j == 'oilInTemp' or j == 'waterInTemp':
                    pub_dict[j] = ((cdu_analog.registers[i] - 4000) / 16000) * 100
                elif j == 'oilOutTemp' or j == 'waterOutTemp':
                    scale = ((cdu_analog.registers[i] - 4000) / 16000) # need to edit for temperature sensor
                    pub_dict[j] = (scale * 100) - 20
                elif j == 'oilInPress' or j == 'waterInPress':
                    pub_dict[j] = ((cdu_analog.registers[i] - 4000) / 16000) # need to edit for pressure sensor

            for i, j in enumerate(d_col):
                pub_dict[j] = cdu_digital.bits[i]
            # print(pub_dict)
        except Exception as e:
            print(f'CDU Divecs Error: {e}')
            pass
        
        try:
            # M100
            res = client1.read_holding_registers(address=9, count=6, slave=10)
            inverter_status[0] = res.registers[0]
            inverter_status[1] = res.registers[5]
            inverter_status[2] = res.registers[4]
            res = client1.read_holding_registers(address=9, count=6, slave=11)
            inverter_status[3] = res.registers[0]
            inverter_status[4] = res.registers[5]
            inverter_status[5] = res.registers[4]

            for i, j in enumerate(p_col):
                pub_dict[j] = inverter_status[i]
        except Exception as e:
            print(f'Inverter Status Error: {e}')

        try:
            if control_data['mode'] == 'auto':
                if control_data['cmd'] == 'init': # 방어코드 필요
                    if cmd_sub_status == 'None':
                        valve_status[0], valve_status[1] = True, True
                        res = serial_obj.write_coils(address=0, values=valve_status, slave=5)

                        client1.write_registers(address=5, values=[193], slave=10)
                        client1.write_registers(address=5, values=[193], slave=11)

                        cmd_sub_status = 'workingInit'
                        pub_dict['cmd'] = 'init'
                    elif cmd_sub_status == 'workingInit':
                        if (pub_dict['valve1OpenStatus'] == True
                            and pub_dict['valve2OpenStatus'] == True
                            and (pub_dict['pump1StatusRunning'] & 0x01)
                            and (pub_dict['pump2StatusRunning'] & 0x01)):
                            cmd_sub_status = 'None'
                            pub_dict['cmd'] = 'None'
                            control_data['cmd'] = 'None'

                elif control_data['cmd'] == 'act1':
                    if cmd_sub_status == 'None':
                        if (pub_dict['pump1StatusRunning'] & 0x02
                            and pub_dict['pump2StatusRunning'] & 0x01
                            and pub_dict['valve1OpenStatus'] == True
                            and pub_dict['valve2CloseStatus'] == True):
                                cmd_stataus = 'None'
                                control_data['cmd'] = 'None'
                        else:
                            cmd_stataus = 'act1'
                            pub_dict['cmd'] = 'act1'
                            cmd_sub_status = 'stopPump1'

                    elif cmd_sub_status == 'stopPump1':
                        if not (pub_dict['pump1StatusRunning'] & 0x01):
                            client1.write_registers(address=5, values=[193], slave=10)
                            cmd_sub_status = 'stoppingPump1'
                        else:
                            cmd_sub_status = 'stopPump2'

                    elif cmd_sub_status == 'stoppingPump1':
                        if pub_dict['pump1StatusRunning'] & 0x01:
                            cmd_sub_status = 'stopPump2'

                    elif cmd_sub_status == 'stopPump2':
                        if not (pub_dict['pump2StatusRunning'] & 0x01):
                            client1.write_registers(address=5, values=[193], slave=11)
                            cmd_sub_status = 'stoppingPump2'
                        else:
                            cmd_sub_status = 'closeValve2'
                    
                    elif cmd_sub_status == 'stoppingPump2':
                        if pub_dict['pump2StatusRunning'] & 0x01:
                            cmd_sub_status = 'closeValve2'
                    
                    elif cmd_sub_status == 'closeValve2':
                        if pub_dict['valve2OpenStatus'] == True:
                            valve_status[1] = False
                            res = serial_obj.write_coils(address=0, values=valve_status, slave=5)
                            cmd_sub_status = 'closingValve2'
                        elif pub_dict['valve2CloseStatus'] == True:
                            cmd_sub_status = 'openValve1'
                    
                    elif cmd_sub_status == 'closingValve2':
                        if pub_dict['valve2CloseStatus'] == True:
                            cmd_sub_status = 'openValve1'

                    elif cmd_sub_status == 'openValve1':
                        if pub_dict['valve1CloseStatus'] == True:
                            valve_status[0] = True
                            res = serial_obj.write_coils(address=0, values=valve_status, slave=5)
                            cmd_sub_status = 'openningValve1'
                        elif pub_dict['valve1OpenStatus'] == True:
                            cmd_sub_status = 'startPump1'

                    elif cmd_sub_status == 'openningValve1':
                        if pub_dict['valve1OpenStatus'] == True:
                            cmd_sub_status = 'startPump1'
                        
                    elif cmd_sub_status == 'startPump1':
                        frq = int(control_data['inverter']['inverter1Frq'] * 100)
                        acc = int(control_data['inverter']['inverter1Acc'] * 10)
                        dec = int(control_data['inverter']['inverter1Dec'] * 10)
                        client1.write_registers(address=4, values=[frq, 194, acc, dec], slave=10)
                        cmd_sub_status = 'startingPump1'

                    elif cmd_sub_status == 'startingPump1':
                        if pub_dict['pump1StatusRunning'] & 0x40:
                            cmd_sub_status = 'None'
                            control_data['cmd'] = 'None'
                            
                elif control_data['cmd'] == 'act2':
                    if cmd_sub_status == 'None':
                        if (pub_dict['pump1StatusRunning'] & 0x01
                            and pub_dict['pump2StatusRunning'] & 0x02
                            and pub_dict['valve1CloseStatus'] == True
                            and pub_dict['valve2OpenStatus'] == True):
                                cmd_stataus = 'None'
                                control_data['cmd'] = 'None'
                        else:
                            cmd_stataus = 'act2'
                            pub_dict['cmd'] = 'act2'
                            cmd_sub_status = 'stopPump1'

                    elif cmd_sub_status == 'stopPump1':
                        if not (pub_dict['pump1StatusRunning'] & 0x01):
                            client1.write_registers(address=5, values=[193], slave=10)
                            cmd_sub_status = 'stoppingPump1'
                        else:
                            cmd_sub_status = 'stopPump2'

                    elif cmd_sub_status == 'stoppingPump1':
                        if pub_dict['pump1StatusRunning'] & 0x01:
                            cmd_sub_status = 'stopPump2'

                    elif cmd_sub_status == 'stopPump2':
                        if not (pub_dict['pump2StatusRunning'] & 0x01):
                            client1.write_registers(address=5, values=[193], slave=11)
                            cmd_sub_status = 'stoppingPump2'
                        else:
                            cmd_sub_status = 'closeValve1'
                    
                    elif cmd_sub_status == 'stoppingPump2':
                        if pub_dict['pump2StatusRunning'] & 0x01:
                            cmd_sub_status = 'closeValve1'
                    
                    elif cmd_sub_status == 'closeValve1':
                        if pub_dict['valve1OpenStatus'] == True:
                            valve_status[0] = False
                            res = serial_obj.write_coils(address=0, values=valve_status, slave=5)
                            cmd_sub_status = 'closingValve1'
                        elif pub_dict['valve1CloseStatus'] == True:
                            cmd_sub_status = 'openValve2'
                    
                    elif cmd_sub_status == 'closingValve1':
                        if pub_dict['valve1CloseStatus'] == True:
                            cmd_sub_status = 'openValve2'

                    elif cmd_sub_status == 'openValve2':
                        if pub_dict['valve2CloseStatus'] == True:
                            valve_status[1] = True
                            res = serial_obj.write_coils(address=0, values=valve_status, slave=5)
                            cmd_sub_status = 'openningValve2'
                        elif pub_dict['valve2OpenStatus'] == True:
                            cmd_sub_status = 'startPump2'

                    elif cmd_sub_status == 'openningValve2':
                        if pub_dict['valve2OpenStatus'] == True:
                            cmd_sub_status = 'startPump2'
                        
                    elif cmd_sub_status == 'startPump2':
                        frq = int(control_data['inverter']['inverter2Frq'] * 100)
                        acc = int(control_data['inverter']['inverter2Acc'] * 10)
                        dec = int(control_data['inverter']['inverter2Dec'] * 10)
                        client1.write_registers(address=4, values=[frq, 194, acc, dec], slave=11)
                        cmd_sub_status = 'startingPump2'

                    elif cmd_sub_status == 'startingPump2':
                        if pub_dict['pump2StatusRunning'] & 0x40:
                            cmd_sub_status = 'None'
                            control_data['cmd'] = 'None'

                elif control_data['cmd'] == 'emer': # 어느 조건에서든 입력되면 바로 수행
                    if cmd_sub_status == 'None':
                        valve_status[0], valve_status[1] = False, False
                        res = serial_obj.write_coils(address=0, values=valve_status, slave=5)

                        client1.write_registers(address=5, values=[208], slave=10)
                        client1.write_registers(address=5, values=[208], slave=11)

                        cmd_sub_status = 'workingEmer'
                        pub_dict['cmd'] = 'emer'
                    elif cmd_sub_status == 'workingEmer':
                        if (pub_dict['valve1CloseStatus'] == True
                            and pub_dict['valve2CloseStatus'] == True
                            and (pub_dict['pump1StatusRunning'] & 0x01)
                            and (pub_dict['pump2StatusRunning'] & 0x01)):
                            cmd_sub_status = 'None'
                            pub_dict['cmd'] = 'None'
                            control_data['cmd'] = 'None'

            elif control_data['mode'] == 'manual':
                if control_data['inverter']['inverter1'] == 'On' and cmd_status[0] == 0:
                    frq = int(control_data['inverter']['inverter1Frq'] * 100)
                    acc = int(control_data['inverter']['inverter1Acc'] * 10)
                    dec = int(control_data['inverter']['inverter1Dec'] * 10)
                    client1.write_registers(address=4, values=[frq, 194, acc, dec], slave=10)
                    cmd_status[0] = 1
                elif control_data['inverter']['inverter1'] == 'Off' and cmd_status[0] == 1:
                    client1.write_registers(address=5, values=[193], slave=10)
                    cmd_status[0] = 0
                
                if control_data['inverter']['inverter2'] == 'On' and cmd_status[1] == 0:
                    frq = int(control_data['inverter']['inverter1Frq'] * 100)
                    acc = int(control_data['inverter']['inverter1Acc'] * 10)
                    dec = int(control_data['inverter']['inverter1Dec'] * 10)
                    client1.write_registers(address=4, values=[frq, 194, acc, dec], slave=10)
                    cmd_status[1] = 1
                elif control_data['inverter']['inverter2'] == 'Off' and cmd_status[1] == 1:
                    client1.write_registers(address=5, values=[193], slave=10)
                    cmd_status[1] = 0

                if control_data['valve1'] == 'On' and cmd_status[2] == 0:
                    valve_status[0] = True
                    cmd_status[2] = 1
                elif control_data['valve1'] == 'Off' and cmd_status[2] == 1:
                    valve_status[0] = False
                    cmd_status[2] = 0

                if control_data['valve2'] == 'On' and cmd_status[3] == 0:
                    valve_status[1] = True
                    cmd_status[3] = 1
                elif control_data['valve2'] == 'Off' and cmd_status[3] == 1:
                    valve_status[1] = False
                    cmd_status[3] = 0

                res = serial_obj.write_coils(address=0, values=valve_status, slave=5)

            elif control_data['mode'] == 'None':
                valve_status[0], valve_status[1] = False, False
                res = serial_obj.write_coils(address=0, values=valve_status, slave=5)
                client1.write_registers(address=5, values=[193], slave=10)
                client1.write_registers(address=5, values=[193], slave=11)
            
            else:
                pub_dict['cmd'] = 'None'

        except Exception as e:
            print(f'Device Setting Error: {e}')

        if pre_dict != control_data:
            # with open(os.path.join(path, 'control.json'), 'w') as f:
            with open('./control.json', 'w') as f:
                json.dump(control_data, f, indent=4)

def get_sensor(evt, serial_obj):
    global isThread, pub_dict, s_col
    while isThread:
        evt.wait()
        evt.clear()
        try:
            res = serial_obj.read_holding_registers(address=0, count=2, slave=20)
            # print(f'humidity: {res.registers[0]/10}% / temperature: {res.registers[1]/10}°C')
            for i, j in enumerate(s_col):
                pub_dict[j] = res.registers[i] / 10
        except Exception as e:
            print(f'Error: {e}')

def timer_s0(evt):
    global isThread

    while isThread:
        evt.set()
        time.sleep(1)

def timer_s1(evt):
    global isThread

    while isThread:
        evt.set()
        time.sleep(5)

def runAction():
    global ctl_data, pub_dict
    sum_data = {key: 0.0 for key in pub_dict.keys()}
    
    cnt, cnt_limit = 0, 0    
    interval = int(ctl_data['get_data_interval'])

    if interval <= 5:
        cnt_limit = 1
    elif interval <= 1800:
        cnt_limit = 10
    else:
        cnt_limit = 100
        
    act = 0

    while True:
        start = int(time.time() * 1000)
                
        for key, value in pub_dict.items():
            try:
                sum_data[key] += value
            except:
                sum_data[key] = value
        
        end = int(time.time() * 1000)
        diff = end - start
        sleep_time = int(interval / cnt_limit) - (diff * 0.001)
        cnt += 1
        if cnt == cnt_limit:
            snd_data = {key: value / cnt_limit for key, value in sum_data.items()}
            # print(snd_data)
            sdtcloud.pubMessage(snd_data)
            cnt = 0
            sum_data = {key: 0.0 for key in pub_dict.keys()}
            
        time.sleep(sleep_time)

if __name__ == "__main__":
    os.system(f'chmod 777 /dev/ttyS0')
    os.system(f'chmod 777 /dev/ttyS1')
    port_name_1 = '/dev/ttyS0'
    client1 = ModbusClient(port=port_name_1, baudrate=19200, parity='N', stopbits=1, bytesize=8, timeout=0.25)
    port_name_2 = '/dev/ttyS1'
    client2 = ModbusClient(port=port_name_2, baudrate=9600, parity='N', stopbits=1, bytesize=8, timeout=0.25)

    try:
        client1.connect()
        client2.connect()
    except:
        sys.exit(0)
    
    g_zero_point = [0] * 2

    # path = '/home/ecn-2u/Aquarack/21u'
    # with open(os.path.join(path, 'config.json'), 'r') as config:
    with open('./config.json', 'r') as config:
        cfg_data = json.load(config)

    # with open(os.path.join(path, 'control.json'), 'r') as control:
    with open('./control.json', 'r') as control:
        ctl_data = json.load(control)
        
    len_a_col = len(cfg_data['cdu_analog_list'])
    len_t_col = len(cfg_data['tank_device_list'])
    len_d_col = len(cfg_data['cdu_digital_list'])
    len_s_col = len(cfg_data['cdu_sensor_list'])
    len_p_col = len(cfg_data['cdu_device_list'])
    len_S_col = len(cfg_data['status'])
    a_col = [0] * len_a_col 
    t_col = [0] * len_t_col
    d_col = [0] * len_d_col
    s_col = [0] * len_s_col
    p_col = [0] * len_p_col
    S_col = [0] * len_S_col

    for key, value in cfg_data['cdu_analog_list'].items():
        if value[1] == 'y':
            a_col[value[0] - 1] = key

    for key, value in cfg_data['tank_device_list'].items():
        if value[1] == 'y':
            t_col[value[0] - 1] = key

    for key, value in cfg_data['cdu_digital_list'].items():
        if value[1] == 'y':
            d_col[value[0] - 1] = key
    
    for key, value in cfg_data['cdu_sensor_list'].items():
        if value[1] == 'y':
            s_col[value[0] - 1] = key
    
    for key, value in cfg_data['cdu_device_list'].items():
        if value[1] == 'y':
            p_col[value[0] - 1] = key
    
    for key, value in cfg_data['status'].items():
        if value[1] == 'y':
            S_col[value[0] - 1] = key

    pub_dict = {key: 0.0 for key in (a_col+t_col+d_col+s_col+p_col+S_col)}

    # print(f'a_col: {a_col}')
    # print(f't_col: {t_col}')
    # print(f'd_col: {d_col}')
    # print(f's_col: {s_col}')
    # print(f'p_col: {p_col}')
    # print(f'S_col: {S_col}')
    # runAction()
    isThread = True
    
    timer_evt_s0 = threading.Event()
    timer_evt_s1 = threading.Event()
    timer_evt_s0.set()
    timer_evt_s1.set()

    thr_modbus_rtu = threading.Thread(target=get_modbus, args=(timer_evt_s0, client1, ))
    thr_modbus_rtu.start()

    thr_get_sensor = threading.Thread(target=get_sensor, args=(timer_evt_s1, client2,))
    thr_get_sensor.start()

    thr_evt_s0 = threading.Thread(target=timer_s0, args=(timer_evt_s0,))
    thr_evt_s1 = threading.Thread(target=timer_s1, args=(timer_evt_s1,))
    thr_evt_s0.start()
    thr_evt_s1.start()

    thr_run = threading.Thread(target=runAction, args=())
    thr_run.start()