mirror of
https://github.com/goldenfishs/MRobot.git
synced 2025-07-27 08:49:01 +08:00
重构完代码结构了
This commit is contained in:
parent
47e0b8419f
commit
501a9ddff4
11
MRobot.iss
11
MRobot.iss
@ -8,10 +8,11 @@ OutputBaseFilename=MRobotInstaller
|
||||
|
||||
[Files]
|
||||
Source: "dist\MRobot.exe"; DestDir: "{app}"; Flags: ignoreversion
|
||||
Source: "img\*"; DestDir: "{app}\img"; Flags: ignoreversion recursesubdirs
|
||||
Source: "User_code\*"; DestDir: "{app}\User_code"; Flags: ignoreversion recursesubdirs
|
||||
Source: "mech_lib\*"; DestDir: "{app}\mech_lib"; Flags: ignoreversion recursesubdirs
|
||||
Source: "assets\logo\*"; DestDir: "{app}\assets\logo"; Flags: ignoreversion recursesubdirs
|
||||
Source: "assets\User_code\*"; DestDir: "{app}\assets\User_code"; Flags: ignoreversion recursesubdirs
|
||||
Source: "assets\mech_lib\*"; DestDir: "{app}\assets\mech_lib"; Flags: ignoreversion recursesubdirs
|
||||
Source: "assets\logo\M.ico"; DestDir: "{app}\assets\logo"; Flags: ignoreversion
|
||||
|
||||
[Icons]
|
||||
Name: "{group}\MRobot"; Filename: "{app}\MRobot.exe"; IconFilename: "{app}\img\M.ico"
|
||||
Name: "{userdesktop}\MRobot"; Filename: "{app}\MRobot.exe"; IconFilename: "{app}\img\M.ico"
|
||||
Name: "{group}\MRobot"; Filename: "{app}\MRobot.exe"; IconFilename: "{app}\assets\logo\M.ico"
|
||||
Name: "{userdesktop}\MRobot"; Filename: "{app}\MRobot.exe"; IconFilename: "{app}\assets\logo\M.ico"
|
1714
MRobot_old.py
1714
MRobot_old.py
File diff suppressed because it is too large
Load Diff
@ -123,3 +123,10 @@ pyinstaller --noconfirm --onefile --windowed --add-data "img;img" --add-data "Us
|
||||
pyinstaller MRobot.py
|
||||
|
||||
pyinstaller --noconfirm --onefile --windowed --icon=img/M.ico --add-data "img;img" --add-data "User_code;User_code" --add-data "mech_lib;mech_lib" MRobot.py
|
||||
|
||||
|
||||
pyinstaller --onefile --windowed --icon=assets/logo/M.ico --add-data "assets/logo:assets/logo" --add-data "assets/User_code:assets/User_code" --add-data "assets/mech_lib:assets/mech_lib" --collect-all pandas MRobot.py
|
||||
|
||||
pyinstaller --onefile --windowed --icon=assets/logo/M.ico --add-data "assets/logo:assets/logo" --add-data "assets/User_code:assets/User_code" --add-data "assets/mech_lib:assets/mech_lib" MRobot.py
|
||||
|
||||
python3 -m pyinstaller MRobot.py --onefile --windowed --add-data "assets:assets" --add-data "app:app" --add-data "app/tools:app/tools"
|
21
ai.py
21
ai.py
@ -1,21 +0,0 @@
|
||||
import requests
|
||||
import json
|
||||
|
||||
url = "http://154.37.215.220:11434/api/generate"
|
||||
payload = {
|
||||
"model": "qwen3:0.6b",
|
||||
"prompt": "你好,介绍一下你自己"
|
||||
}
|
||||
response = requests.post(url, json=payload, stream=True)
|
||||
|
||||
for line in response.iter_lines():
|
||||
if line:
|
||||
try:
|
||||
data = json.loads(line.decode('utf-8'))
|
||||
# 只输出 response 字段内容
|
||||
print(data.get("response", ""), end="", flush=True)
|
||||
# 如果 done 为 True,则换行
|
||||
if data.get("done", False):
|
||||
print()
|
||||
except Exception as e:
|
||||
pass # 忽略解析异常
|
BIN
app/__pycache__/__init__.cpython-39.pyc
Normal file
BIN
app/__pycache__/__init__.cpython-39.pyc
Normal file
Binary file not shown.
BIN
app/__pycache__/about_interface.cpython-39.pyc
Normal file
BIN
app/__pycache__/about_interface.cpython-39.pyc
Normal file
Binary file not shown.
BIN
app/__pycache__/ai_interface.cpython-39.pyc
Normal file
BIN
app/__pycache__/ai_interface.cpython-39.pyc
Normal file
Binary file not shown.
BIN
app/__pycache__/data_interface.cpython-39.pyc
Normal file
BIN
app/__pycache__/data_interface.cpython-39.pyc
Normal file
Binary file not shown.
BIN
app/__pycache__/function_fit_interface.cpython-39.pyc
Normal file
BIN
app/__pycache__/function_fit_interface.cpython-39.pyc
Normal file
Binary file not shown.
BIN
app/__pycache__/home_interface.cpython-39.pyc
Normal file
BIN
app/__pycache__/home_interface.cpython-39.pyc
Normal file
Binary file not shown.
BIN
app/__pycache__/main_window.cpython-39.pyc
Normal file
BIN
app/__pycache__/main_window.cpython-39.pyc
Normal file
Binary file not shown.
BIN
app/__pycache__/mini_tool_interface.cpython-39.pyc
Normal file
BIN
app/__pycache__/mini_tool_interface.cpython-39.pyc
Normal file
Binary file not shown.
BIN
app/__pycache__/part_library_interface.cpython-39.pyc
Normal file
BIN
app/__pycache__/part_library_interface.cpython-39.pyc
Normal file
Binary file not shown.
BIN
app/__pycache__/serial_terminal_interface.cpython-39.pyc
Normal file
BIN
app/__pycache__/serial_terminal_interface.cpython-39.pyc
Normal file
Binary file not shown.
@ -1,7 +1,9 @@
|
||||
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QMessageBox
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtCore import Qt, QUrl
|
||||
from PyQt5.QtGui import QDesktopServices
|
||||
|
||||
from qfluentwidgets import PrimaryPushSettingCard, FluentIcon
|
||||
from qfluentwidgets import InfoBar, InfoBarPosition
|
||||
from qfluentwidgets import InfoBar, InfoBarPosition, SubtitleLabel
|
||||
|
||||
from .function_fit_interface import FunctionFitInterface
|
||||
from app.tools.check_update import check_update
|
||||
@ -15,11 +17,18 @@ class AboutInterface(QWidget):
|
||||
|
||||
layout = QVBoxLayout(self)
|
||||
layout.setAlignment(Qt.AlignTop)
|
||||
layout.setContentsMargins(20, 30, 20, 20) # 添加边距
|
||||
|
||||
title = SubtitleLabel("MRobot 帮助页面", self)
|
||||
title.setAlignment(Qt.AlignCenter)
|
||||
layout.addWidget(title)
|
||||
# 添加空间隔
|
||||
layout.addSpacing(10)
|
||||
|
||||
card = PrimaryPushSettingCard(
|
||||
text="检查更新",
|
||||
icon=FluentIcon.DOWNLOAD,
|
||||
title="关于",
|
||||
title="更新",
|
||||
content=f"MRobot_Toolbox 当前版本:{__version__}",
|
||||
)
|
||||
card.clicked.connect(self.on_check_update_clicked)
|
||||
@ -29,9 +38,11 @@ class AboutInterface(QWidget):
|
||||
try:
|
||||
latest = check_update(__version__)
|
||||
if latest:
|
||||
# 直接用浏览器打开下载链接
|
||||
QDesktopServices.openUrl(QUrl("https://github.com/goldenfishs/MRobot/releases/latest"))
|
||||
InfoBar.success(
|
||||
title="发现新版本",
|
||||
content=f"检测到新版本:{latest},请前往官网或仓库下载更新。",
|
||||
content=f"检测到新版本:{latest},已为你打开下载页面。",
|
||||
parent=self,
|
||||
position=InfoBarPosition.TOP,
|
||||
duration=5000
|
||||
|
@ -6,8 +6,10 @@ import os
|
||||
import requests
|
||||
import zipfile
|
||||
import io
|
||||
import re
|
||||
import shutil
|
||||
import yaml
|
||||
import textwrap
|
||||
from jinja2 import Template
|
||||
|
||||
class IocConfig:
|
||||
@ -253,7 +255,7 @@ class DataInterface(QWidget):
|
||||
|
||||
def update_user_template(self):
|
||||
url = "http://gitea.qutrobot.top/robofish/MRobot/archive/User_code.zip"
|
||||
local_dir = "User_code"
|
||||
local_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../assets/User_code")
|
||||
try:
|
||||
resp = requests.get(url, timeout=30)
|
||||
resp.raise_for_status()
|
||||
@ -287,7 +289,7 @@ class DataInterface(QWidget):
|
||||
|
||||
def show_user_code_files(self):
|
||||
self.file_tree.clear()
|
||||
base_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../User_code")
|
||||
base_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../assets/User_code")
|
||||
user_dir = os.path.join(self.project_path, "User")
|
||||
sub_dirs = ["bsp", "component", "device", "module"]
|
||||
|
||||
@ -407,7 +409,7 @@ class DataInterface(QWidget):
|
||||
|
||||
def generate_code(self):
|
||||
import shutil
|
||||
base_dir = "User_code"
|
||||
base_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../assets/User_code")
|
||||
user_dir = os.path.join(self.project_path, "User")
|
||||
copied = []
|
||||
files = self.get_checked_files()
|
||||
@ -690,7 +692,7 @@ class DataInterface(QWidget):
|
||||
def generate_task_code(self, task_list):
|
||||
|
||||
base_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
template_dir = os.path.join(base_dir, "User_code", "task")
|
||||
template_dir = os.path.join(base_dir, "../assets/User_code/task")
|
||||
output_dir = os.path.join(self.project_path, "User", "task")
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QFileDialog, QTableWidgetItem, QApplication
|
||||
from PyQt5.QtCore import Qt
|
||||
from qfluentwidgets import TitleLabel, BodyLabel, TableWidget, PushButton, SubtitleLabel, SpinBox, ComboBox, InfoBar,InfoBarPosition, FluentIcon
|
||||
import pandas as pd
|
||||
from openpyxl import load_workbook, Workbook
|
||||
|
||||
import numpy as np
|
||||
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
|
||||
from matplotlib.figure import Figure
|
||||
@ -131,24 +132,30 @@ class FunctionFitInterface(QWidget):
|
||||
self.dataTable.removeRow(row)
|
||||
|
||||
def import_excel(self):
|
||||
path, _ = QFileDialog.getOpenFileName(self, "导入 Excel", "", "Excel Files (*.xlsx *.xls)")
|
||||
path, _ = QFileDialog.getOpenFileName(self, "导入 Excel", "", "Excel Files (*.xlsx)")
|
||||
if path:
|
||||
df = pd.read_excel(path)
|
||||
self.dataTable.setRowCount(0) # 清空原有数据
|
||||
for row_data in df.values.tolist():
|
||||
wb = load_workbook(path)
|
||||
ws = wb.active
|
||||
self.dataTable.setRowCount(0)
|
||||
for row_data in ws.iter_rows(min_row=2, values_only=True): # 跳过表头
|
||||
row = self.dataTable.rowCount()
|
||||
self.dataTable.insertRow(row)
|
||||
for col, value in enumerate(row_data):
|
||||
item = QTableWidgetItem(str(value))
|
||||
for col, value in enumerate(row_data[:2]):
|
||||
item = QTableWidgetItem(str(value) if value is not None else "")
|
||||
self.dataTable.setItem(row, col, item)
|
||||
|
||||
|
||||
def export_excel(self):
|
||||
path, _ = QFileDialog.getSaveFileName(self, "导出 Excel", "", "Excel Files (*.xlsx)")
|
||||
if path:
|
||||
data = self.parse_data()
|
||||
if data is not None:
|
||||
df = pd.DataFrame(data, columns=["x", "y"])
|
||||
df.to_excel(path, index=False)
|
||||
wb = Workbook()
|
||||
ws = wb.active
|
||||
ws.append(["x", "y"])
|
||||
for row in data:
|
||||
ws.append(row)
|
||||
wb.save(path)
|
||||
|
||||
def parse_data(self):
|
||||
data = []
|
||||
@ -184,9 +191,9 @@ class FunctionFitInterface(QWidget):
|
||||
|
||||
self.figure.clear()
|
||||
ax = self.figure.add_subplot(111)
|
||||
ax.scatter(x, y, color='blue', label='原始数据')
|
||||
ax.plot(x_fit, y_fit, color='red', label=f'拟合: {degree}阶')
|
||||
ax.set_title('函数图像')
|
||||
ax.scatter(x, y, color='blue', label='raw data')
|
||||
ax.plot(x_fit, y_fit, color='red', label=f'Fitted curve')
|
||||
ax.set_title('graph of a function')
|
||||
ax.set_xlabel('x')
|
||||
ax.set_ylabel('y')
|
||||
ax.legend()
|
||||
|
@ -1,6 +1,15 @@
|
||||
from PyQt5.QtWidgets import QWidget, QVBoxLayout
|
||||
from PyQt5.QtCore import Qt
|
||||
from qfluentwidgets import SubtitleLabel, BodyLabel, HorizontalSeparator, ImageLabel, FluentLabelBase, TitleLabel
|
||||
import sys
|
||||
import os
|
||||
|
||||
def resource_path(relative_path):
|
||||
"""获取资源文件的绝对路径,兼容打包和开发环境"""
|
||||
if hasattr(sys, '_MEIPASS'):
|
||||
# PyInstaller 打包后的临时目录
|
||||
return os.path.join(sys._MEIPASS, relative_path)
|
||||
return os.path.join(os.path.abspath("."), relative_path)
|
||||
|
||||
class HomeInterface(QWidget):
|
||||
def __init__(self, parent=None):
|
||||
@ -19,7 +28,7 @@ class HomeInterface(QWidget):
|
||||
content_layout.setContentsMargins(48, 48, 48, 48)
|
||||
|
||||
# Logo
|
||||
logo = ImageLabel('img/MRobot.png')
|
||||
logo = ImageLabel(resource_path('assets/logo/MRobot.png'))
|
||||
logo.scaledToHeight(80)
|
||||
content_layout.addWidget(logo, alignment=Qt.AlignHCenter) # 居中对齐
|
||||
|
||||
|
@ -106,7 +106,7 @@ class MainWindow(FluentWindow):
|
||||
|
||||
# main_window.py 只需修改关闭事件
|
||||
def closeEvent(self, e):
|
||||
if self.themeListener and self.themeListener.isRunning():
|
||||
self.themeListener.terminate()
|
||||
self.themeListener.deleteLater()
|
||||
# if self.themeListener and self.themeListener.isRunning():
|
||||
# self.themeListener.terminate()
|
||||
# self.themeListener.deleteLater()
|
||||
super().closeEvent(e)
|
||||
|
@ -10,12 +10,13 @@ from urllib.parse import quote
|
||||
class PartLibraryInterface(QWidget):
|
||||
SERVER_URL = "http://154.37.215.220:5000"
|
||||
SECRET_KEY = "MRobot_Download"
|
||||
LOCAL_LIB_DIR = "mech_lib"
|
||||
LOCAL_LIB_DIR = "assets/mech_lib"
|
||||
|
||||
def __init__(self, parent=None):
|
||||
super().__init__(parent=parent)
|
||||
self.setObjectName("partLibraryInterface")
|
||||
layout = QVBoxLayout(self)
|
||||
layout.setContentsMargins(20, 20, 20, 20) # 添加边距
|
||||
layout.setSpacing(16)
|
||||
|
||||
layout.addWidget(SubtitleLabel("零件库(在线bate版)"))
|
||||
|
BIN
app/tools/__pycache__/check_update.cpython-39.pyc
Normal file
BIN
app/tools/__pycache__/check_update.cpython-39.pyc
Normal file
Binary file not shown.
BIN
app/tools/__pycache__/part_download.cpython-39.pyc
Normal file
BIN
app/tools/__pycache__/part_download.cpython-39.pyc
Normal file
Binary file not shown.
20
app/tools/code_utils.py
Normal file
20
app/tools/code_utils.py
Normal file
@ -0,0 +1,20 @@
|
||||
import re
|
||||
|
||||
def preserve_user_region(new_code, old_code, region_name):
|
||||
"""
|
||||
替换 new_code 中 region_name 区域为 old_code 中的内容(如果有)
|
||||
region_name: 如 'USER INCLUDE'
|
||||
"""
|
||||
pattern = re.compile(
|
||||
rf"/\*\s*{region_name}\s*BEGIN\s*\*/(.*?)/\*\s*{region_name}\s*END\s*\*/",
|
||||
re.DOTALL
|
||||
)
|
||||
old_match = pattern.search(old_code or "")
|
||||
if not old_match:
|
||||
return new_code # 旧文件没有该区域,直接返回新代码
|
||||
|
||||
old_content = old_match.group(1)
|
||||
def repl(m):
|
||||
return m.group(0).replace(m.group(1), old_content)
|
||||
# 替换新代码中的该区域
|
||||
return pattern.sub(repl, new_code, count=1)
|
25
app/tools/ioc_config.py
Normal file
25
app/tools/ioc_config.py
Normal file
@ -0,0 +1,25 @@
|
||||
class IocConfig:
|
||||
def __init__(self, ioc_path):
|
||||
self.ioc_path = ioc_path
|
||||
self.config = {}
|
||||
self._parse()
|
||||
|
||||
def _parse(self):
|
||||
with open(self.ioc_path, encoding='utf-8') as f:
|
||||
for line in f:
|
||||
line = line.strip()
|
||||
if not line or line.startswith('#'):
|
||||
continue
|
||||
if '=' in line:
|
||||
key, value = line.split('=', 1)
|
||||
self.config[key.strip()] = value.strip()
|
||||
|
||||
def is_freertos_enabled(self):
|
||||
ip_keys = [k for k in self.config if k.startswith('Mcu.IP')]
|
||||
for k in ip_keys:
|
||||
if self.config[k] == 'FREERTOS':
|
||||
return True
|
||||
for k in self.config:
|
||||
if k.startswith('FREERTOS.'):
|
||||
return True
|
||||
return False
|
109
app/tools/task_code_generator.py
Normal file
109
app/tools/task_code_generator.py
Normal file
@ -0,0 +1,109 @@
|
||||
import os
|
||||
import yaml
|
||||
import textwrap
|
||||
from jinja2 import Template
|
||||
from .code_utils import preserve_user_region
|
||||
|
||||
def generate_task_code(task_list, project_path):
|
||||
# base_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../"))
|
||||
template_dir = os.path.join(project_root, "User_code", "task")
|
||||
output_dir = os.path.join(project_path, "User", "task")
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
|
||||
user_task_h_tpl = os.path.join(template_dir, "user_task.h.template")
|
||||
user_task_c_tpl = os.path.join(template_dir, "user_task.c.template")
|
||||
init_c_tpl = os.path.join(template_dir, "init.c.template")
|
||||
task_c_tpl = os.path.join(template_dir, "task.c.template")
|
||||
|
||||
freq_tasks = [t for t in task_list if t.get("freq_control", True)]
|
||||
|
||||
def render_template(path, context):
|
||||
with open(path, encoding="utf-8") as f:
|
||||
tpl = Template(f.read())
|
||||
return tpl.render(**context)
|
||||
|
||||
context_h = {
|
||||
"thread_definitions": "\n".join([f" osThreadId_t {t['name']};" for t in task_list]),
|
||||
"freq_definitions": "\n".join([f" float {t['name']};" for t in freq_tasks]),
|
||||
"stack_definitions": "\n".join([f" UBaseType_t {t['name']};" for t in task_list]),
|
||||
"last_up_time_definitions": "\n".join([f" float {t['name']};" for t in freq_tasks]),
|
||||
"task_frequency_definitions": "\n".join([f"#define {t['name'].upper()}_FREQ ({t['frequency']})" for t in freq_tasks]),
|
||||
"task_init_delay_definitions": "\n".join([f"#define {t['name'].upper()}_INIT_DELAY ({t['delay']})" for t in task_list]),
|
||||
"task_attr_declarations": "\n".join([f"extern const osThreadAttr_t attr_{t['name']};" for t in task_list]),
|
||||
"task_function_declarations": "\n".join([f"void {t['function']}(void *argument);" for t in task_list]),
|
||||
}
|
||||
|
||||
# ----------- 生成 user_task.h -----------
|
||||
user_task_h_path = os.path.join(output_dir, "user_task.h")
|
||||
new_user_task_h = render_template(user_task_h_tpl, context_h)
|
||||
|
||||
if os.path.exists(user_task_h_path):
|
||||
with open(user_task_h_path, "r", encoding="utf-8") as f:
|
||||
old_code = f.read()
|
||||
for region in ["USER INCLUDE", "USER MESSAGE", "USER CONFIG"]:
|
||||
new_user_task_h = preserve_user_region(new_user_task_h, old_code, region)
|
||||
with open(user_task_h_path, "w", encoding="utf-8") as f:
|
||||
f.write(new_user_task_h)
|
||||
|
||||
# ----------- 生成 user_task.c -----------
|
||||
context_c = {
|
||||
"task_attr_definitions": "\n".join([
|
||||
f"const osThreadAttr_t attr_{t['name']} = {{\n"
|
||||
f" .name = \"{t['name']}\",\n"
|
||||
f" .priority = osPriorityNormal,\n"
|
||||
f" .stack_size = {t['stack']} * 4,\n"
|
||||
f"}};"
|
||||
for t in task_list
|
||||
])
|
||||
}
|
||||
user_task_c = render_template(user_task_c_tpl, context_c)
|
||||
with open(os.path.join(output_dir, "user_task.c"), "w", encoding="utf-8") as f:
|
||||
f.write(user_task_c)
|
||||
|
||||
# ----------- 生成 init.c -----------
|
||||
thread_creation_code = "\n".join([
|
||||
f" task_runtime.thread.{t['name']} = osThreadNew({t['function']}, NULL, &attr_{t['name']});"
|
||||
for t in task_list
|
||||
])
|
||||
context_init = {
|
||||
"thread_creation_code": thread_creation_code,
|
||||
}
|
||||
init_c = render_template(init_c_tpl, context_init)
|
||||
init_c_path = os.path.join(output_dir, "init.c")
|
||||
if os.path.exists(init_c_path):
|
||||
with open(init_c_path, "r", encoding="utf-8") as f:
|
||||
old_code = f.read()
|
||||
for region in ["USER INCLUDE", "USER CODE", "USER CODE INIT"]:
|
||||
init_c = preserve_user_region(init_c, old_code, region)
|
||||
with open(init_c_path, "w", encoding="utf-8") as f:
|
||||
f.write(init_c)
|
||||
|
||||
# ----------- 生成 task.c -----------
|
||||
for t in task_list:
|
||||
desc = t.get("description", "")
|
||||
desc_wrapped = "\n ".join(textwrap.wrap(desc, 20))
|
||||
context_task = {
|
||||
"task_name": t["name"],
|
||||
"task_function": t["function"],
|
||||
"task_frequency": f"{t['name'].upper()}_FREQ" if t.get("freq_control", True) else None,
|
||||
"task_delay": f"{t['name'].upper()}_INIT_DELAY",
|
||||
"task_description": desc_wrapped,
|
||||
"freq_control": t.get("freq_control", True)
|
||||
}
|
||||
with open(task_c_tpl, encoding="utf-8") as f:
|
||||
tpl = Template(f.read())
|
||||
code = tpl.render(**context_task)
|
||||
task_c_path = os.path.join(output_dir, f"{t['name']}.c")
|
||||
if os.path.exists(task_c_path):
|
||||
with open(task_c_path, "r", encoding="utf-8") as f:
|
||||
old_code = f.read()
|
||||
for region in ["USER INCLUDE", "USER STRUCT", "USER CODE", "USER CODE INIT"]:
|
||||
code = preserve_user_region(code, old_code, region)
|
||||
with open(task_c_path, "w", encoding="utf-8") as f:
|
||||
f.write(code)
|
||||
|
||||
# ----------- 保存任务配置到 config.yaml -----------
|
||||
config_yaml_path = os.path.join(output_dir, "config.yaml")
|
||||
with open(config_yaml_path, "w", encoding="utf-8") as f:
|
||||
yaml.safe_dump(task_list, f, allow_unicode=True)
|
@ -1,114 +0,0 @@
|
||||
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
|
BIN
mech_lib/.DS_Store → assets/mech_lib/.DS_Store
vendored
BIN
mech_lib/.DS_Store → assets/mech_lib/.DS_Store
vendored
Binary file not shown.
136
fluent.py
136
fluent.py
@ -1,136 +0,0 @@
|
||||
import os
|
||||
import sys
|
||||
# 将当前工作目录设置为程序所在的目录,确保无论从哪里执行,其工作目录都正确设置为程序本身的位置,避免路径错误。
|
||||
os.chdir(os.path.dirname(sys.executable) if getattr(sys, 'frozen', False)else os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
import pyuac
|
||||
if not pyuac.isUserAdmin():
|
||||
try:
|
||||
pyuac.runAsAdmin(False)
|
||||
sys.exit(0)
|
||||
except Exception:
|
||||
sys.exit(1)
|
||||
|
||||
import atexit
|
||||
import base64
|
||||
|
||||
|
||||
|
||||
|
||||
def first_run():
|
||||
# if not cfg.get_value(base64.b64decode("YXV0b191cGRhdGU=").decode("utf-8")):
|
||||
# log.error("首次使用请先打开图形界面 March7th Launcher.exe")
|
||||
input("按回车键关闭窗口. . .")
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
def run_main_actions():
|
||||
while True:
|
||||
version.start()
|
||||
game.start()
|
||||
reward.start_specific("dispatch")
|
||||
Daily.start()
|
||||
reward.start()
|
||||
game.stop(True)
|
||||
|
||||
|
||||
def run_sub_task(action):
|
||||
game.start()
|
||||
sub_tasks = {
|
||||
"daily": lambda: (Daily.run(), reward.start()),
|
||||
"power": Power.run,
|
||||
"fight": Fight.start,
|
||||
"universe": Universe.start,
|
||||
"forgottenhall": lambda: challenge.start("memoryofchaos"),
|
||||
"purefiction": lambda: challenge.start("purefiction"),
|
||||
"apocalyptic": lambda: challenge.start("apocalyptic"),
|
||||
"redemption": Redemption.start
|
||||
}
|
||||
task = sub_tasks.get(action)
|
||||
if task:
|
||||
task()
|
||||
game.stop(False)
|
||||
|
||||
|
||||
def run_sub_task_gui(action):
|
||||
gui_tasks = {
|
||||
"universe_gui": Universe.gui,
|
||||
"fight_gui": Fight.gui
|
||||
}
|
||||
task = gui_tasks.get(action)
|
||||
if task and not task():
|
||||
input("按回车键关闭窗口. . .")
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
def run_sub_task_update(action):
|
||||
update_tasks = {
|
||||
"universe_update": Universe.update,
|
||||
"fight_update": Fight.update
|
||||
}
|
||||
task = update_tasks.get(action)
|
||||
if task:
|
||||
task()
|
||||
input("按回车键关闭窗口. . .")
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
def run_notify_action():
|
||||
notif.notify(cfg.notify_template['TestMessage'], "./assets/app/images/March7th.jpg")
|
||||
input("按回车键关闭窗口. . .")
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
def main(action=None):
|
||||
first_run()
|
||||
|
||||
# 完整运行
|
||||
if action is None or action == "main":
|
||||
run_main_actions()
|
||||
|
||||
# 子任务
|
||||
elif action in ["daily", "power", "fight", "universe", "forgottenhall", "purefiction", "apocalyptic", "redemption"]:
|
||||
run_sub_task(action)
|
||||
|
||||
# 子任务 原生图形界面
|
||||
elif action in ["universe_gui", "fight_gui"]:
|
||||
run_sub_task_gui(action)
|
||||
|
||||
# 子任务 更新项目
|
||||
elif action in ["universe_update", "fight_update"]:
|
||||
run_sub_task_update(action)
|
||||
|
||||
elif action in ["screenshot", "plot"]:
|
||||
tool.start(action)
|
||||
|
||||
elif action == "game":
|
||||
game.start()
|
||||
|
||||
elif action == "notify":
|
||||
run_notify_action()
|
||||
|
||||
else:
|
||||
log.error(f"未知任务: {action}")
|
||||
input("按回车键关闭窗口. . .")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
# 程序结束时的处理器
|
||||
def exit_handler():
|
||||
"""注册程序退出时的处理函数,用于清理OCR资源."""
|
||||
ocr.exit_ocr()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
atexit.register(exit_handler)
|
||||
main(sys.argv[1]) if len(sys.argv) > 1 else main()
|
||||
except KeyboardInterrupt:
|
||||
log.error("发生错误: 手动强制停止")
|
||||
input("按回车键关闭窗口. . .")
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
log.error(cfg.notify_template['ErrorOccurred'].format(error=e))
|
||||
notif.notify(cfg.notify_template['ErrorOccurred'].format(error=e))
|
||||
input("按回车键关闭窗口. . .")
|
||||
sys.exit(1)
|
BIN
img/.DS_Store
vendored
BIN
img/.DS_Store
vendored
Binary file not shown.
BIN
img/M2.ico
BIN
img/M2.ico
Binary file not shown.
Before Width: | Height: | Size: 34 KiB |
BIN
img/MR.ico
BIN
img/MR.ico
Binary file not shown.
Before Width: | Height: | Size: 32 KiB |
BIN
img/MR.png
BIN
img/MR.png
Binary file not shown.
Before Width: | Height: | Size: 130 KiB |
BIN
img/MRobot.ico
BIN
img/MRobot.ico
Binary file not shown.
Before Width: | Height: | Size: 14 KiB |
BIN
img/MRobot.png
BIN
img/MRobot.png
Binary file not shown.
Before Width: | Height: | Size: 96 KiB |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user