import threading
import time
import multiprocessing
import importlib.util
from multiprocessing.managers import BaseManager
from myutils.ConfigManager import myCongif
from myutils.MyLogger_logger import LogHandler
from core.ACLModelManager import ACLModeManger
from core.DataStruct import ModelinData,ModeloutData
from threading import Lock


#2024-10-14model处理调整为独立子进程
def model_process(device,model,model_platform,m_p_status,brun,in_mq,out_mq):
        # 初始化模型运行资源
        context = None
        if model_platform == "acl":  # ACL线程中初始化内容
            context = ACLModeManger.pro_init_acl(device)  # 初始化acl资源,并创建context
            # 初始化模型资源 -- 加载模型文件
            ret = model.init_acl_resource()  # 加载和初始化离线模型文件--om文件
            if not ret:
                print("初始化模型资源出错,退出线程!")
                m_p_status.value = 2
                return

        #执行工作
        m_p_status.value = 1
        use_time = 0
        icount = 0
        while brun.value:
            try:
                inData = in_mq.get(timeout=0.1) #空时-block,直到有值  #(self,channel_id,img,image,scale_ratio, pad_size):
            except:
                #print("in_mq_空")
                continue
            if inData:
                #print(f"{time.time()}--{inData.channel_id}--数据取出进行处理!")
                s_time = time.time()
                outputs = model.execute([inData.img,])#创建input,执行模型,返回结果 --失败返回None
                e_time = time.time()
                outdata = ModeloutData(inData.image,inData.scale_ratio,inData.pad_size,outputs,inData.channel_id)
                del inData.img
                #结果输出
                if out_mq.full():
                    tmp = out_mq.get()
                    #print("model_输出mq满!")
                    del tmp
                out_mq.put(outdata)  # 需要确保out_mq只有在这里put
            # else:   #正常情况不会执行到该条件
            #     time.sleep(0.01)
            icount += 1
            use_time += (e_time - s_time)
            if icount == 500:
                avg_time = use_time / 500
                print(f"model_process耗时--{avg_time}秒")
                use_time = 0
                icount = 0

        #结束进程,释放资源
        m_p_status.value = 0
        while not in_mq.empty():
            try:
                in_mq.get_nowait()  # Get without blocking
            except Exception as e:
                break  # In case of any unexpected errors

        # 反初始化
        if model_platform == "acl":
            try:
                model.release()  # 释放模型资源资源
                # 删除模型对象
                del model
                # 释放ACL资源
                ACLModeManger.pro_del_acl(device,context)
            except Exception as e:
                print(e)


