from comm_device import class_comm_device
from comm_thread import class_comm_master_thread
from mqtt_device import class_comm_mqtt_thread, class_comm_mqtt_interface
import time
import comm_device
from threading import Thread
import menu_utils as utils
import struct
import math
import json
import random
from enum import Enum
from comm_protocol  import class_protocol
from mqtt_object import class_mqtt_info_object
from print_color import *
from comm_protocol_modbus import class_protocol_modbus

BASE_FAIL_DELAY = 1000  # 通信失败基础延迟（毫秒）
MAX_FAIL_DELAY = 30000  # 最大通信失败延迟（毫秒）
BASE_GLOBAL_FAIL_DELAY = 500  # 全局通信失败基础延迟（毫秒）
MAX_GLOBAL_FAIL_DELAY = 60000  # 最大全局通信失败延迟（毫秒）
DEFAULT_TIMEOUT = 20  # 默认超时时间（毫秒）
# MAX_FAIL_COUNT = 15  # 最大失败次数


class class_comm_table_item() :

    
    
    def __init__(self, name = "", reg_addr = 0, reg_count = 0, cycle = 0, mqtt_pack = None, remap_addr = 0):
        self.reg_addr = reg_addr
        self.reg_count = reg_count
        self.cycle = cycle
        self.name = name
        self.remain_cycle = 0
        self.comm_fail_delay = 0
        self.comm_count = 0
        self.comm_fail_count = 0
        self.comm_success_count = 0
        self.comm_success = False
        self.comm_trigger = False
        self.mqtt_pack = mqtt_pack
        self.remap_addr = remap_addr
        self.flush_request_delay = 0
        self.base_time = time.time()
        self.reg_buf = []
        self.comm_fail_delay = 0  # 通信失败延迟

    def in_range(self, reg_addr, reg_count) :
        reg_addr_begin = self.reg_addr
        reg_addr_end = self.reg_addr + self.reg_count 
        if reg_addr >= reg_addr_begin and reg_addr + reg_count <= reg_addr_end :
            return True
        return False
    
    def update_item_value(self, reg_addr, reg_count, new_value) : 
        if self.in_range(reg_addr, reg_count) and len(self.reg_buf) :
            reg_off = reg_addr - self.reg_addr
            new_reg = list(self.reg_buf)
            for i in range(reg_count) :
                new_reg[reg_off + i] = new_value[i]
            self.reg_buf = new_reg
            self.comm_trigger = True

    def read_register_count(self, reg_addr, reg_count) :
        reg_addr_begin = self.reg_addr
        reg_addr_end = self.reg_addr + self.reg_count 
        result = None
        if reg_addr >= reg_addr_begin and reg_addr + reg_count <= reg_addr_end :
            if len(self.reg_buf) >= self.reg_count:
                result = []
                reg_off = reg_addr - self.reg_addr
                for i in range(reg_count):
                    result.append(self.reg_buf[reg_off + i])

        return result

#为了防止阻塞 mqtt订阅流程, 采用class_mqtt_process_thread线程来处理 接收到的主题与消息
class class_mqtt_process_thread(Thread):  
    def __init__(self, 
                 parent_device,
                 topic : str = None,  
                 message : str = None,
                 ):

        Thread.__init__(self)
        self.topic = topic
        self.message = message
        self.parent_device : class_modbus_comm_device = parent_device

    def run(self):
        self.parent_device.on_process_topic_message(self.topic, self.message)
    
