华荣三照明、合信、荣欣八组合馈电
This commit is contained in:
625
comm_modbus_device.py
Normal file
625
comm_modbus_device.py
Normal file
@ -0,0 +1,625 @@
|
||||
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 # 返回成功计数和总计数
|
||||
Reference in New Issue
Block a user