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