modbus tk库例程使用,曲线(暂时取消)

This commit is contained in:
冯佳
2025-07-25 17:04:59 +08:00
parent 50f7531518
commit 805299ec2a
17 changed files with 1237 additions and 5321 deletions

2
1.py
View File

@ -1,2 +0,0 @@
from kivy.properties import ObjectProperty, StringProperty
# print("ok")

View File

@ -1,99 +0,0 @@
import struct
from comm_protocol import class_protocol
DEFAULT_COMM_WRITE_TIMEOUT_MS = 500
#通讯设备基类, 派生类需要实现 device_comm_cycle函数, read_register函数, write_registers函数
class class_comm_device() :
def __init__(self,
comm_addr = 0,
device_remap = None,
unique_name = None,
protocol : class_protocol = None) :
self.comm_active = True
self.comm_addr = comm_addr
self.device_remap = device_remap
self.uniqut_name = unique_name
self.protocol = protocol
#该函数为虚基类, 本身未实现read_register, 以下为实现范例, 需要在派生类中实现
def read_register(self, reg_addr, reg_count) :
raise NotImplementedError()
#bool write_registers(int reg_addr, reg_count, uint16_t value[])
#该函数为虚基类, 本身未实现write_registers, 以下为实现范例, 需要在派生类中实现
def write_register(self, reg_addr, reg_count, new_value, timeout_ms = DEFAULT_COMM_WRITE_TIMEOUT_MS) :
raise NotImplementedError()
#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 = DEFAULT_COMM_WRITE_TIMEOUT_MS) :
raise NotImplementedError()
def device_comm_cycle(self) :
comm_success_count = 0
total_comm_count = 0
return comm_success_count, total_comm_count
def device_comm_flush_request(self, reg_addr, reg_count, cycle = 3000) :
return True
def device_comm_deactive(self) :
self.comm_active = False
def device_comm_active(self) :
self.comm_active = True
def value_u16_to_s16(self, value) :
if value == None:
return None
svalue = value
if (svalue & 0x8000) :
svalue = -((svalue ^ 0xFFFF) + 1)
return svalue
def value_u32_to_s32(self, value) :
if value == None:
return None
svalue = value
if (svalue & 0x80000000) :
svalue = -((svalue ^ 0xFFFFFFFF) + 1)
return svalue
def read_register_count(self, reg_addr, reg_count) :
return self.read_register(reg_addr, reg_count)
def read_register_u16(self, reg_addr) :
result = self.read_register_count(reg_addr, 1)
if result :
result = (result[0] & 0xFFFF)
return result
def read_register_s16(self, reg_addr) :
result = self.read_register_u16(reg_addr)
sign_result = self.value_u16_to_s16(result)
return sign_result
def read_register_u32(self, reg_addr, big_endian = 0) :
result = self.read_register_count(reg_addr, 2)
if result :
if big_endian:
result = (result[1] & 0xFFFF) | ((result[0] & 0xFFFF) << 16)
else :
result = (result[0] & 0xFFFF) | ((result[1] & 0xFFFF) << 16)
return result
def read_register_s32(self, reg_addr, big_endian = 0) :
result = self.read_register_u32(reg_addr, big_endian)
sign_value = self.value_u32_to_s32(result)
return sign_value
def read_register_f32(self, reg_addr, big_endian = 0) :
result = self.read_register_u32(reg_addr, big_endian)
fvalue = None
if result :
bytes = struct.pack('BBBB', result & 0xFF, (result >> 8) & 0xFF, (result >> 16) & 0xFF, (result >> 24) & 0xFF)
fvalue = struct.unpack("<f", bytes)
return fvalue

View File

@ -1,625 +0,0 @@
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 # 返回成功计数和总计数

View File

@ -1,45 +0,0 @@
import string
import modbus_tk.defines as cst
from modbus_tk import modbus_rtu, modbus_tcp, modbus
from print_color import *
import enum as Enum
#所有协议基类
class class_protocol() :
def __init__(self, protocol_name) :
self.protocol_name = protocol_name
self.mode : string = None
self.timeout = 5.0
return
def set_timeout(self, timeout = 5.0) :
self.timeout = timeout
def open(self, mode : string) :
self.mode = mode
print_error_msg("class_protocol派生类未实现open函数")
raise NotImplementedError()
def close(self) :
print_error_msg("class_protocol派生类未实现close函数")
raise NotImplementedError()
#返回 math.nan 表示读取失败, 否则读取成功
def read_float(self, device_addr : int, comm_str : string, scale : float = 1.0, offset : float = 0.0) -> float:
raise NotImplementedError()
#返回 False 表示读取失败
#返回 True 表示读取成功
def write_float(self, device_addr : int, comm_str : string, fvalue : float, scale : float = 1.0, offset : float = 0.0) -> bool:
raise NotImplementedError()
#读取comm_str地址开始的连续地址count个数 地址连续
#返回 math.nan 表示读取失败, 否则读取成功
def read_float_arr(self, device_addr : int, comm_str : string, count : int) :
raise NotImplementedError()
#写入comm_str地址开始的连续地址 写入总个数由 len(fvalue_arr)决定
#返回 False 表示读取失败, 否则读取成功
def write_float_arr(self, device_addr : int, comm_str : string, fvalue_arr : list) -> bool:
raise NotImplementedError()

View File

