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

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

2
1.py
View File

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

View File

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

View File

@ -1,625 +0,0 @@
from comm_device import class_comm_device
from comm_thread import class_comm_master_thread
from mqtt_device import class_comm_mqtt_thread, class_comm_mqtt_interface
import time
import comm_device
from threading import Thread
import menu_utils as utils
import struct
import math
import json
import random
from enum import Enum
from comm_protocol import class_protocol
from mqtt_object import class_mqtt_info_object
from print_color import *
from comm_protocol_modbus import class_protocol_modbus
BASE_FAIL_DELAY = 1000 # 通信失败基础延迟(毫秒)
MAX_FAIL_DELAY = 30000 # 最大通信失败延迟(毫秒)
BASE_GLOBAL_FAIL_DELAY = 500 # 全局通信失败基础延迟(毫秒)
MAX_GLOBAL_FAIL_DELAY = 60000 # 最大全局通信失败延迟(毫秒)
DEFAULT_TIMEOUT = 20 # 默认超时时间(毫秒)
# MAX_FAIL_COUNT = 15 # 最大失败次数
class class_comm_table_item() :
def __init__(self, name = "", reg_addr = 0, reg_count = 0, cycle = 0, mqtt_pack = None, remap_addr = 0):
self.reg_addr = reg_addr
self.reg_count = reg_count
self.cycle = cycle
self.name = name
self.remain_cycle = 0
self.comm_fail_delay = 0
self.comm_count = 0
self.comm_fail_count = 0
self.comm_success_count = 0
self.comm_success = False
self.comm_trigger = False
self.mqtt_pack = mqtt_pack
self.remap_addr = remap_addr
self.flush_request_delay = 0
self.base_time = time.time()
self.reg_buf = []
self.comm_fail_delay = 0 # 通信失败延迟
def in_range(self, reg_addr, reg_count) :
reg_addr_begin = self.reg_addr
reg_addr_end = self.reg_addr + self.reg_count
if reg_addr >= reg_addr_begin and reg_addr + reg_count <= reg_addr_end :
return True
return False
def update_item_value(self, reg_addr, reg_count, new_value) :
if self.in_range(reg_addr, reg_count) and len(self.reg_buf) :
reg_off = reg_addr - self.reg_addr
new_reg = list(self.reg_buf)
for i in range(reg_count) :
new_reg[reg_off + i] = new_value[i]
self.reg_buf = new_reg
self.comm_trigger = True
def read_register_count(self, reg_addr, reg_count) :
reg_addr_begin = self.reg_addr
reg_addr_end = self.reg_addr + self.reg_count
result = None
if reg_addr >= reg_addr_begin and reg_addr + reg_count <= reg_addr_end :
if len(self.reg_buf) >= self.reg_count:
result = []
reg_off = reg_addr - self.reg_addr
for i in range(reg_count):
result.append(self.reg_buf[reg_off + i])
return result
#为了防止阻塞 mqtt订阅流程, 采用class_mqtt_process_thread线程来处理 接收到的主题与消息
class class_mqtt_process_thread(Thread):
def __init__(self,
parent_device,
topic : str = None,
message : str = None,
):
Thread.__init__(self)
self.topic = topic
self.message = message
self.parent_device : class_modbus_comm_device = parent_device
def run(self):
self.parent_device.on_process_topic_message(self.topic, self.message)
#modbus通讯设备, 管理modbus主机通讯, 实现mqtt客户端
class class_modbus_comm_device(class_comm_device, class_comm_mqtt_interface) :
def __init__(self,
comm_addr = 0,
device_remap = None,
comm_table = None,
func_comm_trigger = None,
mqtt_thread : class_comm_mqtt_thread = None,
protocol : class_protocol_modbus = None,
mqtt_info_object : class_mqtt_info_object = None,
unique_name = None,
action_process_func = None, #action处理函数
request_alarm_func = None, #故障查询函数
alias_table = None):
class_comm_device.__init__(self, comm_addr, device_remap, unique_name)
class_comm_mqtt_interface.__init__(self)
self.comm_loop_count = 0
self.comm_index = 0
self.mqtt_thread = mqtt_thread
self.comm_device_comm_active = True #设置该设备是否需要通讯
self.comm_fail_delay = 0 #通讯延时
self.func_comm_trigger = func_comm_trigger
self.protocol : class_protocol_modbus = protocol
self.mqtt_info_object : class_mqtt_info_object = mqtt_info_object
self.unique_name : str = unique_name
self.action_process_func = action_process_func
self.request_alarm_func = request_alarm_func
self.alias_table = alias_table
self.comm_table = comm_table
self.comm_table_item_list : class_comm_table_item = [] #list of class_comm_table_item
self.create_comm_item_list(comm_table)
def modbus_regs_flush(self, reg_addr, reg_count, values) :
for each in self.comm_table_item_list:
comm_item : class_comm_table_item = each
comm_item.update_item_value(reg_addr, reg_count, values)
def create_comm_item_list(self, comm_table : class_comm_table_item) :
self.comm_table_item_list = []
if comm_table != None :
for comm_item_dict in comm_table :
_name = utils.dict_or_object_get_attr(comm_item_dict, "name", " ")
_reg_addr = utils.dict_or_object_get_attr(comm_item_dict, "reg_addr", 0)
_reg_count = utils.dict_or_object_get_attr(comm_item_dict, "reg_count", 0)
_cycle = utils.dict_or_object_get_attr(comm_item_dict, "cycle", 0)
_mqtt_pack = utils.dict_or_object_get_attr(comm_item_dict, "mqtt_pack", None)
self.comm_table_item_list.append(class_comm_table_item(name = _name,
reg_addr =_reg_addr,
reg_count = _reg_count,
cycle = _cycle,
mqtt_pack = _mqtt_pack))
#@override, mqtt on connect
def on_connect(self, mqtt_thread, userdata, flags, rc) :
if rc == 0 :
#获取线程类 class_comm_mqtt_thread)
mqtt_comm_thread : class_comm_mqtt_thread= mqtt_thread
#订阅相关主题, 获取参数, 修改参数, 获取测量值, 获取报警信息
mqtt_comm_thread.subscribe("request/param/info/#")
mqtt_comm_thread.subscribe("request/param/modify/#")
mqtt_comm_thread.subscribe("request/measure/#")
mqtt_comm_thread.subscribe("request/status/#")
mqtt_comm_thread.subscribe("request/alarm/#")
mqtt_comm_thread.subscribe("request/action/#")
mqtt_comm_thread.subscribe("request/alias/#")
def alias_table_convert(self) :
new_list_dict = []
for each_dict in self.alias_table :
for key_info, value_dict in each_dict.items() :
if isinstance(key_info, Enum) :
name = key_info.name
else :
name = key_info
new_dict = {}
for dict_key, dict_value in value_dict.items() :
if isinstance(dict_key, Enum) :
dict_key_value = dict_key.value[0]
else :
dict_key_value = dict_key
new_dict[dict_key_value] = dict_value
new_list_dict.append({name : new_dict})
return new_list_dict
#该函数在线程里面调用, 不会阻塞mqtt处理订阅的消息
def on_process_topic_message(self, topic, message) :
try :
if isinstance(message, bytes) :
json_dict = json.loads(message.decode('utf-8'))
else :
json_dict = json.loads(message.encode('utf-8'))
print_normal_msg(f"有效的mqtt(topic = {topic}, message = {message})")
except Exception as e:
print_error_msg(f"{str(e)}无效的mqtt(topic = {topic}, message = {message})")
json_dict = None
mqtt_info_name = utils.dict_or_object_get_attr(json_dict, "name", None)
value_str = utils.dict_or_object_get_attr(json_dict, "value", math.nan)
# if mqtt_info_name != None :
# mqtt_menu_item = self.mqtt_info_object.search_menu_item(mqtt_info_name)
# else :
# mqtt_menu_item = None
if mqtt_info_name != None:
mqtt_menu_item = self.mqtt_info_object.search_menu_item(mqtt_info_name)
else :
mqtt_menu_item = None
print_normal_msg(f"执行操作: topic={topic}, mqtt_info_name={mqtt_info_name}")
if "request/alias" in topic : #获取别名
new_alias_table = self.alias_table_convert()
if mqtt_info_name == None :
msg : str = json.dumps(new_alias_table, ensure_ascii=False)
if self.mqtt_thread != None :
self.mqtt_thread.publish("response/alias/" + self.unique_name, msg)
else :
search_dict = None
for each_dict in new_alias_table :
if mqtt_info_name in each_dict.keys() :
search_dict = each_dict
break
if search_dict :
msg : str = json.dumps(search_dict, ensure_ascii=False)
else :
msg : str = '{"%s" : None}'%(mqtt_info_name)
if self.mqtt_thread != None :
self.mqtt_thread.publish("response/alias/" + self.unique_name, msg)
elif "request/alarm" in topic: #故障查询
if self.request_alarm_func != None:
fvalue = utils.dict_or_object_get_attr(json_dict, "index", math.nan)
if isinstance(fvalue, str) :
fvalue = float(fvalue)
if fvalue != math.nan :
alarm_index = round(fvalue)
self.request_alarm_func(self, alarm_index)
elif "request/action" in topic :
if self.action_process_func :
self.action_process_func(self, mqtt_menu_item, value_str)
elif "request/param/info" in topic: #读取参数
_scale = utils.dict_or_object_get_attr(mqtt_menu_item, "scale", 1.0)
_offset = utils.dict_or_object_get_attr(mqtt_menu_item, "offset", 0.0)
_comm_str = utils.dict_or_object_get_attr(mqtt_menu_item, "addr", None)
_alias_name = utils.dict_or_object_get_attr(mqtt_menu_item, "alias", None)
if isinstance(_alias_name, Enum):
_alias_name = _alias_name.name
_format = utils.dict_or_object_get_attr(mqtt_menu_item, "format", "%f")
_fvalue = math.nan
if _comm_str :
_fvalue = self.protocol.read_float(device_addr = self.comm_addr,
comm_str = _comm_str,
scale = _scale,
offset = _offset
)
# if _alias_name != None :
# # if _fvalue == None or _fvalue == math.nan:
# # _format_str = "NAN"
# if _fvalue is None or math.isnan(_fvalue):
# _fvalue = 0
# else :
# _format_str = "%d"%(round(_fvalue))
# if self.mqtt_thread != None :
# self.mqtt_thread.publish("response/param/info/" + self.unique_name, '{"%s" : {"alias" : "%s", "value": "%s"}}'%(mqtt_info_name, _alias_name, _format_str))
# else :
# _format_str = _format%(_fvalue)
# if self.mqtt_thread != None :
# self.mqtt_thread.publish("response/param/info/" + self.unique_name, '{"%s" : "%s"}'%(mqtt_info_name, _format_str))
# else:
# print_warning_msg(f"未处理的请求:{topic, json_dict}")
if _alias_name is not None:
# 处理 _fvalue 的情况
if _fvalue is None or math.isnan(_fvalue):
_fvalue = 0
_format_str = "%d" % (round(_fvalue))
else:
# 当 _alias_name 为 None 时处理
_format_str = _format % (_fvalue) if _fvalue is not None and not math.isnan(_fvalue) else "UNKNOWN"
# 发布消息
if self.mqtt_thread is not None:
if _alias_name is not None:
self.mqtt_thread.publish("response/param/info/" + self.unique_name,
'{"%s" : {"alias" : "%s", "value": "%s"}}' % (mqtt_info_name, _alias_name, _format_str))
else:
self.mqtt_thread.publish("response/param/info/" + self.unique_name,
'{"%s" : "%s"}' % (mqtt_info_name, _format_str))
else:
print_warning_msg(f"未处理的请求:{topic, message}")
elif "request/param/modify" in topic : #写入参数
mqtt_menu_item = self.comm_item_item_adjust(mqtt_menu_item)
scale = utils.dict_or_object_get_attr(mqtt_menu_item, "scale", 1.0)
format_str = utils.dict_or_object_get_attr(mqtt_menu_item, "format", "%1.0f")
offset = utils.dict_or_object_get_attr(mqtt_menu_item, "offset", 0.0)
_comm_str = utils.dict_or_object_get_attr(mqtt_menu_item, "addr", None)
_fvalue = value_str
if isinstance(_fvalue, str) :
modify_fvalue = float(_fvalue)
else :
modify_fvalue = _fvalue
_fvalue_origin = modify_fvalue / scale - offset
if _comm_str :
result = self.protocol.write_float(device_addr = self.comm_addr,
comm_str = _comm_str,
fvalue = _fvalue_origin)
if self.mqtt_thread != None :
self.mqtt_thread.publish("response/param/modify/" + self.unique_name, '{"name" : "%s", "result":%d}'%(mqtt_info_name, result))
else:
print_warning_msg(f"未处理的请求:{topic, json_dict}")
elif mqtt_menu_item == None :
print_warning_msg(f"mqtt无法找到对应信息项(topic = {topic}, message = {json_dict})")
else :
pass
#@override, mqtt subscirbe message process
def on_message(self, mqtt_thread, topic, message) :
if self.mqtt_info_object == None :
return False
if "request/alias" in topic : #获得别名不需要协议通讯, 此处直接处理即可
self.on_process_topic_message(topic, message)
elif "request" in topic :
new_thread : class_mqtt_process_thread = class_mqtt_process_thread(self, topic, message)
new_thread.start()
return True
#通过通讯字符串与scale类型, 获取浮点格式实际值, 通讯失败时返回None
def comm_item_get_value_float(self, comm_addr_str, scale = 1.0, offset = 0) :
reg_value = None
if comm_addr_str != None:
reg_addr, bit, reg_count = utils.comm_str_unpack(comm_addr_str)
is_float = True if "#f" in comm_addr_str else False
is_big_endian = True if "#>" in comm_addr_str else False
is_sign = True if "#s" in comm_addr_str else False
if reg_count == 1 :
if is_sign :
reg_value = self.read_register_s16(reg_addr)
else :
reg_value = self.read_register_u16(reg_addr)
if reg_count == 2 :
if is_float :
reg_value = self.read_register_f32(reg_addr, is_big_endian)
elif is_sign :
reg_value = self.read_register_s32(reg_addr, is_big_endian)
else :
reg_value = self.read_register_u32(reg_addr, is_big_endian)
if bit >= 0 and reg_value != None:
reg_value = (reg_value >> bit)
reg_value = reg_value & (0xFFFFFFFF >> (32 - reg_count))
if reg_value != None :
reg_value = reg_value * scale + offset
return reg_value
#有些菜单项的scale, format或其他属性可以动态调节, 返回调整后的菜单项或 原菜单项
def comm_item_item_adjust(self, menu_item) :
adjust_menu_item = None
func_item_adjust = utils.dict_or_object_get_attr(menu_item, "adjust", None)
if func_item_adjust != None :
adjust_menu_item = func_item_adjust(self, menu_item)
if adjust_menu_item == None :
adjust_menu_item = menu_item
return adjust_menu_item
#通过菜单项中 特定的回调函数, 使每个菜单项可以独立处理一些任务, 比如添加自定义key, value
def comm_call_menu_item_func(self, menu_item_dict, mqtt_dict : dict = None) :
call_func = utils.dict_or_object_get_attr(menu_item_dict, "call", None)
if call_func != None :
call_func(self, menu_item_dict, mqtt_dict)
#通过菜单项中 获取浮点格式实际值, reg_value在通讯失败时返回None, 需要额外检查
def comm_get_value_from_menu_item(self, menu_item_dict) :
menu_item_dict = self.comm_item_item_adjust(menu_item_dict)
scale = utils.dict_or_object_get_attr(menu_item_dict, "scale", 1.0)
offset = utils.dict_or_object_get_attr(menu_item_dict, "offset", 0.0)
comm_addr_str = utils.dict_or_object_get_attr(menu_item_dict, "addr", None)
reg_value = self.comm_item_get_value_float(comm_addr_str, scale, offset)
return reg_value
#通过mqtt名称中 获取浮点格式实际值, reg_value在通讯失败时返回None, 需要额外检查
def comm_get_value_from_mqtt_name(self, mqtt_name : str) :
if mqtt_name in self.mqtt_info_object.mqtt_dict.keys() :
mqtt_menu_item = self.mqtt_info_object.mqtt_dict[mqtt_name]
return self.comm_get_value_from_menu_item(mqtt_menu_item)
return None
#触发一组mqtt菜单组的 key, value字典
def comm_item_trigger_mqtt_pack(self, mqtt_pack) :
if mqtt_pack == None :
return None
mqtt_dict = {}
for pack_item_dict in mqtt_pack :
self.comm_call_menu_item_func(pack_item_dict, mqtt_dict)
reg_value = self.comm_get_value_from_menu_item(pack_item_dict)
format_str = utils.dict_or_object_get_attr(pack_item_dict, "format", "%1.0f")
mqtt_name = utils.dict_or_object_get_attr(pack_item_dict, "mqtt", None)
min_value = utils.dict_or_object_get_attr(pack_item_dict, "min", None)
max_value = utils.dict_or_object_get_attr(pack_item_dict, "max", None)
# 检查是否有最小值和最大值的约束,并验证 reg_value 是否在范围内
if reg_value is not None:
if min_value is not None and reg_value < min_value:
reg_value = min_value # 如果小于最小值,设为最小值
if max_value is not None and reg_value > max_value:
reg_value = max_value # 如果大于最大值,设为最大值
if mqtt_name != None and reg_value != None:
mqtt_dict[mqtt_name] = format_str%(reg_value)
return mqtt_dict
def trigger_comm(self, name):
for item in self.comm_table_item_list:
comm_table_item : class_comm_table_item = item
if comm_table_item.name == name :
comm_table_item.comm_trigger = True
return
def read_register_count(self, reg_addr, reg_count) :
result = None
for item in self.comm_table_item_list :
comm_table_item : class_comm_table_item = item
if comm_table_item.in_range(reg_addr, reg_count) :
if comm_table_item.reg_buf == None :
comm_table_item.comm_trigger = True
elif len(comm_table_item.reg_buf) == 0 :
comm_table_item.comm_trigger = True
else :
result = comm_table_item.read_register_count(reg_addr, reg_count)
if result != None :
break
return result
#@device_comm_flush_request override
def device_comm_flush_request(self, reg_addr, reg_count, cycle = 3000) :
for item in self.comm_table_item_list :
comm_table_item : class_comm_table_item = item
if comm_table_item.in_range(reg_addr, reg_count) :
if comm_table_item.flush_request_delay == 0 :
comm_table_item.flush_request_delay = cycle
#@read_register override
def read_register(self, comm_str) :
reg_addr, bit, reg_count = utils.comm_str_unpack(comm_str)
if reg_count == 0:
return None
return self.read_register_count(reg_addr, reg_count)
#@read_register override
#bool write_register(int reg_addr, reg_count, uint16_t value[])
def write_register(self, reg_addr, reg_count, new_value, timeout_ms = comm_device.DEFAULT_COMM_WRITE_TIMEOUT_MS) :
try:
_protocol : class_protocol_modbus = self.protocol
_old_timeout = _protocol.get_timeout()
_protocol.set_timeout(timeout_ms / BASE_FAIL_DELAY)
write_result = _protocol.modbus_write_regs(self.comm_addr,
reg_addr,
reg_count,
new_value,
)
if write_result == True:
for item in self.comm_table_item_list :
comm_item : class_comm_table_item = item
if comm_item.in_range(reg_addr, reg_count) :
comm_item.update_item_value(reg_addr, reg_count, new_value)
except Exception :
write_result = False
finally :
_protocol.set_timeout(_old_timeout)
return write_result
#bool write_bit_register(int reg_addr, uint16_t value[])
#bool write_bit_register(int reg_addr, uint16_t value)
def write_bit_register(self, reg_addr, value, timeout_ms = comm_device.DEFAULT_COMM_WRITE_TIMEOUT_MS) :
write_result = False
_protocol : class_protocol_modbus = self.protocol
try :
old_timeout = _protocol.get_timeout()
_protocol.set_timeout(timeout_ms / BASE_FAIL_DELAY)
_protocol.modbus_write_regs(self.comm_addr,
reg_addr % 100000 + 100000,
len(value),
value)
write_result = True
except Exception as e:
write_result = False
print_error_msg(f"modbus寄存器无法写入{str(e)}")
finally :
_protocol.set_timeout(old_timeout)
return write_result
#@override cycle function
def update_time_difference(self, table_item):
cur_time = time.time() # 获取当前时间
time_dif = cur_time - table_item.base_time # 计算时间差
table_item.base_time = cur_time # 更新基准时间
return time_dif * BASE_FAIL_DELAY # 返回时间差(毫秒)
def should_trigger_comm(self, table_item, time_dif_ms):
comm_trigger = False # 初始化通信触发标志为False
if self.func_comm_trigger:
comm_trigger = self.func_comm_trigger(self, table_item) # 调用通信触发函数
if table_item.remain_cycle > time_dif_ms:
table_item.remain_cycle -= time_dif_ms # 减少剩余周期
else:
table_item.remain_cycle = table_item.cycle + table_item.comm_fail_delay + self.comm_fail_delay # 重新计算剩余周期
if table_item.cycle > 0 or (table_item.cycle == 0 and not table_item.comm_success):
comm_trigger = True # 如果周期大于0或周期为0但上次通信不成功则触发通信
if table_item.flush_request_delay > 0:
if table_item.flush_request_delay > time_dif_ms:
table_item.flush_request_delay -= time_dif_ms # 减少刷新请求延迟
else:
table_item.flush_request_delay = 0 # 重置刷新请求延迟
comm_trigger = True # 触发通信
if not comm_trigger:
comm_trigger = table_item.comm_trigger # 如果前面没有触发通信,则使用通信触发标志
return comm_trigger # 返回是否触发通信
def execute_communication(self, table_item):
read_result = [] # 初始化读取结果
timeout_ms = getattr(table_item, "通信超时", DEFAULT_TIMEOUT) # 获取超时时间默认500ms
_protocol = self.protocol # 获取协议对象
try:
old_timeout = _protocol.get_timeout() # 保存旧的超时时间
_protocol.set_timeout(timeout_ms / BASE_FAIL_DELAY) # 设置新的超时时间
read_result = _protocol.modbus_read_regs(self.comm_addr, table_item.reg_addr, table_item.reg_count) # 执行Modbus读取
if len(read_result) != table_item.reg_count:
read_result = [] # 如果读取结果数量不匹配,视为读取失败
except Exception as e:
print_error_msg("周期读异常: ", e, f"modbus_read_regs(addr={self.comm_addr}, reg={table_item.reg_addr}, count={table_item.reg_count})") # 打印错误信息
finally:
_protocol.set_timeout(old_timeout) # 恢复旧的超时时间
return read_result # 返回读取结果
def handle_comm_result(self, table_item, read_result):
if not read_result: # 如果读取结果为空,表示通信失败
self.handle_comm_failure(table_item)
else: # 否则通信成功
self.handle_comm_success(table_item, read_result)
table_item.comm_trigger = False # 重置通信触发标志
def handle_comm_failure(self, table_item):
table_item.comm_fail_count += 1 # 增加通信失败计数
table_item.comm_fail_delay = self.limit_delay(table_item.comm_fail_delay + BASE_FAIL_DELAY, MAX_FAIL_DELAY) # 增加并限制通信失败延迟
self.comm_fail_delay = self.limit_delay(self.comm_fail_delay + BASE_GLOBAL_FAIL_DELAY, 60000) # 增加并限制全局通信失败延迟
table_item.comm_success = False # 标记通信失败
def limit_delay(self, delay, max_delay):
"""限制延迟不超过最大值"""
return min(delay, max_delay)
def handle_comm_success(self, table_item, read_result):
table_item.comm_fail_delay = 0 # 重置通信失败延迟
self.comm_fail_delay = 0 # 重置全局通信失败延迟
table_item.reg_buf = read_result # 更新寄存器缓存
table_item.comm_success = True # 标记通信成功
table_item.comm_success_count += 1 # 增加成功计数
if table_item.mqtt_pack:
self.publish_mqtt_message(table_item) # 发布MQTT消息
def publish_mqtt_message(self, table_item):
json_topic = f"{table_item.name}/{self.unique_name}" # 构建MQTT主题
msg_dict = self.comm_item_trigger_mqtt_pack(table_item.mqtt_pack) # 触发MQTT打包
if msg_dict:
json_message = json.dumps(msg_dict, ensure_ascii=False) # 序列化消息字典
if self.mqtt_thread:
self.mqtt_thread.publish(json_topic, json_message) # 发布MQTT消息
def device_comm_cycle(self):
comm_success_count = 0 # 初始化成功计数
total_comm_count = 0 # 初始化总计数
if self.comm_table is None:
return comm_success_count, total_comm_count # 如果通信表为空,提前返回
for item in self.comm_table_item_list:
table_item = item # 获取表项
if table_item.reg_count == 0:
continue # 如果寄存器计数为0跳过此表项
time_dif_ms = self.update_time_difference(table_item) # 更新时间差
comm_trigger = self.should_trigger_comm(table_item, time_dif_ms) # 检查是否应触发通信
if comm_trigger:
total_comm_count += 1 # 增加总通信计数
read_result = self.execute_communication(table_item) # 执行通信
if read_result:
comm_success_count += 1 # 如果读取成功,增加成功计数
self.handle_comm_result(table_item, read_result) # 处理通信结果
return comm_success_count, total_comm_count # 返回成功计数和总计数

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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

1514
kv/app.kv

File diff suppressed because it is too large Load Diff

376
main.py
View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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