import json
import os
import threading
import PySimpleGUI as sg
import telnetlib
import time
import queue
import logging
from datetime import datetime
# ============== 全局配置 ==============
DATA_FILE = 'pyremote_config.json'
PROJECTS_FILE = 'projects_config.json'
END_STR = "ROUTER_MPU /home\x1b[m # "
LOG_FORMAT = '%(asctime)s - %(levelname)s - %(message)s'
ENCODING = 'utf-8'
BUFFER_SIZE = 4096
CHUNK_SIZE = 10 # 每10个字符发送一次
CHUNK_DELAY = 0.15 # 每个分块之间的延迟(秒)
COMMAND_DELAY = 0.5 # 命令之间的延迟(秒)
# ============== 日志配置 ==============
def setup_logger():
"""配置日志系统"""
logger = logging.getLogger('RemoteControl')
logger.setLevel(logging.DEBUG)
# 文件处理器
file_handler = logging.FileHandler('remote_control.log')
file_handler.setFormatter(logging.Formatter(LOG_FORMAT))
# 控制台处理器
console_handler = logging.StreamHandler()
console_handler.setFormatter(logging.Formatter(LOG_FORMAT))
logger.addHandler(file_handler)
logger.addHandler(console_handler)
return logger
logger = setup_logger()
# ============== 项目管理类 ==============
class ProjectManager:
def __init__(self):
self.projects = self.load_projects()
def load_projects(self):
"""加载项目配置"""
if os.path.exists(PROJECTS_FILE):
try:
with open(PROJECTS_FILE, 'r') as f:
return json.load(f)
except Exception as e:
logger.error(f"加载项目配置失败: {e}")
return {"projects": []}
return {"projects": []}
def save_projects(self):
"""保存项目配置"""
try:
with open(PROJECTS_FILE, 'w') as f:
json.dump(self.projects, f, indent=4)
return True
except Exception as e:
logger.error(f"保存项目配置失败: {e}")
return False
def add_project(self, name, commands):
"""添加新项目"""
project = {
"name": name,
"commands": commands,
"created_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
}
self.projects["projects"].append(project)
if self.save_projects():
return project
return None
def update_project(self, index, name, commands):
"""更新项目"""
if 0 <= index < len(self.projects["projects"]):
self.projects["projects"][index]["name"] = name
self.projects["projects"][index]["commands"] = commands
if self.save_projects():
return True
return False
def delete_project(self, index):
"""删除项目"""
if 0 <= index < len(self.projects["projects"]):
del self.projects["projects"][index]
return self.save_projects()
return False
def import_projects(self, file_path):
"""导入项目配置"""
try:
with open(file_path, 'r') as f:
imported = json.load(f)
# 验证导入格式
if "projects" in imported and isinstance(imported["projects"], list):
self.projects = imported
return self.save_projects()
except Exception as e:
logger.error(f"导入项目失败: {e}")
return False
def export_projects(self, file_path):
"""导出项目配置"""
try:
with open(file_path, 'w') as f:
json.dump(self.projects, f, indent=4)
return True
except Exception as e:
logger.error(f"导出项目失败: {e}")
return False
def get_project_commands(self, index):
"""获取项目的命令列表"""
if 0 <= index < len(self.projects["projects"]):
return self.projects["projects"][index]["commands"]
return []
# ============== 指令执行类(增强版) ==============
class CommandExecutor:
def __init__(self):
self.tn = None
self.is_connected = False
self.response = ""
self.prev_cmd = ""
self.log_queue = queue.Queue()
self.lock = threading.Lock()
self.stop_event = threading.Event()
self.pause_event = threading.Event() # 暂停事件
self.last_response = ""
self.expected_prompt = END_STR
self.current_command_index = -1 # 当前执行命令索引
self.current_command = "" # 当前执行的命令
self.total_commands = 0 # 总命令数
def connect(self, host, port):
"""连接设备"""
try:
with self.lock:
self.tn = telnetlib.Telnet(host, port, timeout=10)
self.is_connected = True
self.log_queue.put(f"已连接到 {host}:{port}")
# 初始读取以清除欢迎信息
self.read_until_prompt(timeout=3)
return True
except Exception as e:
self.log_queue.put(f"连接失败: {str(e)}")
return False
def disconnect(self):
"""断开连接"""
with self.lock:
if self.tn:
try:
self.tn.close()
except:
pass
self.is_connected = False
self.log_queue.put("连接已断开")
def send(self, content):
"""发送命令(分块发送)"""
if not self.is_connected:
self.log_queue.put("错误:未连接到设备")
return False
try:
with self.lock:
# 保存当前命令
self.current_command = content
# 使用正确的编码发送
encoded_content = content.encode(ENCODING)
# 分块发送(每10个字符)
chunks = [encoded_content[i:i+CHUNK_SIZE] for i in range(0, len(encoded_content), CHUNK_SIZE)]
for chunk in chunks:
# 检查停止信号
if self.stop_event.is_set():
self.log_queue.put("发送已中止")
return False
# 检查暂停信号
while self.pause_event.is_set():
time.sleep(0.1)
if self.stop_event.is_set():
return False
# 发送分块
self.tn.write(chunk)
self.log_queue.put(f"发送分块: {chunk.decode(ENCODING, errors='replace')}")
# 添加分块之间的延迟
time.sleep(CHUNK_DELAY)
# 发送回车符(模拟敲回车)
self.tn.write(b"\r\n")
self.log_queue.put("发送回车符")
self.log_queue.put(f"完整发送: {content.strip()}")
self.prev_cmd = content
return True
except Exception as e:
self.log_queue.put(f"发送命令失败: {str(e)}")
return False
def read_until_prompt(self, timeout=5):
"""读取直到遇到提示符"""
if not self.is_connected:
return ""
try:
# 使用正确的编码读取
response = self.tn.read_until(self.expected_prompt.encode(ENCODING), timeout=timeout)
decoded_response = response.decode(ENCODING, errors='replace')
# 移除命令回显
if self.prev_cmd and decoded_response.startswith(self.prev_cmd.strip()):
decoded_response = decoded_response[len(self.prev_cmd.strip()):].lstrip()
# 移除尾部的提示符
if decoded_response.endswith(self.expected_prompt):
decoded_response = decoded_response[:-len(self.expected_prompt)].rstrip()
self.last_response = decoded_response
self.log_queue.put(decoded_response)
return decoded_response
except Exception as e:
self.log_queue.put(f"接收响应失败: {str(e)}")
return ""
def execute_commands(self, commands):
"""执行命令序列(增强版)"""
if not self.is_connected:
self.log_queue.put("错误:未连接到设备")
return False
self.stop_event.clear()
self.pause_event.clear()
self.total_commands = len(commands)
self.log_queue.put(f"开始执行 {self.total_commands} 条命令")
try:
for idx, cmd in enumerate(commands):
if self.stop_event.is_set():
self.log_queue.put("命令执行已中止")
return False
# 设置当前命令索引
self.current_command_index = idx
# 检查暂停状态
while self.pause_event.is_set():
time.sleep(0.1)
if self.stop_event.is_set():
return False
if cmd.strip(): # 跳过空行
# 发送命令
if not self.send(cmd):
return False
# 等待命令完成
self.read_until_prompt(timeout=10)
# 命令间延迟
delay_remaining = COMMAND_DELAY
while delay_remaining > 0:
if self.stop_event.is_set():
return False
if self.pause_event.is_set():
time.sleep(0.1)
continue
time.sleep(0.1)
delay_remaining -= 0.1
self.log_queue.put("命令执行完成")
self.current_command_index = -1
return True
except Exception as e:
self.log_queue.put(f"命令执行失败: {str(e)}")
return False
def stop_execution(self):
"""停止当前执行"""
self.stop_event.set()
self.log_queue.put("正在停止执行...")
def pause_execution(self):
"""暂停当前执行"""
if not self.pause_event.is_set():
self.pause_event.set()
self.log_queue.put("执行已暂停")
return True
return False
def resume_execution(self):
"""继续执行"""
if self.pause_event.is_set():
self.pause_event.clear()
self.log_queue.put("执行已继续")
return True
return False
def get_execution_status(self):
"""获取执行状态"""
return {
"is_running": not self.stop_event.is_set() and not self.pause_event.is_set(),
"is_paused": self.pause_event.is_set(),
"is_stopped": self.stop_event.is_set(),
"current_command": self.current_command.strip(),
"current_index": self.current_command_index,
"total_commands": self.total_commands
}
# ============== GUI 界面类(增强版) ==============
class RemoteControlApp:
def __init__(self):
self.project_manager = ProjectManager()
self.executor = CommandExecutor()
self.default_data = self.load_default_data()
self.window = None
self.setup_gui()
self.log_thread = None
self.status_update_thread = None
self.running = True
def load_default_data(self):
"""加载默认配置"""
default_data = {
"IP": "71.19.0.120",
"port": "1001",
"FTP_IP": "71.19.0.120",
"芯片名称": "Hi1260SV100",
'发送信息': "",
"board_local_ip": "71.19.0.53",
'interval': "1",
"start_addr": "",
"end_addr": "",
"文件FTP路径": "",
}
if os.path.exists(DATA_FILE):
try:
with open(DATA_FILE, 'r') as f:
data = json.load(f)
default_data.update({k: data[k] for k in default_data if k in data})
except Exception as e:
logger.error(f"加载默认配置失败: {e}")
with open(DATA_FILE, 'w') as f:
json.dump(default_data, f)
else:
with open(DATA_FILE, 'w') as f:
json.dump(default_data, f)
return default_data
def save_config(self):
"""保存配置"""
try:
values = self.window.read()[1] if self.window else {}
with open(DATA_FILE, 'w') as f:
json.dump({
"IP": values.get("IP", self.default_data["IP"]),
"port": values.get("port", self.default_data["port"]),
"FTP_IP": values.get("FTP_IP", self.default_data["FTP_IP"]),
"芯片名称": values.get("芯片名称", self.default_data["芯片名称"]),
'发送信息': values.get("发送信息", self.default_data["发送信息"]),
"board_local_ip": values.get("board_local_ip", self.default_data["board_local_ip"]),
'interval': values.get("interval", self.default_data["interval"]),
"start_addr": values.get("start_addr", self.default_data["start_addr"]),
"end_addr": values.get("end_addr", self.default_data["end_addr"]),
"文件FTP路径": values.get("文件FTP路径", self.default_data["文件FTP路径"]),
}, f)
return True
except Exception as e:
logger.error(f"保存配置失败: {e}")
return False
def create_project_buttons(self):
"""创建项目按钮区域"""
layout = []
projects = self.project_manager.projects["projects"]
if not projects:
layout.append([sg.Text("没有项目,请添加新项目", text_color='red')])
for i, project in enumerate(projects):
row = [
sg.Button(project["name"], key=f'-PROJECT-{i}-', size=(15,1),
tooltip=f"创建于: {project['created_at']}\n命令数: {len(project['commands'])}"),
sg.Button("一键升级", key=f'-UPGRADE-{i}-', button_color=('white', 'green')),
sg.Button("编辑", key=f'-EDIT-{i}-', button_color=('white', 'blue')),
sg.Button("删除", key=f'-DELETE-{i}-', button_color=('white', 'red'))
]
layout.append(row)
# 添加项目管理按钮
layout.append([
sg.Button("添加新项目", key='-ADD-PROJECT-', button_color=('white', 'purple')),
sg.Button("导入项目", key='-IMPORT-PROJECTS-'),
sg.Button("导出项目", key='-EXPORT-PROJECTS-'),
sg.Button("生成模板", key='-CREATE-TEMPLATE-')
])
return layout
def edit_project_window(self, index=None):
"""项目编辑窗口"""
project = None
if index is not None and 0 <= index < len(self.project_manager.projects["projects"]):
project = self.project_manager.projects["projects"][index]
layout = [
[sg.Text("项目名称:"), sg.Input(key='-PROJECT-NAME-', default_text=project["name"] if project else "")],
[sg.Text("升级指令:")],
[sg.Multiline(key='-PROJECT-COMMANDS-', size=(60, 10),
default_text='\n'.join(project["commands"]) if project else "",
tooltip="每行一个命令,命令将按顺序执行")],
[sg.Button("保存", key='-SAVE-PROJECT-'), sg.Button("取消", key='-CANCEL-PROJECT-')]
]
window = sg.Window("项目编辑", layout, modal=True)
while True:
event, values = window.read()
if event in (sg.WINDOW_CLOSED, '-CANCEL-PROJECT-'):
break
elif event == '-SAVE-PROJECT-':
name = values['-PROJECT-NAME-'].strip()
commands = [cmd.strip() for cmd in values['-PROJECT-COMMANDS-'].split('\n') if cmd.strip()]
if not name:
sg.popup("项目名称不能为空")
continue
if index is None:
new_project = self.project_manager.add_project(name, commands)
if new_project:
sg.popup(f"项目 '{name}' 添加成功")
break
else:
if self.project_manager.update_project(index, name, commands):
sg.popup(f"项目 '{name}' 更新成功")
break
sg.popup("操作失败,请查看日志")
window.close()
return True
def setup_gui(self):
"""设置GUI界面(增强版)"""
# 创建主布局
sg.theme('LightBlue1')
# 输出区域
output = sg.Multiline(
size=(80, 20),
key='-OUTPUT-',
autoscroll=True,
background_color='#f0f0f0',
text_color='black'
)
# 状态信息区域
status_info = [
sg.Text("当前命令: ", size=(10,1)),
sg.Text("无", key='-CURRENT-COMMAND-', size=(40,1), text_color='blue'),
sg.Text("进度: ", size=(5,1)),
sg.Text("0/0", key='-COMMAND-PROGRESS-', size=(10,1))
]
# 项目列表区域
projects_frame = sg.Frame("项目列表", [
[sg.Column(
self.create_project_buttons(),
scrollable=True,
vertical_scroll_only=True,
size=(700, 300),
key='-PROJECTS-COLUMN-'
)]
], key='-PROJECTS-FRAME-')
# 执行控制区域
control_buttons = [
sg.Button('暂停执行', key='-PAUSE-EXECUTION-', button_color=('white', 'orange'), size=(10,1)),
sg.Button('继续执行', key='-RESUME-EXECUTION-', button_color=('white', 'green'), size=(10,1)),
sg.Button('停止执行', key='-STOP-EXECUTION-', button_color=('white', 'red'), size=(10,1))
]
# 连接状态区域
status_bar = [
sg.Text('●', key='-STATUS-LIGHT-', text_color='red', font=('Arial', 12)),
sg.Text("未连接", key='-CONNECTION-STATUS-'),
sg.StatusBar("就绪", key='-STATUS-', size=(50, 1))
]
# 主布局
layout = [
[sg.Text('IP:', size=(6,1)), sg.Input(key="IP", default_text=self.default_data["IP"], size=(15,1)),
sg.Text('Port:', size=(5,1)), sg.Input(key="port", default_text=self.default_data["port"], size=(8,1)),
sg.Button('连接', button_color=('white', 'green')),
sg.Button('断开连接', button_color=('white', 'red'))],
[projects_frame],
status_info,
# 执行控制按钮
[sg.Frame("执行控制", [control_buttons], key='-CONTROL-FRAME-')],
[sg.Text("发送信息:", size=(8,1)), sg.Input(key='发送信息', size=(50, 1), default_text=self.default_data["发送信息"])],
[sg.Button('ROUTER_MPU下发送'), sg.Button('Shell下发送'),
sg.Text('每隔', size=(3,1)), sg.Input(key='interval', default_text=self.default_data["interval"], size=(5,1)),
sg.Text("秒", size=(2,1)), sg.Button('定时发送'), sg.Button("停止定时发送")],
[sg.Button('接收1s')],
[sg.Text('芯片名称:', size=(8,1)), sg.Input(key="芯片名称", default_text=self.default_data["芯片名称"], size=(15,1)),
sg.Text('文件FTP路径:', size=(10,1)), sg.Input(key="文件FTP路径", default_text=self.default_data["文件FTP路径"], size=(30,1))],
[sg.Text('FTP_IP:', size=(6,1)), sg.Input(key="FTP_IP", default_text=self.default_data["FTP_IP"], size=(15,1)),
sg.Text('单板IP:', size=(6,1)), sg.Input(key="board_local_ip", default_text=self.default_data["board_local_ip"], size=(15,1))],
[sg.Text("SLT测试用"), sg.Button('重新获取sdk.out'), sg.Button("一键升级MT1"), sg.Button("一键升级MT2")],
[sg.Button("使用说明", button_color=('white', 'blue'))],
[sg.Text('起始地址:', size=(8,1)), sg.Input(key='start_addr', default_text=self.default_data["start_addr"], size=(12,1)),
sg.Text('结束地址:', size=(8,1)), sg.Input(key='end_addr', default_text=self.default_data["end_addr"], size=(12,1)),
sg.Button('dump寄存器')],
[output],
status_bar
]
self.window = sg.Window('远程单板连接工具', layout, finalize=True, resizable=True)
self.update_status_light()
self.start_log_thread()
self.start_status_update_thread()
def update_status_light(self):
"""更新状态指示灯"""
color = 'green' if self.executor.is_connected else 'red'
status = "已连接" if self.executor.is_connected else "未连接"
self.window['-STATUS-LIGHT-'].update(text_color=color)
self.window['-CONNECTION-STATUS-'].update(status)
def update_output(self):
"""更新输出区域"""
while not self.executor.log_queue.empty():
try:
message = self.executor.log_queue.get_nowait()
current_text = self.window['-OUTPUT-'].get()
self.window['-OUTPUT-'].update(current_text + message + '\n')
except queue.Empty:
break
def start_log_thread(self):
"""启动日志更新线程"""
self.log_thread = threading.Thread(target=self.log_update_loop, daemon=True)
self.log_thread.start()
def log_update_loop(self):
"""日志更新循环"""
while self.running:
self.update_output()
time.sleep(0.1)
def start_status_update_thread(self):
"""启动状态更新线程"""
self.status_update_thread = threading.Thread(target=self.status_update_loop, daemon=True)
self.status_update_thread.start()
def status_update_loop(self):
"""状态更新循环"""
while self.running:
# 更新执行状态
status = self.executor.get_execution_status()
# 更新当前命令显示
if status["current_command"]:
self.window['-CURRENT-COMMAND-'].update(status["current_command"])
# 更新进度显示
if status["current_index"] >= 0 and status["total_commands"] > 0:
progress_text = f"{status['current_index'] + 1}/{status['total_commands']}"
self.window['-COMMAND-PROGRESS-'].update(progress_text)
# 更新按钮状态
if status["is_running"]:
self.window['-PAUSE-EXECUTION-'].update(disabled=False)
self.window['-RESUME-EXECUTION-'].update(disabled=True)
self.window['-STOP-EXECUTION-'].update(disabled=False)
elif status["is_paused"]:
self.window['-PAUSE-EXECUTION-'].update(disabled=True)
self.window['-RESUME-EXECUTION-'].update(disabled=False)
self.window['-STOP-EXECUTION-'].update(disabled=False)
else:
self.window['-PAUSE-EXECUTION-'].update(disabled=True)
self.window['-RESUME-EXECUTION-'].update(disabled=True)
self.window['-STOP-EXECUTION-'].update(disabled=True)
time.sleep(0.2)
def refresh_project_list(self):
"""刷新项目列表"""
self.window['-PROJECTS-COLUMN-'].update(visible=False)
self.window['-PROJECTS-COLUMN-'].update(self.create_project_buttons())
self.window['-PROJECTS-COLUMN-'].update(visible=True)
def run(self):
"""运行主循环(增强版)"""
stop_loop = 0
interval = 10000
loop_msg = ""
while True:
event, values = self.window.read(timeout=100)
if event == sg.WINDOW_CLOSED:
break
# 更新输出区域
self.update_output()
# 连接管理
if event == '连接':
if values["IP"] and values["port"]:
if self.executor.connect(values["IP"], int(values["port"])):
self.window['-STATUS-'].update("已连接")
self.update_status_light()
else:
sg.popup("请输入IP和端口")
elif event == '断开连接':
self.executor.disconnect()
self.window['-STATUS-'].update("已断开")
self.update_status_light()
# 项目管理
elif event == '-ADD-PROJECT-':
self.edit_project_window()
self.refresh_project_list()
elif event.startswith('-EDIT-'):
index = int(event.split('-')[2])
self.edit_project_window(index)
self.refresh_project_list()
elif event.startswith('-DELETE-'):
index = int(event.split('-')[2])
if self.project_manager.delete_project(index):
sg.popup("项目删除成功")
self.refresh_project_list()
else:
sg.popup("项目删除失败")
elif event == '-IMPORT-PROJECTS-':
file_path = sg.popup_get_file("选择项目配置文件", file_types=(("JSON Files", "*.json"),))
if file_path and self.project_manager.import_projects(file_path):
sg.popup("项目导入成功")
self.refresh_project_list()
else:
sg.popup("项目导入失败")
elif event == '-EXPORT-PROJECTS-':
file_path = sg.popup_get_file("保存项目配置文件", save_as=True, file_types=(("JSON Files", "*.json"),))
if file_path and self.project_manager.export_projects(file_path):
sg.popup("项目导出成功")
else:
sg.popup("项目导出失败")
elif event == '-CREATE-TEMPLATE-':
self.create_template_file()
sg.popup("模板文件已生成: projects_template.json")
# 一键升级
elif event.startswith('-UPGRADE-'):
index = int(event.split('-')[2])
if index < len(self.project_manager.projects["projects"]):
project = self.project_manager.projects["projects"][index]
self.window['-STATUS-'].update(f"执行 {project['name']} 升级...")
# 在新线程中执行升级
threading.Thread(
target=self.execute_project_upgrade,
args=(index,),
daemon=True
).start()
# 执行控制
elif event == '-PAUSE-EXECUTION-':
if self.executor.pause_execution():
self.window['-STATUS-'].update("执行已暂停")
elif event == '-RESUME-EXECUTION-':
if self.executor.resume_execution():
self.window['-STATUS-'].update("执行已继续")
elif event == '-STOP-EXECUTION-':
self.executor.stop_execution()
self.window['-STATUS-'].update("执行已停止")
# 其他原有功能...
elif event == 'ROUTER_MPU下发送':
if self.save_config() and values["发送信息"]:
self.executor.send(values["发送信息"])
# 等待响应
self.executor.read_until_prompt()
elif event == 'Shell下发送':
if self.save_config() and values["发送信息"]:
self.executor.send(values["发送信息"])
# 等待响应
self.executor.read_until_prompt()
elif event == '定时发送':
if self.save_config() and values["发送信息"] and values["interval"]:
try:
interval = float(values["interval"])
if interval <= 0:
raise ValueError("间隔必须大于0")
loop_msg = values["发送信息"]
threading.Thread(
target=self.periodic_send,
args=(loop_msg, interval),
daemon=True
).start()
except Exception as e:
sg.popup(f"无效的间隔: {str(e)}")
else:
sg.popup("请输入发送信息和有效间隔")
elif event == "停止定时发送":
self.executor.stop_execution()
# 其他事件处理
elif event == '接收1s':
self.executor.read_until_prompt(timeout=1)
elif event == 'dump寄存器':
if values["start_addr"] and values["end_addr"]:
self.executor.send(f"dump {values['start_addr']} {values['end_addr']}")
self.executor.read_until_prompt()
elif event == '重新获取sdk.out':
self.executor.send("get_sdk.out")
self.executor.read_until_prompt()
elif event == '一键升级MT1':
self.executor.send("upgrade_mt1")
self.executor.read_until_prompt()
elif event == '一键升级MT2':
self.executor.send("upgrade_mt2")
self.executor.read_until_prompt()
elif event == '使用说明':
self.show_help()
self.running = False
self.executor.disconnect()
if self.log_thread:
self.log_thread.join(timeout=1.0)
if self.status_update_thread:
self.status_update_thread.join(timeout=1.0)
self.window.close()
def execute_project_upgrade(self, index):
"""执行项目升级(增强版)"""
commands = self.project_manager.get_project_commands(index)
if commands:
# 重置进度显示
self.window['-CURRENT-COMMAND-'].update("")
self.window['-COMMAND-PROGRESS-'].update("0/0")
success = self.executor.execute_commands(commands)
self.window['-STATUS-'].update("升级完成" if success else "升级失败")
else:
self.executor.log_queue.put("错误:项目没有配置命令")
def periodic_send(self, message, interval):
"""周期性发送消息(优化版)"""
while not self.executor.stop_event.is_set():
self.executor.send(message)
# 等待响应
self.executor.read_until_prompt()
# 等待指定间隔
time.sleep(interval)
def show_help(self):
"""显示使用说明(增强版)"""
help_text = """
=== 远程单板连接工具使用说明 ===
1. 连接管理:
- 输入IP和端口后点击"连接"按钮
- 状态指示灯显示连接状态(红:未连接, 绿:已连接)
2. 项目管理:
- 添加项目:点击"添加新项目"按钮
- 编辑项目:点击项目右侧的"编辑"按钮
- 删除项目:点击项目右侧的"删除"按钮
- 一键升级:点击项目右侧的"一键升级"按钮
- 导入/导出:支持JSON格式配置导入导出
3. 命令执行控制:
- 暂停执行:点击"暂停执行"按钮
- 继续执行:点击"继续执行"按钮
- 停止执行:点击"停止执行"按钮
- 进度显示:显示当前执行的命令和进度
4. 发送机制:
- 分块发送:每条命令按10字符分块发送
- 回车发送:每条命令发送后自动发送回车符
- 命令间延迟:每条命令之间有0.5秒延迟
- 命令同步:每条命令发送后等待响应完成
5. 特殊功能:
- dump寄存器:输入起始和结束地址
- SLT测试:提供快速测试按钮
6. 优化说明:
- 使用UTF-8编码避免乱码
- 命令发送后自动等待响应完成
- 自动过滤命令回显和提示符
- 实时显示执行状态和进度
"""
sg.popup(help_text, title="使用说明", font=("Arial", 11))
def create_template_file(self):
"""创建项目配置模板"""
try:
template = {
"projects": [
{
"name": "项目A",
"commands": [
"ifconfig eth0 192.168.0.100",
"ftpget -u user -p pass 192.168.0.1 /home/firmware.bin firmware.bin",
"flash -w 0 0x0000000 0xa000000 0x2000000"
],
"created_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
},
{
"name": "项目B",
"commands": [
"ifconfig eth0 192.168.0.101",
"ping 192.168.0.1",
"reboot"
],
"created_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S")
}
]
}
with open("projects_template.json", 'w') as f:
json.dump(template, f, indent=4)
return True
except Exception as e:
logger.error(f"创建模板文件失败: {e}")
return False
# ============== 主程序入口 ==============
if __name__ == "__main__":
# 启动应用
try:
app = RemoteControlApp()
app.run()
except Exception as e:
logger.exception("应用程序崩溃")
sg.popup_error(f"应用程序发生致命错误: {str(e)}")