@ -1,656 +0,0 @@
#modbus协议处理
import sys
import string
import json
import serial
import time
import menu_utils as utils
import string
import struct
import math
import modbus_tk.defines as cst
from modbus_tk import modbus_rtu, modbus_tcp, modbus
from print_color import *
from comm_protocol import class_protocol
from comm_thread import class_comm_master_thread
from threading import Thread, Lock
from modbus_tk.exceptions import(
ModbusError, ModbusFunctionNotSupportedError, DuplicatedKeyError, MissingKeyError, InvalidModbusBlockError,
InvalidArgumentError, OverlapModbusBlockError, OutOfModbusBlockError, ModbusInvalidResponseError,
ModbusInvalidRequestError
)
def json_load_message(message) :
json_dict = {}
if isinstance(message, bytes) :
json_dict = json.loads(message.decode('utf-8'))
elif isinstance(message, str) :
json_dict = json.loads(message.encode('utf-8'))
return json_dict
#从通讯字符串中取出寄存器地址,位地址,个数
# 以下为允许的Modbus通讯地址信息
#"8000#2" 从8000地址开始连续取两个寄存器
#"8000#2#s" 从8000地址开始连续取两个寄存器, #s 代表有符号数, 缺省为无符号数
#"8000" 或 "8000#1" 8000地址 1个寄存器
#"8000.2#3" 取8000地址的第2位开始取 3个位的数据
#"8000#2#f#>" 8000地址开始连续取两个寄存器 #> 大端模式, #f 浮点数,
#"8000#2#f" 或 "8000#2#f#<" 8000地址开始连续取两个寄存器 #< 小端模式(缺省模式) #f 浮点数, 单精度
#"8000#4#f" 或 "8000#4#f#<" 8000地址开始连续取4个寄存器 #< 小端模式(缺省模式) #f 浮点数, 双精度
#"100618" 位地址618, 超过100000就表示是位地址
def get_modbus_comm_info(addr_string : string) : #tuple(int, int, int, bool, bool, bool)
reg_addr = 0
reg_bit = -1
reg_count = 0
is_sign = True if "#s" in addr_string else False
is_float = True if "#f" in addr_string else False
is_big_endian = True if "#>" in addr_string else False
info_splits = addr_string.split("#") # 使用'#'作为分隔符
if info_splits != None :
reg_bit_str : string = info_splits[0]
reg_bit_splits = reg_bit_str.split('.')
if len(reg_bit_splits) == 2 :
reg_addr = int(reg_bit_splits[0])
reg_bit = int(reg_bit_splits[1])
else :
reg_addr = int(reg_bit_splits[0])
reg_bit = -1
if len(info_splits) >= 2:
reg_count = int(info_splits[1])
else :
reg_count = 1
return (reg_addr, reg_bit, reg_count, is_float, is_big_endian, is_sign)
#modbus协议基类, 无法独立工作, 需要派生类实现对应函数
class class_protocol_modbus(class_protocol) :
#protocol_port_name = "COM1", "dev/tty1" 之类的设备
def __init__(self, protocol_name) :
class_protocol.__init__(self, protocol_name)
self.mode : string = None #协议open时的mode字符串
self.master : modbus.Master = None #modbus主机
self.last_errcode = 0 #协议最后一次故障代码
self.timeout = 1.0
self.mutex = Lock()
return
def set_modbus_master(self, master : modbus.Master) :
if self.master != None :
self.master.close()
self.master = master
if self.master != None :
self.master.set_timeout(self.timeout)
#reg_value最大为64位数据, reg_count最大为4
def modbus_value_to_list(self, reg_value, reg_count, is_big_endian = False) -> list :
result = []
if reg_count == 1 :
result.append(reg_value & 0xFFFF)
elif reg_count == 2:
if is_big_endian :
result.append((reg_value >> 16) & 0xFFFF)
result.append((reg_value >> 0) & 0xFFFF)
else :
result.append((reg_value >> 0) & 0xFFFF)
result.append((reg_value >> 16) & 0xFFFF)
elif reg_count == 3:
if is_big_endian :
result.append((reg_value >> 32) & 0xFFFF)
result.append((reg_value >> 16) & 0xFFFF)
result.append((reg_value >> 0) & 0xFFFF)
else :
result.append((reg_value >> 0) & 0xFFFF)
result.append((reg_value >> 16) & 0xFFFF)
result.append((reg_value >> 32) & 0xFFFF)
elif reg_count == 4:
if is_big_endian :
result.append((reg_value >> 48) & 0xFFFF)
result.append((reg_value >> 32) & 0xFFFF)
result.append((reg_value >> 16) & 0xFFFF)
result.append((reg_value >> 0) & 0xFFFF)
else :
result.append((reg_value >> 0) & 0xFFFF)
result.append((reg_value >> 16) & 0xFFFF)
result.append((reg_value >> 32) & 0xFFFF)
result.append((reg_value >> 48) & 0xFFFF)
return result
def modbus_list_to_value(self, read_regs : tuple, is_big_endian : bool = False) -> int:
read_reg_cunt = len(read_regs)
read_value = 0
if read_reg_cunt == 4 :
if is_big_endian :
read_value = (read_regs[0] << 48) | (read_regs[1] << 32) | (read_regs[2] << 16) | (read_regs[3] << 0)
else :
read_value = (read_regs[0] << 0) | (read_regs[1] << 16) | (read_regs[2] << 32) | (read_regs[3] << 48)
if read_reg_cunt == 3 :
if is_big_endian :
read_value = (read_regs[0] << 32) | (read_regs[1] << 16) | (read_regs[2] << 0)
else :
read_value = (read_regs[0] << 0) | (read_regs[1] << 16) | (read_regs[2] << 32)
elif read_reg_cunt == 2 :
if is_big_endian :
read_value = (read_regs[0] << 16) | (read_regs[1] << 0)
else :
read_value = (read_regs[0] << 0) | (read_regs[1] << 16)
elif read_reg_cunt == 1 :
read_value = read_regs[0]
return read_value
def modbus_read_regs(self, device_addr, reg_addr, reg_count) -> tuple:
result_tuple = []
try:
self.mutex.acquire()
if self.master is None:
raise Exception("Call set_modbus_master() first to init protocol master")
read_reg_count = reg_count
is_bit_addr = reg_addr >= 100000
func_code = cst.READ_COILS if is_bit_addr else cst.READ_HOLDING_REGISTERS
result_tuple = self.master.execute(slave=device_addr,
function_code=func_code,
starting_address=reg_addr % 100000,
quantity_of_x=read_reg_count)
except ModbusError as e:
self.last_errcode = e.get_exception_code()
err_msg = f"Modbus 读寄存器错误: {str(e)}, modbus 读寄存器(addr={device_addr}, reg={reg_addr}, count={reg_count})"
print_error_msg(err_msg)
raise
except Exception as e:
err_msg = f"其他错误: {str(e)}"
print_error_msg(err_msg)
raise
finally:
self.mutex.release()
return result_tuple
#reg_addr >= 100000 时为写 位地址, 否则写保持寄存器
#values 传入整数或浮点数时, 表示写单个寄存器, 或 强制单个位,
#强制单个位时modbus-tk会转化成0xFF00, 0x0000, 要是其他值时需要修改modbus-tk底层库
def modbus_write_regs(self, device_addr, reg_addr, reg_count, values) -> bool :
try :
success = False
self.mutex.acquire()
if self.master == None :
raise Exception("Call set_modbus_master() first to init protocol master")
if isinstance(values, float) :
reg_value = (int)(round(values))
values = [reg_value]
elif isinstance(values, int) :
reg_value = (int)(values)
values = [reg_value]
if len(values) != reg_count or reg_count == 0:
raise Exception("error, value size %d not match to reg_count"%(len(values)))
is_bit_addr = True if reg_addr >= 100000 else False
if len(values) >= 2 :
func_code = cst.WRITE_MULTIPLE_COILS if is_bit_addr else cst.WRITE_MULTIPLE_REGISTERS
self.master.execute(slave = device_addr,
function_code = func_code,
starting_address = reg_addr % 100000,
quantity_of_x = len(values),
output_value= values)
else :
func_code = cst.WRITE_SINGLE_COIL if is_bit_addr else cst.WRITE_SINGLE_REGISTER
if func_code == cst.WRITE_SINGLE_COIL :
write_value= values[0] & 0xFFFF
else :
write_value = values[0]
self.master.execute(slave = device_addr,
function_code = func_code,
starting_address = reg_addr % 100000,
quantity_of_x = len(values),
output_value= write_value)
success = True
except ModbusError as e:
self.last_errcode = e.get_exception_code()
print_error_msg(f"modbus写寄存器错误: {str(e)}, modbus写寄存器(addr={device_addr}, reg={reg_addr}, count={reg_count})")
success = False
except Exception as e:
success = False
print_error_msg(f"其他错误: {str(e)}")
finally:
self.mutex.release()
return success
def modbus_read_float(self, device_addr, comm_str :string, scale = 1.0, offset = 0.0) -> float:
fvalue = math.nan
try :
reg_addr, reg_bit, reg_count, is_float, is_big_endian, is_sign = get_modbus_comm_info(comm_str)
if self.master == None :
raise Exception("Call set_modbus_master() first to init protocol master")
read_reg_count = reg_count
if reg_bit >= 0 :
read_reg_count = 1 + (int)((reg_bit + reg_count - 1) / 16)
result = self.modbus_read_regs(device_addr, reg_addr, read_reg_count)
if read_reg_count == 4 :
if is_big_endian :
read_value = (result[0] << 48) | (result[1] << 32) | (result[2] << 16) | (result[3] << 0)
else :
read_value = (result[0] << 0) | (result[1] << 16) | (result[2] << 32) | (result[3] << 48)
if is_sign :
read_value = utils.value_u64_to_s64(read_value)
elif read_reg_count == 3 :
if is_big_endian :
read_value = (result[0] << 32) | (result[1] << 16) | (result[2] << 0)
else :
read_value = (result[0] << 0) | (result[1] << 16) | (result[2] << 32)
elif read_reg_count == 2 :
if is_big_endian :
read_value = (result[0] << 16) | (result[1] << 0)
else :
read_value = (result[0] << 0) | (result[1] << 16)
if is_sign :
read_value = utils.value_u32_to_s32(read_value)
elif read_reg_count == 1 :
read_value = result[0]
if is_sign :
read_value = utils.value_u16_to_s16(read_value)
else :
raise Exception("目前只支持读取(1,2,3,4)个寄存器个数")
if reg_bit >= 0 :
read_value = (read_value >> reg_bit) & (0xFFFFFFFF >> (32 - reg_count))
if is_float :
fvalue = math.nan
if read_reg_count == 2:
bytes = struct.pack('BBBB', read_value & 0xFF, (read_value >> 8) & 0xFF, (read_value >> 16) & 0xFF, (read_value >> 24) & 0xFF)
fvalue = struct.unpack("<f", bytes)
elif read_reg_count == 4:
low_part = read_value & 0xFFFFFFFF
low_bytes = struct.pack('BBBB', low_part & 0xFF, (low_part >> 8) & 0xFF, (low_part >> 16) & 0xFF, (low_part >> 24) & 0xFF)
high_part = (read_value >> 32) & 0xFFFFFFFF
high_bytes = struct.pack('BBBB', high_part & 0xFF, (high_part >> 8) & 0xFF, (high_part >> 16) & 0xFF, (high_part >> 24) & 0xFF)
bytes = low_bytes + high_bytes
fvalue = struct.unpack("<d", bytes)
else :
fvalue = float(read_value)
if fvalue != math.nan :
fvalue = fvalue * scale + offset
except Exception as e:
fvalue = math.nan #无效浮点数
print_error_msg(f"modbus读浮点数{str(e)} 读保持寄存器(addr={device_addr}, reg={reg_addr}, count={read_reg_count})")
finally:
return fvalue
def modbus_read_float_arr(self, device_addr, comm_str :string, count) : # -> tuple(float)
fvalue = math.nan
total_reg_count = 0
try :
reg_addr, reg_bit, reg_count, is_float, is_big_endian, is_sign = get_modbus_comm_info(comm_str)
if self.master == None :
raise Exception("Call set_modbus_master() first to init protocol master")
float_result_arr = []
read_reg_count = reg_count
if reg_bit >= 0 :
raise Exception("multi read for reg.bit is not support")
#读连续寄存器数据, 再添加到浮点数 数组列表
total_reg_count = read_reg_count * count
result = self.modbus_read_regs(device_addr, reg_addr, total_reg_count)
for i in range(count) :
if read_reg_count == 4 :
if is_big_endian :
read_value = (result[0] << 48) | (result[1] << 32) | (result[2] << 16) | (result[3] << 0)
else :
read_value = (result[0] << 0) | (result[1] << 16) | (result[2] << 32) | (result[3] << 48)
if is_sign :
read_value = utils.value_u64_to_s64(read_value)
elif read_reg_count == 3 :
if is_big_endian :
read_value = (result[0] << 32) | (result[1] << 16) | (result[2] << 0)
else :
read_value = (result[0] << 0) | (result[1] << 16) | (result[2] << 32)
elif read_reg_count == 2 :
if is_big_endian :
read_value = (result[0] << 16) | (result[1] << 0)
else :
read_value = (result[0] << 0) | (result[1] << 16)
if is_sign :
read_value = utils.value_u32_to_s32(read_value)
elif read_reg_count == 1 :
read_value = result[0]
if is_sign :
read_value = utils.value_u16_to_s16(read_value)
else :
raise Exception("目前只支持读取(1,2,3,4)个寄存器个数")
if is_float :
fvalue = math.nan
if read_reg_count == 2:
bytes = struct.pack('BBBB', read_value & 0xFF, (read_value >> 8) & 0xFF, (read_value >> 16) & 0xFF, (read_value >> 24) & 0xFF)
fvalue = struct.unpack("<f", bytes)
elif read_reg_count == 4:
low_part = read_value & 0xFFFFFFFF
low_bytes = struct.pack('BBBB', low_part & 0xFF, (low_part >> 8) & 0xFF, (low_part >> 16) & 0xFF, (low_part >> 24) & 0xFF)
high_part = (read_value >> 32) & 0xFFFFFFFF
high_bytes = struct.pack('BBBB', high_part & 0xFF, (high_part >> 8) & 0xFF, (high_part >> 16) & 0xFF, (high_part >> 24) & 0xFF)
bytes = low_bytes + high_bytes
fvalue = struct.unpack("<d", bytes)
else :
fvalue = float(read_value)
float_result_arr.append(fvalue)
except Exception as e:
float_result_arr = []
print_error_msg(f"modbus读浮点数数组{str(e)},读保持寄存器(addr={device_addr}, reg={reg_addr}, count={total_reg_count})")
finally:
return float_result_arr
def modbus_write_float(self, device_addr, comm_str, fvalue, scale = 1.0, offset = 0.0) -> bool:
success = False
write_value = 0
is_writing = False
is_reading = False
try :
reg_addr, reg_bit, reg_count, is_float, is_big_endian, is_sign = get_modbus_comm_info(comm_str)
if self.master == None :
raise Exception("Call set_modbus_master() first to init protocol master")
fvalue_origin = round((fvalue - offset) / scale, 0)
write_reg_count = reg_count
if reg_bit >= 0 :
write_reg_count = 1 + (int)((reg_bit + reg_count - 1) / 16)
if write_reg_count > 4 : #寄存器个数大于4个, 目前不支持
raise Exception("write_reg_count is more than 4")
write_reg_values = [] #定义空列表
if reg_addr >= 100000 and reg_addr < 200000 : #位数据
if fvalue > 0 :
write_reg_values.append(1)
else :
write_reg_values.append(0)
elif reg_bit >= 0 :
if write_reg_count > 2 :
raise Exception("register count is > 2")
is_reading = True
read_regs = self.modbus_read_regs(device_addr, reg_addr, write_reg_count)
is_reading = False
origin_read_value = self.modbus_list_to_value(read_regs, is_big_endian)
write_value = (int)(fvalue_origin)
bit_mask = (0xFFFFFFFF >> (32 - reg_count))
set_mask = write_value & bit_mask
modify_value = origin_read_value
modify_value = modify_value & ~(bit_mask << reg_bit)
modify_value = modify_value | (set_mask << reg_bit)
write_reg_values = self.modbus_value_to_list(modify_value, write_reg_count, is_big_endian)
elif is_float :
if is_big_endian :
format_pack = ">d" if write_reg_count == 4 else ">f"
else :
format_pack = "<d" if write_reg_count == 4 else "<f"
format_unpack = "HHHH" if write_reg_count == 4 else "HH"
bytes = struct.pack(format_pack, fvalue_origin)
write_reg_values = struct.unpack(format_unpack, bytes)
else :
modify_value = round(fvalue_origin)
write_reg_values = self.modbus_value_to_list(modify_value, write_reg_count, is_big_endian)
is_writing = True #以下真正开始写寄存器
self.modbus_write_regs(device_addr, reg_addr, write_reg_count, write_reg_values)
is_writing = False
success = True
except Exception as e:
success = False
if is_reading : #正在读时发送异常
print_error_msg(f"正在读浮点数时异常{str(e)}, 读保持寄存器(addr={device_addr}, reg={reg_addr}, count={write_reg_count})")
elif is_writing : #正在写时发送异常
print_error_msg(f"正在写浮点数时异常{str(e)}, 写保持寄存器(addr={device_addr}, reg={reg_addr}, count={write_reg_count})")
else :
print_error_msg(f"其他错误: {str(e)}")
finally:
return success
def modbus_write_float_arr(self, device_addr, comm_str, fvalue_arr) -> bool:
success = False
is_writing = False
is_read = False
try :
reg_addr, reg_bit, reg_count, is_float, is_big_endian, is_sign = get_modbus_comm_info(comm_str)
if self.master == None :
raise Exception("Call set_modbus_master() first to init protocol master")
write_reg_values = []
write_reg_count = reg_count
if reg_bit >= 0 :
raise Exception("multi write for reg.bit is not support")
if write_reg_count > 4 : #单次写入寄存器个数大于4个, 目前不支持
raise Exception("write_reg_count is more than 4")
for fvalue_origin in fvalue_arr:
if reg_addr >= 100000 and reg_addr < 200000 : #位数据
if fvalue_origin > 0 :
write_reg_values.append(1)
else :
write_reg_values.append(0)
elif is_float :
if is_big_endian :
format_pack = ">d" if write_reg_count == 4 else ">f"
else :
format_pack = "<d" if write_reg_count == 4 else "<f"
format_unpack = "HHHH" if write_reg_count == 4 else "HH"
bytes = struct.pack(format_pack, fvalue_origin)
write_reg_values += struct.unpack(format_unpack, bytes)
else :
modify_value = round(fvalue_origin)
write_reg_values += self.modbus_value_to_list(modify_value, write_reg_count, is_big_endian)
is_writing = True #以下真正开始写寄存器
self.modbus_write_regs(device_addr, reg_addr, len(write_reg_values), write_reg_values)
is_writing = False
success = True
except Exception as e:
success = False
if is_read : #正在读时发送异常
print_error_msg(f"正在读浮点数时异常{str(e)}, 读保持寄存器(addr={device_addr}, reg={reg_addr}, count={write_reg_count})")
elif is_writing : #正在写时发送异常
print_error_msg(f"正在写浮点数时异常{str(e)}, 写保持寄存器(addr={device_addr}, reg={reg_addr}, count={write_reg_count})")
else :
print_error_msg(f"其他错误: {str(e)}")
finally:
return success
#@override
def set_timeout(self, timeout = 5.0) :
self.timeout = timeout
if self.master != None :
self.master.set_timeout(self.timeout)
def get_timeout(self) :
return self.timeout
#@override 返回 math.nan 表示读取失败, 否则读取成功
def read_float(self, device_addr, comm_str, scale = 1.0, offset = 0.0) -> float:
return self.modbus_read_float(device_addr, comm_str, scale, offset)
#@override 返回 False 表示读取失败, 否则读取成功
def write_float(self, device_addr, comm_str, fvalue, scale = 1.0, offset = 0.0) -> bool:
return self.modbus_write_float(device_addr, comm_str, fvalue, scale, offset)
#@override
#读取comm_str地址开始的连续地址count个数 地址连续
#返回 math.nan 表示读取失败, 否则读取成功
def read_float_arr(self, device_addr : int, comm_str : string, count : int) :
return self.modbus_read_float_arr(device_addr, comm_str, count)
#@override
#写入comm_str地址开始的连续地址 写入总个数由 len(fvalue_arr)决定
#返回 False 表示读取失败, 否则读取成功
def write_float_arr(self, device_addr : int, comm_str : string, fvalue_arr : list) -> bool:
return self.modbus_write_float_arr(device_addr, comm_str, fvalue_arr)
#串口Modbus_rtu主机类
class class_protocol_modbus_rtu(class_protocol_modbus) :
def __init__(self, protocol_name = "modbus_rtu") :
class_protocol_modbus.__init__(self, protocol_name)
self.comm_parity = serial.PARITY_NONE
self.comm_bytesize = serial.EIGHTBITS
self.comm_stopbits = serial.STOPBITS_ONE
self.is_open = False
self.comm_uart = None
self.rtu_master : modbus_rtu.RtuMaster = None
return
#@override
#parity('N', 'E', 'O', 'M', 'S') 选其一, 含义分别代表 ("无校验", "偶校验", "奇校验", "校验1", "校验0"), 缺省为无校验
#stop(1, 1.5, 2)选其一, 含义分别代表 ("1个停止位", "1.5个停止位", "2个停止位"), 缺省为1个停止位
#bit(5, 6, 7, 8)选其一, 含义分别代表 ("5位数据", "6位数据", "7位数据""8位数据"), 缺省为8位数据
#baud(50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200)选其一, 缺省为9600位数据
#mode 需要是一个json字符串, exam: "{"name" : 'COM1', "baud" : 115200, "parity" :'E', "stop" : 1}" 代表COM1, 115200波特率, 偶校验, 1停止位
def open(self, mode : string) :
try :
class_protocol_modbus.mode = mode
mode_dict = json_load_message(mode)
print_inform_msg(f"打开: 协议名称:{self.protocol_name} ,工作端口:{self.mode}")
except Exception as e:
print_error_msg(f"错误modbus rtu信息:{str(e)}")
mode_dict = None
if mode_dict :
self.comm_port_name = utils.dict_or_object_get_attr(mode_dict, "name", "COM1")
self.comm_parity = utils.dict_or_object_get_attr(mode_dict, "parity", serial.PARITY_NONE)
self.comm_baud = utils.dict_or_object_get_attr(mode_dict, "baud", 9600)
self.comm_stopbits = utils.dict_or_object_get_attr(mode_dict, "stop", serial.STOPBITS_ONE)
self.comm_bytesize = utils.dict_or_object_get_attr(mode_dict, "bit", serial.EIGHTBITS)
try :
self.comm_uart = serial.Serial(port= self.comm_port_name,
baudrate= self.comm_baud,
bytesize= self.comm_bytesize,
parity=self.comm_parity,
stopbits=self.comm_stopbits)
self.rtu_master = modbus_rtu.RtuMaster(self.comm_uart)
self.set_modbus_master(self.rtu_master)
self.set_timeout(self.timeout)
self.is_open = True
except Exception as e:
print_error_msg(f"错误modbus rtu信息:{str(e)}")
self.comm_uart = None
self.is_open = False
#@override 关闭到modbus rtu 对应的串口
def close(self) :
if self.is_open :
try :
print_inform_msg(f"关闭 : 协议名称:{self.protocol_name} ,工作端口:{self.mode}")
if self.rtu_master :
self.rtu_master.close()
if self.comm_uart :
self.comm_uart.close()
except Exception as e:
print_error_msg(f"错误关闭modbus rtu信息:{str(e)}")
pass
finally :
self.rtu_master = None
self.comm_uart = None
self.is_open = False
#以太网Modbus_tcp主机类
class class_protocol_modbus_tcp(class_protocol_modbus) :
def __init__(self, protocol_name = "modbus_tcp") :
class_protocol_modbus.__init__(self, protocol_name)
self.ip_str = "127.0.0.1"
self.port = 502
self.tcp_master : modbus_tcp.TcpMaster = None
return
#ip: 服务器ip地址, 缺省"127.0.0.1"
#port: 服务器端口, 缺省502
#timeout: 超时时间, 缺省 5.0S
#mode 需要是一个json字符串, exam: '{"ip" : '192.168.1.201', "port" : 502, "timeout": 5.0}' 代表192.168.1.201, 502端口, 通讯超时 5.0S
def open(self, mode: str):
try:
self.mode = mode
print_inform_msg(f"打开 Modbus TCP: 协议名称:{self.protocol_name} ,工作端口:{self.mode}")
mode_dict = json_load_message(mode)
except json.decoder.JSONDecodeError as e:
print_error_msg(f"解析模式失败: {str(e)}")
mode_dict = None
except Exception as e:
print_error_msg(f"错误打开 Modbus TCP 信息: {str(e)}")
mode_dict = None
if mode_dict:
self.ip_str = utils.dict_or_object_get_attr(mode_dict, "ip", "127.0.0.1")
self.port = utils.dict_or_object_get_attr(mode_dict, "port", 502)
if "timeout" in mode_dict:
self.timeout = mode_dict["timeout"]
try:
self.tcp_master = modbus_tcp.TcpMaster(host=self.ip_str, port=self.port, timeout_in_sec=self.timeout)
self.set_modbus_master(self.tcp_master)
self.tcp_master.open()
self.is_open = True
except Exception as e:
print_error_msg(f"打开 Modbus TCP 失败: {str(e)} IP地址:{self.ip_str} ,端口号:{self.port}")
self.is_open = False
#关闭到modbus tcp服务器的连接
def close(self):
if self.is_open or self.tcp_master is not None:
try:
if self.tcp_master:
print_inform_msg(f"关闭 Modbus TCP: 协议名称:{self.protocol_name} ,工作端口:{self.mode}")
self.tcp_master.close()
except Exception as e:
print_error_msg(f"错误关闭 Modbus TCP 信息: {str(e)}")
pass
finally:
self.tcp_master = None
self.is_open = False
print_inform_msg("已断开与 Modbus TCP 服务器的连接")
if __name__ == "__main__":
protocol_modbus_rtu = class_protocol_modbus_rtu("modbus_rtu")
protocol_modbus_rtu.open('{"name" : "COM2", "baud":115200, "parity" :"E", "stop" : 1}')
protocol_modbus_rtu.close()
protocol_modbus_tcp : class_protocol_modbus_tcp = class_protocol_modbus_tcp("modbus_tcp")
protocol_modbus_tcp.open('{"ip" : "127.0.0.1", "port" : 502, "timeout" : 4.0}')
protocol_modbus_tcp.close()

