调整版本,实现wifi认证

This commit is contained in:
冯佳
2025-07-25 08:08:11 +08:00
parent 97a7d848d4
commit 50f7531518
20 changed files with 5323 additions and 429 deletions

2
1.py Normal file
View File

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

BIN
assets/_img.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 450 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 870 KiB

99
comm_device.py Normal file
View 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
View 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
View 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
View 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
View 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
View 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("与保护器连接超时, 重新进行连接")

View File

@ -1,4 +1,3 @@
Student_Name,Student_PRN,Student_Email,Student_Whatsapp_no,Student_pass,Branch,Semester
Ravikiran Yavalkar,12444444,abcd.efgh24@gmail.com,9607111115,India,AIML-F,III
jfen,55775577,test@example.com,NULL,China,AIML-F,III
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,NULL
测试用户,11111111,test@example.com,NULL,0000,测试,III,zhizhan-2

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

127
device_conf.py Normal file
View 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

769
kv/app.kv

File diff suppressed because it is too large Load Diff

313
main.py
View File

@ -1,5 +1,5 @@
from kivy.properties import ObjectProperty
from kivy.properties import ObjectProperty, StringProperty
from datetime import datetime, timedelta
from kivy.core.text import LabelBase
from kivy.metrics import dp
@ -24,16 +24,21 @@ from kivymd.uix.dialog import MDDialog
from kivymd.uix.label import MDLabel
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="BPoppins", 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="RPac", fn_regular="fonts/Chinese/msyh.ttf")
def get_books():
pass
class MainScreen(Screen):
pass
@ -41,7 +46,7 @@ class LoginScreen(Screen):
login = ObjectProperty(None)
class SignUpScreen(Screen):
signup: ObjectProperty = ObjectProperty(None)
signup = ObjectProperty(None)
class ForgetPassScreen(Screen):
forget_Password = ObjectProperty(None)
@ -82,7 +87,10 @@ class RenewBookScreen(Screen):
class HistoryScreen(Screen):
history = ObjectProperty(None)
# Window.size = (dp(360), dp(680))
class app(MDApp):
wifi_status_text = StringProperty("")
def __init__(self):
super().__init__()
self.signup_sem = None
@ -140,6 +148,19 @@ class app(MDApp):
self.password = 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):
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(HistoryScreen(name="history"))
Clock.schedule_once(self.update_wifi_status, 2)
return screen_manager
#############################################ALL INPUT TEXT############################################################
def on_start(self):
# 从根窗口中获取名为"login"的屏幕(登录界面)
login_screen = self.root.get_screen("login")
self.username = login_screen.ids.username
self.password = login_screen.ids.password
# 通过界面ID获取登录界面中的用户名输入框组件
# self.username = login_screen.ids.username
# 通过界面ID获取登录界面中的密码输入框组件
# self.password = login_screen.ids.password
# 从根窗口中获取名为"home"的屏幕(主界面)
home_screen = self.root.get_screen("home")
# 通过界面ID获取主界面中的用户信息组件
self.user = home_screen.ids.user
# 通过界面ID获取主界面中的书架位置名称组件
self.book_pos_name = home_screen.ids.book_pos_name
# 通过界面ID获取主界面中的书架位置组件
self.book_pos = home_screen.ids.book_pos
# 从根窗口中获取名为"forgot_pass"的屏幕(忘记密码界面)
forgot_pass_screen = self.root.get_screen("forgot_pass")
# 通过界面ID获取忘记密码界面中的手机号输入框组件
self.mobile_no = forgot_pass_screen.ids.forgotPass_no
# 从根窗口中获取名为"reset_pass"的屏幕(重置密码界面)
reset_pass_screen = self.root.get_screen("reset_pass")
# 通过界面ID获取重置密码界面中的新密码输入框组件
self.new_pass = reset_pass_screen.ids.new_pass
# 通过界面ID获取重置密码界面中的确认新密码输入框组件
self.new_pass1 = reset_pass_screen.ids.new_pass1
# 从根窗口中获取名为"otp"的屏幕(验证码界面)
otp_screen = self.root.get_screen("otp")
# 通过界面ID获取验证码界面中的"验证"按钮组件
self.otp_button = otp_screen.ids.otp_button
# 通过界面ID获取验证码界面中的"返回"按钮组件
self.back_button = otp_screen.ids.back_button
# 通过界面ID获取验证码界面中的第一个数字输入框组件
self.d1 = otp_screen.ids.d1
# 通过界面ID获取验证码界面中的第二个数字输入框组件
self.d2 = otp_screen.ids.d2
# 通过界面ID获取验证码界面中的第三个数字输入框组件
self.d3 = otp_screen.ids.d3
# 通过界面ID获取验证码界面中的第四个数字输入框组件
self.d4 = otp_screen.ids.d4
# 从根窗口中获取名为"signup"的屏幕(注册界面)
signup_screen = self.root.get_screen("signup")
# 通过界面ID获取注册界面中的用户名输入框组件
self.user_name = signup_screen.ids.signup_name
# 通过界面ID获取注册界面中的PRN可能是学号/身份标识)输入框组件
self.user_prn = signup_screen.ids.signup_prn
# 通过界面ID获取注册界面中的邮箱输入框组件
self.user_email = signup_screen.ids.signup_email
# 通过界面ID获取注册界面中的手机号输入框组件
self.user_no = signup_screen.ids.signup_no
# 通过界面ID获取注册界面中的密码输入框组件
self.user_pass = signup_screen.ids.signup_pass
# 通过界面ID获取注册界面中的所属部门/专业选择组件
self.signup_branch = signup_screen.ids.signup_branch
# 通过界面ID获取注册界面中的年级/学期选择组件
self.signup_sem = signup_screen.ids.signup_sem
# 从根窗口中获取名为"profile"的屏幕(个人资料界面)
profile_screen = self.root.get_screen("profile")
# 通过界面ID获取个人资料界面中的姓名展示组件
self.profile_name = profile_screen.ids.profile_name
# 通过界面ID获取个人资料界面中的邮箱展示组件
self.profile_email = profile_screen.ids.profile_email
# 通过界面ID获取个人资料界面中的PRN身份标识展示组件
self.profile_prn = profile_screen.ids.profile_prn
# 通过界面ID获取个人资料界面中的手机号展示组件
self.profile_number = profile_screen.ids.profile_number
# 通过界面ID获取个人资料界面中的所属部门/专业展示组件
self.profile_branch = profile_screen.ids.profile_branch
# 通过界面ID获取个人资料界面中的年级/学期展示组件
self.profile_semester = profile_screen.ids.profile_semester
# 从根窗口中获取名为"profile_edit"的屏幕(个人资料编辑界面)
self.profile_edit_screen = self.root.get_screen("profile_edit")
# 通过界面ID获取资料编辑界面中的姓名编辑输入框组件
self.edit_name = self.profile_edit_screen.ids.edit_name
# 通过界面ID获取资料编辑界面中的邮箱编辑输入框组件
self.edit_email = self.profile_edit_screen.ids.edit_email
# 通过界面ID获取资料编辑界面中的PRN身份标识编辑输入框组件
self.edit_prn = self.profile_edit_screen.ids.edit_prn
# 通过界面ID获取资料编辑界面中的手机号编辑输入框组件
self.edit_number = self.profile_edit_screen.ids.edit_no
# 通过界面ID获取资料编辑界面中的所属部门/专业编辑选择组件
self.edit_branch = self.profile_edit_screen.ids.edit_branch
# 通过界面ID获取资料编辑界面中的年级/学期编辑选择组件
self.edit_semester = self.profile_edit_screen.ids.edit_sem
# 从根窗口中获取名为"borrow_book"的屏幕(借书界面)
borrow_screen = self.root.get_screen("borrow_book")
# 通过界面ID获取借书界面中的ISBN图书编号输入框组件
self.isbn = borrow_screen.ids.isbn
# 从根窗口中获取名为"return_book"的屏幕(还书界面)
return_book_screen = self.root.get_screen("return_book")
# 通过界面ID获取还书界面中的图片与标签网格布局组件
self.image_label_grid = return_book_screen.ids.image_label_grid
# 从根窗口中获取名为"renew_book"的屏幕(图书续借界面)
renew_book_screen = self.root.get_screen("renew_book")
# 通过界面ID获取续借界面中的网格布局组件
self.renew_grid = renew_book_screen.ids.renew_grid
# 从根窗口中获取名为"history"的屏幕(借阅历史界面)
history_screen = self.root.get_screen("history")
# 通过界面ID获取借阅历史界面中的网格布局组件
self.history_grid = history_screen.ids.history_grid
# 从根窗口中获取名为"searchBook"的屏幕(图书搜索界面)
search_book_screen = self.root.get_screen("searchBook")
# 通过界面ID获取图书搜索界面中的搜索输入框组件
self.search_field = search_book_screen.ids.search_field
# 通过界面ID获取图书搜索界面中的搜索结果网格布局组件
self.search_grid = search_book_screen.ids.search_grid
# 从根窗口中获取名为"recommend"的屏幕(图书推荐界面)
self.recommend_screen = self.root.get_screen("recommend")
# 通过界面ID获取推荐界面中的图书输入框组件
self.book_input = self.recommend_screen.ids.book_input
# 通过界面ID获取推荐界面中的推荐结果网格布局组件
self.rec_grid = self.recommend_screen.ids.rec_grid
# 通过界面ID获取推荐界面中的推荐信息标签组件
self.rec_label = self.recommend_screen.ids.rec_label
#############################################@Frequently used functions##################################################
@staticmethod
def change_cursor(is_enter):
"""改变鼠标指针样式"""
# 如果is_enter为True鼠标进入目标区域将鼠标指针改为手型
if is_enter:
Window.set_system_cursor('hand')
# 否则(鼠标离开目标区域),将鼠标指针改回默认箭头样式
else:
Window.set_system_cursor('arrow')
@staticmethod
def toggle_password_visibility(password_field, icon_button):
"""切换密码输入框的可见性状态"""
# 检查密码输入框当前是否处于密码隐藏状态
if password_field.password:
# 如果是隐藏状态,切换为可见状态
password_field.password = False
# 更新图标为"eye"(眼睛图标,表示当前可见)
icon_button.icon = "eye"
else:
# 如果是可见状态,切换为隐藏状态(显示为圆点/星号)
password_field.password = True
# 更新图标为"eye-off"(闭眼图标,表示当前隐藏)
icon_button.icon = "eye-off"
def clear_text(self):
"""清空界面中所有输入框和文本组件的内容"""
# 清空图书搜索框内容
self.search_field.text = ""
# 清空图书推荐输入框内容
self.book_input.text = ""
self.username.text = ""
self.password.text = ""
# 清空登录界面的用户名输入框内容
# self.username.text = ""
# 清空登录界面的密码输入框内容
# self.password.text = ""
# 清空忘记密码界面的手机号输入框内容
self.mobile_no.text = ""
# 清空验证码界面的第一个输入框内容
self.d1.text = ""
# 清空验证码界面的第二个输入框内容
self.d2.text = ""
# 清空验证码界面的第三个输入框内容
self.d3.text = ""
# 清空验证码界面的第四个输入框内容
self.d4.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 = ""
# 清空借书界面的ISBN输入框内容
self.isbn.text = ''
def on_press_back_arrow(self):
self.username.text = ""
self.password.text = ""
# self.username.text = ""
# self.password.text = ""
self.mobile_no.text = ""
self.d1.text = ""
self.d2.text = ""
@ -340,54 +443,154 @@ class app(MDApp):
###################################LoginPageWork-Start#################################################
# login Verification function "verify"
def verify(self, obj):
status = False
if self.username.text == "" or self.password.text == "":
check = "请输入账户和密码"
return self.dialog1(check)
else:
name = ""
with open('data/Users.csv', "r", encoding="utf-8") as file:
csv_reader = csv.reader(file, delimiter=",")
for line in csv_reader:
try:
if not obj:
a = (line[1] == self.username.text or line[2] == self.username.text)
else:
a = (line[1] == self.edit_prn.text or line[2] == self.edit_email.text)
if a and line[4] == self.password.text:
status = True
name = line[0]
prn = line[1]
email = line[2]
number = line[3]
branch = line[5]
semester = line[6]
break
except IndexError:
pass
except Exception as e:
print(e)
# # login Verification function "verify"
# def verify(self, obj):
# status = False
# if self.username.text == "" or self.password.text == "":
# check = "请输入账户和密码"
# return self.dialog1(check)
# else:
# name = ""
# with open('data/Users.csv', "r", encoding="utf-8") as file:
# csv_reader = csv.reader(file, delimiter=",")
# for line in csv_reader:
# try:
# if not obj:
# a = (line[1] == self.username.text or line[2] == self.username.text)
# else:
# a = (line[1] == self.edit_prn.text or line[2] == self.edit_email.text)
# if a and line[4] == self.password.text:
# status = True
# name = line[0]
# prn = line[1]
# email = line[2]
# number = line[3]
# branch = line[5]
# semester = line[6]
# break
# except IndexError:
# pass
# except Exception as e:
# print(e)
if status:
if not obj:
check = "Login Successful" + "\nHello! " + name
else:
check = "Profile Updated Successfully!"
self.secure_profile()
self.root.current = "home"
self.user.text = f"""[b]Hey! {name}[/b]"""
self.profile_name.text = self.edit_name.text = name
self.profile_email.text = self.edit_email.text = email
self.profile_prn.text = self.edit_prn.text = prn
self.profile_number.text = self.edit_number.text = number
self.profile_semester.text = self.edit_semester.text = semester
self.profile_branch.text = self.edit_branch.text = branch
# if status:
# if not obj:
# check = "Login Successful" + "\nHello! " + name
# else:
# check = "Profile Updated Successfully!"
# self.secure_profile()
# self.root.current = "home"
# self.user.text = f"""[b]Hey! {name}[/b]"""
# self.profile_name.text = self.edit_name.text = name
# self.profile_email.text = self.edit_email.text = email
# self.profile_prn.text = self.edit_prn.text = prn
# self.profile_number.text = self.edit_number.text = number
# self.profile_semester.text = self.edit_semester.text = semester
# self.profile_branch.text = self.edit_branch.text = branch
# else:
# check = "Login Failed!. " + "Enter correct username and password."
# self.dialog1(check)
def update_wifi_status(self, dt):
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:
check = "Login Failed!. " + "Enter correct username and password."
self.dialog1(check)
ssid_display = "SSID Unknown"
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
def login_mode(self, username_text, button_text):

2536
menu_mine.py Normal file

File diff suppressed because it is too large Load Diff

6
menu_page.py Normal file
View 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
View 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
View 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
View 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
View 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()