class ModelNode:
    def __init__(self,device,model_path,ch_max_count=1):
        self.device = device
        self.model_path = model_path
        self.channel_id = []    #channel_id_list
        self.model = None       #模型对象
        self.ch_max_count = ch_max_count
        self.ch_count = 0       #关联启动的通道数量
        self.count_Lock = Lock()    #count的维护锁
        self.model_platform = myCongif.get_data("model_platform")
        self.logger = LogHandler().get_logger("ModelNode")
        #分发线程相关
        self.model_out_th = None
        self.channel_dict = {}
        self.cdict_Lock = Lock()
        self.in_mq_Lock = Lock()
        self.last_in_c_id = 0

        #独立进程方案--共享参数
        self.process = None
        self.imq_count = ch_max_count * 20  #一个通道20帧缓冲区间
        self.in_mq = multiprocessing.Queue(maxsize=self.imq_count)
        self.out_mq = multiprocessing.Queue(maxsize=self.imq_count) #调整结构,多线程(预处理)-》in_mq-子进程-out_mq-》线程分发outdata->多线程(后处理)
        self.brun = multiprocessing.Value('b',True) #brun.value = False,brun.value = True
        self.m_p_status = multiprocessing.Value('i',0)

    def __del__(self):
        pass

    def _import_model(self, model_path, threshold=0.5, iou_thres=0.5):
        '''
        根据路径,动态导入模块
        :param model_path: 模块路径
        :param threshold:   置信阈值
        :param iou_thres:   iou阈值
        :return:
        '''
        try:
            module_path = model_path.replace("/", ".").rsplit(".", 1)[0]
            print(module_path)
            # 动态导入模块
            module = importlib.import_module(module_path)
            # 从模块中获取指定的类
            Model = getattr(module, "Model")
            # 使用 Model 类
            model_instance = Model(model_path, threshold, iou_thres)
            return model_instance
        except ModuleNotFoundError as e:
            print(f"Module not found: {e}")
            return None
        except AttributeError as e:
            print(f"Class not found in module: {e}")
            return None
        except Exception as e:
            print(f"An unexpected error occurred: {e}")
            return None

    def pro_add_data(self,data):
        # try:
        #     self.in_mq.put(data,timeout=0.1)
        # except multiprocessing.queues.Full:
        #     print("mdel_inmq输入满!")
        #     del data
        with self.in_mq_Lock:
            if self.ch_count>1 and self.last_in_c_id == data.channel_id:
                return
            self.last_in_c_id = data.channel_id
            if self.in_mq.full():
                tmp = self.in_mq.get()
                #print("mdel_inmq输入满!")
                del tmp
            self.in_mq.put(data)  # 需要确保out_mq只有在这里put

    def _modle_th(self):
        '''根据channel_id分发out_data到out_mq'''
        s_time = time.time()
        icount = 0
        while self.brun.value:
            try:
                outdata = self.out_mq.get(timeout=0.1)
            except:
                continue
            with self.cdict_Lock:
                if outdata.channel_id in self.channel_dict:

                    self.channel_dict[outdata.channel_id].myappend(outdata) #后面就交给后处理线程了
                else:
                    print(f"{outdata.channel_id}不在channel_dict里面")
            # icount += 1
            # if icount ==500:
            #     e_time = time.time()
            #     use_time = (e_time-s_time) /500
            #     print(f"{self.channel_id}_modle_th耗时--{use_time}秒")
            #     s_time = time.time()
            #     icount = 0


    #2024-10-14调整为独立进程执行 -- 一个线程一个MQ    MyDeque
    def start_model_th(self,channel_id,out_mq):
        with self.count_Lock:
            with self.cdict_Lock:
                if channel_id in self.channel_dict:
                    print(f"{channel_id}已经在channel_dict内")
                    return #这个可以删除老的,新增新的--后续验证,若需要则进行修改
                self.channel_dict[channel_id] = out_mq  #增加一个记录
                print(f"新增一个channel节点--{channel_id}")

            if self.ch_count == 0:  #第一次启动--需要启动处理线程和进程
                #加载自定义模型文件
                self.model = self._import_model(self.model_path)  # 动态加载模型处理文件py  --置信阈值一直没使用
                if not self.model:
                    self.logger.error("自定义模型文件加载失败,不启动model子进程")
                    self.m_p_status.value = 2
                    return

                self.brun.value = True
                #创建outMQ的分发线程
                self.model_out_th = threading.Thread(target=self._modle_th)
                self.model_out_th.start()

                # 创建子进程
                self.process = multiprocessing.Process(target=model_process,
                                                       args=(self.device,self.model,self.model_platform,
                                                             self.m_p_status,self.brun,self.in_mq,self.out_mq))
                self.process.start()

            self.ch_count += 1      #有通道调用一次就加一

    def stop_model_th(self,channel_id):
        with self.count_Lock:
            with self.cdict_Lock:
                if channel_id in self.channel_dict:
                    del self.channel_dict[channel_id]
            self.ch_count -= 1
            if self.ch_count == 0:   #所有通道结束
                self.brun.value = False
                self.model_out_th.join()    #等待线程结束
                self.model_out_th = None

                self.process.join()     #等待子进程结束
                self.process = None