View File

@ -1,19 +0,0 @@
COMM_REMAP_MEASURE_SWITCH_STATUS = 0
COMM_REMAP_MEASURE_SWITCH_ALARM = 2
COMM_REMAP_MEASURE_FLOAT_IA = 4
COMM_REMAP_MEASURE_FLOAT_IB = 5
COMM_REMAP_MEASURE_FLOAT_IC = 6
COMM_REMAP_MEASURE_FLOAT_UAB = 10
COMM_REMAP_MEASURE_FLOAT_UBC = 12
COMM_REMAP_MEASURE_FLOAT_UCA = 14
COMM_REMAP_MEASURE_FLOAT_RO = 16
COMM_REMAP_MEASURE_FLOAT_IO = 18
COMM_REMAP_MEASURE_FLOAT_UO = 20
COMM_REMAP_PARA_CURRENT_RATE = 1000
COMM_REMAP_PARA_CURRENT_SHORT_MUL = 1002
COMM_REMAP_PARA_RATE_VOLTAGE = 1004
COMM_REMAP_PARA_LOW_VOLTAGE_RATIO = 1006
COMM_REMAP_PARA_LOW_VOLTAGE_DELAY = 1008

View File

@ -1,78 +0,0 @@
import threading
from threading import Thread
import time
import menu_utils as utils
from comm_device import class_comm_device
from print_color import *
class class_comm_master_thread(Thread):
thread_object_id = 0
CHECK_INTERVAL = 0.05 # 检查间隔(秒)
MAX_FAIL_TIME = 5 # 最大失败时间(秒)
def __init__(self,
device_comm_object_list=None,
thread_name="device"):
super().__init__()
self.stop_request = False
self.device_comm_object_list = device_comm_object_list if device_comm_object_list is not None else []
if thread_name is None:
thread_name = "device" + str(class_comm_master_thread.thread_object_id)
class_comm_master_thread.thread_object_id += 1
self.thread_name = thread_name
def close(self):
self.stop_request = True
def run(self):
print_inform_msg(f"{self.thread_name} modbus 主线程启动")
total_fail_time = 0
base_time = time.time()
while not self.stop_request:
comm_success_count = 0
total_comm_count = 0
for device in self.device_comm_object_list:
try:
if device.comm_active:
success_count, comm_count = device.device_comm_cycle()
comm_success_count += success_count
total_comm_count += comm_count
except Exception as err:
print_error_msg(err)
cur_time = time.time()
if comm_success_count > 0:
total_fail_time = 0
elif total_comm_count > 0:
total_fail_time += cur_time - base_time
base_time = cur_time
if total_fail_time >= self.MAX_FAIL_TIME:
self.reconnect_devices()
total_fail_time = 0
time.sleep(self.CHECK_INTERVAL)
print_error_msg(f"{self.thread_name} modbus 主线程停止")
def reconnect_devices(self):
"""重新连接所有设备的协议"""
for device in self.device_comm_object_list:
try:
device.protocol.close()
except Exception as err:
print_error_msg(f"关闭协议时出错: {err}")
for device in self.device_comm_object_list:
try:
device.protocol.open(device.protocol.mode)
except Exception as err:
print_error_msg(f"重新打开协议时出错: {err}")
print_warning_msg("与保护器连接超时, 重新进行连接")

