调整版本,实现wifi认证
This commit is contained in:
2
1.py
Normal file
2
1.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
from kivy.properties import ObjectProperty, StringProperty
|
||||||
|
# print("ok")
|
||||||
BIN
assets/_img.png
Normal file
BIN
assets/_img.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.7 KiB |
BIN
assets/img.bmp
BIN
assets/img.bmp
Binary file not shown.
|
Before Width: | Height: | Size: 450 KiB |
BIN
assets/img.png
BIN
assets/img.png
Binary file not shown.
|
Before Width: | Height: | Size: 5.7 KiB After Width: | Height: | Size: 870 KiB |
99
comm_device.py
Normal file
99
comm_device.py
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
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
|
||||||
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 # 返回成功计数和总计数
|
||||||
45
comm_protocol.py
Normal file
45
comm_protocol.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
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()
|
||||||
|
|
||||||
656
comm_protocol_modbus.py
Normal file
656
comm_protocol_modbus.py
Normal file
@ -0,0 +1,656 @@
|
|||||||
|
#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()
|
||||||
19
comm_remap.py
Normal file
19
comm_remap.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
|
||||||
|
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
|
||||||
78
comm_thread.py
Normal file
78
comm_thread.py
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
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("与保护器连接超时, 重新进行连接")
|
||||||
@ -1,4 +1,3 @@
|
|||||||
Student_Name,Student_PRN,Student_Email,Student_Whatsapp_no,Student_pass,Branch,Semester
|
Student_Name,Student_PRN,Student_Email,Student_Whatsapp_no,Student_pass,Branch,Semester,ssid
|
||||||
Ravikiran Yavalkar,12444444,abcd.efgh24@gmail.com,9607111115,India,AIML-F,III
|
Ravikiran Yavalkar,12444444,abcd.efgh24@gmail.com,9607111115,India,AIML-F,III,NULL
|
||||||
jfen,55775577,test@example.com,NULL,China,AIML-F,III
|
测试用户,11111111,test@example.com,NULL,0000,测试,III,zhizhan-2
|
||||||
|
|
||||||
|
|||||||
|
127
device_conf.py
Normal file
127
device_conf.py
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
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
|
||||||
313
main.py
313
main.py
@ -1,5 +1,5 @@
|
|||||||
|
|
||||||
from kivy.properties import ObjectProperty
|
from kivy.properties import ObjectProperty, StringProperty
|
||||||
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
|
||||||
@ -24,16 +24,21 @@ from kivymd.uix.dialog import MDDialog
|
|||||||
from kivymd.uix.label import MDLabel
|
from kivymd.uix.label import MDLabel
|
||||||
from kivy.uix.label import Label
|
from kivy.uix.label import Label
|
||||||
|
|
||||||
|
from kivy.utils import platform
|
||||||
|
from kivy.clock import Clock
|
||||||
|
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")
|
||||||
LabelBase.register(name="BPoppins", fn_regular="fonts/Chinese/msyh.ttf")
|
LabelBase.register(name="BPoppins", fn_regular="fonts/Chinese/msyh.ttf")
|
||||||
LabelBase.register(name="RRubik", fn_regular="fonts/Chinese/msyh.ttf")
|
LabelBase.register(name="RRubik", fn_regular="fonts/Chinese/msyh.ttf")
|
||||||
LabelBase.register(name="RCro", fn_regular="fonts/Chinese/msyh.ttf")
|
LabelBase.register(name="RCro", fn_regular="fonts/Chinese/msyh.ttf")
|
||||||
LabelBase.register(name="RPac", fn_regular="fonts/Chinese/msyh.ttf")
|
LabelBase.register(name="RPac", fn_regular="fonts/Chinese/msyh.ttf")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_books():
|
def get_books():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class MainScreen(Screen):
|
class MainScreen(Screen):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@ -41,7 +46,7 @@ class LoginScreen(Screen):
|
|||||||
login = ObjectProperty(None)
|
login = ObjectProperty(None)
|
||||||
|
|
||||||
class SignUpScreen(Screen):
|
class SignUpScreen(Screen):
|
||||||
signup: ObjectProperty = ObjectProperty(None)
|
signup = ObjectProperty(None)
|
||||||
|
|
||||||
class ForgetPassScreen(Screen):
|
class ForgetPassScreen(Screen):
|
||||||
forget_Password = ObjectProperty(None)
|
forget_Password = ObjectProperty(None)
|
||||||
@ -82,7 +87,10 @@ class RenewBookScreen(Screen):
|
|||||||
class HistoryScreen(Screen):
|
class HistoryScreen(Screen):
|
||||||
history = ObjectProperty(None)
|
history = ObjectProperty(None)
|
||||||
|
|
||||||
|
# Window.size = (dp(360), dp(680))
|
||||||
|
|
||||||
class app(MDApp):
|
class app(MDApp):
|
||||||
|
wifi_status_text = StringProperty("")
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.signup_sem = None
|
self.signup_sem = None
|
||||||
@ -140,6 +148,19 @@ class app(MDApp):
|
|||||||
self.password = None
|
self.password = None
|
||||||
self.username = None
|
self.username = None
|
||||||
|
|
||||||
|
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["H3"] = ["BPoppins", 48, False, 0]
|
||||||
|
self.theme_cls.font_styles["H4"] = ["BPoppins", 34, False, 0.25]
|
||||||
|
self.theme_cls.font_styles["H5"] = ["BPoppins", 25, False, 0.15]
|
||||||
|
self.theme_cls.font_styles["H6"] = ["BPoppins", 16, False, 0.15]
|
||||||
|
self.theme_cls.font_styles["Subtitle1"] = ["BPoppins", 16, False, 0.15]
|
||||||
|
self.theme_cls.font_styles["Subtitle2"] = ["BPoppins", 14, False, 0.1]
|
||||||
|
self.theme_cls.font_styles["Body1"] = ["BPoppins", 16, False, 0.5]
|
||||||
|
self.theme_cls.font_styles["Body2"] = ["BPoppins", 14, False, 0.25]
|
||||||
|
self.theme_cls.font_styles["Button"] = ["BPoppins", 14, True, 1.25]
|
||||||
|
self.theme_cls.font_styles["Caption"] = ["BPoppins", 13, False, 0.15]
|
||||||
|
self.theme_cls.font_styles["Overline"] = ["BPoppins", 10, True, 1.5]
|
||||||
|
|
||||||
def build(self):
|
def build(self):
|
||||||
Builder.load_file('kv/app.kv')
|
Builder.load_file('kv/app.kv')
|
||||||
@ -161,115 +182,197 @@ class app(MDApp):
|
|||||||
screen_manager.add_widget(RenewBookScreen(name="renew_book"))
|
screen_manager.add_widget(RenewBookScreen(name="renew_book"))
|
||||||
screen_manager.add_widget(HistoryScreen(name="history"))
|
screen_manager.add_widget(HistoryScreen(name="history"))
|
||||||
|
|
||||||
|
Clock.schedule_once(self.update_wifi_status, 2)
|
||||||
return screen_manager
|
return screen_manager
|
||||||
|
|
||||||
#############################################ALL INPUT TEXT############################################################
|
#############################################ALL INPUT TEXT############################################################
|
||||||
def on_start(self):
|
def on_start(self):
|
||||||
|
|
||||||
|
# 从根窗口中获取名为"login"的屏幕(登录界面)
|
||||||
login_screen = self.root.get_screen("login")
|
login_screen = self.root.get_screen("login")
|
||||||
self.username = login_screen.ids.username
|
# 通过界面ID获取登录界面中的用户名输入框组件
|
||||||
self.password = login_screen.ids.password
|
# self.username = login_screen.ids.username
|
||||||
|
# 通过界面ID获取登录界面中的密码输入框组件
|
||||||
|
# self.password = login_screen.ids.password
|
||||||
|
|
||||||
|
# 从根窗口中获取名为"home"的屏幕(主界面)
|
||||||
home_screen = self.root.get_screen("home")
|
home_screen = self.root.get_screen("home")
|
||||||
|
# 通过界面ID获取主界面中的用户信息组件
|
||||||
self.user = home_screen.ids.user
|
self.user = home_screen.ids.user
|
||||||
|
# 通过界面ID获取主界面中的书架位置名称组件
|
||||||
self.book_pos_name = home_screen.ids.book_pos_name
|
self.book_pos_name = home_screen.ids.book_pos_name
|
||||||
|
# 通过界面ID获取主界面中的书架位置组件
|
||||||
self.book_pos = home_screen.ids.book_pos
|
self.book_pos = home_screen.ids.book_pos
|
||||||
|
|
||||||
|
# 从根窗口中获取名为"forgot_pass"的屏幕(忘记密码界面)
|
||||||
forgot_pass_screen = self.root.get_screen("forgot_pass")
|
forgot_pass_screen = self.root.get_screen("forgot_pass")
|
||||||
|
# 通过界面ID获取忘记密码界面中的手机号输入框组件
|
||||||
self.mobile_no = forgot_pass_screen.ids.forgotPass_no
|
self.mobile_no = forgot_pass_screen.ids.forgotPass_no
|
||||||
|
|
||||||
|
# 从根窗口中获取名为"reset_pass"的屏幕(重置密码界面)
|
||||||
reset_pass_screen = self.root.get_screen("reset_pass")
|
reset_pass_screen = self.root.get_screen("reset_pass")
|
||||||
|
# 通过界面ID获取重置密码界面中的新密码输入框组件
|
||||||
self.new_pass = reset_pass_screen.ids.new_pass
|
self.new_pass = reset_pass_screen.ids.new_pass
|
||||||
|
# 通过界面ID获取重置密码界面中的确认新密码输入框组件
|
||||||
self.new_pass1 = reset_pass_screen.ids.new_pass1
|
self.new_pass1 = reset_pass_screen.ids.new_pass1
|
||||||
|
|
||||||
|
# 从根窗口中获取名为"otp"的屏幕(验证码界面)
|
||||||
otp_screen = self.root.get_screen("otp")
|
otp_screen = self.root.get_screen("otp")
|
||||||
|
# 通过界面ID获取验证码界面中的"验证"按钮组件
|
||||||
self.otp_button = otp_screen.ids.otp_button
|
self.otp_button = otp_screen.ids.otp_button
|
||||||
|
# 通过界面ID获取验证码界面中的"返回"按钮组件
|
||||||
self.back_button = otp_screen.ids.back_button
|
self.back_button = otp_screen.ids.back_button
|
||||||
|
# 通过界面ID获取验证码界面中的第一个数字输入框组件
|
||||||
self.d1 = otp_screen.ids.d1
|
self.d1 = otp_screen.ids.d1
|
||||||
|
# 通过界面ID获取验证码界面中的第二个数字输入框组件
|
||||||
self.d2 = otp_screen.ids.d2
|
self.d2 = otp_screen.ids.d2
|
||||||
|
# 通过界面ID获取验证码界面中的第三个数字输入框组件
|
||||||
self.d3 = otp_screen.ids.d3
|
self.d3 = otp_screen.ids.d3
|
||||||
|
# 通过界面ID获取验证码界面中的第四个数字输入框组件
|
||||||
self.d4 = otp_screen.ids.d4
|
self.d4 = otp_screen.ids.d4
|
||||||
|
|
||||||
|
# 从根窗口中获取名为"signup"的屏幕(注册界面)
|
||||||
signup_screen = self.root.get_screen("signup")
|
signup_screen = self.root.get_screen("signup")
|
||||||
|
# 通过界面ID获取注册界面中的用户名输入框组件
|
||||||
self.user_name = signup_screen.ids.signup_name
|
self.user_name = signup_screen.ids.signup_name
|
||||||
|
# 通过界面ID获取注册界面中的PRN(可能是学号/身份标识)输入框组件
|
||||||
self.user_prn = signup_screen.ids.signup_prn
|
self.user_prn = signup_screen.ids.signup_prn
|
||||||
|
# 通过界面ID获取注册界面中的邮箱输入框组件
|
||||||
self.user_email = signup_screen.ids.signup_email
|
self.user_email = signup_screen.ids.signup_email
|
||||||
|
# 通过界面ID获取注册界面中的手机号输入框组件
|
||||||
self.user_no = signup_screen.ids.signup_no
|
self.user_no = signup_screen.ids.signup_no
|
||||||
|
# 通过界面ID获取注册界面中的密码输入框组件
|
||||||
self.user_pass = signup_screen.ids.signup_pass
|
self.user_pass = signup_screen.ids.signup_pass
|
||||||
|
# 通过界面ID获取注册界面中的所属部门/专业选择组件
|
||||||
self.signup_branch = signup_screen.ids.signup_branch
|
self.signup_branch = signup_screen.ids.signup_branch
|
||||||
|
# 通过界面ID获取注册界面中的年级/学期选择组件
|
||||||
self.signup_sem = signup_screen.ids.signup_sem
|
self.signup_sem = signup_screen.ids.signup_sem
|
||||||
|
|
||||||
|
# 从根窗口中获取名为"profile"的屏幕(个人资料界面)
|
||||||
profile_screen = self.root.get_screen("profile")
|
profile_screen = self.root.get_screen("profile")
|
||||||
|
# 通过界面ID获取个人资料界面中的姓名展示组件
|
||||||
self.profile_name = profile_screen.ids.profile_name
|
self.profile_name = profile_screen.ids.profile_name
|
||||||
|
# 通过界面ID获取个人资料界面中的邮箱展示组件
|
||||||
self.profile_email = profile_screen.ids.profile_email
|
self.profile_email = profile_screen.ids.profile_email
|
||||||
|
# 通过界面ID获取个人资料界面中的PRN(身份标识)展示组件
|
||||||
self.profile_prn = profile_screen.ids.profile_prn
|
self.profile_prn = profile_screen.ids.profile_prn
|
||||||
|
# 通过界面ID获取个人资料界面中的手机号展示组件
|
||||||
self.profile_number = profile_screen.ids.profile_number
|
self.profile_number = profile_screen.ids.profile_number
|
||||||
|
# 通过界面ID获取个人资料界面中的所属部门/专业展示组件
|
||||||
self.profile_branch = profile_screen.ids.profile_branch
|
self.profile_branch = profile_screen.ids.profile_branch
|
||||||
|
# 通过界面ID获取个人资料界面中的年级/学期展示组件
|
||||||
self.profile_semester = profile_screen.ids.profile_semester
|
self.profile_semester = profile_screen.ids.profile_semester
|
||||||
|
|
||||||
|
# 从根窗口中获取名为"profile_edit"的屏幕(个人资料编辑界面)
|
||||||
self.profile_edit_screen = self.root.get_screen("profile_edit")
|
self.profile_edit_screen = self.root.get_screen("profile_edit")
|
||||||
|
# 通过界面ID获取资料编辑界面中的姓名编辑输入框组件
|
||||||
self.edit_name = self.profile_edit_screen.ids.edit_name
|
self.edit_name = self.profile_edit_screen.ids.edit_name
|
||||||
|
# 通过界面ID获取资料编辑界面中的邮箱编辑输入框组件
|
||||||
self.edit_email = self.profile_edit_screen.ids.edit_email
|
self.edit_email = self.profile_edit_screen.ids.edit_email
|
||||||
|
# 通过界面ID获取资料编辑界面中的PRN(身份标识)编辑输入框组件
|
||||||
self.edit_prn = self.profile_edit_screen.ids.edit_prn
|
self.edit_prn = self.profile_edit_screen.ids.edit_prn
|
||||||
|
# 通过界面ID获取资料编辑界面中的手机号编辑输入框组件
|
||||||
self.edit_number = self.profile_edit_screen.ids.edit_no
|
self.edit_number = self.profile_edit_screen.ids.edit_no
|
||||||
|
# 通过界面ID获取资料编辑界面中的所属部门/专业编辑选择组件
|
||||||
self.edit_branch = self.profile_edit_screen.ids.edit_branch
|
self.edit_branch = self.profile_edit_screen.ids.edit_branch
|
||||||
|
# 通过界面ID获取资料编辑界面中的年级/学期编辑选择组件
|
||||||
self.edit_semester = self.profile_edit_screen.ids.edit_sem
|
self.edit_semester = self.profile_edit_screen.ids.edit_sem
|
||||||
|
|
||||||
|
# 从根窗口中获取名为"borrow_book"的屏幕(借书界面)
|
||||||
borrow_screen = self.root.get_screen("borrow_book")
|
borrow_screen = self.root.get_screen("borrow_book")
|
||||||
|
# 通过界面ID获取借书界面中的ISBN(图书编号)输入框组件
|
||||||
self.isbn = borrow_screen.ids.isbn
|
self.isbn = borrow_screen.ids.isbn
|
||||||
|
|
||||||
|
# 从根窗口中获取名为"return_book"的屏幕(还书界面)
|
||||||
return_book_screen = self.root.get_screen("return_book")
|
return_book_screen = self.root.get_screen("return_book")
|
||||||
|
# 通过界面ID获取还书界面中的图片与标签网格布局组件
|
||||||
self.image_label_grid = return_book_screen.ids.image_label_grid
|
self.image_label_grid = return_book_screen.ids.image_label_grid
|
||||||
|
|
||||||
|
# 从根窗口中获取名为"renew_book"的屏幕(图书续借界面)
|
||||||
renew_book_screen = self.root.get_screen("renew_book")
|
renew_book_screen = self.root.get_screen("renew_book")
|
||||||
|
# 通过界面ID获取续借界面中的网格布局组件
|
||||||
self.renew_grid = renew_book_screen.ids.renew_grid
|
self.renew_grid = renew_book_screen.ids.renew_grid
|
||||||
|
|
||||||
|
# 从根窗口中获取名为"history"的屏幕(借阅历史界面)
|
||||||
history_screen = self.root.get_screen("history")
|
history_screen = self.root.get_screen("history")
|
||||||
|
# 通过界面ID获取借阅历史界面中的网格布局组件
|
||||||
self.history_grid = history_screen.ids.history_grid
|
self.history_grid = history_screen.ids.history_grid
|
||||||
|
|
||||||
|
# 从根窗口中获取名为"searchBook"的屏幕(图书搜索界面)
|
||||||
search_book_screen = self.root.get_screen("searchBook")
|
search_book_screen = self.root.get_screen("searchBook")
|
||||||
|
# 通过界面ID获取图书搜索界面中的搜索输入框组件
|
||||||
self.search_field = search_book_screen.ids.search_field
|
self.search_field = search_book_screen.ids.search_field
|
||||||
|
# 通过界面ID获取图书搜索界面中的搜索结果网格布局组件
|
||||||
self.search_grid = search_book_screen.ids.search_grid
|
self.search_grid = search_book_screen.ids.search_grid
|
||||||
|
|
||||||
|
# 从根窗口中获取名为"recommend"的屏幕(图书推荐界面)
|
||||||
self.recommend_screen = self.root.get_screen("recommend")
|
self.recommend_screen = self.root.get_screen("recommend")
|
||||||
|
# 通过界面ID获取推荐界面中的图书输入框组件
|
||||||
self.book_input = self.recommend_screen.ids.book_input
|
self.book_input = self.recommend_screen.ids.book_input
|
||||||
|
# 通过界面ID获取推荐界面中的推荐结果网格布局组件
|
||||||
self.rec_grid = self.recommend_screen.ids.rec_grid
|
self.rec_grid = self.recommend_screen.ids.rec_grid
|
||||||
|
# 通过界面ID获取推荐界面中的推荐信息标签组件
|
||||||
self.rec_label = self.recommend_screen.ids.rec_label
|
self.rec_label = self.recommend_screen.ids.rec_label
|
||||||
|
|
||||||
#############################################@Frequently used functions##################################################
|
#############################################@Frequently used functions##################################################
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def change_cursor(is_enter):
|
def change_cursor(is_enter):
|
||||||
|
"""改变鼠标指针样式"""
|
||||||
|
# 如果is_enter为True(鼠标进入目标区域),将鼠标指针改为手型
|
||||||
if is_enter:
|
if is_enter:
|
||||||
Window.set_system_cursor('hand')
|
Window.set_system_cursor('hand')
|
||||||
|
# 否则(鼠标离开目标区域),将鼠标指针改回默认箭头样式
|
||||||
else:
|
else:
|
||||||
Window.set_system_cursor('arrow')
|
Window.set_system_cursor('arrow')
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def toggle_password_visibility(password_field, icon_button):
|
def toggle_password_visibility(password_field, icon_button):
|
||||||
|
"""切换密码输入框的可见性状态"""
|
||||||
|
# 检查密码输入框当前是否处于密码隐藏状态
|
||||||
if password_field.password:
|
if password_field.password:
|
||||||
|
# 如果是隐藏状态,切换为可见状态
|
||||||
password_field.password = False
|
password_field.password = False
|
||||||
|
# 更新图标为"eye"(眼睛图标,表示当前可见)
|
||||||
icon_button.icon = "eye"
|
icon_button.icon = "eye"
|
||||||
else:
|
else:
|
||||||
|
# 如果是可见状态,切换为隐藏状态(显示为圆点/星号)
|
||||||
password_field.password = True
|
password_field.password = True
|
||||||
|
# 更新图标为"eye-off"(闭眼图标,表示当前隐藏)
|
||||||
icon_button.icon = "eye-off"
|
icon_button.icon = "eye-off"
|
||||||
|
|
||||||
def clear_text(self):
|
def clear_text(self):
|
||||||
|
"""清空界面中所有输入框和文本组件的内容"""
|
||||||
|
# 清空图书搜索框内容
|
||||||
self.search_field.text = ""
|
self.search_field.text = ""
|
||||||
|
# 清空图书推荐输入框内容
|
||||||
self.book_input.text = ""
|
self.book_input.text = ""
|
||||||
self.username.text = ""
|
# 清空登录界面的用户名输入框内容
|
||||||
self.password.text = ""
|
# self.username.text = ""
|
||||||
|
# 清空登录界面的密码输入框内容
|
||||||
|
# self.password.text = ""
|
||||||
|
# 清空忘记密码界面的手机号输入框内容
|
||||||
self.mobile_no.text = ""
|
self.mobile_no.text = ""
|
||||||
|
# 清空验证码界面的第一个输入框内容
|
||||||
self.d1.text = ""
|
self.d1.text = ""
|
||||||
|
# 清空验证码界面的第二个输入框内容
|
||||||
self.d2.text = ""
|
self.d2.text = ""
|
||||||
|
# 清空验证码界面的第三个输入框内容
|
||||||
self.d3.text = ""
|
self.d3.text = ""
|
||||||
|
# 清空验证码界面的第四个输入框内容
|
||||||
self.d4.text = ""
|
self.d4.text = ""
|
||||||
|
# 清空书架位置名称文本内容
|
||||||
self.book_pos_name.text = ""
|
self.book_pos_name.text = ""
|
||||||
self.book_pos.text = "Example.,Book Position is 1L42 , 1 represent row no., L represent left side of row, 4 represent rack/shelves no. ,2 represent shelves step no."
|
# 重置书架位置说明文本为示例内容
|
||||||
|
self.book_pos.text = "测试内容示例"
|
||||||
|
# 再次清空手机号输入框(可能是为了确保完全清空)
|
||||||
self.mobile_no.text = ""
|
self.mobile_no.text = ""
|
||||||
|
# 清空借书界面的ISBN输入框内容
|
||||||
self.isbn.text = ''
|
self.isbn.text = ''
|
||||||
|
|
||||||
def on_press_back_arrow(self):
|
def on_press_back_arrow(self):
|
||||||
self.username.text = ""
|
# self.username.text = ""
|
||||||
self.password.text = ""
|
# self.password.text = ""
|
||||||
self.mobile_no.text = ""
|
self.mobile_no.text = ""
|
||||||
self.d1.text = ""
|
self.d1.text = ""
|
||||||
self.d2.text = ""
|
self.d2.text = ""
|
||||||
@ -340,54 +443,154 @@ class app(MDApp):
|
|||||||
|
|
||||||
###################################LoginPageWork-Start#################################################
|
###################################LoginPageWork-Start#################################################
|
||||||
|
|
||||||
# login Verification function "verify"
|
# # login Verification function "verify"
|
||||||
def verify(self, obj):
|
# def verify(self, obj):
|
||||||
status = False
|
# status = False
|
||||||
if self.username.text == "" or self.password.text == "":
|
# if self.username.text == "" or self.password.text == "":
|
||||||
check = "请输入账户和密码"
|
# check = "请输入账户和密码"
|
||||||
return self.dialog1(check)
|
# return self.dialog1(check)
|
||||||
else:
|
# else:
|
||||||
name = ""
|
# name = ""
|
||||||
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, delimiter=",")
|
# csv_reader = csv.reader(file, delimiter=",")
|
||||||
for line in csv_reader:
|
# for line in csv_reader:
|
||||||
try:
|
# try:
|
||||||
if not obj:
|
# if not obj:
|
||||||
a = (line[1] == self.username.text or line[2] == self.username.text)
|
# a = (line[1] == self.username.text or line[2] == self.username.text)
|
||||||
else:
|
# else:
|
||||||
a = (line[1] == self.edit_prn.text or line[2] == self.edit_email.text)
|
# a = (line[1] == self.edit_prn.text or line[2] == self.edit_email.text)
|
||||||
if a and line[4] == self.password.text:
|
# if a and line[4] == self.password.text:
|
||||||
status = True
|
# status = True
|
||||||
name = line[0]
|
# name = line[0]
|
||||||
prn = line[1]
|
# prn = line[1]
|
||||||
email = line[2]
|
# email = line[2]
|
||||||
number = line[3]
|
# number = line[3]
|
||||||
branch = line[5]
|
# branch = line[5]
|
||||||
semester = line[6]
|
# semester = line[6]
|
||||||
break
|
# break
|
||||||
except IndexError:
|
# except IndexError:
|
||||||
pass
|
# pass
|
||||||
except Exception as e:
|
# except Exception as e:
|
||||||
print(e)
|
# print(e)
|
||||||
|
|
||||||
if status:
|
# if status:
|
||||||
if not obj:
|
# if not obj:
|
||||||
check = "Login Successful" + "\nHello! " + name
|
# check = "Login Successful" + "\nHello! " + name
|
||||||
else:
|
# else:
|
||||||
check = "Profile Updated Successfully!"
|
# check = "Profile Updated Successfully!"
|
||||||
self.secure_profile()
|
# self.secure_profile()
|
||||||
self.root.current = "home"
|
# self.root.current = "home"
|
||||||
self.user.text = f"""[b]Hey! {name}[/b]"""
|
# self.user.text = f"""[b]Hey! {name}[/b]"""
|
||||||
self.profile_name.text = self.edit_name.text = name
|
# self.profile_name.text = self.edit_name.text = name
|
||||||
self.profile_email.text = self.edit_email.text = email
|
# self.profile_email.text = self.edit_email.text = email
|
||||||
self.profile_prn.text = self.edit_prn.text = prn
|
# self.profile_prn.text = self.edit_prn.text = prn
|
||||||
self.profile_number.text = self.edit_number.text = number
|
# self.profile_number.text = self.edit_number.text = number
|
||||||
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
|
||||||
|
|
||||||
|
# else:
|
||||||
|
# check = "Login Failed!. " + "Enter correct username and password."
|
||||||
|
# self.dialog1(check)
|
||||||
|
def update_wifi_status(self, dt):
|
||||||
|
self.check_wifi()
|
||||||
|
Clock.schedule_once(self.update_wifi_status, 1) # 5秒刷新一次
|
||||||
|
|
||||||
|
def check_wifi(self):
|
||||||
|
if platform != 'android':
|
||||||
|
self.wifi_status_text = '非Android设备'
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
from jnius import autoclass, cast
|
||||||
|
|
||||||
|
PythonActivity = autoclass('org.kivy.android.PythonActivity')
|
||||||
|
Context = autoclass('android.content.Context')
|
||||||
|
activity = PythonActivity.mActivity
|
||||||
|
|
||||||
|
WifiManager = autoclass('android.net.wifi.WifiManager')
|
||||||
|
Formatter = autoclass('android.text.format.Formatter')
|
||||||
|
|
||||||
|
wifi_service = activity.getSystemService(Context.WIFI_SERVICE)
|
||||||
|
wifi_manager = cast('android.net.wifi.WifiManager', wifi_service)
|
||||||
|
|
||||||
|
if not wifi_manager.isWifiEnabled():
|
||||||
|
self.wifi_status_text = 'WiFi 未启用'
|
||||||
|
return # 这里直接返回,不再继续取SSID等信息
|
||||||
|
|
||||||
|
wifi_info = wifi_manager.getConnectionInfo()
|
||||||
|
if wifi_info is None:
|
||||||
|
self.wifi_status_text = 'WiFi 信息不可用'
|
||||||
|
return
|
||||||
|
|
||||||
|
ssid = wifi_info.getSSID()
|
||||||
|
if ssid:
|
||||||
|
ssid_display = ssid.strip('"')
|
||||||
else:
|
else:
|
||||||
check = "Login Failed!. " + "Enter correct username and password."
|
ssid_display = "SSID Unknown"
|
||||||
self.dialog1(check)
|
|
||||||
|
ip_int = wifi_info.getIpAddress()
|
||||||
|
ip_str = Formatter.formatIpAddress(ip_int) if ip_int != 0 else "0.0.0.0"
|
||||||
|
link_speed = wifi_info.getLinkSpeed() # Mbps
|
||||||
|
bssid = wifi_info.getBSSID()
|
||||||
|
rssi = wifi_info.getRssi()
|
||||||
|
|
||||||
|
self.wifi_status_text = (
|
||||||
|
f'SSID: {ssid_display}\n'
|
||||||
|
f'IP: {ip_str}\n'
|
||||||
|
f'速度: {link_speed} Mbps\n'
|
||||||
|
f'BSSID: {bssid}\n'
|
||||||
|
f'信号: {rssi} dBm'
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.wifi_status_text = f'获取WiFi信息失败\n{e}'
|
||||||
|
|
||||||
|
def verify(self, obj=None):
|
||||||
|
try:
|
||||||
|
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()
|
||||||
|
wifi_id = wifi_info.getSSID().strip('"').lower()
|
||||||
|
else:
|
||||||
|
wifi_id = "zhizhan-2" # 非 Android 用模拟 WiFi
|
||||||
|
except Exception as e:
|
||||||
|
self.dialog1(f"获取WiFi信息失败:{e}")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open("data/Users.csv", "r", encoding="utf-8") as file:
|
||||||
|
csv_reader = csv.reader(file)
|
||||||
|
for line in csv_reader:
|
||||||
|
# 确保至少有7列数据
|
||||||
|
if len(line) < 7:
|
||||||
|
continue
|
||||||
|
|
||||||
|
csv_ssid = line[7].strip().lower() if len(line) > 7 else ""
|
||||||
|
|
||||||
|
if wifi_id == csv_ssid or wifi_id in map(str.lower, line):
|
||||||
|
name, prn, email, number, password, branch, semester = line[:7]
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
self.dialog1(f"欢迎你,{name} wifi认证成功")
|
||||||
|
return
|
||||||
|
except Exception as e:
|
||||||
|
self.dialog1(f"读取用户信息失败:{e}")
|
||||||
|
return
|
||||||
|
|
||||||
|
self.dialog1("当前WiFi认证失败,请检查网络或退出")
|
||||||
|
|
||||||
|
|
||||||
click_count = 0
|
click_count = 0
|
||||||
def login_mode(self, username_text, button_text):
|
def login_mode(self, username_text, button_text):
|
||||||
|
|||||||
2536
menu_mine.py
Normal file
2536
menu_mine.py
Normal file
File diff suppressed because it is too large
Load Diff
6
menu_page.py
Normal file
6
menu_page.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#昆仑通态组合页面设计
|
||||||
|
|
||||||
|
KUNLUN_GRAOUP_PAGE_MAIN = 20 #主选择菜单页面
|
||||||
|
KUNLUN_GRAOUP_PAGE_MENU = 5 #文字菜单页面
|
||||||
|
KUNLUN_GRAOUP_PAGE_ALARM = 7 #报警页面
|
||||||
|
KUNLUN_GRAOUP_PAGE_DEVICE_KD = 21 #馈电页面
|
||||||
109
menu_utils.py
Normal file
109
menu_utils.py
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
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
|
||||||
|
|
||||||
218
mqtt_device.py
Normal file
218
mqtt_device.py
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
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()
|
||||||
102
mqtt_object.py
Normal file
102
mqtt_object.py
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
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
|
||||||
|
|
||||||
41
print_color.py
Normal file
41
print_color.py
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
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()
|
||||||
Reference in New Issue
Block a user