mirror of
https://github.com/goldenfishs/MRobot.git
synced 2026-03-31 21:07:14 +08:00
优化了ai个零件库
This commit is contained in:
@@ -2,19 +2,13 @@ import requests
|
||||
from packaging.version import parse as vparse
|
||||
|
||||
def check_update(local_version, repo="goldenfishs/MRobot"):
|
||||
"""
|
||||
检查 GitHub 上是否有新版本
|
||||
:param local_version: 当前版本号字符串,如 "1.0.2"
|
||||
:param repo: 仓库名,格式 "用户名/仓库名"
|
||||
:return: 最新版本号字符串(如果有新版本),否则 None
|
||||
"""
|
||||
url = f"https://api.github.com/repos/{repo}/releases/latest"
|
||||
try:
|
||||
resp = requests.get(url, timeout=5)
|
||||
if resp.status_code == 200:
|
||||
latest = resp.json()["tag_name"].lstrip("v")
|
||||
if vparse(latest) > vparse(local_version):
|
||||
return latest
|
||||
except Exception as e:
|
||||
print(f"检查更新失败: {e}")
|
||||
return None
|
||||
resp = requests.get(url, timeout=5)
|
||||
if resp.status_code == 200:
|
||||
latest = resp.json()["tag_name"].lstrip("v")
|
||||
if vparse(latest) > vparse(local_version):
|
||||
return latest
|
||||
else:
|
||||
return None
|
||||
else:
|
||||
raise RuntimeError("GitHub API 请求失败")
|
||||
45
app/tools/part_download.py
Normal file
45
app/tools/part_download.py
Normal file
@@ -0,0 +1,45 @@
|
||||
from PyQt5.QtCore import QThread, pyqtSignal
|
||||
import requests
|
||||
import shutil
|
||||
import os
|
||||
from urllib.parse import quote
|
||||
|
||||
class DownloadThread(QThread):
|
||||
progressChanged = pyqtSignal(int)
|
||||
finished = pyqtSignal(list, list) # success, fail
|
||||
|
||||
def __init__(self, files, server_url, secret_key, local_dir, parent=None):
|
||||
super().__init__(parent)
|
||||
self.files = files
|
||||
self.server_url = server_url
|
||||
self.secret_key = secret_key
|
||||
self.local_dir = local_dir
|
||||
|
||||
def run(self):
|
||||
success, fail = [], []
|
||||
total = len(self.files)
|
||||
max_retry = 3
|
||||
for idx, rel_path in enumerate(self.files):
|
||||
retry = 0
|
||||
while retry < max_retry:
|
||||
try:
|
||||
rel_path_unix = rel_path.replace("\\", "/")
|
||||
encoded_path = quote(rel_path_unix)
|
||||
url = f"{self.server_url}/download/{encoded_path}"
|
||||
params = {"key": self.secret_key}
|
||||
resp = requests.get(url, params=params, stream=True, timeout=10)
|
||||
if resp.status_code == 200:
|
||||
local_path = os.path.join(self.local_dir, rel_path)
|
||||
os.makedirs(os.path.dirname(local_path), exist_ok=True)
|
||||
with open(local_path, "wb") as f:
|
||||
shutil.copyfileobj(resp.raw, f)
|
||||
success.append(rel_path)
|
||||
break
|
||||
else:
|
||||
retry += 1
|
||||
except Exception:
|
||||
retry += 1
|
||||
else:
|
||||
fail.append(rel_path)
|
||||
self.progressChanged.emit(int((idx + 1) / total * 100))
|
||||
self.finished.emit(success, fail)
|
||||
114
app/tools/task_config.py
Normal file
114
app/tools/task_config.py
Normal file
@@ -0,0 +1,114 @@
|
||||
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QHBoxLayout
|
||||
from qfluentwidgets import TitleLabel, BodyLabel, TableWidget, PushButton, SubtitleLabel, SpinBox, InfoBar, InfoBarPosition, LineEdit, CheckBox
|
||||
from PyQt5.QtCore import Qt
|
||||
import yaml
|
||||
import os
|
||||
|
||||
class TaskConfigDialog(QDialog):
|
||||
def __init__(self, parent=None, config_path=None):
|
||||
super().__init__(parent)
|
||||
self.setWindowTitle("任务配置")
|
||||
self.resize(900, 480)
|
||||
layout = QVBoxLayout(self)
|
||||
layout.setContentsMargins(32, 32, 32, 32)
|
||||
layout.setSpacing(18)
|
||||
|
||||
layout.addWidget(TitleLabel("FreeRTOS 任务配置"))
|
||||
layout.addWidget(BodyLabel("请添加并配置您的任务参数,支持频率控制与描述。"))
|
||||
|
||||
self.table = TableWidget(self)
|
||||
self.table.setColumnCount(6)
|
||||
self.table.setHorizontalHeaderLabels(["任务名称", "运行频率", "初始化延迟", "堆栈大小", "任务描述", "频率控制"])
|
||||
self.table.horizontalHeader().setSectionResizeMode(0, self.table.horizontalHeader().Stretch)
|
||||
self.table.horizontalHeader().setSectionResizeMode(1, self.table.horizontalHeader().ResizeToContents)
|
||||
self.table.horizontalHeader().setSectionResizeMode(2, self.table.horizontalHeader().ResizeToContents)
|
||||
self.table.horizontalHeader().setSectionResizeMode(3, self.table.horizontalHeader().ResizeToContents)
|
||||
self.table.horizontalHeader().setSectionResizeMode(4, self.table.horizontalHeader().Stretch)
|
||||
self.table.horizontalHeader().setSectionResizeMode(5, self.table.horizontalHeader().ResizeToContents)
|
||||
self.table.setMinimumHeight(260)
|
||||
layout.addWidget(self.table)
|
||||
|
||||
btn_layout = QHBoxLayout()
|
||||
self.add_btn = PushButton("添加任务")
|
||||
self.del_btn = PushButton("删除选中")
|
||||
self.ok_btn = PushButton("生成")
|
||||
self.cancel_btn = PushButton("取消")
|
||||
btn_layout.addWidget(self.add_btn)
|
||||
btn_layout.addWidget(self.del_btn)
|
||||
btn_layout.addStretch()
|
||||
btn_layout.addWidget(self.ok_btn)
|
||||
btn_layout.addWidget(self.cancel_btn)
|
||||
layout.addLayout(btn_layout)
|
||||
|
||||
self.add_btn.clicked.connect(self.add_row)
|
||||
self.del_btn.clicked.connect(self.del_row)
|
||||
self.ok_btn.clicked.connect(self.accept)
|
||||
self.cancel_btn.clicked.connect(self.reject)
|
||||
|
||||
# 自动读取配置文件
|
||||
if config_path and os.path.exists(config_path):
|
||||
try:
|
||||
with open(config_path, "r", encoding="utf-8") as f:
|
||||
tasks = yaml.safe_load(f)
|
||||
if tasks:
|
||||
for t in tasks:
|
||||
self.add_row(t)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def add_row(self, task=None):
|
||||
row = self.table.rowCount()
|
||||
self.table.insertRow(row)
|
||||
name = LineEdit(task.get("name", f"Task{row+1}") if task else f"Task{row+1}")
|
||||
freq = SpinBox()
|
||||
freq.setRange(1, 10000)
|
||||
freq.setValue(task.get("frequency", 500) if task else 500)
|
||||
delay = SpinBox()
|
||||
delay.setRange(0, 10000)
|
||||
delay.setValue(task.get("delay", 0) if task else 0)
|
||||
stack = SpinBox()
|
||||
stack.setRange(128, 8192)
|
||||
stack.setSingleStep(128)
|
||||
stack.setValue(task.get("stack", 256) if task else 256)
|
||||
desc = LineEdit(task.get("description", "") if task else "请填写任务描述")
|
||||
freq_ctrl = CheckBox("启用")
|
||||
freq_ctrl.setChecked(task.get("freq_control", True) if task else True)
|
||||
|
||||
self.table.setCellWidget(row, 0, name)
|
||||
self.table.setCellWidget(row, 1, freq)
|
||||
self.table.setCellWidget(row, 2, delay)
|
||||
self.table.setCellWidget(row, 3, stack)
|
||||
self.table.setCellWidget(row, 4, desc)
|
||||
self.table.setCellWidget(row, 5, freq_ctrl)
|
||||
|
||||
def del_row(self):
|
||||
selected = self.table.selectedItems()
|
||||
if selected:
|
||||
rows = set(item.row() for item in selected)
|
||||
for row in sorted(rows, reverse=True):
|
||||
self.table.removeRow(row)
|
||||
|
||||
def get_tasks(self):
|
||||
tasks = []
|
||||
for row in range(self.table.rowCount()):
|
||||
name = self.table.cellWidget(row, 0).text().strip()
|
||||
freq = self.table.cellWidget(row, 1).value()
|
||||
delay = self.table.cellWidget(row, 2).value()
|
||||
stack = self.table.cellWidget(row, 3).value()
|
||||
desc = self.table.cellWidget(row, 4).text().strip()
|
||||
freq_ctrl = self.table.cellWidget(row, 5).isChecked()
|
||||
# 校验 stack 必须为 128*2^n
|
||||
if stack < 128 or (stack & (stack - 1)) != 0 or stack % 128 != 0:
|
||||
raise ValueError(f"第{row+1}行任务“{name}”的堆栈大小必须为128、256、512、1024等(128*2^n)")
|
||||
task = {
|
||||
"name": name,
|
||||
"function": f"Task_{name}",
|
||||
"delay": delay,
|
||||
"stack": stack,
|
||||
"description": desc,
|
||||
"freq_control": freq_ctrl
|
||||
}
|
||||
if freq_ctrl:
|
||||
task["frequency"] = freq
|
||||
tasks.append(task)
|
||||
return tasks
|
||||
Reference in New Issue
Block a user