View File

@ -1,3 +1,2 @@
Student_Name,Student_PRN,Student_Email,Student_Whatsapp_no,Student_pass,Branch,Semester,ssid Student_Name,Student_PRN,Student_Email,Student_Whatsapp_no,Student_pass,Branch,Semester,ssid,Modbus_IP,Modbus_Port
Ravikiran Yavalkar,12444444,abcd.efgh24@gmail.com,9607111115,India,AIML-F,III,NULL 测试用户,11111111,test@example.com,NULL,0000,测试,III,zhizhan-2,192.168.1.119,502
测试用户,11111111,test@example.com,NULL,0000,测试,III,zhizhan-2

1 Student_Name Student_PRN Student_Email Student_Whatsapp_no Student_pass Branch Semester ssid Modbus_IP Modbus_Port
2 Ravikiran Yavalkar 测试用户 12444444 11111111 abcd.efgh24@gmail.com test@example.com 9607111115 NULL India 0000 AIML-F 测试 III NULL zhizhan-2 192.168.1.119 502
测试用户 11111111 test@example.com NULL 0000 测试 III zhizhan-2

View File

@ -1,127 +0,0 @@
import menu_page
import menu_utils as utils
def func_comm_test_leak_exam(object_device, topic_item, topic, message) :
return None
def func_comm_table_trigger_item_exam(object_comm_table, comm_table_item) :
return False
exam_menu_alias_table = [
{"alias_bool": {0:"关闭", 1:"打开"}},
]
exam_mqtt_topic_table = [
{"name": "test_exam", "execute": func_comm_test_leak_exam},
]
exam_measure_pack = [
{
"name": "OnStatus",
"addr": "8162.0",
"alias": "alias_onoff",
},
]
exam_comm_table = [
{"name":"measure", "reg_addr":8139, "reg_count":35, "cycle": 200, "mqtt_pack": "exam_measure_pack"},
]
exam_menu_top = [
{
"name": "退出",
"action": "exit",
},
]
exam_menu_caption = [
{"name" : "exam_menu_top", "menu" : exam_menu_top, "next" : exam_menu_top, "prev" : exam_menu_top, "caption": "测试菜单", "page" : 10, "max_items" : 8},
]
#通讯设备配置基类, 不同的modbus设备需要重载各个函数
class class_comm_device_config :
def __init__(self):
pass
#获取别名表
def get_alias_table(self) :
return exam_menu_alias_table
#获取通讯数据表, 及触发函数
def get_comm_table(self) :
return exam_comm_table, func_comm_table_trigger_item_exam
#获取主菜单
def get_menu_top(self) :
return exam_menu_top
#获取mqtt表 实现mqtt通讯
def get_mqtt_table(self) :
return exam_mqtt_topic_table
#获取昆仑通态显示页面
def menu_get_display_page(self, active_menu):
return menu_page.KUNLUN_GRAOUP_PAGE_MENU
def get_menu_caption_info(self) :
return exam_menu_caption
def modify_menu_item(self, menu_item, value) :
return False
#获取菜单相关信息
def search_menu_info(self, menu_name) :
search_dict = None
list_caption_info = self.get_menu_caption_info()
for menu_dict in list_caption_info :
if "name" in menu_dict.keys() :
if menu_dict["name"] == menu_name :
search_dict = menu_dict
break
return search_dict
#获取菜单相关信息
def search_object_menu_info(self, menu_object) :
search_dict = None
list_caption_info = self.get_menu_caption_info()
for menu_dict in list_caption_info :
search_menu_object = utils.dict_or_object_get_attr(menu_dict, "menu", None)
if search_menu_object == menu_object :
search_dict = menu_dict
break
return search_dict
#获取自身菜单
def get_menu(self, menu_name) :
if menu_name != None :
search_dict = self.search_menu_info(menu_name)
if search_dict != None :
return utils.dict_or_object_get_attr(search_dict, "menu", None)
return None
#获取自身菜单
def get_menu_name(self, menu_object) :
menu_name = None
search_dict = self.search_object_menu_info(menu_object)
if search_dict != None :
menu_name = utils.dict_or_object_get_attr(search_dict, "name", None)
return menu_name
#获取下一个兄弟菜单
def get_menu_next(self, menu_name) :
search_dict = self.search_menu_info(menu_name)
menu_name = utils.dict_or_object_get_attr(search_dict, "next", None)
return self.get_menu(menu_name)
#获取上一个兄弟菜单
def get_menu_next(self, menu_name) :
search_dict = self.search_menu_info(menu_name)
menu_name = utils.dict_or_object_get_attr(search_dict, "prev", None)
return self.get_menu(menu_name)
#获取菜单显示页面
def get_menu_page(self, menu_name) :
search_dict = self.search_menu_info(menu_name)
page = utils.dict_or_object_get_attr(search_dict, "page", menu_page.KUNLUN_GRAOUP_PAGE_MENU)
return page

