import modbus_tk.defines as cst from modbus_tk import modbus_tcp, hooks, modbus import menu_utils as utils import menu_key_def as KEYDEF import ZZ_9TE as device_9TE import struct from threading import Thread from typing import Tuple from device_conf import class_comm_device_config from comm_device import class_comm_device from menu_base import class_menu_layer_info, class_device_menu import menu_page import modbus_server from modbus_server import class_modbus_tcp_server import comm_global from comm_thread import class_comm_master_thread DEFAULT_EMPTY_STR = " " MAX_NUMS_OF_STRING = 20 #每个字符串占用寄存器数 MAX_NUMS_OF_MENU_LINE = MAX_NUMS_OF_STRING * 2 MAX_NUMS_OF_MODIFY_STRING = 20 MENU_PAGE_DYNAMIC_MENU =1 MENU_PAGE_ALARM =2 class class_menu_group(Thread, class_device_menu) : def __init__(self, device_remap = 0, max_menu_lines = 8, comm_config : class_comm_device_config = None, modbus_tcp_server : class_modbus_tcp_server = None): Thread.__init__(self) class_device_menu.__init__(self, device_remap, comm_config) self.modbus_tcp_server = modbus_tcp_server self.max_menu_lines = max_menu_lines self.server = None self.menu_reg_count = 10000 self.menu_active = None if self.config == None else self.config.get_menu_top() return def menu_get_regsiter_addr(self, row, col) : if col == 1: addr = modbus_server.REGISTER_MENU_STRING_ROW0_COL1 + row * MAX_NUMS_OF_STRING else : addr = modbus_server.REGISTER_MENU_STRING_ROW0_COL0 + row * MAX_NUMS_OF_STRING return addr def register_menu_string_fill(self, addr, menu_str, max_count) : index = 0 for chr in menu_str : self.update_server_register(addr + index, ord(chr)) if index >= max_count - 1: break index += 1 self.update_server_register(addr + index, 0) def menu_display_string(self, row, col, menu_str) : addr = self.menu_get_regsiter_addr(row, col) self.register_menu_string_fill(addr, menu_str, MAX_NUMS_OF_STRING) def menu_display_modify_caption_string(self, menu_str) : addr = modbus_server.REGISTER_MENU_MODIFY_CAPTION_STRING self.register_menu_string_fill(addr, menu_str, MAX_NUMS_OF_MODIFY_STRING) def menu_display_modify_string(self, menu_str) : addr = modbus_server.REGISTER_MENU_MODIFY_STRING self.register_menu_string_fill(addr, menu_str, MAX_NUMS_OF_MODIFY_STRING) def menu_display_modify_select_string(self, menu_str) : addr = modbus_server.REGISTER_MENU_MODIFY_SELECT_STRING self.register_menu_string_fill(addr, menu_str, MAX_NUMS_OF_MODIFY_STRING) def menu_get_value_regs(self, menu_item) : comm_str = utils.dict_or_object_get_attr(menu_item, "addr", None) value = None comm_thread : class_comm_master_thread= None comm_device : class_comm_device = None comm_device, comm_thread, device_menu = comm_global.search_device_remap_infos(self.device_remap) if comm_str != None: reg_addr, bit, reg_count = utils.comm_str_unpack(comm_str) if comm_thread == None or reg_count == 0: value = None elif bit >= 0: start_index = int(bit / 16) end_index = int((bit + reg_count + 15) / 16) reg_items = end_index - start_index value = comm_device.read_register_count(reg_addr + start_index, reg_items) #value = comm_thread.comm_hold_register_read(comm_addr, reg_addr + start_index, reg_items) comm_device.device_comm_flush_request(reg_addr + start_index, reg_items, cycle = 3000) else : value = comm_device.read_register_count(reg_addr, reg_count) #value = comm_thread.comm_hold_register_read(comm_addr, reg_addr, reg_count) #刷新请求 comm_device.device_comm_flush_request(reg_addr, reg_count, cycle = 3000) return value def menu_get_value_scale_float(self, menu_item) : comm_str = utils.dict_or_object_get_attr(menu_item, "addr", None) reg_addr, bit, reg_count = utils.comm_str_unpack(comm_str) value = self.menu_get_value_regs(menu_item) is_big_endian = self.menu_data_is_big_endian(menu_item) is_float = self.menu_data_is_float(menu_item) new_value = None if value != None : if len(value) == 2: if is_big_endian: new_value = value[1] + (value[0] << 16) else : new_value = value[0] + (value[1] << 16) if is_float : bytes = struct.pack('BBBB', new_value & 0xFF, (new_value >> 8) & 0xFF, (new_value >> 16) & 0xFF, (new_value >> 24) & 0xFF) new_value = struct.unpack("= 0 and new_value != None and is_float == False: bit = bit % 16 new_value = (new_value >> bit) & (0xFFFFFFFF >> (32 - reg_count)) if new_value != None : scale = utils.dict_or_object_get_attr(menu_item, "scale", 1.0) new_value = new_value * scale return new_value def menu_get_value(self, menu_item) : return self.menu_get_value_scale_float(menu_item) def menu_write_value(self, menu_item, new_value) : comm_str = utils.dict_or_object_get_attr(menu_item, "addr", None) if comm_str != None and new_value != None: reg_addr, bit, reg_count = utils.comm_str_unpack(comm_str) comm_thread : class_comm_master_thread = None comm_device : class_comm_device = None comm_device, comm_thread, device_menu = comm_global.search_device_remap_infos(self.device_remap) if comm_thread != None and comm_device != None and reg_count == len(new_value) : return comm_thread.comm_hold_register_write(comm_device.comm_addr, reg_addr, reg_count, new_value) return False def menu_get_value_string(self, menu_item) : value_str = None value = None format_str = utils.dict_or_object_get_attr(menu_item, "format", "%f") is_big_endian = self.menu_data_is_big_endian(menu_item) action_str = utils.dict_or_object_get_attr(menu_item, "action", None) if action_str == "time" : value = self.menu_get_value_regs(menu_item) value_str = self.menu_get_time_string(value) elif action_str == "date" : value = self.menu_get_value_regs(menu_item) value_str = self.menu_get_date_string(value) elif action_str == "ip" : value = self.menu_get_value_regs(menu_item) value_str = self.menu_get_ip_string(value, is_big_endian) elif action_str != None: return None else : new_value = self.menu_get_value_scale_float(menu_item) if new_value != None : value_str = format_str%(new_value) return value_str def menu_display_page(self, page) : self.update_server_register(modbus_server.REGISTER_MENU_DISPLAY_PAGE, page) def menu_popup_modify_status_change(self, active = False) : self.update_server_register(modbus_server.REGISTER_MENU_POPUP_MODIFY_ACTIVE, int(active)) def menu_remap_addr_flush(self, menu_table) : if menu_table == None: return for menu_item in menu_table: sub_menu_name = utils.dict_or_object_get_attr(menu_item, "sub_menu", None) sub_menu = self.config.get_menu(sub_menu_name) if sub_menu : #子菜单递归调用 self.menu_remap_addr_flush(sub_menu) continue remap_addr = utils.dict_or_object_get_attr(menu_item, "remap_addr", None) if remap_addr != None : #需要加上设备重映射基础地址 remap_addr += self.device_remap new_value = self.menu_get_value_scale_float(menu_item) if type(new_value) == float: reg_values = [] bs = struct.pack('f', new_value) reg_values.append(bs[0] | bs[1] << 8) reg_values.append(bs[2] | bs[3] << 8) self.update_server_register(remap_addr, reg_values) def menu_top_remap_addr_flush(self) : self.menu_remap_addr_flush(self.menu_table) def menu_display(self): menu_count = 0 row = 0 col = 0 active_layer : class_menu_layer_info = self.menu_get_layer_info() menu_active = active_layer.menu_active menu_index = 0 for menu_item in menu_active : if menu_index < active_layer.menu_display_start: menu_index += 1 continue caption = DEFAULT_EMPTY_STR if "name" in menu_item.keys() : caption = menu_item["name"] col = 0 self.menu_display_string(row, col, caption) sub_menu_name = utils.dict_or_object_get_attr(menu_item, "sub_menu", None) sub_menu = self.config.get_menu(sub_menu_name) if sub_menu != None: self.menu_display_string(row, col + 1, " ") else : alias_str = self.menu_get_alias_value_string(menu_item) if alias_str : self.menu_display_string(row, col + 1, alias_str) else : value_str = self.menu_get_value_string(menu_item) if value_str != None : unit_str = utils.dict_or_object_get_attr(menu_item, "unit", DEFAULT_EMPTY_STR) value_str = value_str + " " + unit_str self.menu_display_string(row, col + 1, value_str) else : self.menu_display_string(row, col + 1, DEFAULT_EMPTY_STR) row = row + 1 menu_count += 1 if menu_count >= self.max_menu_lines : break while row < self.max_menu_lines : self.menu_display_string(row, 0, DEFAULT_EMPTY_STR) self.menu_display_string(row, 1, DEFAULT_EMPTY_STR) row += 1 def menu_get_alias_dict(self, alias_name) : alias_value_dict = None if alias_name != None and self.alias_table != None: for alias_dict in self.alias_table : if alias_name in alias_dict.keys() : alias_value_dict = alias_dict[alias_name] return alias_value_dict def menu_get_alias_value_string(self, menu_item) : alias = utils.dict_or_object_get_attr(menu_item, "alias", None) if alias == None : return None alias_dict = self.menu_get_alias_dict(alias) if alias_dict == None : return None value = self.menu_get_value_scale_float(menu_item) alias_value_str = None if alias_dict != None and value != None: alias_value = int(value) if alias_value in alias_dict.keys() : alias_value_str = alias_dict[alias_value] return alias_value_str def menu_modify_value_string(self, value_string, unit_string = DEFAULT_EMPTY_STR, caption_string = DEFAULT_EMPTY_STR) : sel_bit = 0 if value_string == None: return None modify_value_string = value_string key = KEYDEF.MENU_PACK_KEY_NULL while True : key = comm_global.g_key_thread.menu_wait_key(0.1) value_string_display = modify_value_string if key == KEYDEF.MENU_PACK_KEY_RESET_OR_UP or key == KEYDEF.MENU_PACK_KEY_ESCAPE : break if key == KEYDEF.MENU_PACK_KEY_DOWN : if str.isdigit(modify_value_string[sel_bit]) : modify_value_list = list(modify_value_string) modify_value_list[sel_bit] = chr(ord(modify_value_list[sel_bit]) - 1) if modify_value_list[sel_bit] < '0': modify_value_list[sel_bit] = '9' modify_value_string = ''.join(modify_value_list) elif key == KEYDEF.MENU_PACK_KEY_UP : if str.isdigit(modify_value_string[sel_bit]) : modify_value_list = list(modify_value_string) modify_value_list[sel_bit] = chr(ord(modify_value_list[sel_bit]) + 1) if modify_value_list[sel_bit] > '9': modify_value_list[sel_bit] = '0' modify_value_string = ''.join(modify_value_list) elif key == KEYDEF.MENU_PACK_KEY_ENTER_OR_RIGHT or key == KEYDEF.MENU_PACK_KEY_RIGHT: while sel_bit < len(modify_value_string): sel_bit += 1 if sel_bit >= len(modify_value_string) : key = KEYDEF.MENU_PACK_KEY_ENTER break if str.isdigit(modify_value_string[sel_bit]) : break if sel_bit >= len(modify_value_string): break value_string_list = list(value_string_display) value_string_list[sel_bit] = ' ' value_string_display = ''.join(value_string_list) value_string_display_select = modify_value_string[0 : sel_bit + 1] self.menu_display_modify_caption_string(caption_string) self.menu_display_modify_string(value_string_display + unit_string) self.menu_display_modify_select_string(value_string_display_select) if key == KEYDEF.MENU_PACK_KEY_ENTER or key == KEYDEF.MENU_PACK_KEY_ENTER_OR_RIGHT: return modify_value_string return value_string def menu_modify_date_menu_item(self, menu_item) : origin_value = self.menu_get_value_regs(menu_item) value_string = self.menu_get_value_string(menu_item) unit_string = utils.dict_or_object_get_attr(menu_item, "unit", DEFAULT_EMPTY_STR) new_value_string:str = self.menu_modify_value_string(value_string, unit_string) if new_value_string != value_string : date_str = new_value_string.split('-') new_value = origin_value if len(date_str) == 3: new_value = [] year = int(date_str[0]) month = int(date_str[1]) day = int(date_str[2]) new_value.append(year) new_value.append(month) new_value.append(day) return new_value, True else : return origin_value, False def menu_modify_time_menu_item(self, menu_item) : origin_value = self.menu_get_value_regs(menu_item) value_string = self.menu_get_value_string(menu_item) unit_string = utils.dict_or_object_get_attr(menu_item, "unit", DEFAULT_EMPTY_STR) new_value_string:str = self.menu_modify_value_string(value_string, unit_string) if new_value_string != value_string : date_str = new_value_string.split(':') new_value = origin_value if len(date_str) == 3: new_value = [] hour = int(date_str[0]) minute = int(date_str[1]) second = int(date_str[2]) new_value.append(hour) new_value.append(minute) new_value.append(second) return new_value, True else : return origin_value, False def menu_get_date_string(self, date_value) : value_string = None if date_value != None: if len(date_value) == 3 : year = date_value[0] month = date_value[1] day = date_value[2] value_string = "%04d-%02d-%02d"%(year, month, day) return value_string def menu_get_time_string(self, time_value) : value_string = None if time_value != None : if len(time_value) == 3 : hour = time_value[0] minute = time_value[1] second = time_value[2] value_string = "%02d:%02d:%02d"%(hour, minute, second) return value_string def menu_get_ip_string(self, ip_value, is_big_endian = False) : value_string:str = None if ip_value != None: if len(ip_value) == 2: if is_big_endian: str_ip1 = "%03d"%(((ip_value[1] >> 0) & 0xFF),) str_ip2 = "%03d"%(((ip_value[1] >> 8) & 0xFF),) str_ip3 = "%03d"%(((ip_value[0] >> 16) & 0xFF),) str_ip4 = "%03d"%(((ip_value[0] >> 24) & 0xFF),) else : str_ip1 = "%03d"%(((ip_value[0] >> 0) & 0xFF),) str_ip2 = "%03d"%(((ip_value[0] >> 8) & 0xFF),) str_ip3 = "%03d"%(((ip_value[1] >> 16) & 0xFF),) str_ip4 = "%03d"%(((ip_value[1] >> 24) & 0xFF),) value_string = "%s.%s.%s.%s"%(str_ip1, str_ip2, str_ip3, str_ip4) return value_string def menu_get_ip_value(self, ip_str:str) : ip = ip_str.split(sep = '.') ip_value = [] if len(ip) == 4 : ip0 = int(ip[0]) ip1 = int(ip[1]) ip2 = int(ip[2]) ip3 = int(ip[3]) ip_value.append((ip0 << 0) | (ip1 << 8)) ip_value.append((ip2 << 16) | (ip3 << 24)) return ip_value def menu_data_is_big_endian(self, menu_item) : comm_str = utils.dict_or_object_get_attr(menu_item, "addr", DEFAULT_EMPTY_STR) is_big_endian = False if comm_str : if "#>" in comm_str : is_big_endian = True return is_big_endian def menu_data_is_float(self, menu_item) : comm_str = utils.dict_or_object_get_attr(menu_item, "addr", DEFAULT_EMPTY_STR) is_float = False if comm_str : if "#f" in comm_str : is_float = True return is_float def menu_modify_ip_menu_item(self, menu_item) : origin_value = self.menu_get_value_regs(menu_item) is_big_endian = self.menu_data_is_big_endian(menu_item) value_string:str = self.menu_get_ip_string(origin_value, is_big_endian) unit_string = DEFAULT_EMPTY_STR new_value_string = self.menu_modify_value_string(value_string, unit_string) if new_value_string != value_string : new_value = self.menu_get_ip_value(new_value_string) return new_value, True else : return origin_value, False def menu_update_modify_regs(self, menu_item, old_value_regs, modify_value) : comm_str = utils.dict_or_object_get_attr(menu_item, "addr", DEFAULT_EMPTY_STR) reg_addr, bit, reg_count = utils.comm_str_unpack(comm_str) scale = utils.dict_or_object_get_attr(menu_item, "scale", 1.0) is_float = self.menu_data_is_float(menu_item) is_big_endian = self.menu_data_is_big_endian(menu_item) if modify_value == None : return old_value_regs modify_regs = None if len(old_value_regs) == 2: modify_regs = [] modify_value = modify_value / scale if is_float : #把浮点数转换成4字节数, 然后转化成32位整数 bs = struct.pack("f", modify_value) modify_value = bs[0] | (bs[1] << 8) | (bs[2] << 16) | (bs[3] << 24) else : modify_value = int(modify_value) if bit >= 0: if is_big_endian : old_value = (old_value_regs[0] << 16) | old_value_regs[1] else : old_value = (old_value_regs[1] << 16) | old_value_regs[0] clear_mask = (0xFFFFFFFF >> (32 - reg_count)) << bit set_mask = (modify_value << bit) & clear_mask modify_value = (old_value & ~clear_mask) | set_mask if is_big_endian : modify_regs.append((modify_value >> 16) & 0xFFFF) modify_regs.append(modify_value & 0xFFFF) else : modify_regs.append(modify_value & 0xFFFF) modify_regs.append((modify_value >> 16) & 0xFFFF) elif len(old_value_regs) == 1: modify_regs = [] modify_value = int(modify_value / scale) if bit >= 0: old_value = old_value_regs[0] clear_mask = (0xFFFFFFFF >> (32 - reg_count)) << bit set_mask = (modify_value << bit) & clear_mask modify_value = (old_value & ~clear_mask) | set_mask modify_regs.append(int(modify_value) & 0xFFFF) return modify_regs def menu_modify_normal_menu_item(self, menu_item) ->Tuple[list, bool]: old_value_regs = self.menu_get_value_regs(menu_item) value_string = self.menu_get_value_string(menu_item) new_value_string = value_string if value_string != None: unit_string = utils.dict_or_object_get_attr(menu_item, "unit", DEFAULT_EMPTY_STR) new_value_string = self.menu_modify_value_string(value_string, unit_string) if new_value_string != value_string and value_string != None: modify_value = float(new_value_string) new_value_regs = self.menu_update_modify_regs(menu_item, old_value_regs, modify_value) return new_value_regs, True else : return old_value_regs, False def menu_modify_alias_menu_item(self, menu_item): alias = utils.dict_or_object_get_attr(menu_item, "alias", None) alias_dict = self.menu_get_alias_dict(alias) old_value_regs = self.menu_get_value_regs(menu_item) if old_value_regs == None: return old_value_regs, False fvalue = self.menu_get_value_scale_float(menu_item) alias_value = int(fvalue) alias_value_index = -1 search_index = 0 for item in alias_dict.keys(): if item == alias_value : alias_value_index = search_index break search_index += 1 if alias_value_index < 0 : return old_value_regs, False while True : key = comm_global.g_key_thread.menu_wait_key(0.1) if key == KEYDEF.MENU_PACK_KEY_DOWN : alias_value_index += 1 if alias_value_index >= len(alias_dict) : alias_value_index = 0 elif key == KEYDEF.MENU_PACK_KEY_UP : alias_value_index -= 1 if alias_value_index < 0 : alias_value_index = len(alias_dict) - 1 new_alias_value = None new_alias_str = DEFAULT_EMPTY_STR search_index = 0 for new_alias_value, new_alias_str in alias_dict.items() : if search_index == alias_value_index : break search_index += 1 self.menu_display_modify_string(new_alias_str) self.menu_display_modify_select_string(new_alias_str) if key == KEYDEF.MENU_PACK_KEY_ENTER or key == KEYDEF.MENU_PACK_KEY_ENTER_OR_RIGHT : new_value_regs = self.menu_update_modify_regs(menu_item, old_value_regs, new_alias_value) is_modified = True if (new_value_regs != old_value_regs) else False return new_value_regs, is_modified if key == KEYDEF.MENU_PACK_KEY_RESET_OR_UP: return old_value_regs, False def _menu_adjust_select_item(self, increase) : active_layer : class_menu_layer_info = self.menu_get_layer_info() active_layer.menu_select += increase if increase > 0 and active_layer.menu_select >= active_layer.menu_item_count : active_layer.menu_select = 0 if active_layer.menu_select < 0 : active_layer.menu_select = active_layer.menu_item_count - 1 if active_layer.menu_display_start + self.max_menu_lines <= active_layer.menu_select : active_layer.menu_display_start = active_layer.menu_select - self.max_menu_lines + 1 if active_layer.menu_select < active_layer.menu_display_start : active_layer.menu_display_start = active_layer.menu_select def menu_process(self, key = KEYDEF.MENU_PACK_KEY_NULL) -> Tuple[bool] : active_layer : class_menu_layer_info = self.menu_get_layer_info() if active_layer == None : return False result = False select_row = active_layer.menu_select - active_layer.menu_display_start if select_row >= 0: self.update_server_register(modbus_server.REGISTER_MENU_ITEM_SELECT_MASK, 0x01 << select_row) if key == KEYDEF.MENU_PACK_KEY_UP : self._menu_adjust_select_item(-1) self.update_server_register(modbus_server.REGISTER_MENU_ITEM_SELECT_MASK, 0x01 << select_row) elif key == KEYDEF.MENU_PACK_KEY_DOWN : self._menu_adjust_select_item(1) self.update_server_register(modbus_server.REGISTER_MENU_ITEM_SELECT_MASK, 0x01 << select_row) elif key == KEYDEF.MENU_PACK_KEY_RESET_OR_UP or key == KEYDEF.MENU_PACK_KEY_ESCAPE: self.menu_return_to_upper_layer() elif key == KEYDEF.MENU_PACK_KEY_ENTER_OR_RIGHT or key == KEYDEF.MENU_PACK_KEY_ENTER : menu_item = self.menu_get_active_item() #地址映射有效, 修改映射地址 device_remap = utils.dict_or_object_get_attr(menu_item, "device_remap", None) if device_remap != None: self.menu_update_device_remap_addr(device_remap) function = utils.dict_or_object_get_attr(menu_item, "function", None) if function != None : device_menu = self result = function(device_menu, menu_item) if result : return True sub_menu_name = utils.dict_or_object_get_attr(menu_item, "sub_menu", None) sub_menu = self.config.get_menu(sub_menu_name) if sub_menu : self.menu_enter_sub_menu(sub_menu) return True format_str = utils.dict_or_object_get_attr(menu_item, "format", None) action_str = utils.dict_or_object_get_attr(menu_item, "action", None) if action_str == "exit" : self.menu_return_to_upper_layer() return True elif action_str == "exit2" : self.menu_return_to_most_upper_layer() return True self.menu_popup_modify_status_change(active = True) changed = False new_value = None if action_str == "time": new_value, changed = self.menu_modify_time_menu_item(menu_item) elif action_str == "date" : new_value, changed = self.menu_modify_date_menu_item(menu_item) elif action_str == "ip" : new_value, changed = self.menu_modify_ip_menu_item(menu_item) elif action_str != None : return False else : alias = utils.dict_or_object_get_attr(menu_item, "alias", None) if alias != None : new_value, changed = self.menu_modify_alias_menu_item(menu_item) else: new_value, changed = self.menu_modify_normal_menu_item(menu_item) comm_str = utils.dict_or_object_get_attr(menu_item, "addr", DEFAULT_EMPTY_STR) reg_addr, bit, reg_count = utils.comm_str_unpack(comm_str) if changed and new_value != None: comm_device :class_comm_device = None comm_device, comm_thread, device_menu = comm_global.search_device_remap_infos(self.device_remap) if comm_thread != None and comm_device != None and reg_count > 0: result = comm_device.write_register(reg_addr, reg_count, new_value) #result = comm_thread.comm_hold_register_write(comm_addr, reg_addr, reg_count, new_value) self.menu_popup_modify_status_change(active = False) return result def update_server_register(self, addr, value) : if self.modbus_tcp_server : self.modbus_tcp_server.update_server_register(addr, value) def run(self) : while True: key = comm_global.g_key_thread.menu_wait_key(0.1) page = None active_menu = self.menu_get_active_menu() if self.config != None : page = self.config.menu_get_display_page(active_menu) if page == None : page = menu_page.KUNLUN_GRAOUP_PAGE_MENU self.menu_display_page(page) self.menu_process(key) self.menu_display() ''' 可使用的函数: 创建从站: server.add_slave(slave_id) slave_id(int):从站id 为从站添加存储区: slave.add_block(block_name, block_type, starting_address, size) block_name(str):block名 block_type(int):block类型,COILS = 1,DISCRETE_INPUTS = 2,HOLDING_REGISTERS = 3,ANALOG_INPUTS = 4 starting_address(int):起始地址 size(int):block大小 设置block值:slave.set_values(block_name, address, values) block_name(str):block名 address(int):开始修改的地址 values(a list or a tuple or a number):要修改的一个(a number)或多个(a list or a tuple)值 获取block值:slave.get_values(block_name, address, size) block_name(str):block名 address(int):开始获取的地址 size(int):要获取的值的数量 ''' if __name__ == '__main__': comm_device_config: class_comm_device_config = device_9TE.comm_device_config _slave_addr = 2 _device_remap = 2000 _modbus_tcp_server = class_modbus_tcp_server(slave_addr = _slave_addr, slave_ip= "192.168.1.54", slave_port = 502) object_menu_group = class_menu_group(device_remap =_device_remap, comm_config = comm_device_config, modbus_tcp_server = _modbus_tcp_server, max_menu_lines = 8) object_menu_group.start()