mirror of
https://github.com/goldenfishs/MRobot.git
synced 2025-07-05 06:54:17 +08:00
新版MRobotv1.0
This commit is contained in:
parent
a1da927d9c
commit
c737ec79d4
2339
MR_Tool.py
2339
MR_Tool.py
File diff suppressed because it is too large
Load Diff
17
MRobot.iss
Normal file
17
MRobot.iss
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
[Setup]
|
||||||
|
AppName=MRobot
|
||||||
|
AppVersion=1.0
|
||||||
|
DefaultDirName={userappdata}\MRobot
|
||||||
|
DefaultGroupName=MRobot
|
||||||
|
OutputDir=.
|
||||||
|
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
|
||||||
|
|
||||||
|
[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"
|
719
MRobot_old.py
719
MRobot_old.py
@ -1,719 +0,0 @@
|
|||||||
import tkinter as tk
|
|
||||||
from tkinter import ttk
|
|
||||||
from PIL import Image, ImageTk
|
|
||||||
import sys
|
|
||||||
import os
|
|
||||||
import threading
|
|
||||||
import shutil
|
|
||||||
import re
|
|
||||||
from git import Repo
|
|
||||||
from collections import defaultdict
|
|
||||||
import csv
|
|
||||||
import xml.etree.ElementTree as ET
|
|
||||||
|
|
||||||
# 配置常量
|
|
||||||
REPO_DIR = "MRobot_repo"
|
|
||||||
REPO_URL = "http://gitea.qutrobot.top/robofish/MRobot.git"
|
|
||||||
if getattr(sys, 'frozen', False): # 检查是否为打包后的环境
|
|
||||||
CURRENT_DIR = os.path.dirname(sys.executable) # 使用可执行文件所在目录
|
|
||||||
else:
|
|
||||||
CURRENT_DIR = os.path.dirname(os.path.abspath(__file__)) # 使用脚本所在目录
|
|
||||||
|
|
||||||
MDK_ARM_DIR = os.path.join(CURRENT_DIR, "MDK-ARM")
|
|
||||||
USER_DIR = os.path.join(CURRENT_DIR, "User")
|
|
||||||
|
|
||||||
class MRobotApp:
|
|
||||||
def __init__(self):
|
|
||||||
self.ioc_data = None
|
|
||||||
self.add_gitignore_var = None # 延迟初始化
|
|
||||||
self.header_file_vars = {}
|
|
||||||
self.task_vars = [] # 用于存储任务的变量
|
|
||||||
|
|
||||||
# 初始化
|
|
||||||
def initialize(self):
|
|
||||||
print("初始化中,正在克隆仓库...")
|
|
||||||
self.clone_repo()
|
|
||||||
self.ioc_data = self.find_and_read_ioc_file()
|
|
||||||
print("初始化完成,启动主窗口...")
|
|
||||||
self.show_main_window()
|
|
||||||
|
|
||||||
# 克隆仓库
|
|
||||||
def clone_repo(self):
|
|
||||||
try:
|
|
||||||
if os.path.exists(REPO_DIR):
|
|
||||||
shutil.rmtree(REPO_DIR)
|
|
||||||
print(f"正在克隆仓库到 {REPO_DIR}(仅克隆当前文件内容)...")
|
|
||||||
Repo.clone_from(REPO_URL, REPO_DIR, multi_options=["--depth=1"])
|
|
||||||
print("仓库克隆成功!")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"克隆仓库时出错: {e}")
|
|
||||||
|
|
||||||
# 删除克隆的仓库
|
|
||||||
def delete_repo(self):
|
|
||||||
try:
|
|
||||||
if os.path.exists(REPO_DIR):
|
|
||||||
shutil.rmtree(REPO_DIR)
|
|
||||||
print(f"已删除克隆的仓库目录: {REPO_DIR}")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"删除仓库目录时出错: {e}")
|
|
||||||
|
|
||||||
|
|
||||||
# 复制文件
|
|
||||||
def copy_file_from_repo(self, src_path, dest_path):
|
|
||||||
try:
|
|
||||||
# 修复路径拼接问题,确保 src_path 不重复包含 REPO_DIR
|
|
||||||
if src_path.startswith(REPO_DIR):
|
|
||||||
full_src_path = src_path
|
|
||||||
else:
|
|
||||||
full_src_path = os.path.join(REPO_DIR, src_path.lstrip(os.sep))
|
|
||||||
|
|
||||||
# 检查源文件是否存在
|
|
||||||
if not os.path.exists(full_src_path):
|
|
||||||
print(f"文件 {full_src_path} 不存在!(检查路径或仓库内容)")
|
|
||||||
return
|
|
||||||
|
|
||||||
# 检查目标路径是否有效
|
|
||||||
if not dest_path or not dest_path.strip():
|
|
||||||
print("目标路径为空或无效,无法复制文件!")
|
|
||||||
return
|
|
||||||
|
|
||||||
# 创建目标目录(如果不存在)
|
|
||||||
dest_dir = os.path.dirname(dest_path)
|
|
||||||
if dest_dir and not os.path.exists(dest_dir):
|
|
||||||
os.makedirs(dest_dir, exist_ok=True)
|
|
||||||
|
|
||||||
# 执行文件复制
|
|
||||||
shutil.copy(full_src_path, dest_path)
|
|
||||||
print(f"文件已从 {full_src_path} 复制到 {dest_path}")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"复制文件时出错: {e}")
|
|
||||||
|
|
||||||
# 查找并读取 .ioc 文件
|
|
||||||
def find_and_read_ioc_file(self):
|
|
||||||
try:
|
|
||||||
for file in os.listdir("."):
|
|
||||||
if file.endswith(".ioc"):
|
|
||||||
print(f"找到 .ioc 文件: {file}")
|
|
||||||
with open(file, "r", encoding="utf-8") as f:
|
|
||||||
return f.read()
|
|
||||||
print("未找到 .ioc 文件!")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"读取 .ioc 文件时出错: {e}")
|
|
||||||
return None
|
|
||||||
|
|
||||||
# 检查是否启用了 FreeRTOS
|
|
||||||
def check_freertos_enabled(self, ioc_data):
|
|
||||||
try:
|
|
||||||
return bool(re.search(r"Mcu\.IP\d+=FREERTOS", ioc_data))
|
|
||||||
except Exception as e:
|
|
||||||
print(f"检查 FreeRTOS 配置时出错: {e}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# 生成操作
|
|
||||||
def generate_action(self):
|
|
||||||
def task():
|
|
||||||
# 检查并创建目录
|
|
||||||
self.create_directories()
|
|
||||||
|
|
||||||
if self.add_gitignore_var.get():
|
|
||||||
self.copy_file_from_repo(".gitignore", ".gitignore")
|
|
||||||
if self.ioc_data and self.check_freertos_enabled(self.ioc_data):
|
|
||||||
self.copy_file_from_repo("src/freertos.c", os.path.join("Core", "Src", "freertos.c"))
|
|
||||||
|
|
||||||
# 定义需要处理的文件夹
|
|
||||||
folders = ["bsp", "component", "device", "module"]
|
|
||||||
|
|
||||||
# 遍历每个文件夹,复制选中的 .h 和 .c 文件
|
|
||||||
for folder in folders:
|
|
||||||
folder_dir = os.path.join(REPO_DIR, "User", folder)
|
|
||||||
if not os.path.exists(folder_dir):
|
|
||||||
continue # 如果文件夹不存在,跳过
|
|
||||||
|
|
||||||
for file_name in os.listdir(folder_dir):
|
|
||||||
file_base, file_ext = os.path.splitext(file_name)
|
|
||||||
if file_ext not in [".h", ".c"]:
|
|
||||||
continue # 只处理 .h 和 .c 文件
|
|
||||||
|
|
||||||
# 强制复制与文件夹同名的文件
|
|
||||||
if file_base == folder:
|
|
||||||
src_path = os.path.join(folder_dir, file_name)
|
|
||||||
dest_path = os.path.join("User", folder, file_name)
|
|
||||||
self.copy_file_from_repo(src_path, dest_path)
|
|
||||||
continue # 跳过后续检查,直接复制
|
|
||||||
|
|
||||||
# 检查是否选中了对应的文件
|
|
||||||
if file_base in self.header_file_vars and self.header_file_vars[file_base].get():
|
|
||||||
src_path = os.path.join(folder_dir, file_name)
|
|
||||||
dest_path = os.path.join("User", folder, file_name)
|
|
||||||
self.copy_file_from_repo(src_path, dest_path)
|
|
||||||
|
|
||||||
threading.Thread(target=task).start()
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# 创建必要的目录
|
|
||||||
def create_directories(self):
|
|
||||||
try:
|
|
||||||
directories = [
|
|
||||||
"User/bsp",
|
|
||||||
"User/component",
|
|
||||||
"User/device",
|
|
||||||
"User/module",
|
|
||||||
]
|
|
||||||
# 根据是否启用 FreeRTOS 决定是否创建 User/task
|
|
||||||
if self.ioc_data and self.check_freertos_enabled(self.ioc_data):
|
|
||||||
directories.append("User/task")
|
|
||||||
|
|
||||||
for directory in directories:
|
|
||||||
if not os.path.exists(directory):
|
|
||||||
os.makedirs(directory, exist_ok=True)
|
|
||||||
print(f"已创建目录: {directory}")
|
|
||||||
else:
|
|
||||||
print(f"目录已存在: {directory}")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"创建目录时出错: {e}")
|
|
||||||
|
|
||||||
|
|
||||||
# 更新 FreeRTOS 状态标签
|
|
||||||
def update_freertos_status(self, label):
|
|
||||||
if self.ioc_data:
|
|
||||||
status = "已启用" if self.check_freertos_enabled(self.ioc_data) else "未启用"
|
|
||||||
else:
|
|
||||||
status = "未检测到 .ioc 文件"
|
|
||||||
label.config(text=f"FreeRTOS 状态: {status}")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# 显示主窗口
|
|
||||||
# ...existing code...
|
|
||||||
# ...existing code...
|
|
||||||
|
|
||||||
# 显示主窗口
|
|
||||||
def show_main_window(self):
|
|
||||||
root = tk.Tk()
|
|
||||||
root.title("MRobot 自动生成脚本")
|
|
||||||
root.geometry("1000x650") # 调整窗口大小以适应布局
|
|
||||||
|
|
||||||
# 在窗口关闭时调用 on_closing 方法
|
|
||||||
root.protocol("WM_DELETE_WINDOW", lambda: self.on_closing(root))
|
|
||||||
|
|
||||||
# 初始化 BooleanVar
|
|
||||||
self.add_gitignore_var = tk.BooleanVar(value=False)
|
|
||||||
self.auto_configure_var = tk.BooleanVar(value=False) # 新增复选框变量
|
|
||||||
|
|
||||||
# 创建主框架
|
|
||||||
main_frame = ttk.Frame(root)
|
|
||||||
main_frame.pack(fill="both", expand=True)
|
|
||||||
|
|
||||||
# 添加标题
|
|
||||||
title_label = ttk.Label(main_frame, text="MRobot 自动生成脚本", font=("Arial", 16, "bold"))
|
|
||||||
title_label.pack(pady=10)
|
|
||||||
|
|
||||||
# 添加 FreeRTOS 状态标签
|
|
||||||
freertos_status_label = ttk.Label(main_frame, text="FreeRTOS 状态: 检测中...", font=("Arial", 12))
|
|
||||||
freertos_status_label.pack(pady=10)
|
|
||||||
self.update_freertos_status(freertos_status_label)
|
|
||||||
|
|
||||||
# 模块文件选择和任务管理框架(添加滚动功能)
|
|
||||||
module_task_frame = ttk.Frame(main_frame)
|
|
||||||
module_task_frame.pack(fill="both", expand=True, padx=10, pady=10)
|
|
||||||
|
|
||||||
# 创建 Canvas 和 Scrollbar
|
|
||||||
canvas = tk.Canvas(module_task_frame)
|
|
||||||
scrollbar = ttk.Scrollbar(module_task_frame, orient="vertical", command=canvas.yview)
|
|
||||||
scrollable_frame = ttk.Frame(canvas)
|
|
||||||
|
|
||||||
# 配置滚动区域
|
|
||||||
scrollable_frame.bind(
|
|
||||||
"<Configure>",
|
|
||||||
lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
|
|
||||||
)
|
|
||||||
canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
|
|
||||||
canvas.configure(yscrollcommand=scrollbar.set)
|
|
||||||
|
|
||||||
# 绑定鼠标滚轮事件
|
|
||||||
def on_mouse_wheel(event):
|
|
||||||
canvas.yview_scroll(-1 * int(event.delta / 120), "units")
|
|
||||||
|
|
||||||
canvas.bind_all("<MouseWheel>", on_mouse_wheel)
|
|
||||||
|
|
||||||
# 布局 Canvas 和 Scrollbar
|
|
||||||
canvas.pack(side="left", fill="both", expand=True)
|
|
||||||
scrollbar.pack(side="right", fill="y")
|
|
||||||
|
|
||||||
# 左右布局:模块文件选择框和任务管理框
|
|
||||||
left_frame = ttk.Frame(scrollable_frame)
|
|
||||||
left_frame.pack(side="left", fill="both", expand=True, padx=5, pady=5)
|
|
||||||
|
|
||||||
right_frame = ttk.Frame(scrollable_frame)
|
|
||||||
right_frame.pack(side="right", fill="both", expand=True, padx=5, pady=5)
|
|
||||||
|
|
||||||
# 模块文件选择框
|
|
||||||
header_files_frame = ttk.LabelFrame(left_frame, text="模块文件选择", padding=(10, 10))
|
|
||||||
header_files_frame.pack(fill="both", expand=True, padx=5)
|
|
||||||
self.header_files_frame = header_files_frame
|
|
||||||
self.update_header_files()
|
|
||||||
|
|
||||||
# 任务管理框
|
|
||||||
if self.ioc_data and self.check_freertos_enabled(self.ioc_data):
|
|
||||||
task_frame = ttk.LabelFrame(right_frame, text="任务管理", padding=(10, 10))
|
|
||||||
task_frame.pack(fill="both", expand=True, padx=5)
|
|
||||||
self.task_frame = task_frame
|
|
||||||
self.update_task_ui()
|
|
||||||
|
|
||||||
# 添加消息框和生成按钮在同一行
|
|
||||||
bottom_frame = ttk.Frame(main_frame)
|
|
||||||
bottom_frame.pack(fill="x", pady=10, side="bottom")
|
|
||||||
|
|
||||||
# 消息框
|
|
||||||
self.message_box = tk.Text(bottom_frame, wrap="word", state="disabled", height=5, width=60)
|
|
||||||
self.message_box.pack(side="left", fill="x", expand=True, padx=5, pady=5)
|
|
||||||
|
|
||||||
# 生成按钮和复选框选项
|
|
||||||
button_frame = ttk.Frame(bottom_frame)
|
|
||||||
button_frame.pack(side="right", padx=10)
|
|
||||||
|
|
||||||
# 添加复选框容器(横向排列复选框)
|
|
||||||
checkbox_frame = ttk.Frame(button_frame)
|
|
||||||
checkbox_frame.pack(side="top", pady=5)
|
|
||||||
|
|
||||||
# 添加 .gitignore 复选框(左侧)
|
|
||||||
ttk.Checkbutton(checkbox_frame, text=".gitignore", variable=self.add_gitignore_var).pack(side="left", padx=5)
|
|
||||||
|
|
||||||
# 添加自动配置环境复选框(右侧)
|
|
||||||
ttk.Checkbutton(checkbox_frame, text="自动环境", variable=self.auto_configure_var).pack(side="left", padx=5)
|
|
||||||
|
|
||||||
# 添加生成按钮(竖向排列在复选框下方)
|
|
||||||
generate_button = ttk.Button(button_frame, text="一键生成MRobot代码", command=self.generate_action)
|
|
||||||
generate_button.pack(side="top", pady=10)
|
|
||||||
generate_button.config(width=25) # 设置按钮宽度
|
|
||||||
|
|
||||||
# 重定向输出到消息框
|
|
||||||
self.redirect_output()
|
|
||||||
|
|
||||||
# 打印欢迎信息
|
|
||||||
print("欢迎使用 MRobot 自动生成脚本!")
|
|
||||||
print("请根据需要选择模块文件和任务。")
|
|
||||||
print("点击“一键生成MRobot代码”按钮开始生成。")
|
|
||||||
|
|
||||||
# 启动 Tkinter 主事件循环
|
|
||||||
root.mainloop()
|
|
||||||
|
|
||||||
# ...existing code...
|
|
||||||
# ...existing code...
|
|
||||||
|
|
||||||
def redirect_output(self):
|
|
||||||
"""
|
|
||||||
重定向标准输出到消息框
|
|
||||||
"""
|
|
||||||
class TextRedirector:
|
|
||||||
def __init__(self, text_widget):
|
|
||||||
self.text_widget = text_widget
|
|
||||||
|
|
||||||
def write(self, message):
|
|
||||||
self.text_widget.config(state="normal")
|
|
||||||
self.text_widget.insert("end", message)
|
|
||||||
self.text_widget.see("end")
|
|
||||||
self.text_widget.config(state="disabled")
|
|
||||||
|
|
||||||
def flush(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
sys.stdout = TextRedirector(self.message_box)
|
|
||||||
sys.stderr = TextRedirector(self.message_box)
|
|
||||||
|
|
||||||
# 修改 update_task_ui 方法
|
|
||||||
def update_task_ui(self):
|
|
||||||
# 检查是否有已存在的任务文件
|
|
||||||
task_dir = os.path.join("User", "task")
|
|
||||||
if os.path.exists(task_dir):
|
|
||||||
for file_name in os.listdir(task_dir):
|
|
||||||
file_base, file_ext = os.path.splitext(file_name)
|
|
||||||
if file_ext == ".c" and file_base not in ["init", "user_task"] and file_base not in [task_var.get() for task_var, _ in self.task_vars]:
|
|
||||||
frequency = 100 # 默认频率
|
|
||||||
user_task_header_path = os.path.join("User", "task", "user_task.h")
|
|
||||||
if os.path.exists(user_task_header_path):
|
|
||||||
try:
|
|
||||||
with open(user_task_header_path, "r", encoding="utf-8") as f:
|
|
||||||
content = f.read()
|
|
||||||
pattern = rf"#define\s+TASK_FREQ_{file_base.upper()}\s*\((\d+)[uU]?\)"
|
|
||||||
match = re.search(pattern, content)
|
|
||||||
if match:
|
|
||||||
frequency = int(match.group(1))
|
|
||||||
print(f"从 user_task.h 文件中读取到任务 {file_base} 的频率: {frequency}")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"读取 user_task.h 文件时出错: {e}")
|
|
||||||
|
|
||||||
new_task_var = tk.StringVar(value=file_base)
|
|
||||||
self.task_vars.append((new_task_var, tk.IntVar(value=frequency)))
|
|
||||||
|
|
||||||
# 清空任务框架中的所有子组件
|
|
||||||
for widget in self.task_frame.winfo_children():
|
|
||||||
widget.destroy()
|
|
||||||
|
|
||||||
|
|
||||||
# 设置任务管理框的固定宽度
|
|
||||||
self.task_frame.config(width=400)
|
|
||||||
|
|
||||||
# 显示任务列表
|
|
||||||
for i, (task_var, freq_var) in enumerate(self.task_vars):
|
|
||||||
task_row = ttk.Frame(self.task_frame, width=400)
|
|
||||||
task_row.pack(fill="x", pady=5)
|
|
||||||
|
|
||||||
ttk.Entry(task_row, textvariable=task_var, width=20).pack(side="left", padx=5)
|
|
||||||
ttk.Label(task_row, text="频率:").pack(side="left", padx=5)
|
|
||||||
ttk.Spinbox(task_row, from_=1, to=1000, textvariable=freq_var, width=5).pack(side="left", padx=5)
|
|
||||||
ttk.Button(task_row, text="删除", command=lambda idx=i: self.remove_task(idx)).pack(side="left", padx=5)
|
|
||||||
|
|
||||||
# 添加新任务按钮
|
|
||||||
add_task_button = ttk.Button(self.task_frame, text="添加任务", command=self.add_task)
|
|
||||||
add_task_button.pack(pady=10)
|
|
||||||
|
|
||||||
|
|
||||||
# 修改 add_task 方法
|
|
||||||
def add_task(self):
|
|
||||||
new_task_var = tk.StringVar(value=f"Task_{len(self.task_vars) + 1}")
|
|
||||||
new_freq_var = tk.IntVar(value=100) # 默认频率为 100
|
|
||||||
self.task_vars.append((new_task_var, new_freq_var))
|
|
||||||
self.update_task_ui()
|
|
||||||
|
|
||||||
# 修改 remove_task 方法
|
|
||||||
def remove_task(self, idx):
|
|
||||||
del self.task_vars[idx]
|
|
||||||
self.update_task_ui()
|
|
||||||
|
|
||||||
# 更新文件夹显示
|
|
||||||
def update_folder_display(self):
|
|
||||||
for widget in self.folder_frame.winfo_children():
|
|
||||||
widget.destroy()
|
|
||||||
|
|
||||||
folders = ["User/bsp", "User/component", "User/device", "User/module"]
|
|
||||||
# if self.ioc_data and self.check_freertos_enabled(self.ioc_data):
|
|
||||||
# folders.append("User/task")
|
|
||||||
|
|
||||||
for folder in folders:
|
|
||||||
# 去掉 "User/" 前缀
|
|
||||||
display_name = folder.replace("User/", "")
|
|
||||||
tk.Label(self.folder_frame, text=display_name).pack()
|
|
||||||
|
|
||||||
# 更新 .h 文件复选框
|
|
||||||
def update_header_files(self):
|
|
||||||
for widget in self.header_files_frame.winfo_children():
|
|
||||||
widget.destroy()
|
|
||||||
|
|
||||||
folders = ["bsp", "component", "device", "module"]
|
|
||||||
dependencies = defaultdict(list)
|
|
||||||
|
|
||||||
for folder in folders:
|
|
||||||
folder_dir = os.path.join(REPO_DIR, "User", folder)
|
|
||||||
if os.path.exists(folder_dir):
|
|
||||||
dependencies_file = os.path.join(folder_dir, "dependencies.csv")
|
|
||||||
if os.path.exists(dependencies_file):
|
|
||||||
with open(dependencies_file, "r", encoding="utf-8") as f:
|
|
||||||
reader = csv.reader(f)
|
|
||||||
for row in reader:
|
|
||||||
if len(row) == 2:
|
|
||||||
dependencies[row[0]].append(row[1])
|
|
||||||
|
|
||||||
# 创建复选框
|
|
||||||
for folder in folders:
|
|
||||||
folder_dir = os.path.join(REPO_DIR, "User", folder)
|
|
||||||
if os.path.exists(folder_dir):
|
|
||||||
module_frame = ttk.LabelFrame(self.header_files_frame, text=folder.capitalize(), padding=(10, 10))
|
|
||||||
module_frame.pack(fill="x", pady=5)
|
|
||||||
|
|
||||||
row, col = 0, 0
|
|
||||||
for file in os.listdir(folder_dir):
|
|
||||||
file_base, file_ext = os.path.splitext(file)
|
|
||||||
if file_ext == ".h" and file_base != folder:
|
|
||||||
var = tk.BooleanVar(value=False)
|
|
||||||
self.header_file_vars[file_base] = var
|
|
||||||
|
|
||||||
checkbox = ttk.Checkbutton(
|
|
||||||
module_frame,
|
|
||||||
text=file_base,
|
|
||||||
variable=var,
|
|
||||||
command=lambda fb=file_base: self.handle_dependencies(fb, dependencies)
|
|
||||||
)
|
|
||||||
checkbox.grid(row=row, column=col, padx=5, pady=5, sticky="w")
|
|
||||||
col += 1
|
|
||||||
if col >= 6:
|
|
||||||
col = 0
|
|
||||||
row += 1
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def handle_dependencies(self, file_base, dependencies):
|
|
||||||
"""
|
|
||||||
根据依赖关系自动勾选相关模块
|
|
||||||
"""
|
|
||||||
if file_base in self.header_file_vars and self.header_file_vars[file_base].get():
|
|
||||||
# 如果当前模块被选中,自动勾选其依赖项
|
|
||||||
for dependency in dependencies.get(file_base, []):
|
|
||||||
dep_base = os.path.basename(dependency)
|
|
||||||
if dep_base in self.header_file_vars:
|
|
||||||
self.header_file_vars[dep_base].set(True)
|
|
||||||
|
|
||||||
# 在 MRobotApp 类中添加以下方法
|
|
||||||
def generate_task_files(self):
|
|
||||||
try:
|
|
||||||
template_file_path = os.path.join(REPO_DIR, "User", "task", "task.c.template")
|
|
||||||
task_dir = os.path.join("User", "task")
|
|
||||||
|
|
||||||
if not os.path.exists(template_file_path):
|
|
||||||
print(f"模板文件 {template_file_path} 不存在,无法生成 task.c 文件!")
|
|
||||||
return
|
|
||||||
|
|
||||||
os.makedirs(task_dir, exist_ok=True)
|
|
||||||
|
|
||||||
with open(template_file_path, "r", encoding="utf-8") as f:
|
|
||||||
template_content = f.read()
|
|
||||||
|
|
||||||
# 为每个任务生成对应的 task.c 文件
|
|
||||||
for task_var, _ in self.task_vars: # 解包元组
|
|
||||||
task_name = f"Task_{task_var.get()}" # 添加前缀 Task_
|
|
||||||
task_file_path = os.path.join(task_dir, f"{task_var.get().lower()}.c") # 文件名保持原始小写
|
|
||||||
|
|
||||||
# 替换模板中的占位符
|
|
||||||
task_content = template_content.replace("{{task_name}}", task_name)
|
|
||||||
task_content = task_content.replace("{{task_function}}", task_name)
|
|
||||||
task_content = task_content.replace(
|
|
||||||
"{{task_frequency}}", f"TASK_FREQ_{task_var.get().upper()}"
|
|
||||||
) # 替换为 user_task.h 中的宏定义
|
|
||||||
task_content = task_content.replace("{{task_delay}}", f"TASK_INIT_DELAY_{task_var.get().upper()}")
|
|
||||||
|
|
||||||
with open(task_file_path, "w", encoding="utf-8") as f:
|
|
||||||
f.write(task_content)
|
|
||||||
|
|
||||||
print(f"已成功生成 {task_file_path} 文件!")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"生成 task.c 文件时出错: {e}")
|
|
||||||
# 修改 user_task.c 文件
|
|
||||||
def modify_user_task_file(self):
|
|
||||||
try:
|
|
||||||
template_file_path = os.path.join(REPO_DIR, "User", "task", "user_task.c.template")
|
|
||||||
generated_task_file_path = os.path.join("User", "task", "user_task.c")
|
|
||||||
|
|
||||||
if not os.path.exists(template_file_path):
|
|
||||||
print(f"模板文件 {template_file_path} 不存在,无法生成 user_task.c 文件!")
|
|
||||||
return
|
|
||||||
|
|
||||||
os.makedirs(os.path.dirname(generated_task_file_path), exist_ok=True)
|
|
||||||
|
|
||||||
with open(template_file_path, "r", encoding="utf-8") as f:
|
|
||||||
template_content = f.read()
|
|
||||||
|
|
||||||
# 生成任务属性定义
|
|
||||||
task_attr_definitions = "\n".join([
|
|
||||||
f"""const osThreadAttr_t attr_{task_var.get().lower()} = {{
|
|
||||||
.name = "{task_var.get()}",
|
|
||||||
.priority = osPriorityNormal,
|
|
||||||
.stack_size = 128 * 4,
|
|
||||||
}};"""
|
|
||||||
for task_var, _ in self.task_vars # 解包元组
|
|
||||||
])
|
|
||||||
|
|
||||||
# 替换模板中的占位符
|
|
||||||
task_content = template_content.replace("{{task_attr_definitions}}", task_attr_definitions)
|
|
||||||
|
|
||||||
with open(generated_task_file_path, "w", encoding="utf-8") as f:
|
|
||||||
f.write(task_content)
|
|
||||||
|
|
||||||
print(f"已成功生成 {generated_task_file_path} 文件!")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"修改 user_task.c 文件时出错: {e}")
|
|
||||||
# ...existing code...
|
|
||||||
|
|
||||||
def generate_user_task_header(self):
|
|
||||||
try:
|
|
||||||
template_file_path = os.path.join(REPO_DIR, "User", "task", "user_task.h.template")
|
|
||||||
header_file_path = os.path.join("User", "task", "user_task.h")
|
|
||||||
|
|
||||||
if not os.path.exists(template_file_path):
|
|
||||||
print(f"模板文件 {template_file_path} 不存在,无法生成 user_task.h 文件!")
|
|
||||||
return
|
|
||||||
|
|
||||||
os.makedirs(os.path.dirname(header_file_path), exist_ok=True)
|
|
||||||
|
|
||||||
# 如果 user_task.h 已存在,提取 /* USER MESSAGE BEGIN */ 和 /* USER MESSAGE END */ 区域内容
|
|
||||||
existing_msgq_content = ""
|
|
||||||
if os.path.exists(header_file_path):
|
|
||||||
with open(header_file_path, "r", encoding="utf-8") as f:
|
|
||||||
content = f.read()
|
|
||||||
# 提取 /* USER MESSAGE BEGIN */ 和 /* USER MESSAGE END */ 区域内容
|
|
||||||
match = re.search(r"/\* USER MESSAGE BEGIN \*/\s*(.*?)\s*/\* USER MESSAGE END \*/", content, re.DOTALL)
|
|
||||||
if match:
|
|
||||||
existing_msgq_content = match.group(1).strip()
|
|
||||||
print("已存在的 msgq 区域内容:")
|
|
||||||
print(existing_msgq_content)
|
|
||||||
|
|
||||||
with open(template_file_path, "r", encoding="utf-8") as f:
|
|
||||||
template_content = f.read()
|
|
||||||
|
|
||||||
# 定义占位符内容
|
|
||||||
thread_definitions = "\n".join([f" osThreadId_t {task_var.get().lower()};" for task_var, _ in self.task_vars])
|
|
||||||
msgq_definitions = existing_msgq_content if existing_msgq_content else " osMessageQueueId_t default_msgq;"
|
|
||||||
freq_definitions = "\n".join([f" float {task_var.get().lower()};" for task_var, _ in self.task_vars])
|
|
||||||
last_up_time_definitions = "\n".join([f" uint32_t {task_var.get().lower()};" for task_var, _ in self.task_vars])
|
|
||||||
task_attr_declarations = "\n".join([f"extern const osThreadAttr_t attr_{task_var.get().lower()};" for task_var, _ in self.task_vars])
|
|
||||||
task_function_declarations = "\n".join([f"void Task_{task_var.get()}(void *argument);" for task_var, _ in self.task_vars])
|
|
||||||
task_frequency_definitions = "\n".join([
|
|
||||||
f"#define TASK_FREQ_{task_var.get().upper()} ({freq_var.get()}u)"
|
|
||||||
for task_var, freq_var in self.task_vars
|
|
||||||
])
|
|
||||||
task_init_delay_definitions = "\n".join([f"#define TASK_INIT_DELAY_{task_var.get().upper()} (0u)" for task_var, _ in self.task_vars])
|
|
||||||
task_handle_definitions = "\n".join([f" osThreadId_t {task_var.get().lower()};" for task_var, _ in self.task_vars])
|
|
||||||
|
|
||||||
# 替换模板中的占位符
|
|
||||||
header_content = template_content.replace("{{thread_definitions}}", thread_definitions)
|
|
||||||
header_content = header_content.replace("{{msgq_definitions}}", msgq_definitions)
|
|
||||||
header_content = header_content.replace("{{freq_definitions}}", freq_definitions)
|
|
||||||
header_content = header_content.replace("{{last_up_time_definitions}}", last_up_time_definitions)
|
|
||||||
header_content = header_content.replace("{{task_attr_declarations}}", task_attr_declarations)
|
|
||||||
header_content = header_content.replace("{{task_function_declarations}}", task_function_declarations)
|
|
||||||
header_content = header_content.replace("{{task_frequency_definitions}}", task_frequency_definitions)
|
|
||||||
header_content = header_content.replace("{{task_init_delay_definitions}}", task_init_delay_definitions)
|
|
||||||
header_content = header_content.replace("{{task_handle_definitions}}", task_handle_definitions)
|
|
||||||
|
|
||||||
# 如果存在 /* USER MESSAGE BEGIN */ 区域内容,则保留
|
|
||||||
if existing_msgq_content:
|
|
||||||
header_content = re.sub(
|
|
||||||
r"/\* USER MESSAGE BEGIN \*/\s*.*?\s*/\* USER MESSAGE END \*/",
|
|
||||||
f"/* USER MESSAGE BEGIN */\n\n {existing_msgq_content}\n\n /* USER MESSAGE END */",
|
|
||||||
header_content,
|
|
||||||
flags=re.DOTALL
|
|
||||||
)
|
|
||||||
|
|
||||||
with open(header_file_path, "w", encoding="utf-8") as f:
|
|
||||||
f.write(header_content)
|
|
||||||
|
|
||||||
print(f"已成功生成 {header_file_path} 文件!")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"生成 user_task.h 文件时出错: {e}")
|
|
||||||
|
|
||||||
def generate_init_file(self):
|
|
||||||
try:
|
|
||||||
template_file_path = os.path.join(REPO_DIR, "User", "task", "init.c.template")
|
|
||||||
generated_file_path = os.path.join("User", "task", "init.c")
|
|
||||||
|
|
||||||
if not os.path.exists(template_file_path):
|
|
||||||
print(f"模板文件 {template_file_path} 不存在,无法生成 init.c 文件!")
|
|
||||||
return
|
|
||||||
|
|
||||||
os.makedirs(os.path.dirname(generated_file_path), exist_ok=True)
|
|
||||||
|
|
||||||
# 如果 init.c 已存在,提取 /* USER MESSAGE BEGIN */ 和 /* USER MESSAGE END */ 区域内容
|
|
||||||
existing_msgq_content = ""
|
|
||||||
if os.path.exists(generated_file_path):
|
|
||||||
with open(generated_file_path, "r", encoding="utf-8") as f:
|
|
||||||
content = f.read()
|
|
||||||
# 提取 /* USER MESSAGE BEGIN */ 和 /* USER MESSAGE END */ 区域内容
|
|
||||||
match = re.search(r"/\* USER MESSAGE BEGIN \*/\s*(.*?)\s*/\* USER MESSAGE END \*/", content, re.DOTALL)
|
|
||||||
if match:
|
|
||||||
existing_msgq_content = match.group(1).strip()
|
|
||||||
print("已存在的消息队列区域内容:")
|
|
||||||
print(existing_msgq_content)
|
|
||||||
|
|
||||||
with open(template_file_path, "r", encoding="utf-8") as f:
|
|
||||||
template_content = f.read()
|
|
||||||
|
|
||||||
# 生成任务创建代码
|
|
||||||
thread_creation_code = "\n".join([
|
|
||||||
f" task_runtime.thread.{task_var.get().lower()} = osThreadNew(Task_{task_var.get()}, NULL, &attr_{task_var.get().lower()});"
|
|
||||||
for task_var, _ in self.task_vars # 解包元组
|
|
||||||
])
|
|
||||||
|
|
||||||
# 替换模板中的占位符
|
|
||||||
init_content = template_content.replace("{{thread_creation_code}}", thread_creation_code)
|
|
||||||
|
|
||||||
# 如果存在 /* USER MESSAGE BEGIN */ 区域内容,则保留
|
|
||||||
if existing_msgq_content:
|
|
||||||
init_content = re.sub(
|
|
||||||
r"/\* USER MESSAGE BEGIN \*/\s*.*?\s*/\* USER MESSAGE END \*/",
|
|
||||||
f"/* USER MESSAGE BEGIN */\n {existing_msgq_content}\n /* USER MESSAGE END */",
|
|
||||||
init_content,
|
|
||||||
flags=re.DOTALL
|
|
||||||
)
|
|
||||||
|
|
||||||
with open(generated_file_path, "w", encoding="utf-8") as f:
|
|
||||||
f.write(init_content)
|
|
||||||
|
|
||||||
print(f"已成功生成 {generated_file_path} 文件!")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"生成 init.c 文件时出错: {e}")
|
|
||||||
|
|
||||||
# 修改 generate_action 方法
|
|
||||||
|
|
||||||
def generate_action(self):
|
|
||||||
def task():
|
|
||||||
# 检查并创建目录(与 FreeRTOS 状态无关的模块始终创建)
|
|
||||||
self.create_directories()
|
|
||||||
|
|
||||||
# 复制 .gitignore 文件
|
|
||||||
if self.add_gitignore_var.get():
|
|
||||||
self.copy_file_from_repo(".gitignore", ".gitignore")
|
|
||||||
|
|
||||||
# 如果启用了 FreeRTOS,复制相关文件
|
|
||||||
if self.ioc_data and self.check_freertos_enabled(self.ioc_data):
|
|
||||||
self.copy_file_from_repo("src/freertos.c", os.path.join("Core", "Src", "freertos.c"))
|
|
||||||
|
|
||||||
# 定义需要处理的文件夹(与 FreeRTOS 状态无关)
|
|
||||||
folders = ["bsp", "component", "device", "module"]
|
|
||||||
|
|
||||||
# 遍历每个文件夹,复制选中的 .h 和 .c 文件
|
|
||||||
for folder in folders:
|
|
||||||
folder_dir = os.path.join(REPO_DIR, "User", folder)
|
|
||||||
if not os.path.exists(folder_dir):
|
|
||||||
continue # 如果文件夹不存在,跳过
|
|
||||||
|
|
||||||
for file_name in os.listdir(folder_dir):
|
|
||||||
file_base, file_ext = os.path.splitext(file_name)
|
|
||||||
if file_ext not in [".h", ".c"]:
|
|
||||||
continue # 只处理 .h 和 .c 文件
|
|
||||||
|
|
||||||
# 强制复制与文件夹同名的文件
|
|
||||||
if file_base == folder:
|
|
||||||
src_path = os.path.join(folder_dir, file_name)
|
|
||||||
dest_path = os.path.join("User", folder, file_name)
|
|
||||||
self.copy_file_from_repo(src_path, dest_path)
|
|
||||||
print(f"强制复制与文件夹同名的文件: {file_name}")
|
|
||||||
continue # 跳过后续检查,直接复制
|
|
||||||
|
|
||||||
# 检查是否选中了对应的文件
|
|
||||||
if file_base in self.header_file_vars and self.header_file_vars[file_base].get():
|
|
||||||
src_path = os.path.join(folder_dir, file_name)
|
|
||||||
dest_path = os.path.join("User", folder, file_name)
|
|
||||||
self.copy_file_from_repo(src_path, dest_path)
|
|
||||||
|
|
||||||
# 如果启用了 FreeRTOS,执行任务相关的生成逻辑
|
|
||||||
if self.ioc_data and self.check_freertos_enabled(self.ioc_data):
|
|
||||||
# 修改 user_task.c 文件
|
|
||||||
self.modify_user_task_file()
|
|
||||||
|
|
||||||
# 生成 user_task.h 文件
|
|
||||||
self.generate_user_task_header()
|
|
||||||
|
|
||||||
# 生成 init.c 文件
|
|
||||||
self.generate_init_file()
|
|
||||||
|
|
||||||
# 生成 task.c 文件
|
|
||||||
self.generate_task_files()
|
|
||||||
|
|
||||||
# # 自动配置环境
|
|
||||||
# if self.auto_configure_var.get():
|
|
||||||
|
|
||||||
# self.auto_configure_environment()
|
|
||||||
|
|
||||||
|
|
||||||
threading.Thread(target=task).start()
|
|
||||||
|
|
||||||
# 程序关闭时清理
|
|
||||||
def on_closing(self, root):
|
|
||||||
self.delete_repo()
|
|
||||||
root.destroy()
|
|
||||||
|
|
||||||
|
|
||||||
# 程序入口
|
|
||||||
if __name__ == "__main__":
|
|
||||||
app = MRobotApp()
|
|
||||||
app.initialize()
|
|
32
README.md
32
README.md
@ -91,3 +91,35 @@ pyinstaller --onefile --windowed
|
|||||||
pyinstaller MR_Toolbox.py --onefile --noconsole --icon=img\M.ico --add-data "mr_tool_img\MRobot.png;mr_tool_img"
|
pyinstaller MR_Toolbox.py --onefile --noconsole --icon=img\M.ico --add-data "mr_tool_img\MRobot.png;mr_tool_img"
|
||||||
|
|
||||||
pyinstaller MR_Tool.py --onefile --noconsole --icon=img\M.ico --add-data "mr_tool_img\MRobot.png;mr_tool_img" --add-data "src;src" --add-data "User;User"
|
pyinstaller MR_Tool.py --onefile --noconsole --icon=img\M.ico --add-data "mr_tool_img\MRobot.png;mr_tool_img" --add-data "src;src" --add-data "User;User"
|
||||||
|
|
||||||
|
pyinstaller --noconfirm --onefile --windowed ^
|
||||||
|
--add-data "User_code;User_code" ^
|
||||||
|
--add-data "img;img" ^
|
||||||
|
--icon "img\M.ico" ^
|
||||||
|
MRobot.py
|
||||||
|
|
||||||
|
|
||||||
|
pyinstaller --noconfirm --onefile --windowed --add-data "img;img" --add-data "User_code;User_code" --add-data "mech_lib;mech_lib" --icon=img/MRobot.ico MRobot.py
|
||||||
|
|
||||||
|
python3 -m PyInstaller --noconfirm --onefile --windowed \
|
||||||
|
--add-data "img:img" \
|
||||||
|
--add-data "User_code:User_code" \
|
||||||
|
--add-data "mech_lib:mech_lib" \
|
||||||
|
--icon=img/MRobot.ico \
|
||||||
|
MRobot.py
|
||||||
|
|
||||||
|
|
||||||
|
python3 -m PyInstaller --windowed --name MRobot \
|
||||||
|
--add-data "img:MRobot.app/Contents/Resources/img" \
|
||||||
|
--add-data "User_code:MRobot.app/Contents/Resources/User_code" \
|
||||||
|
--add-data "mech_lib:MRobot.app/Contents/Resources/mech_lib" \
|
||||||
|
MRobot.py
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
pyinstaller --noconfirm --onefile --windowed --add-data "img;img" --add-data "User_code;User_code" --icon=img/M.ico MRobot.py
|
||||||
|
|
||||||
|
|
||||||
|
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
|
BIN
User_code/.DS_Store
vendored
BIN
User_code/.DS_Store
vendored
Binary file not shown.
2
User_code/bsp/describe.csv
Normal file
2
User_code/bsp/describe.csv
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
uart,要求开启dma和中断
|
||||||
|
can,要求开启can的中断
|
|
1
User_code/component/describe.csv
Normal file
1
User_code/component/describe.csv
Normal file
@ -0,0 +1 @@
|
|||||||
|
pid,好用的
|
|
1
User_code/device/describe.csv
Normal file
1
User_code/device/describe.csv
Normal file
@ -0,0 +1 @@
|
|||||||
|
servo,测试消息测试消息测试消息测试消息测试消息测试消息测试消息测试消息测试消息测试消息测试消息测试消息测试消息测试消息测试消息
|
|
5
User_code/module/dependencies.csv
Normal file
5
User_code/module/dependencies.csv
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
oled_i2c,bsp/i2c
|
||||||
|
bmp280_i2c,bsp/i2c
|
||||||
|
pc_uart,bsp/uart
|
||||||
|
key_gpio,bsp/gpio_exti
|
||||||
|
servo,bsp/servo_pwm
|
|
0
User_code/module/describe.csv
Normal file
0
User_code/module/describe.csv
Normal file
|
@ -1,9 +1,10 @@
|
|||||||
/*
|
/*
|
||||||
初始化任务
|
Init Task
|
||||||
|
任务初始化,创建各个线程任务和消息队列
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Includes ----------------------------------------------------------------- */
|
/* Includes ----------------------------------------------------------------- */
|
||||||
#include "task\user_task.h"
|
#include "task/user_task.h"
|
||||||
|
|
||||||
/* USER INCLUDE BEGIN */
|
/* USER INCLUDE BEGIN */
|
||||||
|
|
||||||
@ -23,16 +24,19 @@
|
|||||||
*/
|
*/
|
||||||
void Task_Init(void *argument) {
|
void Task_Init(void *argument) {
|
||||||
(void)argument; /* 未使用argument,消除警告 */
|
(void)argument; /* 未使用argument,消除警告 */
|
||||||
|
/* USER CODE BEGIN Task_Init */
|
||||||
|
|
||||||
osKernelLock(); // 锁定内核,防止任务切换
|
/* USER CODE END Task_Init */
|
||||||
|
osKernelLock(); /* 锁定内核,防止任务切换 */
|
||||||
|
|
||||||
// 创建线程
|
/* 创建任务线程 */
|
||||||
{{thread_creation_code}}
|
{{thread_creation_code}}
|
||||||
|
|
||||||
// 创建消息队列
|
// 创建消息队列
|
||||||
/* USER MESSAGE BEGIN */
|
/* USER MESSAGE BEGIN */
|
||||||
task_runtime.msgq.user_msg= osMessageQueueNew(2u, 10, NULL);
|
task_runtime.msgq.user_msg= osMessageQueueNew(2u, 10, NULL);
|
||||||
/* USER MESSAGE END */
|
/* USER MESSAGE END */
|
||||||
|
|
||||||
osKernelUnlock(); // 解锁内核
|
osKernelUnlock(); // 解锁内核
|
||||||
osThreadTerminate(osThreadGetId()); // 任务完成后结束自身
|
osThreadTerminate(osThreadGetId()); // 任务完成后结束自身
|
||||||
}
|
}
|
||||||
|
@ -1,22 +1,24 @@
|
|||||||
/*
|
/*
|
||||||
{{task_name}} Task
|
{{task_name}} Task
|
||||||
|
{{task_description}}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Includes ----------------------------------------------------------------- */
|
/* Includes ----------------------------------------------------------------- */
|
||||||
#include "task\user_task.h"
|
#include "task/user_task.h"
|
||||||
|
/* USER INCLUDE BEGIN*/
|
||||||
|
|
||||||
|
/* USER INCLUDE END*/
|
||||||
|
|
||||||
/* Private typedef ---------------------------------------------------------- */
|
/* Private typedef ---------------------------------------------------------- */
|
||||||
/* Private define ----------------------------------------------------------- */
|
/* Private define ----------------------------------------------------------- */
|
||||||
/* Private macro ------------------------------------------------------------ */
|
/* Private macro ------------------------------------------------------------ */
|
||||||
/* Private variables -------------------------------------------------------- */
|
/* Private variables -------------------------------------------------------- */
|
||||||
|
/* USER STRUCT BEGIN*/
|
||||||
|
|
||||||
|
/* USER STRUCT END*/
|
||||||
|
|
||||||
/* Private function --------------------------------------------------------- */
|
/* Private function --------------------------------------------------------- */
|
||||||
/* Exported functions ------------------------------------------------------- */
|
/* Exported functions ------------------------------------------------------- */
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief {{task_name}} Task
|
|
||||||
*
|
|
||||||
* \param argument 未使用
|
|
||||||
*/
|
|
||||||
void {{task_function}}(void *argument) {
|
void {{task_function}}(void *argument) {
|
||||||
(void)argument; /* 未使用argument,消除警告 */
|
(void)argument; /* 未使用argument,消除警告 */
|
||||||
|
|
||||||
@ -28,11 +30,9 @@ void {{task_function}}(void *argument) {
|
|||||||
uint32_t tick = osKernelGetTickCount(); /* 控制任务运行频率的计时 */
|
uint32_t tick = osKernelGetTickCount(); /* 控制任务运行频率的计时 */
|
||||||
while (1) {
|
while (1) {
|
||||||
tick += delay_tick; /* 计算下一个唤醒时刻 */
|
tick += delay_tick; /* 计算下一个唤醒时刻 */
|
||||||
|
/* USER CODE BEGIN */
|
||||||
|
|
||||||
/*User code begin*/
|
/* USER CODE END */
|
||||||
|
|
||||||
/*User code end*/
|
|
||||||
|
|
||||||
osDelayUntil(tick); /* 运行结束,等待下一次唤醒 */
|
osDelayUntil(tick); /* 运行结束,等待下一次唤醒 */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
#include "task\user_task.h"
|
#include "task/user_task.h"
|
||||||
|
|
||||||
Task_Runtime_t task_runtime;
|
Task_Runtime_t task_runtime;
|
||||||
|
|
||||||
@ -8,5 +8,5 @@ const osThreadAttr_t attr_init = {
|
|||||||
.stack_size = 256 * 4,
|
.stack_size = 256 * 4,
|
||||||
};
|
};
|
||||||
|
|
||||||
// USER TASK
|
/* User_task */
|
||||||
{{task_attr_definitions}}
|
{{task_attr_definitions}}
|
||||||
|
@ -3,12 +3,27 @@
|
|||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
/* Includes ----------------------------------------------------------------- */
|
||||||
#include <cmsis_os2.h>
|
#include <cmsis_os2.h>
|
||||||
#include "FreeRTOS.h"
|
#include "FreeRTOS.h"
|
||||||
#include "task.h"
|
#include "task.h"
|
||||||
|
|
||||||
// 定义任务运行时结构体
|
/* USER INCLUDE BEGIN */
|
||||||
|
|
||||||
|
/* USER INCLUDE END */
|
||||||
|
/* Exported constants ------------------------------------------------------- */
|
||||||
|
/* 任务运行频率 */
|
||||||
|
{{task_frequency_definitions}}
|
||||||
|
|
||||||
|
/* 任务初始化延时ms */
|
||||||
|
#define TASK_INIT_DELAY (100u)
|
||||||
|
{{task_init_delay_definitions}}
|
||||||
|
|
||||||
|
/* Exported defines --------------------------------------------------------- */
|
||||||
|
/* Exported macro ----------------------------------------------------------- */
|
||||||
|
/* Exported types ----------------------------------------------------------- */
|
||||||
|
|
||||||
|
/* 任务运行时结构体 */
|
||||||
typedef struct {
|
typedef struct {
|
||||||
/* 各任务,也可以叫做线程 */
|
/* 各任务,也可以叫做线程 */
|
||||||
struct {
|
struct {
|
||||||
@ -16,41 +31,47 @@ typedef struct {
|
|||||||
} thread;
|
} thread;
|
||||||
|
|
||||||
/* USER MESSAGE BEGIN */
|
/* USER MESSAGE BEGIN */
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
osMessageQueueId_t user_msg; /* 用户自定义任务消息队列 */
|
osMessageQueueId_t user_msg; /* 用户自定义任务消息队列 */
|
||||||
} msgq;
|
} msgq;
|
||||||
|
|
||||||
/* USER MESSAGE END */
|
/* USER MESSAGE END */
|
||||||
|
|
||||||
|
/* 机器人状态 */
|
||||||
|
struct {
|
||||||
|
float battery; /* 电池电量百分比 */
|
||||||
|
float vbat; /* 电池电压 */
|
||||||
|
float cpu_temp; /* CPU温度 */
|
||||||
|
} status;
|
||||||
|
|
||||||
|
/* USER CONFIG BEGIN */
|
||||||
|
|
||||||
|
/* USER CONFIG END */
|
||||||
|
|
||||||
|
/* 各任务的stack使用 */
|
||||||
|
struct {
|
||||||
|
{{stack_definitions}}
|
||||||
|
} stack_water_mark;
|
||||||
|
|
||||||
|
/* 各任务运行频率 */
|
||||||
struct {
|
struct {
|
||||||
{{freq_definitions}}
|
{{freq_definitions}}
|
||||||
} freq; /* 任务运行频率 */
|
} freq;
|
||||||
|
|
||||||
|
/* 任务最近运行时间 */
|
||||||
struct {
|
struct {
|
||||||
{{last_up_time_definitions}}
|
{{last_up_time_definitions}}
|
||||||
} last_up_time; /* 任务最近运行时间 */
|
} last_up_time;
|
||||||
|
|
||||||
} Task_Runtime_t;
|
} Task_Runtime_t;
|
||||||
|
|
||||||
// 任务频率
|
/* 任务运行时结构体 */
|
||||||
{{task_frequency_definitions}}
|
|
||||||
// 任务初始化延时
|
|
||||||
#define TASK_INIT_DELAY (100u)
|
|
||||||
{{task_init_delay_definitions}}
|
|
||||||
|
|
||||||
// 任务句柄
|
|
||||||
typedef struct {
|
|
||||||
{{task_handle_definitions}}
|
|
||||||
} Task_Handles_t;
|
|
||||||
|
|
||||||
// 任务运行时结构体
|
|
||||||
extern Task_Runtime_t task_runtime;
|
extern Task_Runtime_t task_runtime;
|
||||||
|
|
||||||
// 初始化任务句柄
|
/* 初始化任务句柄 */
|
||||||
extern const osThreadAttr_t attr_init;
|
extern const osThreadAttr_t attr_init;
|
||||||
{{task_attr_declarations}}
|
{{task_attr_declarations}}
|
||||||
|
|
||||||
// 任务函数声明
|
/* 任务函数声明 */
|
||||||
void Task_Init(void *argument);
|
void Task_Init(void *argument);
|
||||||
{{task_function_declarations}}
|
{{task_function_declarations}}
|
||||||
|
|
||||||
|
168
fluentui.py
168
fluentui.py
@ -1,168 +0,0 @@
|
|||||||
import sys
|
|
||||||
import webbrowser
|
|
||||||
import serial
|
|
||||||
import serial.tools.list_ports
|
|
||||||
|
|
||||||
from PyQt5.QtCore import Qt, QSize, pyqtSignal
|
|
||||||
from PyQt5.QtGui import QPixmap, QFont
|
|
||||||
from PyQt5.QtWidgets import (
|
|
||||||
QWidget, QVBoxLayout, QApplication, QLabel, QGroupBox, QGridLayout, QFrame,
|
|
||||||
QHBoxLayout, QComboBox, QTextEdit, QLineEdit
|
|
||||||
)
|
|
||||||
|
|
||||||
from qfluentwidgets import (
|
|
||||||
NavigationInterface, NavigationItemPosition, MessageBox,
|
|
||||||
setTheme, Theme, FluentWindow, NavigationAvatarWidget,
|
|
||||||
InfoBar, InfoBarPosition, PushButton, FluentIcon
|
|
||||||
)
|
|
||||||
from qfluentwidgets import FluentIcon as FIF
|
|
||||||
|
|
||||||
# ===================== 页面基类 =====================
|
|
||||||
class BaseInterface(QWidget):
|
|
||||||
"""所有页面的基类,页面内容完全自定义"""
|
|
||||||
def __init__(self, parent=None):
|
|
||||||
super().__init__(parent=parent)
|
|
||||||
|
|
||||||
# ===================== 首页界面 =====================
|
|
||||||
class HomeInterface(BaseInterface):
|
|
||||||
def __init__(self, parent=None):
|
|
||||||
super().__init__(parent=parent)
|
|
||||||
self.setObjectName("homeInterface")
|
|
||||||
layout = QVBoxLayout()
|
|
||||||
self.setLayout(layout)
|
|
||||||
# ===================== 代码生成页面 =====================
|
|
||||||
class DataInterface(BaseInterface):
|
|
||||||
def __init__(self, parent=None):
|
|
||||||
super().__init__(parent=parent)
|
|
||||||
self.setObjectName("dataInterface")
|
|
||||||
# 空页面示例
|
|
||||||
layout = QVBoxLayout()
|
|
||||||
self.setLayout(layout)
|
|
||||||
|
|
||||||
# ===================== 串口终端界面 =====================
|
|
||||||
class SerialTerminalInterface(BaseInterface):
|
|
||||||
def __init__(self, parent=None):
|
|
||||||
super().__init__(parent=parent)
|
|
||||||
self.setObjectName("serialTerminalInterface")
|
|
||||||
layout = QVBoxLayout()
|
|
||||||
|
|
||||||
# ===================== 设置界面 =====================
|
|
||||||
class SettingInterface(BaseInterface):
|
|
||||||
def __init__(self, parent=None):
|
|
||||||
super().__init__(parent=parent)
|
|
||||||
self.setObjectName("settingInterface")
|
|
||||||
layout = QVBoxLayout()
|
|
||||||
self.themeBtn = PushButton(
|
|
||||||
"切换夜间", self, FluentIcon.BRUSH
|
|
||||||
)
|
|
||||||
self.themeBtn.setFixedWidth(120)
|
|
||||||
self.themeBtn.clicked.connect(self.onThemeBtnClicked)
|
|
||||||
layout.addWidget(self.themeBtn)
|
|
||||||
layout.addStretch(1)
|
|
||||||
self.setLayout(layout)
|
|
||||||
|
|
||||||
# 监听主题变化
|
|
||||||
mw = self.window()
|
|
||||||
if hasattr(mw, "themeChanged"):
|
|
||||||
mw.themeChanged.connect(self.updateThemeBtn)
|
|
||||||
|
|
||||||
def onThemeBtnClicked(self):
|
|
||||||
mw = self.window()
|
|
||||||
if hasattr(mw, "toggleTheme"):
|
|
||||||
mw.toggleTheme()
|
|
||||||
|
|
||||||
def updateThemeBtn(self, theme):
|
|
||||||
if theme == Theme.LIGHT:
|
|
||||||
self.themeBtn.setText("切换夜间")
|
|
||||||
else:
|
|
||||||
self.themeBtn.setText("切换白天")
|
|
||||||
# ===================== 帮助与关于界面 =====================
|
|
||||||
class HelpInterface(BaseInterface):
|
|
||||||
def __init__(self, parent=None):
|
|
||||||
super().__init__(parent=parent)
|
|
||||||
self.setObjectName("helpInterface")
|
|
||||||
layout = QVBoxLayout()
|
|
||||||
self.setLayout(layout)
|
|
||||||
|
|
||||||
class AboutInterface(BaseInterface):
|
|
||||||
def __init__(self, parent=None):
|
|
||||||
super().__init__(parent=parent)
|
|
||||||
self.setObjectName("aboutInterface")
|
|
||||||
layout = QVBoxLayout()
|
|
||||||
self.setLayout(layout)
|
|
||||||
|
|
||||||
# ===================== 主窗口与导航 =====================
|
|
||||||
class MainWindow(FluentWindow):
|
|
||||||
themeChanged = pyqtSignal(Theme)
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
super().__init__()
|
|
||||||
self.setWindowTitle("MR_ToolBox")
|
|
||||||
self.resize(1000, 700)
|
|
||||||
self.setMinimumSize(800, 600)
|
|
||||||
setTheme(Theme.LIGHT)
|
|
||||||
self.theme = Theme.LIGHT
|
|
||||||
|
|
||||||
self.page_registry = [
|
|
||||||
(HomeInterface(self), FIF.HOME, "首页", NavigationItemPosition.TOP),
|
|
||||||
(DataInterface(self), FIF.LIBRARY, "MRobot代码生成", NavigationItemPosition.SCROLL),
|
|
||||||
(SerialTerminalInterface(self), FIF.COMMAND_PROMPT, "串口终端", NavigationItemPosition.SCROLL),
|
|
||||||
(SettingInterface(self), FIF.SETTING, "设置", NavigationItemPosition.BOTTOM),
|
|
||||||
(HelpInterface(self), FIF.HELP, "帮助", NavigationItemPosition.BOTTOM),
|
|
||||||
(AboutInterface(self), FIF.INFO, "关于", NavigationItemPosition.BOTTOM),
|
|
||||||
]
|
|
||||||
self.initNavigation()
|
|
||||||
|
|
||||||
# 把切换主题按钮放到标题栏右侧
|
|
||||||
self.themeBtn = PushButton("切换夜间", self, FluentIcon.BRUSH)
|
|
||||||
self.themeBtn.setFixedWidth(120)
|
|
||||||
self.themeBtn.clicked.connect(self.toggleTheme)
|
|
||||||
self.addTitleBarWidget(self.themeBtn, align=Qt.AlignRight)
|
|
||||||
|
|
||||||
def initNavigation(self):
|
|
||||||
for page, icon, name, position in self.page_registry:
|
|
||||||
self.addSubInterface(page, icon, name, position)
|
|
||||||
self.navigationInterface.addSeparator()
|
|
||||||
avatar = NavigationAvatarWidget('用户', ':/qfluentwidgets/images/avatar.png')
|
|
||||||
self.navigationInterface.addWidget(
|
|
||||||
routeKey='avatar',
|
|
||||||
widget=avatar,
|
|
||||||
onClick=self.showUserInfo,
|
|
||||||
position=NavigationItemPosition.BOTTOM
|
|
||||||
)
|
|
||||||
|
|
||||||
def toggleTheme(self):
|
|
||||||
if self.theme == Theme.LIGHT:
|
|
||||||
setTheme(Theme.DARK)
|
|
||||||
self.theme = Theme.DARK
|
|
||||||
self.themeBtn.setText("切换白天")
|
|
||||||
else:
|
|
||||||
setTheme(Theme.LIGHT)
|
|
||||||
self.theme = Theme.LIGHT
|
|
||||||
self.themeBtn.setText("切换夜间")
|
|
||||||
self.themeChanged.emit(self.theme)
|
|
||||||
self.refreshStyle()
|
|
||||||
|
|
||||||
def refreshStyle(self):
|
|
||||||
def refresh(widget):
|
|
||||||
widget.setStyleSheet(widget.styleSheet())
|
|
||||||
for child in widget.findChildren(QWidget):
|
|
||||||
refresh(child)
|
|
||||||
refresh(self)
|
|
||||||
|
|
||||||
def showUserInfo(self):
|
|
||||||
MessageBox("用户信息", "当前登录用户:管理员", self).exec()
|
|
||||||
# ===================== 程序入口 =====================
|
|
||||||
def main():
|
|
||||||
QApplication.setHighDpiScaleFactorRoundingPolicy(
|
|
||||||
Qt.HighDpiScaleFactorRoundingPolicy.PassThrough)
|
|
||||||
QApplication.setAttribute(Qt.ApplicationAttribute.AA_EnableHighDpiScaling)
|
|
||||||
QApplication.setAttribute(Qt.ApplicationAttribute.AA_UseHighDpiPixmaps)
|
|
||||||
|
|
||||||
app = QApplication(sys.argv)
|
|
||||||
window = MainWindow()
|
|
||||||
window.show()
|
|
||||||
sys.exit(app.exec_())
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
BIN
img/.DS_Store
vendored
BIN
img/.DS_Store
vendored
Binary file not shown.
BIN
img/M.ico
BIN
img/M.ico
Binary file not shown.
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 20 KiB |
BIN
img/M2.ico
Normal file
BIN
img/M2.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
BIN
img/m1.png
Normal file
BIN
img/m1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 562 KiB |
1
mech_lib/README.md
Normal file
1
mech_lib/README.md
Normal file
@ -0,0 +1 @@
|
|||||||
|
# 机械常用零件库
|
50
pngico.py
50
pngico.py
@ -1,42 +1,18 @@
|
|||||||
from PIL import Image
|
from PIL import Image
|
||||||
import os
|
import os
|
||||||
|
|
||||||
def crop_transparent_background(input_path, output_path):
|
def png_to_ico(png_path, ico_path=None, sizes=[(256,256), (128,128), (64,64), (32,32), (16,16)]):
|
||||||
"""
|
if not os.path.isfile(png_path):
|
||||||
裁切 PNG 图片的透明背景并保存。
|
print(f"文件不存在: {png_path}")
|
||||||
|
return
|
||||||
:param input_path: 输入图片路径
|
if ico_path is None:
|
||||||
:param output_path: 输出图片路径
|
ico_path = os.path.splitext(png_path)[0] + ".ico"
|
||||||
"""
|
img = Image.open(png_path)
|
||||||
try:
|
img.save(ico_path, format='ICO', sizes=sizes)
|
||||||
# 打开图片
|
print(f"已生成: {ico_path}")
|
||||||
img = Image.open(input_path)
|
|
||||||
|
|
||||||
# 确保图片是 RGBA 模式
|
|
||||||
if img.mode != "RGBA":
|
|
||||||
img = img.convert("RGBA")
|
|
||||||
|
|
||||||
# 获取图片的 alpha 通道
|
|
||||||
bbox = img.getbbox()
|
|
||||||
|
|
||||||
if bbox:
|
|
||||||
# 裁切图片
|
|
||||||
cropped_img = img.crop(bbox)
|
|
||||||
# 保存裁切后的图片
|
|
||||||
cropped_img.save(output_path, format="PNG")
|
|
||||||
print(f"图片已保存到: {output_path}")
|
|
||||||
else:
|
|
||||||
print("图片没有透明背景或为空。")
|
|
||||||
except Exception as e:
|
|
||||||
print(f"处理图片时出错: {e}")
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
# 示例:输入和输出路径
|
# 直接写死路径
|
||||||
input_file = "C:\Mac\Home\Desktop\MRobot\img\M.png" # 替换为你的输入图片路径
|
png = r"C:\Mac\Home\Documents\R\MRobot\img\m1.png"
|
||||||
output_file = "C:\Mac\Home\Desktop\MRobot\img\M.png" # 替换为你的输出图片路径
|
ico = r"c:\Mac\Home\Documents\R\MRobot\img\M1.ico"
|
||||||
|
png_to_ico(png, ico)
|
||||||
# 检查文件是否存在
|
|
||||||
if os.path.exists(input_file):
|
|
||||||
crop_transparent_background(input_file, output_file)
|
|
||||||
else:
|
|
||||||
print(f"输入文件不存在: {input_file}")
|
|
286
polynomial.py
286
polynomial.py
@ -1,286 +0,0 @@
|
|||||||
import sys
|
|
||||||
import numpy as np
|
|
||||||
import pandas as pd
|
|
||||||
from PyQt5.QtWidgets import (
|
|
||||||
QApplication, QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QSpinBox,
|
|
||||||
QLabel, QTableWidget, QTableWidgetItem, QFileDialog, QTextEdit,
|
|
||||||
QComboBox, QMessageBox, QHeaderView
|
|
||||||
)
|
|
||||||
from PyQt5.QtGui import QFont
|
|
||||||
from PyQt5.QtCore import Qt
|
|
||||||
import matplotlib
|
|
||||||
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
|
|
||||||
from matplotlib.figure import Figure
|
|
||||||
|
|
||||||
class PolyFitApp(QWidget):
|
|
||||||
def __init__(self):
|
|
||||||
super().__init__()
|
|
||||||
self.setWindowTitle("MRobot 多项式拟合工具")
|
|
||||||
self.resize(1440, 1280)
|
|
||||||
self.setFont(QFont("微软雅黑", 11))
|
|
||||||
self.center()
|
|
||||||
|
|
||||||
self.data_x = []
|
|
||||||
self.data_y = []
|
|
||||||
self.last_coeffs = None
|
|
||||||
self.last_xmin = None
|
|
||||||
self.last_xmax = None
|
|
||||||
|
|
||||||
# 主布局
|
|
||||||
main_layout = QHBoxLayout(self)
|
|
||||||
main_layout.setContentsMargins(20, 20, 20, 20)
|
|
||||||
main_layout.setSpacing(20)
|
|
||||||
left_layout = QVBoxLayout()
|
|
||||||
left_layout.setSpacing(12)
|
|
||||||
right_layout = QVBoxLayout()
|
|
||||||
right_layout.setSpacing(12)
|
|
||||||
main_layout.addLayout(left_layout, 0)
|
|
||||||
main_layout.addLayout(right_layout, 1)
|
|
||||||
|
|
||||||
# 数据输入区
|
|
||||||
self.table = QTableWidget(0, 2)
|
|
||||||
self.table.setFont(QFont("Consolas", 11))
|
|
||||||
self.table.setHorizontalHeaderLabels(["x", "y"])
|
|
||||||
self.table.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
|
|
||||||
self.table.setSelectionBehavior(QTableWidget.SelectRows)
|
|
||||||
left_layout.addWidget(self.table)
|
|
||||||
|
|
||||||
btn_row = QHBoxLayout()
|
|
||||||
self.add_row_btn = QPushButton("添加数据")
|
|
||||||
self.add_row_btn.setStyleSheet("color: #333;")
|
|
||||||
self.add_row_btn.clicked.connect(self.add_point_row)
|
|
||||||
btn_row.addWidget(self.add_row_btn)
|
|
||||||
|
|
||||||
self.del_row_btn = QPushButton("删除选中行")
|
|
||||||
self.del_row_btn.setStyleSheet("color: #333;")
|
|
||||||
self.del_row_btn.clicked.connect(self.delete_selected_rows)
|
|
||||||
btn_row.addWidget(self.del_row_btn)
|
|
||||||
left_layout.addLayout(btn_row)
|
|
||||||
|
|
||||||
# 导入导出按钮区
|
|
||||||
file_btn_row = QHBoxLayout()
|
|
||||||
self.import_btn = QPushButton("导入Excel文件")
|
|
||||||
self.import_btn.setStyleSheet("font-weight: bold; color: #333;")
|
|
||||||
self.import_btn.clicked.connect(self.load_excel)
|
|
||||||
file_btn_row.addWidget(self.import_btn)
|
|
||||||
|
|
||||||
self.export_btn = QPushButton("导出Excel文件")
|
|
||||||
self.export_btn.setStyleSheet("font-weight: bold; color: #333;")
|
|
||||||
self.export_btn.clicked.connect(self.export_excel_and_plot)
|
|
||||||
file_btn_row.addWidget(self.export_btn)
|
|
||||||
left_layout.addLayout(file_btn_row)
|
|
||||||
|
|
||||||
# 拟合参数区
|
|
||||||
param_layout = QHBoxLayout()
|
|
||||||
param_layout.addWidget(QLabel("多项式阶数:"))
|
|
||||||
self.order_spin = QSpinBox()
|
|
||||||
self.order_spin.setRange(1, 10)
|
|
||||||
self.order_spin.setValue(2)
|
|
||||||
param_layout.addWidget(self.order_spin)
|
|
||||||
left_layout.addLayout(param_layout)
|
|
||||||
|
|
||||||
self.fit_btn = QPushButton("拟合并显示")
|
|
||||||
self.fit_btn.setStyleSheet("font-weight: bold; color: #333;")
|
|
||||||
self.fit_btn.clicked.connect(self.fit_and_plot)
|
|
||||||
left_layout.addWidget(self.fit_btn)
|
|
||||||
|
|
||||||
# 输出区
|
|
||||||
self.output = QTextEdit()
|
|
||||||
self.output.setReadOnly(False)
|
|
||||||
self.output.setFont(QFont("Consolas", 10))
|
|
||||||
self.output.setMaximumHeight(150)
|
|
||||||
left_layout.addWidget(self.output)
|
|
||||||
|
|
||||||
code_layout = QHBoxLayout()
|
|
||||||
code_layout.addWidget(QLabel("输出代码格式:"))
|
|
||||||
self.code_type = QComboBox()
|
|
||||||
self.code_type.addItems(["C", "C++", "Python"])
|
|
||||||
code_layout.addWidget(self.code_type)
|
|
||||||
self.gen_code_btn = QPushButton("生成函数代码")
|
|
||||||
self.gen_code_btn.setStyleSheet("color: #333;")
|
|
||||||
self.gen_code_btn.clicked.connect(self.generate_code)
|
|
||||||
code_layout.addWidget(self.gen_code_btn)
|
|
||||||
left_layout.addLayout(code_layout)
|
|
||||||
|
|
||||||
# 拟合曲线区
|
|
||||||
self.figure = Figure(figsize=(5, 4))
|
|
||||||
self.canvas = FigureCanvas(self.figure)
|
|
||||||
right_layout.addWidget(self.canvas)
|
|
||||||
|
|
||||||
def center(self):
|
|
||||||
qr = self.frameGeometry()
|
|
||||||
cp = QApplication.desktop().availableGeometry().center()
|
|
||||||
qr.moveCenter(cp)
|
|
||||||
self.move(qr.topLeft())
|
|
||||||
|
|
||||||
def add_point_row(self, x_val="", y_val=""):
|
|
||||||
row = self.table.rowCount()
|
|
||||||
self.table.insertRow(row)
|
|
||||||
self.table.setItem(row, 0, QTableWidgetItem(str(x_val)))
|
|
||||||
self.table.setItem(row, 1, QTableWidgetItem(str(y_val)))
|
|
||||||
|
|
||||||
def delete_selected_rows(self):
|
|
||||||
selected = self.table.selectionModel().selectedRows()
|
|
||||||
for idx in sorted(selected, reverse=True):
|
|
||||||
self.table.removeRow(idx.row())
|
|
||||||
|
|
||||||
def load_excel(self):
|
|
||||||
file, _ = QFileDialog.getOpenFileName(self, "选择Excel文件", "", "Excel Files (*.xlsx *.xls)")
|
|
||||||
if file:
|
|
||||||
try:
|
|
||||||
data = pd.read_excel(file, usecols=[0, 1])
|
|
||||||
new_x = data.iloc[:, 0].values.tolist()
|
|
||||||
new_y = data.iloc[:, 1].values.tolist()
|
|
||||||
for x, y in zip(new_x, new_y):
|
|
||||||
self.add_point_row(x, y)
|
|
||||||
QMessageBox.information(self, "成功", "数据导入成功!")
|
|
||||||
except Exception as e:
|
|
||||||
QMessageBox.critical(self, "错误", f"读取Excel失败: {e}")
|
|
||||||
|
|
||||||
def export_excel_and_plot(self):
|
|
||||||
file, _ = QFileDialog.getSaveFileName(self, "导出Excel文件", "", "Excel Files (*.xlsx *.xls)")
|
|
||||||
if file:
|
|
||||||
x_list, y_list = [], []
|
|
||||||
for row in range(self.table.rowCount()):
|
|
||||||
try:
|
|
||||||
x = float(self.table.item(row, 0).text())
|
|
||||||
y = float(self.table.item(row, 1).text())
|
|
||||||
x_list.append(x)
|
|
||||||
y_list.append(y)
|
|
||||||
except Exception:
|
|
||||||
continue
|
|
||||||
if not x_list or not y_list:
|
|
||||||
QMessageBox.warning(self, "导出失败", "没有可导出的数据!")
|
|
||||||
return
|
|
||||||
df = pd.DataFrame({'x': x_list, 'y': y_list})
|
|
||||||
try:
|
|
||||||
df.to_excel(file, index=False)
|
|
||||||
# 导出同名png图像
|
|
||||||
png_file = file
|
|
||||||
if png_file.lower().endswith('.xlsx') or png_file.lower().endswith('.xls'):
|
|
||||||
png_file = png_file.rsplit('.', 1)[0] + '.png'
|
|
||||||
else:
|
|
||||||
png_file = png_file + '.png'
|
|
||||||
self.figure.savefig(png_file, dpi=150, bbox_inches='tight')
|
|
||||||
QMessageBox.information(self, "导出成功", f"数据已成功导出到Excel文件!\n图像已导出为:{png_file}")
|
|
||||||
except Exception as e:
|
|
||||||
QMessageBox.critical(self, "导出错误", f"导出Excel或图像失败: {e}")
|
|
||||||
|
|
||||||
def get_manual_points(self):
|
|
||||||
x_list, y_list = [], []
|
|
||||||
for row in range(self.table.rowCount()):
|
|
||||||
try:
|
|
||||||
x = float(self.table.item(row, 0).text())
|
|
||||||
y = float(self.table.item(row, 1).text())
|
|
||||||
x_list.append(x)
|
|
||||||
y_list.append(y)
|
|
||||||
except Exception:
|
|
||||||
continue
|
|
||||||
return x_list, y_list
|
|
||||||
|
|
||||||
def fit_and_plot(self):
|
|
||||||
matplotlib.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei']
|
|
||||||
matplotlib.rcParams['axes.unicode_minus'] = False
|
|
||||||
matplotlib.rcParams['font.size'] = 14
|
|
||||||
self.data_x, self.data_y = self.get_manual_points()
|
|
||||||
try:
|
|
||||||
order = int(self.order_spin.value())
|
|
||||||
except ValueError:
|
|
||||||
QMessageBox.warning(self, "输入错误", "阶数必须为整数!")
|
|
||||||
return
|
|
||||||
n_points = len(self.data_x)
|
|
||||||
if n_points < order + 1:
|
|
||||||
QMessageBox.warning(self, "数据不足", "数据点数量不足以拟合该阶多项式!")
|
|
||||||
return
|
|
||||||
x = np.array(self.data_x, dtype=np.float64)
|
|
||||||
y = np.array(self.data_y, dtype=np.float64)
|
|
||||||
x_min, x_max = x.min(), x.max()
|
|
||||||
if x_max - x_min == 0:
|
|
||||||
QMessageBox.warning(self, "数据错误", "所有x值都相同,无法拟合!")
|
|
||||||
return
|
|
||||||
try:
|
|
||||||
coeffs = np.polyfit(x, y, order)
|
|
||||||
except Exception as e:
|
|
||||||
QMessageBox.critical(self, "拟合错误", f"多项式拟合失败:{e}")
|
|
||||||
return
|
|
||||||
poly = np.poly1d(coeffs)
|
|
||||||
expr = "y = " + " + ".join([f"{c:.6g}*x^{order-i}" for i, c in enumerate(coeffs)])
|
|
||||||
self.output.setPlainText(f"{expr}\n")
|
|
||||||
self.figure.clear()
|
|
||||||
ax = self.figure.add_subplot(111)
|
|
||||||
ax.scatter(x, y, color='red', label='数据点')
|
|
||||||
x_fit = np.linspace(x_min, x_max, 200)
|
|
||||||
y_fit = poly(x_fit)
|
|
||||||
ax.plot(x_fit, y_fit, label='拟合曲线')
|
|
||||||
ax.legend()
|
|
||||||
self.canvas.draw()
|
|
||||||
self.last_coeffs = coeffs
|
|
||||||
self.last_xmin = x_min
|
|
||||||
self.last_xmax = x_max
|
|
||||||
|
|
||||||
def generate_code(self):
|
|
||||||
if self.last_coeffs is None:
|
|
||||||
QMessageBox.warning(self, "未拟合", "请先拟合数据!")
|
|
||||||
return
|
|
||||||
coeffs = self.last_coeffs
|
|
||||||
code_type = self.code_type.currentText()
|
|
||||||
if code_type == "C":
|
|
||||||
code = self.create_c_function(coeffs)
|
|
||||||
elif code_type == "C++":
|
|
||||||
code = self.create_cpp_function(coeffs)
|
|
||||||
else:
|
|
||||||
code = self.create_py_function(coeffs)
|
|
||||||
self.output.setPlainText(code)
|
|
||||||
|
|
||||||
def create_c_function(self, coeffs):
|
|
||||||
lines = ["#include <math.h>", "double polynomial(double x) {", " return "]
|
|
||||||
n = len(coeffs)
|
|
||||||
terms = []
|
|
||||||
for i, c in enumerate(coeffs):
|
|
||||||
exp = n - i - 1
|
|
||||||
if exp == 0:
|
|
||||||
terms.append(f"{c:.8g}")
|
|
||||||
elif exp == 1:
|
|
||||||
terms.append(f"{c:.8g}*x")
|
|
||||||
else:
|
|
||||||
terms.append(f"{c:.8g}*pow(x,{exp})")
|
|
||||||
lines[-1] += " + ".join(terms) + ";"
|
|
||||||
lines.append("}")
|
|
||||||
return "\n".join(lines)
|
|
||||||
|
|
||||||
def create_cpp_function(self, coeffs):
|
|
||||||
lines = ["#include <cmath>", "double polynomial(double x) {", " return "]
|
|
||||||
n = len(coeffs)
|
|
||||||
terms = []
|
|
||||||
for i, c in enumerate(coeffs):
|
|
||||||
exp = n - i - 1
|
|
||||||
if exp == 0:
|
|
||||||
terms.append(f"{c:.8g}")
|
|
||||||
elif exp == 1:
|
|
||||||
terms.append(f"{c:.8g}*x")
|
|
||||||
else:
|
|
||||||
terms.append(f"{c:.8g}*pow(x,{exp})")
|
|
||||||
lines[-1] += " + ".join(terms) + ";"
|
|
||||||
lines.append("}")
|
|
||||||
return "\n".join(lines)
|
|
||||||
|
|
||||||
def create_py_function(self, coeffs):
|
|
||||||
n = len(coeffs)
|
|
||||||
lines = ["def polynomial(x):", " return "]
|
|
||||||
terms = []
|
|
||||||
for i, c in enumerate(coeffs):
|
|
||||||
exp = n - i - 1
|
|
||||||
if exp == 0:
|
|
||||||
terms.append(f"{c:.8g}")
|
|
||||||
elif exp == 1:
|
|
||||||
terms.append(f"{c:.8g}*x")
|
|
||||||
else:
|
|
||||||
terms.append(f"{c:.8g}*x**{exp}")
|
|
||||||
lines[-1] += " + ".join(terms)
|
|
||||||
return "\n".join(lines)
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
app = QApplication(sys.argv)
|
|
||||||
win = PolyFitApp()
|
|
||||||
win.show()
|
|
||||||
sys.exit(app.exec_())
|
|
131
src/freertos.c
131
src/freertos.c
@ -1,131 +0,0 @@
|
|||||||
/* USER CODE BEGIN Header */
|
|
||||||
/**
|
|
||||||
******************************************************************************
|
|
||||||
* File Name : freertos.c
|
|
||||||
* Description : Code for freertos applications
|
|
||||||
******************************************************************************
|
|
||||||
* @attention
|
|
||||||
*
|
|
||||||
* Copyright (c) 2025 STMicroelectronics.
|
|
||||||
* All rights reserved.
|
|
||||||
*
|
|
||||||
* This software is licensed under terms that can be found in the LICENSE file
|
|
||||||
* in the root directory of this software component.
|
|
||||||
* If no LICENSE file comes with this software, it is provided AS-IS.
|
|
||||||
*
|
|
||||||
******************************************************************************
|
|
||||||
*/
|
|
||||||
/* USER CODE END Header */
|
|
||||||
|
|
||||||
/* Includes ------------------------------------------------------------------*/
|
|
||||||
#include "FreeRTOS.h"
|
|
||||||
#include "task.h"
|
|
||||||
#include "main.h"
|
|
||||||
#include "cmsis_os.h"
|
|
||||||
|
|
||||||
/* Private includes ----------------------------------------------------------*/
|
|
||||||
/* USER CODE BEGIN Includes */
|
|
||||||
#include "task/user_task.h"
|
|
||||||
/* USER CODE END Includes */
|
|
||||||
|
|
||||||
/* Private typedef -----------------------------------------------------------*/
|
|
||||||
/* USER CODE BEGIN PTD */
|
|
||||||
|
|
||||||
/* USER CODE END PTD */
|
|
||||||
|
|
||||||
/* Private define ------------------------------------------------------------*/
|
|
||||||
/* USER CODE BEGIN PD */
|
|
||||||
|
|
||||||
/* USER CODE END PD */
|
|
||||||
|
|
||||||
/* Private macro -------------------------------------------------------------*/
|
|
||||||
/* USER CODE BEGIN PM */
|
|
||||||
|
|
||||||
/* USER CODE END PM */
|
|
||||||
|
|
||||||
/* Private variables ---------------------------------------------------------*/
|
|
||||||
/* USER CODE BEGIN Variables */
|
|
||||||
osThreadId_t initTaskHandle; // 定义 Task_Init 的任务句柄
|
|
||||||
/* USER CODE END Variables */
|
|
||||||
/* Definitions for defaultTask */
|
|
||||||
osThreadId_t defaultTaskHandle;
|
|
||||||
const osThreadAttr_t defaultTask_attributes = {
|
|
||||||
.name = "defaultTask",
|
|
||||||
.stack_size = 128 * 4,
|
|
||||||
.priority = (osPriority_t) osPriorityNormal,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Private function prototypes -----------------------------------------------*/
|
|
||||||
/* USER CODE BEGIN FunctionPrototypes */
|
|
||||||
|
|
||||||
/* USER CODE END FunctionPrototypes */
|
|
||||||
|
|
||||||
void StartDefaultTask(void *argument);
|
|
||||||
|
|
||||||
void MX_FREERTOS_Init(void); /* (MISRA C 2004 rule 8.1) */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief FreeRTOS initialization
|
|
||||||
* @param None
|
|
||||||
* @retval None
|
|
||||||
*/
|
|
||||||
void MX_FREERTOS_Init(void) {
|
|
||||||
/* USER CODE BEGIN Init */
|
|
||||||
|
|
||||||
/* USER CODE END Init */
|
|
||||||
|
|
||||||
/* USER CODE BEGIN RTOS_MUTEX */
|
|
||||||
/* add mutexes, ... */
|
|
||||||
/* USER CODE END RTOS_MUTEX */
|
|
||||||
|
|
||||||
/* USER CODE BEGIN RTOS_SEMAPHORES */
|
|
||||||
/* add semaphores, ... */
|
|
||||||
/* USER CODE END RTOS_SEMAPHORES */
|
|
||||||
|
|
||||||
/* USER CODE BEGIN RTOS_TIMERS */
|
|
||||||
/* start timers, add new ones, ... */
|
|
||||||
/* USER CODE END RTOS_TIMERS */
|
|
||||||
|
|
||||||
/* USER CODE BEGIN RTOS_QUEUES */
|
|
||||||
/* add queues, ... */
|
|
||||||
/* USER CODE END RTOS_QUEUES */
|
|
||||||
|
|
||||||
/* Create the thread(s) */
|
|
||||||
/* creation of defaultTask */
|
|
||||||
defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);
|
|
||||||
|
|
||||||
/* USER CODE BEGIN RTOS_THREADS */
|
|
||||||
initTaskHandle = osThreadNew(Task_Init, NULL, &attr_init); // 创建初始化任务
|
|
||||||
/* add threads, ... */
|
|
||||||
/* USER CODE END RTOS_THREADS */
|
|
||||||
|
|
||||||
/* USER CODE BEGIN RTOS_EVENTS */
|
|
||||||
/* add events, ... */
|
|
||||||
/* USER CODE END RTOS_EVENTS */
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/* USER CODE BEGIN Header_StartDefaultTask */
|
|
||||||
/**
|
|
||||||
* @brief Function implementing the defaultTask thread.
|
|
||||||
* @param argument: Not used
|
|
||||||
* @retval None
|
|
||||||
*/
|
|
||||||
/* USER CODE END Header_StartDefaultTask */
|
|
||||||
void StartDefaultTask(void *argument)
|
|
||||||
{
|
|
||||||
/* USER CODE BEGIN StartDefaultTask */
|
|
||||||
/* Infinite loop */
|
|
||||||
// for(;;)
|
|
||||||
// {
|
|
||||||
// osDelay(1);
|
|
||||||
// }
|
|
||||||
osThreadTerminate(osThreadGetId()); // 结束自身
|
|
||||||
/* USER CODE END StartDefaultTask */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Private application code --------------------------------------------------*/
|
|
||||||
/* USER CODE BEGIN Application */
|
|
||||||
|
|
||||||
/* USER CODE END Application */
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user