1514
kv/app.kv

File diff suppressed because it is too large Load Diff

376
main.py
View File

@ -1,5 +1,5 @@
from kivy.properties import ObjectProperty, StringProperty from kivy.properties import ObjectProperty, StringProperty, ListProperty
from datetime import datetime, timedelta from datetime import datetime, timedelta
from kivy.core.text import LabelBase from kivy.core.text import LabelBase
from kivy.metrics import dp from kivy.metrics import dp
@ -26,6 +26,16 @@ from kivy.uix.label import Label
from kivy.utils import platform from kivy.utils import platform
from kivy.clock import Clock from kivy.clock import Clock
from kivy.uix.widget import Widget
from kivy.graphics import Color, Rectangle
from kivy_garden.graph import Graph, LinePlot
from modbus_tk.modbus_tcp import TcpMaster
import modbus_tk.defines as cst
import socket
from random import randint
from kivymd.theming import ThemeManager from kivymd.theming import ThemeManager
LabelBase.register(name="MPoppins", fn_regular="fonts/Chinese/msyh.ttf") LabelBase.register(name="MPoppins", fn_regular="fonts/Chinese/msyh.ttf")
@ -41,51 +51,58 @@ def get_books():
class MainScreen(Screen): class MainScreen(Screen):
pass pass
class HomeScreen(Screen):
home = ObjectProperty(None)
class LoginScreen(Screen): class LoginScreen(Screen):
login = ObjectProperty(None) login = ObjectProperty(None)
class ProfileScreen(Screen):
profile = ObjectProperty(None)
class ProfileEditScreen(Screen):
profile_edit = ObjectProperty(None)
class HistoryScreen(Screen):
history = ObjectProperty(None)
class ModifyCurrentParamScreen(Screen):
modify_current_param = ObjectProperty(None)
class ModifyVoltageParamScreen(Screen):
modify_voltage_param = ObjectProperty(None)
class ModifyLeakageParamScreen(Screen):
modify_leakage_param = ObjectProperty(None)
class ModifySystemParamScreen(Screen):
modify_system_param = ObjectProperty(None)
class ModifyProtectionParamScreen(Screen):
modify_protection_param = ObjectProperty(None)
class RealTimeCurveScreen(Screen):
real_time_curve = ObjectProperty(None)
class ControlCommandScreen(Screen):
control_command = ObjectProperty(None)
class AboutScreen(Screen):
about = ObjectProperty(None)
class SignUpScreen(Screen): class SignUpScreen(Screen):
signup = ObjectProperty(None) signup = ObjectProperty(None)
class ForgetPassScreen(Screen): class ForgetPassScreen(Screen):
forget_Password = ObjectProperty(None) forget_Password = ObjectProperty(None)
class OtpScreen(Screen): class OtpScreen(Screen):
otp = ObjectProperty(None) otp = ObjectProperty(None)
class ResetPassScreen(Screen): class ResetPassScreen(Screen):
reset_pass = ObjectProperty(None) reset_pass = ObjectProperty(None)
class HomeScreen(Screen):
home = ObjectProperty(None)
class ProfileScreen(Screen):
profile = ObjectProperty(None)
class ProfileEditScreen(Screen):
profile_edit = ObjectProperty(None)
class SearchBookScreen(Screen): class SearchBookScreen(Screen):
search_book = ObjectProperty(None) search_book = ObjectProperty(None)
class NotificationScreen(Screen): class NotificationScreen(Screen):
notifications = ObjectProperty(None) notifications = ObjectProperty(None)
class RecommendScreen(Screen): class RecommendScreen(Screen):
recommendation = ObjectProperty(None) recommendation = ObjectProperty(None)
class BorrowBookScreen(Screen): class BorrowBookScreen(Screen):
borrow_book = ObjectProperty(None) borrow_book = ObjectProperty(None)
class ReturnBookScreen(Screen): class ReturnBookScreen(Screen):
return_book = ObjectProperty(None) return_book = ObjectProperty(None)
class RenewBookScreen(Screen): class RenewBookScreen(Screen):
renew_book = ObjectProperty(None) renew_book = ObjectProperty(None)
class HistoryScreen(Screen):
history = ObjectProperty(None)
# Window.size = (dp(360), dp(680)) # Window.size = (dp(360), dp(680))
@ -148,6 +165,11 @@ class app(MDApp):
self.password = None self.password = None
self.username = None self.username = None
self.modbus_master = None # Modbus连接对象
self.modbus_ip = None # Modbus服务器IP
self.modbus_port = None # Modbus服务器端口
self.theme_cls.font_styles["H1"] = ["BPoppins", 96, False, -1.5] self.theme_cls.font_styles["H1"] = ["BPoppins", 96, False, -1.5]
self.theme_cls.font_styles["H2"] = ["BPoppins", 60, False, -0.5] self.theme_cls.font_styles["H2"] = ["BPoppins", 60, False, -0.5]
self.theme_cls.font_styles["H3"] = ["BPoppins", 48, False, 0] self.theme_cls.font_styles["H3"] = ["BPoppins", 48, False, 0]
@ -168,21 +190,36 @@ class app(MDApp):
screen_manager.add_widget(MainScreen(name="main")) screen_manager.add_widget(MainScreen(name="main"))
screen_manager.add_widget(HomeScreen(name="home")) screen_manager.add_widget(HomeScreen(name="home"))
screen_manager.add_widget(LoginScreen(name="login")) screen_manager.add_widget(LoginScreen(name="login"))
screen_manager.add_widget(HistoryScreen(name="history"))
screen_manager.add_widget(ProfileScreen(name="profile"))
screen_manager.add_widget(ProfileEditScreen(name="profile_edit"))
screen_manager.add_widget(ModifyCurrentParamScreen(name="modify_current_param"))
screen_manager.add_widget(ModifyVoltageParamScreen(name="modify_voltage_param"))
screen_manager.add_widget(ModifyLeakageParamScreen(name="modify_leakage_param"))
screen_manager.add_widget(ModifySystemParamScreen(name="modify_system_param"))
screen_manager.add_widget(ModifyProtectionParamScreen(name="modify_protection_param"))
screen_manager.add_widget(RealTimeCurveScreen(name="real_time_curve"))
screen_manager.add_widget(ControlCommandScreen(name="control_command"))
screen_manager.add_widget(AboutScreen(name="about"))
screen_manager.add_widget(SignUpScreen(name="signup")) screen_manager.add_widget(SignUpScreen(name="signup"))
screen_manager.add_widget(ForgetPassScreen(name="forgot_pass")) screen_manager.add_widget(ForgetPassScreen(name="forgot_pass"))
screen_manager.add_widget(OtpScreen(name="otp")) screen_manager.add_widget(OtpScreen(name="otp"))
screen_manager.add_widget(ResetPassScreen(name="reset_pass")) screen_manager.add_widget(ResetPassScreen(name="reset_pass"))
screen_manager.add_widget(ProfileScreen(name="profile"))
screen_manager.add_widget(ProfileEditScreen(name="profile_edit"))
screen_manager.add_widget(NotificationScreen(name="notification")) screen_manager.add_widget(NotificationScreen(name="notification"))
screen_manager.add_widget(SearchBookScreen(name="searchBook")) screen_manager.add_widget(SearchBookScreen(name="searchBook"))
screen_manager.add_widget(RecommendScreen(name="recommend")) screen_manager.add_widget(RecommendScreen(name="recommend"))
screen_manager.add_widget(BorrowBookScreen(name="borrow_book")) screen_manager.add_widget(BorrowBookScreen(name="borrow_book"))
screen_manager.add_widget(ReturnBookScreen(name="return_book")) screen_manager.add_widget(ReturnBookScreen(name="return_book"))
screen_manager.add_widget(RenewBookScreen(name="renew_book")) screen_manager.add_widget(RenewBookScreen(name="renew_book"))
screen_manager.add_widget(HistoryScreen(name="history"))
Clock.schedule_once(self.update_wifi_status, 2) Clock.schedule_once(self.update_wifi_status, 2)
# Clock.schedule_interval(self.update_register_display, 1)
return screen_manager return screen_manager
#############################################ALL INPUT TEXT############################################################ #############################################ALL INPUT TEXT############################################################
@ -232,21 +269,21 @@ class app(MDApp):
self.d4 = otp_screen.ids.d4 self.d4 = otp_screen.ids.d4
# 从根窗口中获取名为"signup"的屏幕(注册界面) # 从根窗口中获取名为"signup"的屏幕(注册界面)
signup_screen = self.root.get_screen("signup") # signup_screen = self.root.get_screen("signup")
# 通过界面ID获取注册界面中的用户名输入框组件 # 通过界面ID获取注册界面中的用户名输入框组件
self.user_name = signup_screen.ids.signup_name # self.user_name = signup_screen.ids.signup_name
# 通过界面ID获取注册界面中的PRN可能是学号/身份标识)输入框组件 # 通过界面ID获取注册界面中的PRN可能是学号/身份标识)输入框组件
self.user_prn = signup_screen.ids.signup_prn # self.user_prn = signup_screen.ids.signup_prn
# 通过界面ID获取注册界面中的邮箱输入框组件 # 通过界面ID获取注册界面中的邮箱输入框组件
self.user_email = signup_screen.ids.signup_email # self.user_email = signup_screen.ids.signup_email
# 通过界面ID获取注册界面中的手机号输入框组件 # 通过界面ID获取注册界面中的手机号输入框组件
self.user_no = signup_screen.ids.signup_no # self.user_no = signup_screen.ids.signup_no
# 通过界面ID获取注册界面中的密码输入框组件 # 通过界面ID获取注册界面中的密码输入框组件
self.user_pass = signup_screen.ids.signup_pass # self.user_pass = signup_screen.ids.signup_pass
# 通过界面ID获取注册界面中的所属部门/专业选择组件 # 通过界面ID获取注册界面中的所属部门/专业选择组件
self.signup_branch = signup_screen.ids.signup_branch # self.signup_branch = signup_screen.ids.signup_branch
# 通过界面ID获取注册界面中的年级/学期选择组件 # 通过界面ID获取注册界面中的年级/学期选择组件
self.signup_sem = signup_screen.ids.signup_sem # self.signup_sem = signup_screen.ids.signup_sem
# 从根窗口中获取名为"profile"的屏幕(个人资料界面) # 从根窗口中获取名为"profile"的屏幕(个人资料界面)
profile_screen = self.root.get_screen("profile") profile_screen = self.root.get_screen("profile")
@ -314,7 +351,76 @@ class app(MDApp):
# 通过界面ID获取推荐界面中的推荐信息标签组件 # 通过界面ID获取推荐界面中的推荐信息标签组件
self.rec_label = self.recommend_screen.ids.rec_label self.rec_label = self.recommend_screen.ids.rec_label
#############################################@Frequently used functions################################################## self.root.bind(current=self.on_screen_changed)
self.register_update_event = None
def on_screen_changed(self, instance, value):
"""屏幕切换时启动/停止刷新"""
if value == "real_time_curve":
# 进入目标屏幕时启动定时刷新
if not self.register_update_event:
self.register_update_event = Clock.schedule_interval(self.update_register_display, 1)
else:
# 离开时停止刷新
if self.register_update_event:
self.register_update_event.cancel()
self.register_update_event = None
def read_modbus_register(self):
"""读取寄存器值(复用原逻辑)"""
if not self.modbus_master:
try:
self.modbus_master = TcpMaster(self.modbus_ip or '192.168.1.1', self.modbus_port or 502)
self.modbus_master.set_timeout(5.0)
except Exception as e:
print(f"Modbus连接失败: {e}")
return "连接失败"
try:
result = self.modbus_master.execute(1, cst.READ_HOLDING_REGISTERS, 0, 1)
return str(result[0]) # 返回寄存器值
except Exception as e:
print(f"读取失败: {e}")
return "读取失败"
# def update_register_display(self, dt):
# """更新原标签的显示内容"""
# # 仅在目标屏幕时更新
# if self.root.current != "real_time_curve":
# return
# # 读取值并更新到原标签
# value = self.read_modbus_register()
# real_time_screen = self.root.get_screen("real_time_curve")
# real_time_screen.ids.register_label.text = f"寄存器 0: {value}" # 直接更新原标签
def write_modbus_register(self, value):
"""写入寄存器值"""
if not self.modbus_master:
pass
try:
self.modbus_master.execute(1, cst.WRITE_SINGLE_REGISTER, 0, value)
return "修改成功"
except Exception as e:
return f"修改失败: {e}"
def modify_register(self, input_text):
"""处理修改请求,修改后更新原标签"""
if not input_text:
self.show_dialog("错误", "请输入值")
return
try:
value = int(input_text)
result = self.write_modbus_register(value)
self.show_dialog("结果", result)
# 若成功,立即刷新原标签显示
if "成功" in result:
self.update_register_display(0)
except ValueError:
self.show_dialog("错误", "请输入有效整数")
def show_dialog(self, title, message):
self.dialog2(title, message)
#############################################@Frequently used functions##################################################
@staticmethod @staticmethod
def change_cursor(is_enter): def change_cursor(is_enter):
@ -436,61 +542,62 @@ class app(MDApp):
buttons=[close_button] buttons=[close_button]
) )
self.dialog.open() self.dialog.open()
def dialog2(self, title, text):
close_button = MDFlatButton(
text="关闭",
font_name="MPoppins",
on_release=self.close_dialog
)
content_label = Label(
text=text,
font_name="MPoppins",
color=(0, 0, 0, 1),
size_hint_y=None,
height=dp(30),
halign="center",
valign="middle"
)
content_label.bind(size=content_label.setter('text_size'))
self.dialog = MDDialog(
title=title,
type="custom",
content_cls=content_label,
size_hint=(0.84, None),
buttons=[close_button]
)
self.dialog.open()
# MDDialog box dismiss function 'close_dialog' # MDDialog box dismiss function 'close_dialog'
def close_dialog(self, *args): def close_dialog(self, *args):
self.dialog.dismiss() self.dialog.dismiss()
#############################################Modbus Functions############################################################
def read_modbus_register(self):
"""读取Modbus寄存器值并更新界面显示"""
if not self.modbus_master:
pass
try:
# 读取寄存器1号从机保持寄存器地址0长度1
result = self.modbus_master.execute(1, cst.READ_HOLDING_REGISTERS, 0, 1)
# 结果是元组,取第一个值
return str(result[0])
except Exception as e:
pass
def update_register_display(self, dt):
"""定时更新寄存器值显示"""
register_value = self.read_modbus_register()
# 获取显示标签并更新文本
real_time_screen = self.root.get_screen("real_time_curve") # 根据实际屏幕名称调整
if hasattr(real_time_screen.ids, 'register_label'):
real_time_screen.ids.register_label.text = f"寄存器 0: {register_value}"
###################################LoginPageWork-Start################################################# ###################################LoginPageWork-Start#################################################
# # login Verification function "verify"
# def verify(self, obj):
# status = False
# if self.username.text == "" or self.password.text == "":
# check = "请输入账户和密码"
# return self.dialog1(check)
# else:
# name = ""
# with open('data/Users.csv', "r", encoding="utf-8") as file:
# csv_reader = csv.reader(file, delimiter=",")
# for line in csv_reader:
# try:
# if not obj:
# a = (line[1] == self.username.text or line[2] == self.username.text)
# else:
# a = (line[1] == self.edit_prn.text or line[2] == self.edit_email.text)
# if a and line[4] == self.password.text:
# status = True
# name = line[0]
# prn = line[1]
# email = line[2]
# number = line[3]
# branch = line[5]
# semester = line[6]
# break
# except IndexError:
# pass
# except Exception as e:
# print(e)
# if status:
# if not obj:
# check = "Login Successful" + "\nHello! " + name
# else:
# check = "Profile Updated Successfully!"
# self.secure_profile()
# self.root.current = "home"
# self.user.text = f"""[b]Hey! {name}[/b]"""
# self.profile_name.text = self.edit_name.text = name
# self.profile_email.text = self.edit_email.text = email
# self.profile_prn.text = self.edit_prn.text = prn
# self.profile_number.text = self.edit_number.text = number
# self.profile_semester.text = self.edit_semester.text = semester
# self.profile_branch.text = self.edit_branch.text = branch
# else:
# check = "Login Failed!. " + "Enter correct username and password."
# self.dialog1(check)
def update_wifi_status(self, dt): def update_wifi_status(self, dt):
self.check_wifi() self.check_wifi()
Clock.schedule_once(self.update_wifi_status, 1) # 5秒刷新一次 Clock.schedule_once(self.update_wifi_status, 1) # 5秒刷新一次
@ -558,21 +665,23 @@ class app(MDApp):
else: else:
wifi_id = "zhizhan-2" # 非 Android 用模拟 WiFi wifi_id = "zhizhan-2" # 非 Android 用模拟 WiFi
except Exception as e: except Exception as e:
self.dialog1(f"获取WiFi信息失败{e}") self.dialog1(f"获取WiFi信息失败:{e}")
return return
try: try:
with open("data/Users.csv", "r", encoding="utf-8") as file: with open("data/Users.csv", "r", encoding="utf-8") as file:
csv_reader = csv.reader(file) csv_reader = csv.reader(file)
for line in csv_reader: for line in csv_reader:
# 确保至少有7列数据
if len(line) < 7: if len(line) < 8:
continue continue
csv_ssid = line[7].strip().lower() if len(line) > 7 else "" # 假设SSID存储在第8列(索引7)
csv_ssid = line[7].strip().lower()
# 更精确匹配SSID且忽略大小写
if wifi_id == csv_ssid or wifi_id in map(str.lower, line): if wifi_id == csv_ssid or wifi_id in map(str.lower, line):
name, prn, email, number, password, branch, semester = line[:7] name, prn, email, number, password, branch, semester,csv_ssid = line[:8]
self.root.current = "home" self.root.current = "home"
self.user.text = f"[b]Hey! {name}[/b]" self.user.text = f"[b]Hey! {name}[/b]"
@ -583,35 +692,80 @@ class app(MDApp):
self.profile_semester.text = self.edit_semester.text = semester self.profile_semester.text = self.edit_semester.text = semester
self.profile_branch.text = self.edit_branch.text = branch self.profile_branch.text = self.edit_branch.text = branch
self.dialog1(f"欢迎你,{name} wifi认证成功") self.connect_modbus()
self.dialog1(f"欢迎你,{name}!\n认证成功!")
return return
except Exception as e: except Exception as e:
self.dialog1(f"读取用户信息失败:{e}") self.dialog1(f"读取用户信息失败:{e}")
return return
self.dialog1("当前WiFi认证失败,请检查网络或退出") self.dialog1("认证失败,请检查网络或确保手机权限打开定位和连接到目标WiFi")
# 添加Modbus连接函数
def connect_modbus(self):
try:
# 从CSV文件读取Modbus配置
modbus_ip = None
modbus_port = 502 # 默认端口
# 获取当前连接的WiFi SSID
if platform == "android":
from jnius import autoclass
PythonActivity = autoclass('org.kivy.android.PythonActivity')
Context = autoclass('android.content.Context')
activity = PythonActivity.mActivity
wifi_service = activity.getSystemService(Context.WIFI_SERVICE)
wifi_info = wifi_service.getConnectionInfo()
current_wifi_id = wifi_info.getSSID().strip('"').lower()
else:
current_wifi_id = "zhizhan-2" # 非Android环境模拟WiFi
# 读取CSV文件查找匹配的Modbus配置
with open("data/Users.csv", "r", encoding="utf-8") as file:
csv_reader = csv.reader(file)
headers = next(csv_reader) # 获取表头
for line in csv_reader:
if len(line) < 10: # 确保有足够的字段
continue
# 匹配当前连接的WiFi SSID
csv_ssid = line[7].strip().lower()
if current_wifi_id == csv_ssid or current_wifi_id in map(str.lower, line):
modbus_ip = line[8]
modbus_port = int(line[9]) if line[9] else 502
break # 找到匹配项则退出循环
if not modbus_ip:
# self.dialog1("未找到与当前WiFi匹配的Modbus配置")
return
# 断开现有连接(如果存在)
if hasattr(self, 'modbus_master') and self.modbus_master:
self.modbus_master.close()
# 创建新连接
self.modbus_master = TcpMaster(host=modbus_ip, port=modbus_port)
self.modbus_master.set_timeout(5.0) # 设置超时时间
# 测试连接(读取一个保持寄存器)
self.modbus_master.execute(1, cst.READ_HOLDING_REGISTERS, 0, 1)
print(f"Modbus连接成功\nIP: {modbus_ip}\n端口: {modbus_port}")
except FileNotFoundError:
self.dialog1("配置文件user.csv未找到")
except socket.error as e:
self.dialog1(f"Modbus连接失败: 网络错误\n{e}")
self.modbus_master = None
except Exception as e:
self.dialog1(f"Modbus连接失败: {e}")
self.modbus_master = None
click_count = 0
def login_mode(self, username_text, button_text):
self.click_count += 1
if self.click_count % 2 == 0:
username_text.hint_text = "Enter PRN"
button_text.text = "VIT Email Login"
username_text.icon_right = "dialpad"
username_text.helper_text = "PRN of 8 digits starting with 1 2 ......only*"
username_text.max_text_length = 8
else:
username_text.hint_text = "Enter Email"
username_text.icon_right = "email-arrow-left"
button_text.text = "VIT PRN Login"
username_text.helper_text = "enter email ID of vit.edu only*"
username_text.max_text_length = 50
###############################################SIGNUP Page Functions##################################################### ###############################################SIGNUP Page Functions#####################################################
# signup Verification function "check_signup" # signup Verification function "check_signup"
def check_signup(self, name, prn, email, num, password, branch, sem): def check_signup(self, name, prn, email, num, password, branch, sem, ssid, modbus_ip, modbus_port):
if name == "" or prn == "" or email == "" or num == "" or password == "" or branch == "" or sem == "": if name == "" or prn == "" or email == "" or num == "" or password == "" or branch == "" or sem == "":
check = "Enter all required fields" check = "Enter all required fields"
return self.dialog1(check) return self.dialog1(check)
@ -630,7 +784,6 @@ class app(MDApp):
if line[2] == email: if line[2] == email:
status = "email" status = "email"
break break
else: else:
status = False status = False
except IndexError: except IndexError:
@ -647,6 +800,9 @@ class app(MDApp):
else: else:
self.root.current = "otp" self.root.current = "otp"
self.send_otp(num) self.send_otp(num)
# 保存注册信息时包含Modbus信息
self.signup_modbus_ip = modbus_ip
self.signup_modbus_port = modbus_port
check = "One Time Password has been shared on your registered Whatsapp No. Successful!." check = "One Time Password has been shared on your registered Whatsapp No. Successful!."
self.dialog1(check) self.dialog1(check)
@ -1226,14 +1382,6 @@ class app(MDApp):
self.book_pos.text = "Please check the Spell!" self.book_pos.text = "Please check the Spell!"
# LabelBase.register(name="MPoppins", fn_regular="fonts/Poppins/Poppins-Medium.ttf")
# LabelBase.register(name="BPoppins", fn_regular="fonts/Poppins/Poppins-SemiBold.ttf")
# LabelBase.register(name="RRubik", fn_regular="fonts/Rubik_Vinyl/RubikVinyl-Regular.ttf")
# LabelBase.register(name="RCro", fn_regular="fonts/Croissant_One/CroissantOne-Regular.ttf")
# LabelBase.register(name="RPac", fn_regular="fonts/Pacifico/Pacifico-Regular.ttf")
if __name__ == '__main__': if __name__ == '__main__':
app().run() app().run()

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +0,0 @@
#昆仑通态组合页面设计
KUNLUN_GRAOUP_PAGE_MAIN = 20 #主选择菜单页面
KUNLUN_GRAOUP_PAGE_MENU = 5 #文字菜单页面
KUNLUN_GRAOUP_PAGE_ALARM = 7 #报警页面
KUNLUN_GRAOUP_PAGE_DEVICE_KD = 21 #馈电页面