#modbus通讯设备, 管理modbus主机通讯,  实现mqtt客户端
class class_modbus_comm_device(class_comm_device, class_comm_mqtt_interface) :
    def __init__(self, 
                 comm_addr = 0,  
                 device_remap = None,
                 comm_table = None, 
                 func_comm_trigger = None, 
                 mqtt_thread : class_comm_mqtt_thread = None,
                 protocol : class_protocol_modbus = None,
                 mqtt_info_object : class_mqtt_info_object = None,
                 unique_name = None,
                 action_process_func = None,   #action处理函数
                 request_alarm_func = None,    #故障查询函数
                 alias_table = None):
        class_comm_device.__init__(self, comm_addr, device_remap, unique_name)
        class_comm_mqtt_interface.__init__(self)
        
        self.comm_loop_count = 0
        self.comm_index = 0
        self.mqtt_thread = mqtt_thread
        self.comm_device_comm_active = True      #设置该设备是否需要通讯
        self.comm_fail_delay = 0                 #通讯延时
        self.func_comm_trigger = func_comm_trigger
        self.protocol : class_protocol_modbus  = protocol
        self.mqtt_info_object : class_mqtt_info_object = mqtt_info_object
        self.unique_name : str = unique_name
        self.action_process_func = action_process_func
        self.request_alarm_func = request_alarm_func
        self.alias_table  = alias_table
        
        self.comm_table = comm_table
        self.comm_table_item_list : class_comm_table_item = []  #list of class_comm_table_item
        self.create_comm_item_list(comm_table)
        
    def modbus_regs_flush(self, reg_addr, reg_count, values) :
        for each in self.comm_table_item_list:
            comm_item : class_comm_table_item = each
            comm_item.update_item_value(reg_addr, reg_count, values)

    def create_comm_item_list(self, comm_table : class_comm_table_item) :
        self.comm_table_item_list = []
        if comm_table != None :
            for comm_item_dict in comm_table :
                _name =  utils.dict_or_object_get_attr(comm_item_dict, "name", " ")
                _reg_addr =  utils.dict_or_object_get_attr(comm_item_dict, "reg_addr", 0)
                _reg_count =  utils.dict_or_object_get_attr(comm_item_dict, "reg_count", 0)
                _cycle =  utils.dict_or_object_get_attr(comm_item_dict, "cycle", 0)         
                _mqtt_pack =  utils.dict_or_object_get_attr(comm_item_dict, "mqtt_pack", None)      
                self.comm_table_item_list.append(class_comm_table_item(name = _name, 
                                                                       reg_addr =_reg_addr, 
                                                                       reg_count = _reg_count, 
                                                                       cycle = _cycle, 
                                                                       mqtt_pack = _mqtt_pack))

    #@override,  mqtt on connect
    def on_connect(self, mqtt_thread, userdata, flags, rc) :
        if rc == 0 :
            #获取线程类 （class_comm_mqtt_thread)
            mqtt_comm_thread : class_comm_mqtt_thread= mqtt_thread
            #订阅相关主题, 获取参数, 修改参数, 获取测量值, 获取报警信息
            mqtt_comm_thread.subscribe("request/param/info/#")
            mqtt_comm_thread.subscribe("request/param/modify/#")
            mqtt_comm_thread.subscribe("request/measure/#")
            mqtt_comm_thread.subscribe("request/status/#")
            mqtt_comm_thread.subscribe("request/alarm/#")
            mqtt_comm_thread.subscribe("request/action/#")
            mqtt_comm_thread.subscribe("request/alias/#")

    def alias_table_convert(self) :
        new_list_dict = []
        
        for each_dict in self.alias_table :
            for key_info, value_dict in each_dict.items() :
                if isinstance(key_info, Enum) :
                    name = key_info.name
                else :
                    name = key_info
                
                new_dict = {}
                for dict_key, dict_value in value_dict.items() :
                    if isinstance(dict_key, Enum) :
                        dict_key_value = dict_key.value[0]
                    else :
                        dict_key_value = dict_key

                    new_dict[dict_key_value] = dict_value
                    
                new_list_dict.append({name : new_dict})

        return new_list_dict
    
    #该函数在线程里面调用, 不会阻塞mqtt处理订阅的消息
    def on_process_topic_message(self, topic, message) :
        try :
            if isinstance(message, bytes) :
                json_dict = json.loads(message.decode('utf-8'))
            else :
                json_dict = json.loads(message.encode('utf-8'))
            print_normal_msg(f"有效的mqtt(topic = {topic}, message = {message})")
        except Exception as e:
            print_error_msg(f"{str(e)}无效的mqtt(topic = {topic}, message = {message})")
            json_dict = None

        mqtt_info_name  = utils.dict_or_object_get_attr(json_dict, "name",  None)
        value_str = utils.dict_or_object_get_attr(json_dict, "value", math.nan)              

          
        # if mqtt_info_name != None :
        #     mqtt_menu_item = self.mqtt_info_object.search_menu_item(mqtt_info_name)
        # else :
        #     mqtt_menu_item = None
        if mqtt_info_name != None:
            mqtt_menu_item = self.mqtt_info_object.search_menu_item(mqtt_info_name)
        else :
            mqtt_menu_item = None
        print_normal_msg(f"执行操作: topic={topic}, mqtt_info_name={mqtt_info_name}")
              
        if "request/alias" in topic :  #获取别名
                new_alias_table = self.alias_table_convert()
                if mqtt_info_name == None :
                    msg : str = json.dumps(new_alias_table, ensure_ascii=False)
                    if self.mqtt_thread != None :
                        self.mqtt_thread.publish("response/alias/" + self.unique_name, msg)
                else :
                    search_dict = None
                    for each_dict in new_alias_table :
                        if mqtt_info_name in each_dict.keys() :
                            search_dict = each_dict
                            break
                    if search_dict :
                        msg : str = json.dumps(search_dict, ensure_ascii=False)
                    else :
                        msg : str = '{"%s" : None}'%(mqtt_info_name)
                    if self.mqtt_thread != None :
                        self.mqtt_thread.publish("response/alias/" + self.unique_name, msg)
        elif "request/alarm" in topic:   #故障查询
            if self.request_alarm_func != None:
                fvalue = utils.dict_or_object_get_attr(json_dict, "index", math.nan)
                if isinstance(fvalue, str) :
                    fvalue = float(fvalue)
                if fvalue != math.nan :
                    alarm_index = round(fvalue)
                    self.request_alarm_func(self, alarm_index)
        elif "request/action" in topic :
            if self.action_process_func :
                    self.action_process_func(self, mqtt_menu_item, value_str)

        elif "request/param/info" in topic:  #读取参数
                _scale = utils.dict_or_object_get_attr(mqtt_menu_item, "scale", 1.0)
                _offset = utils.dict_or_object_get_attr(mqtt_menu_item, "offset", 0.0)
                _comm_str = utils.dict_or_object_get_attr(mqtt_menu_item, "addr", None)
                _alias_name = utils.dict_or_object_get_attr(mqtt_menu_item, "alias", None)
                if isinstance(_alias_name, Enum):
                    _alias_name = _alias_name.name
                    
                _format = utils.dict_or_object_get_attr(mqtt_menu_item, "format", "%f")
                _fvalue = math.nan

                if _comm_str :
                    _fvalue = self.protocol.read_float(device_addr = self.comm_addr, 
                                                    comm_str = _comm_str, 
                                                    scale = _scale,
                                                    offset = _offset
                                                    )            
                #     if _alias_name != None :
                #         # if _fvalue == None or _fvalue == math.nan:
                #         #     _format_str = "NAN"
                #         if _fvalue is None or math.isnan(_fvalue):
                #             _fvalue = 0
                #         else :
                #             _format_str = "%d"%(round(_fvalue))
                #         if self.mqtt_thread != None :
                #             self.mqtt_thread.publish("response/param/info/" + self.unique_name, '{"%s" : {"alias" : "%s", "value": "%s"}}'%(mqtt_info_name, _alias_name, _format_str))
                #     else :
                #         _format_str = _format%(_fvalue)
                #         if self.mqtt_thread != None :
                #             self.mqtt_thread.publish("response/param/info/" + self.unique_name, '{"%s" : "%s"}'%(mqtt_info_name, _format_str))
                # else:
                #     print_warning_msg(f"未处理的请求：{topic, json_dict}")
                    if _alias_name is not None:
                                # 处理 _fvalue 的情况
                            if _fvalue is None or math.isnan(_fvalue):
                                    _fvalue = 0
                                    _format_str = "%d" % (round(_fvalue))
                            else:
                                # 当 _alias_name 为 None 时处理
                                _format_str = _format % (_fvalue) if _fvalue is not None and not math.isnan(_fvalue) else "UNKNOWN"
                            
                            # 发布消息
                            if self.mqtt_thread is not None:
                                if _alias_name is not None:
                                    self.mqtt_thread.publish("response/param/info/" + self.unique_name, 
                                                            '{"%s" : {"alias" : "%s", "value": "%s"}}' % (mqtt_info_name, _alias_name, _format_str))
                                else:
                                    self.mqtt_thread.publish("response/param/info/" + self.unique_name, 
                                                            '{"%s" : "%s"}' % (mqtt_info_name, _format_str))
                    else:
                            print_warning_msg(f"未处理的请求：{topic, message}")



        elif "request/param/modify" in topic :    #写入参数
                    
                mqtt_menu_item = self.comm_item_item_adjust(mqtt_menu_item)
                scale =  utils.dict_or_object_get_attr(mqtt_menu_item, "scale", 1.0)     
                format_str = utils.dict_or_object_get_attr(mqtt_menu_item, "format", "%1.0f")
                offset = utils.dict_or_object_get_attr(mqtt_menu_item, "offset", 0.0)
                _comm_str = utils.dict_or_object_get_attr(mqtt_menu_item, "addr", None)
                
                _fvalue   = value_str
                if isinstance(_fvalue, str) :
                    modify_fvalue = float(_fvalue)
                else :
                    modify_fvalue = _fvalue
                    
                _fvalue_origin = modify_fvalue / scale - offset
                if _comm_str :
                    result = self.protocol.write_float(device_addr = self.comm_addr, 
                                                comm_str = _comm_str, 
                                                fvalue = _fvalue_origin)
                    if self.mqtt_thread != None :
                        self.mqtt_thread.publish("response/param/modify/" + self.unique_name, '{"name" : "%s", "result":%d}'%(mqtt_info_name, result))
                else:
                    print_warning_msg(f"未处理的请求：{topic, json_dict}")
        elif mqtt_menu_item == None :
            print_warning_msg(f"mqtt无法找到对应信息项(topic = {topic}, message = {json_dict})")
        else :
            pass

    #@override,  mqtt subscirbe message process
    def on_message(self, mqtt_thread, topic, message) :
        if self.mqtt_info_object == None :
            return False
        if "request/alias" in topic :   #获得别名不需要协议通讯, 此处直接处理即可
            self.on_process_topic_message(topic, message)
        elif "request" in topic : 
            new_thread : class_mqtt_process_thread = class_mqtt_process_thread(self, topic, message)
            new_thread.start()
        return True

    #通过通讯字符串与scale类型,  获取浮点格式实际值, 通讯失败时返回None
    def comm_item_get_value_float(self, comm_addr_str, scale = 1.0, offset = 0) :
        reg_value = None

        if comm_addr_str != None:
            reg_addr, bit, reg_count = utils.comm_str_unpack(comm_addr_str)
            is_float = True if "#f" in comm_addr_str else False
            is_big_endian = True if "#>" in comm_addr_str else False
            is_sign = True if "#s" in comm_addr_str else False

            if reg_count == 1 :
                if is_sign :
                    reg_value = self.read_register_s16(reg_addr)
                else :
                    reg_value = self.read_register_u16(reg_addr)
            if reg_count == 2 :
                if is_float :
                    reg_value = self.read_register_f32(reg_addr, is_big_endian)
                elif is_sign :
                    reg_value = self.read_register_s32(reg_addr, is_big_endian)
                else :
                    reg_value = self.read_register_u32(reg_addr, is_big_endian)

            if bit >= 0 and reg_value != None:
                reg_value = (reg_value >> bit)
                reg_value = reg_value & (0xFFFFFFFF >> (32 - reg_count))
        
        if reg_value != None :
            reg_value = reg_value * scale + offset

        return reg_value
    
    #有些菜单项的scale, format或其他属性可以动态调节,  返回调整后的菜单项或 原菜单项
    def comm_item_item_adjust(self, menu_item) :
        adjust_menu_item = None
        func_item_adjust = utils.dict_or_object_get_attr(menu_item, "adjust", None)  
        if func_item_adjust != None :
            adjust_menu_item = func_item_adjust(self, menu_item)
        if adjust_menu_item == None :
            adjust_menu_item = menu_item
        return adjust_menu_item

    #通过菜单项中 特定的回调函数, 使每个菜单项可以独立处理一些任务, 比如添加自定义key, value
    def comm_call_menu_item_func(self, menu_item_dict, mqtt_dict : dict = None) :
        call_func = utils.dict_or_object_get_attr(menu_item_dict, "call", None)  
        if call_func != None :
            call_func(self, menu_item_dict, mqtt_dict)

    #通过菜单项中 获取浮点格式实际值,  reg_value在通讯失败时返回None,  需要额外检查
    def comm_get_value_from_menu_item(self, menu_item_dict) :
        menu_item_dict = self.comm_item_item_adjust(menu_item_dict)
        scale =  utils.dict_or_object_get_attr(menu_item_dict, "scale", 1.0)
        offset =  utils.dict_or_object_get_attr(menu_item_dict, "offset", 0.0)
        comm_addr_str =  utils.dict_or_object_get_attr(menu_item_dict, "addr", None)
        reg_value = self.comm_item_get_value_float(comm_addr_str, scale, offset)
        return reg_value

    #通过mqtt名称中 获取浮点格式实际值,  reg_value在通讯失败时返回None,  需要额外检查
    def comm_get_value_from_mqtt_name(self, mqtt_name : str) :
        if mqtt_name in self.mqtt_info_object.mqtt_dict.keys() :
            mqtt_menu_item = self.mqtt_info_object.mqtt_dict[mqtt_name]
            return self.comm_get_value_from_menu_item(mqtt_menu_item)
        return None
   
    #触发一组mqtt菜单组的 key, value字典
    def comm_item_trigger_mqtt_pack(self, mqtt_pack) :
        if mqtt_pack == None :
            return None
        mqtt_dict = {}
        for pack_item_dict in mqtt_pack :
            self.comm_call_menu_item_func(pack_item_dict, mqtt_dict)
            reg_value = self.comm_get_value_from_menu_item(pack_item_dict)
            format_str = utils.dict_or_object_get_attr(pack_item_dict, "format", "%1.0f")
            mqtt_name = utils.dict_or_object_get_attr(pack_item_dict, "mqtt", None)
            min_value = utils.dict_or_object_get_attr(pack_item_dict, "min", None)
            max_value = utils.dict_or_object_get_attr(pack_item_dict, "max", None)   

            # 检查是否有最小值和最大值的约束，并验证 reg_value 是否在范围内
            if reg_value is not None:
                if min_value is not None and reg_value < min_value:
                    reg_value = min_value  # 如果小于最小值，设为最小值
                if max_value is not None and reg_value > max_value:
                    reg_value = max_value  # 如果大于最大值，设为最大值   
            
            if mqtt_name != None and reg_value != None:
                mqtt_dict[mqtt_name] = format_str%(reg_value)
        return mqtt_dict

    def trigger_comm(self, name):
        for item in self.comm_table_item_list:
            comm_table_item : class_comm_table_item = item
            if comm_table_item.name == name :
                comm_table_item.comm_trigger = True
        return

    def read_register_count(self, reg_addr, reg_count) :
        result = None
        for item in self.comm_table_item_list :
            comm_table_item : class_comm_table_item = item

            if comm_table_item.in_range(reg_addr, reg_count) :
                if comm_table_item.reg_buf == None : 
                    comm_table_item.comm_trigger = True
                elif len(comm_table_item.reg_buf) == 0 :
                    comm_table_item.comm_trigger = True
                else :  
                    result = comm_table_item.read_register_count(reg_addr, reg_count)
                    if result != None :          
                        break
        return result

    #@device_comm_flush_request override
    def device_comm_flush_request(self, reg_addr, reg_count, cycle = 3000) :
        for item in self.comm_table_item_list :
            comm_table_item : class_comm_table_item = item
            if comm_table_item.in_range(reg_addr, reg_count) :
                if comm_table_item.flush_request_delay  == 0 :
                    comm_table_item.flush_request_delay = cycle

    #@read_register  override
    def read_register(self, comm_str) :
        reg_addr, bit, reg_count = utils.comm_str_unpack(comm_str)
        if reg_count == 0:
            return None
        return self.read_register_count(reg_addr, reg_count)
    
    #@read_register  override
    #bool write_register(int reg_addr, reg_count, uint16_t value[])
    def write_register(self, reg_addr, reg_count, new_value, timeout_ms = comm_device.DEFAULT_COMM_WRITE_TIMEOUT_MS) :
        try:
            _protocol : class_protocol_modbus = self.protocol
            
            _old_timeout = _protocol.get_timeout()
            _protocol.set_timeout(timeout_ms / BASE_FAIL_DELAY)
            
            write_result = _protocol.modbus_write_regs(self.comm_addr, 
                                        reg_addr,
                                        reg_count,
                                        new_value,
                                        )

            if write_result == True:
                for item in self.comm_table_item_list :
                    comm_item : class_comm_table_item = item
                    if comm_item.in_range(reg_addr, reg_count) :
                        comm_item.update_item_value(reg_addr, reg_count, new_value)
                                    
        except Exception :
            write_result = False
        finally :
            _protocol.set_timeout(_old_timeout)

        return write_result

    #bool write_bit_register(int reg_addr, uint16_t value[])
    #bool write_bit_register(int reg_addr, uint16_t value)
    def write_bit_register(self, reg_addr, value, timeout_ms = comm_device.DEFAULT_COMM_WRITE_TIMEOUT_MS) :
        write_result = False

        _protocol : class_protocol_modbus = self.protocol
        try :
            old_timeout = _protocol.get_timeout()
            _protocol.set_timeout(timeout_ms / BASE_FAIL_DELAY)
            _protocol.modbus_write_regs(self.comm_addr,
                                        reg_addr % 100000 + 100000,
                                        len(value),
                                        value)

            write_result = True
        except Exception as e:
            write_result = False
            print_error_msg(f"modbus寄存器无法写入{str(e)}")
        finally :
            _protocol.set_timeout(old_timeout)

        return write_result

    #@override cycle function
    def update_time_difference(self, table_item):
        cur_time = time.time()  # 获取当前时间
        time_dif = cur_time - table_item.base_time  # 计算时间差
        table_item.base_time = cur_time  # 更新基准时间
        return time_dif * BASE_FAIL_DELAY  # 返回时间差（毫秒）

    def should_trigger_comm(self, table_item, time_dif_ms):
        comm_trigger = False  # 初始化通信触发标志为False
        if self.func_comm_trigger:
            comm_trigger = self.func_comm_trigger(self, table_item)  # 调用通信触发函数
        
        if table_item.remain_cycle > time_dif_ms:
            table_item.remain_cycle -= time_dif_ms  # 减少剩余周期
        else:
            table_item.remain_cycle = table_item.cycle + table_item.comm_fail_delay + self.comm_fail_delay  # 重新计算剩余周期
            if table_item.cycle > 0 or (table_item.cycle == 0 and not table_item.comm_success):
                comm_trigger = True  # 如果周期大于0或周期为0但上次通信不成功，则触发通信
        
        if table_item.flush_request_delay > 0:
            if table_item.flush_request_delay > time_dif_ms:
                table_item.flush_request_delay -= time_dif_ms  # 减少刷新请求延迟
            else:
                table_item.flush_request_delay = 0  # 重置刷新请求延迟
                comm_trigger = True  # 触发通信
        
        if not comm_trigger:
            comm_trigger = table_item.comm_trigger  # 如果前面没有触发通信，则使用通信触发标志
        
        return comm_trigger  # 返回是否触发通信

    def execute_communication(self, table_item):
        read_result = []  # 初始化读取结果
        timeout_ms = getattr(table_item, "通信超时", DEFAULT_TIMEOUT)  # 获取超时时间，默认500ms
        _protocol = self.protocol  # 获取协议对象
        
        try:
            old_timeout = _protocol.get_timeout()  # 保存旧的超时时间
            _protocol.set_timeout(timeout_ms / BASE_FAIL_DELAY)  # 设置新的超时时间
            
            read_result = _protocol.modbus_read_regs(self.comm_addr, table_item.reg_addr, table_item.reg_count)  # 执行Modbus读取
            if len(read_result) != table_item.reg_count:
                read_result = []  # 如果读取结果数量不匹配，视为读取失败
        except Exception as e:
            print_error_msg("周期读异常: ", e, f"modbus_read_regs(addr={self.comm_addr}, reg={table_item.reg_addr}, count={table_item.reg_count})")  # 打印错误信息
        finally:
            _protocol.set_timeout(old_timeout)  # 恢复旧的超时时间
        
        return read_result  # 返回读取结果

    def handle_comm_result(self, table_item, read_result):
        if not read_result:  # 如果读取结果为空，表示通信失败
            self.handle_comm_failure(table_item)
        else:  # 否则通信成功
            self.handle_comm_success(table_item, read_result)
        
        table_item.comm_trigger = False  # 重置通信触发标志

    def handle_comm_failure(self, table_item):
        table_item.comm_fail_count += 1  # 增加通信失败计数
        table_item.comm_fail_delay = self.limit_delay(table_item.comm_fail_delay + BASE_FAIL_DELAY, MAX_FAIL_DELAY)  # 增加并限制通信失败延迟
        self.comm_fail_delay = self.limit_delay(self.comm_fail_delay + BASE_GLOBAL_FAIL_DELAY, 60000)  # 增加并限制全局通信失败延迟
        table_item.comm_success = False  # 标记通信失败

    def limit_delay(self, delay, max_delay):
        """限制延迟不超过最大值"""
        return min(delay, max_delay)



    def handle_comm_success(self, table_item, read_result):
        table_item.comm_fail_delay = 0  # 重置通信失败延迟
        self.comm_fail_delay = 0  # 重置全局通信失败延迟
        table_item.reg_buf = read_result  # 更新寄存器缓存
        table_item.comm_success = True  # 标记通信成功
        table_item.comm_success_count += 1  # 增加成功计数

        if table_item.mqtt_pack:
            self.publish_mqtt_message(table_item)  # 发布MQTT消息

    def publish_mqtt_message(self, table_item):
        json_topic = f"{table_item.name}/{self.unique_name}"  # 构建MQTT主题
        msg_dict = self.comm_item_trigger_mqtt_pack(table_item.mqtt_pack)  # 触发MQTT打包
        
        if msg_dict:
            json_message = json.dumps(msg_dict, ensure_ascii=False)  # 序列化消息字典
            if self.mqtt_thread:
                self.mqtt_thread.publish(json_topic, json_message)  # 发布MQTT消息


    def device_comm_cycle(self):
        comm_success_count = 0  # 初始化成功计数
        total_comm_count = 0  # 初始化总计数

        if self.comm_table is None:
            return comm_success_count, total_comm_count  # 如果通信表为空，提前返回

        for item in self.comm_table_item_list:
            table_item = item  # 获取表项
            if table_item.reg_count == 0:
                continue  # 如果寄存器计数为0，跳过此表项

            time_dif_ms = self.update_time_difference(table_item)  # 更新时间差
            comm_trigger = self.should_trigger_comm(table_item, time_dif_ms)  # 检查是否应触发通信

            if comm_trigger:
                total_comm_count += 1  # 增加总通信计数
                read_result = self.execute_communication(table_item)  # 执行通信
                
                if read_result:
                    comm_success_count += 1  # 如果读取成功，增加成功计数

                self.handle_comm_result(table_item, read_result)  # 处理通信结果

        return comm_success_count, total_comm_count  # 返回成功计数和总计数