View File

@ -1,109 +0,0 @@
import re
import sys
import inspect
import enum as Enum
def find_variable_name(value):
frame = inspect.currentframe()
try:
for var_name, var_value in frame.f_back.f_locals.items():
if var_value is value:
return var_name
finally:
del frame
def dict_or_object_get_attr(dict_or_object, name, default_attr) :
if dict_or_object == None or name == None:
return default_attr
if type(dict_or_object) == dict :
if name in dict_or_object.keys():
return dict_or_object[name]
return default_attr
return getattr(dict_or_object, name, default_attr)
def value_u64_to_s64(value) :
svalue = value
if (svalue >> 63) & 0x1 :
svalue = -((svalue ^ 0xFFFFFFFFFFFFFFFF) + 1)
return svalue
def value_u32_to_s32(value) :
svalue = value
if (svalue & 0x80000000) :
svalue = -((svalue ^ 0xFFFFFFFF) + 1)
return svalue
def value_u16_to_s16(value) :
svalue = value
if (svalue & 0x8000) :
svalue = -((svalue ^ 0xFFFF) + 1)
return svalue
'''
正则表达式
^:匹配字符串开头。
$:匹配字符串结尾。
.:匹配任意字符。
*:匹配前面的字符零次或多次。
+:匹配前面的字符一次或多次。
?:匹配前面的字符零次或一次。
[]:匹配括号中列举的任意一个字符。
[^]:匹配除了括号中列举的字符以外的任意一个字符。
():标记一个子表达式的开始和结束位置。
\s: 包含空格
\S: 不包含空格
\d: 包含数字
\D: 不包含数字
'''
def comm_str_unpack(comm_str) :
pattern_bit_regs = "(\d+)\.(\d+)((\s*)#(\s*)(\d+))*"
pattern_regs = "(\d+)((\s*)#(\s*)(\d+))*"
reg_addr = 0
reg_count = 0
bit = -1
bit_reg_result = None
reg_result = None
if comm_str == None :
return reg_addr, bit, reg_count
#[8000.2 # 3] or [8000.2]
bit_reg_result = re.search(pattern_bit_regs, comm_str)
if bit_reg_result :
match_str = bit_reg_result.string
bit_result_value = re.findall('\d+', match_str)
item_count = len(bit_result_value)
if item_count >= 2 :
reg_addr = int(bit_result_value[0])
bit = int(bit_result_value[1])
if item_count == 3:
reg_count = int(bit_result_value[2])
else :
reg_count = 1
#[8000 # 2] or [8000]
reg_result = None
if bit_reg_result == None:
reg_result = re.search(pattern_regs, comm_str)
if reg_result != None:
match_str = reg_result.string
reg_result_value = re.findall('\d+', match_str)
item = len(reg_result_value)
if item >= 1 :
reg_addr = int(reg_result_value[0])
if item == 2 :
reg_count = int(reg_result_value[1])
else :
reg_count = 1
return reg_addr, bit, reg_count

View File

@ -1,218 +0,0 @@
import threading
import time
import string
from threading import Thread
import menu_utils as utils
import paho.mqtt.client as mqtt
from print_color import *
#mqtt 消息处理基类, mqtt消息处理
class class_comm_mqtt_interface :
def __init__(self, name = "mqtt handler"):
self.name = name
def on_message(self, mqtt_thread, topic, message) :
return
def on_connect(self, mqtt_thread, userdata, flags, rc) :
return
#mqtt服务器连接处理
def mqtt_on_connect(client, userdata, flags, rc):
if rc == 0:
# 连接成功
print_normal_msg("Mqtt 服务器连接成功")
elif rc == 1:
# 协议版本错误
print_error_msg("协议版本错误")
elif rc == 2:
# 无效的客户端标识
print_error_msg("无效的客户端标识")
elif rc == 3:
# 服务器无法使用
print_error_msg("Mqtt 服务器无法使用")
elif rc == 4:
# 错误的用户名或密码
print_error_msg("错误的用户名或密码")
elif rc == 5:
# 未经授权
print_error_msg("未经授权")
#获取线程类 class_comm_mqtt_thread)
mqtt_thread : class_comm_mqtt_thread= userdata
unique_object : class_comm_mqtt_interface = None
#遍历所有unique_object, 通知其 on_connect 事件
for unique_object in mqtt_thread.unique_object_dict.values() :
if isinstance(unique_object, list) : #unique_object 可能是列表, 所以需要遍历
for each_object in unique_object:
if hasattr(each_object, "on_connect") :
each_object.on_connect(mqtt_thread, userdata, flags, rc)
elif hasattr(unique_object, "on_connect") :
unique_object.on_connect(mqtt_thread, userdata, flags, rc)
#mqtt接收到主题, 通过主题中包含的 unique_name, 定位到 uniuqe_object, 并调用 unique_object.on_message 函数
def mqtt_on_message(client, userdata, message):
#print("mqtt message:", message.topic, message.payload.decode("utf-8"))
#获取线程类 class_comm_mqtt_thread)
mqtt_thread : class_comm_mqtt_thread= userdata
unique_object : class_comm_mqtt_interface = None
for unique_name, search_unique_object in mqtt_thread.unique_object_dict.items() :
if unique_name in message.topic :
unique_object = search_unique_object
break
#mqtt线程本身无法处理对应的主题消息, 由mqtt 所对应的unique_object处理
if unique_object != None :
if isinstance(unique_object, list) : #unique_object 可能是列表, 所以需要遍历
for each_object in unique_object:
if hasattr(each_object, "on_message") :
each_object.on_message(mqtt_thread, message.topic, message.payload)
elif hasattr(unique_object, "on_message") :
unique_object.on_message(mqtt_thread, message.topic, message.payload)
class class_comm_mqtt_thread(Thread):
def __init__(self, user_name = "admin", password = "admin") :
Thread.__init__(self)
self.client = None
self.subscribe_list = []
self.publish_list = []
self.publish_list_lock = threading.Lock()
self.condition = threading.Condition()
self.stop_request = False
self.server = "127.0.0.1"
self.port = 1883
self.user_name = user_name
self.password = password
self.keepalive = 60
self.unique_object_dict = {} #key = unique_name, value = device_object
return
#unique_object: 为unique_object_subscribe_name对应的object, 在回调
def add_unique_object(self, unique_name : string, unique_object : class_comm_mqtt_interface) :
self.unique_object_dict[unique_name] = unique_object
#topic 中 需要包含
def subscribe(self, topic : string) :
if self.is_connect() and self.client:
if topic not in self.subscribe_list :
self.subscribe_list.append(topic)
self.client.subscribe(topic)
def set_mqtt_server(self, server = "127.0.0.1", port = 1883, keep_alive = 60.0, user_name = "admin", password = "admin") :
self.server = server
self.port = port
self.user_name = user_name
self.password = password
self.keepalive = keep_alive
#用于判断 mqtt_thread是否已经连接
def is_connect(self) :
if self.client == None :
return False
return self.client.is_connected()
def open(self):
try :
self.client = mqtt.Client()
self.client.user_data_set(self)
self.client.on_connect = mqtt_on_connect
self.client.on_message = mqtt_on_message
self.client.username_pw_set(self.user_name, self.password)
self.client.connect(self.server, self.port, self.keepalive)
except Exception as e :
print_error_msg(str(e) + "mqtt_server: ip=%s, port = %d"%(self.server, self.port))
if self.client: # 如果self.client已经被初始化了则断开连接
self.client.disconnect()
self.client = None
return None # 添加返回语句,告知调用者连接失败
return self.client
def close(self):
self.stop_request = True
def publish(self, topic, message) :
if self.is_connect :
self.publish_list_lock.acquire()
self.publish_list.append([topic, message])
self.publish_list_lock.release()
self.condition.acquire()
self.condition.notify()
self.condition.release()
return
def publish_wait(self, topic, message, timeout) :
if self.is_connect :
self.publish_list_lock.acquire()
self.publish_list.insert(0, [topic, message])
self.publish_list_lock.release()
self.condition.acquire()
self.condition.notify()
self.condition.release()
return
def run(self):
while self.stop_request == False :
print_warning_msg("mqtt 线程启动, 服务器端口:server = %s:%d"%(self.server, self.port))
wait_open_timeout = 0
self.subscribe_list = [] #取消订阅列表, 连接后重新订阅
while self.client == None and self.stop_request == False:
if wait_open_timeout == 0 :
print_warning_msg("mqtt连接中, 服务器端口:server = %s:%d"%(self.server, self.port))
self.open()
if self.client == None :
time.sleep(0.1)
wait_open_timeout += 0.1
if wait_open_timeout >= 5.0 :
wait_open_timeout = 0
time.sleep(0.2)
if self.client :
self.client.loop_start()
time.sleep(0.2)
while self.stop_request == False and self.client.is_connected() :
topic = None
topic_message = None
self.publish_list_lock.acquire()
if len(self.publish_list) > 0 :
topic_message = self.publish_list.pop(0)
topic = topic_message[0]
message = topic_message[1]
self.publish_list_lock.release()
if topic != None and message != None :
self.client.publish(topic, message)
self.condition.acquire()
if len(self.publish_list) == 0:
self.condition.wait(timeout = 0.1)
self.condition.release()
self.client.loop_stop()
self.client.disconnect()
self.client = None
print_warning_msg("mqtt连接断开, 准备重启mqtt连接")
print_error_msg("mqtt 线程停止")
if __name__ == "__main__":
object_mqtt1 = class_comm_mqtt_interface("object1 handler")
object_mqtt2 = class_comm_mqtt_interface("object2 handler")
global_mqtt_thread = class_comm_mqtt_thread()
global_mqtt_thread.set_mqtt_server(server = "127.0.0.1", port = 1883, keep_alive = 60, user_name="admin", password="admin")
global_mqtt_thread.add_unique_object("object1", object_mqtt1)
global_mqtt_thread.add_unique_object("object2", object_mqtt2)
global_mqtt_thread.start()

View File

@ -1,102 +0,0 @@
import menu_utils as utils
import json
from mqtt_device import class_comm_mqtt_interface, class_comm_mqtt_thread
from device_conf import class_comm_device_config
import string
from enum import Enum
import math
from print_color import *
#创建mqtt 消息属性字典,
class class_mqtt_info_object (class_comm_mqtt_interface):
def __init__(self, device_name):
class_comm_mqtt_interface.__init__(self)
self.mqtt_dict = {}
device_config_info = __import__(device_name)
self.device_config : class_comm_device_config = device_config_info.comm_device_config
self.menu_description = self.device_config.get_menu_caption_info()
menu_process_list = []
for item_dict in self.menu_description :
if "menu" in item_dict.keys() :
menu_object = item_dict["menu"]
self.__create_mqtt_dict__(menu_object, menu_process_list)
#创建mqtt索引, 用于定位mqtt消息相关的菜单属性, 内部函数, 外部不要调用
def __create_mqtt_dict__(self, menu_object, menu_process_list: list): #创建菜单对应的各项mqtt信息包含菜单内部的子菜单的各项mqtt信息
if menu_object == None :
return
if menu_object in menu_process_list : #防止递归进入无限循环
return
menu_process_list.append(menu_object) #添加到处理队列中可以防止menu_object重复处理
for menu_item in menu_object : #遍历菜单内部的各个菜单项
if "mqtt" in menu_item.keys() : #该项菜单具有mqtt属性
mqtt_info_name = menu_item["mqtt"] #获取mqtt的信息名称 mqtt的消息中包含该信息时可快速定位该 菜单项
if mqtt_info_name not in self.mqtt_dict.keys() : #mqtt信息需要具有唯一性,
self.mqtt_dict[mqtt_info_name] = menu_item #把mqtt信息与 菜单项 进行(key, value)绑定. key = mqtt信息, value为菜单项
else :
print_error_msg("Error, 菜单项有相同的mqtt字段(%s)"%(mqtt_info_name))
if "sub_menu" in menu_item.keys() :
sub_menu_name = menu_item["sub_menu"]
if isinstance(sub_menu_name, Enum):
sub_menu_name = sub_menu_name.name
sub_menu_object = self.search_menu_group_object(sub_menu_name)
self.__create_mqtt_dict__(sub_menu_object, menu_process_list) #递归调用, 直到处理完所有子菜单项
#通过菜单名搜索到菜单项
def search_menu_item(self, mqtt_info_name : string) :
if mqtt_info_name in self.mqtt_dict.keys() :
return self.mqtt_dict[mqtt_info_name]
else :
return None
#通过菜单名字来查找菜单组对象, 每个菜单组包含若干个菜单项
def search_menu_group_object(self, menu_group_name) :
menu_group_object = None
for item_dict in self.menu_description:
menu_item_name = utils.dict_or_object_get_attr(item_dict, "name", None)
if menu_item_name == menu_group_name :
menu_group_object = utils.dict_or_object_get_attr(item_dict, "menu", None)
break
return menu_group_object
#定义mqtt信息组所有设备的mqtt信息 都集中于该类
class class_mqtt_info_object_group():
def __init__(self):
self.device_mqtt_info_dict : class_mqtt_info_object = {}
def create_mqtt_info_object(self, device_name) ->class_mqtt_info_object:
try :
mqtt_info_object = None
if device_name not in self.device_mqtt_info_dict.keys() :
new_mqtt_info = class_mqtt_info_object(device_name)
self.device_mqtt_info_dict[device_name] = new_mqtt_info
mqtt_info_object = self.get_mqtt_info_object(device_name)
except Exception as e:
print_error_msg(str(e))
mqtt_info_object = None
finally :
return mqtt_info_object
def get_mqtt_info_object(self, device_name) ->class_mqtt_info_object:
matched_mqtt_info = None
if device_name in self.device_mqtt_info_dict.keys() :
matched_mqtt_info = self.device_mqtt_info_dict[device_name]
return matched_mqtt_info
def search_mqtt_menu_item_info(self, device_name, mqtt_info_name) :
if mqtt_info_name in self.device_mqtt_info_dict.keys() :
mqtt_info_object : class_mqtt_info_object = self.device_mqtt_info_dict[device_name]
menu_item = mqtt_info_object.search_menu_item(mqtt_info_name)
return menu_item
return None

View File

@ -1,41 +0,0 @@
from colorama import init, Fore, Back, Style
from threading import Lock
import datetime
init() # 初始化colorama
print_mutex = Lock()
def print_normal_msg(*args, **kwargs) :
current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
print_mutex.acquire()
print(current_time, *args, **kwargs, end='')
print(Style.RESET_ALL)
print_mutex.release()
def print_warning_msg(*args, **kwargs) :
current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
print_mutex.acquire()
print(current_time, Fore.LIGHTMAGENTA_EX, *args, **kwargs, end='')
print(Style.RESET_ALL)
print_mutex.release()
def print_inform_msg(*args, **kwargs) :
current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
print_mutex.acquire()
print(current_time, Fore.YELLOW, *args, **kwargs, end='')
print(Style.RESET_ALL)
print_mutex.release()
def print_error_msg(*args, **kwargs) :
current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
print_mutex.acquire()
print(current_time, Fore.RED, *args, **kwargs, end='')
print(Style.RESET_ALL)
print_mutex.release()
def print_debug_msg(*args, **kwargs) :
current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
print_mutex.acquire()
print(current_time, Fore.BLUE, *args, **kwargs, end='')
print(Style.RESET_ALL)
print_mutex.release()