mirror of
https://github.com/goldenfishs/MRobot.git
synced 2025-06-14 22:16:38 +08:00
0.02版本
This commit is contained in:
parent
544b3745d5
commit
c4731883f2
4
.gitignore
vendored
4
.gitignore
vendored
@ -29,6 +29,6 @@ Examples/
|
||||
!*.bin
|
||||
!*.hex
|
||||
|
||||
/build
|
||||
/dist
|
||||
build/
|
||||
dist/
|
||||
*.spec
|
790
MR_Tool.py
790
MR_Tool.py
@ -1,16 +1,4 @@
|
||||
import sys
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from PyQt5.QtWidgets import (
|
||||
QApplication, QWidget, QLabel, QPushButton, QTextEdit, QVBoxLayout,
|
||||
QHBoxLayout, QStackedWidget, QSizePolicy, QFrame, QGraphicsDropShadowEffect,
|
||||
QSpinBox, QTableWidget, QTableWidgetItem, QFileDialog, QComboBox, QMessageBox, QHeaderView
|
||||
)
|
||||
from PyQt5.QtGui import QPixmap, QFont, QIcon
|
||||
from PyQt5.QtCore import Qt
|
||||
import matplotlib
|
||||
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
|
||||
from matplotlib.figure import Figure
|
||||
import os
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
@ -18,10 +6,18 @@ import requests
|
||||
import webbrowser
|
||||
import serial
|
||||
import serial.tools.list_ports
|
||||
from PyQt5.QtWidgets import QGroupBox, QGridLayout, QLineEdit, QTextBrowser
|
||||
from PyQt5.QtCore import QTimer, pyqtSlot
|
||||
from PyQt5.QtWidgets import QCheckBox
|
||||
from PyQt5.QtCore import QTimer
|
||||
from PyQt5.QtWidgets import (
|
||||
QApplication, QWidget, QLabel, QPushButton, QTextEdit, QVBoxLayout,
|
||||
QHBoxLayout, QStackedWidget, QSizePolicy, QFrame, QGraphicsDropShadowEffect,
|
||||
QSpinBox, QTableWidget, QTableWidgetItem, QFileDialog, QComboBox, QMessageBox, QHeaderView,
|
||||
QGroupBox, QGridLayout, QLineEdit, QTextBrowser, QCheckBox
|
||||
)
|
||||
from PyQt5.QtGui import QPixmap, QFont, QIcon, QPainter, QPen, QColor
|
||||
from PyQt5.QtCore import Qt, QTimer, QPointF, pyqtSlot
|
||||
import matplotlib
|
||||
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
|
||||
from matplotlib.figure import Figure
|
||||
from PyQt5.QtCore import pyqtSignal, pyqtSlot
|
||||
|
||||
def resource_path(relative_path):
|
||||
"""兼容PyInstaller打包后资源路径"""
|
||||
@ -95,7 +91,6 @@ class HomePage(QWidget):
|
||||
footer.setFixedHeight(100) # 修改为固定高度
|
||||
layout.addWidget(footer)
|
||||
|
||||
|
||||
# --------- 功能一:多项式拟合工具页面 ---------
|
||||
class PolyFitApp(QWidget):
|
||||
def __init__(self):
|
||||
@ -591,8 +586,10 @@ class DownloadPage(QWidget):
|
||||
# desc.setStyleSheet("color: #34495e; margin-bottom: 18px;")
|
||||
# main_layout.addWidget(desc)
|
||||
|
||||
# 两大类布局
|
||||
from PyQt5.QtWidgets import QGridLayout, QGroupBox
|
||||
spacer = QFrame()
|
||||
spacer.setFixedHeight(4) # 可根据需要调整间隔高度
|
||||
spacer.setStyleSheet("background: transparent; border: none;")
|
||||
main_layout.addWidget(spacer)
|
||||
|
||||
# 小工具类
|
||||
tools_tools = [
|
||||
@ -661,6 +658,10 @@ class DownloadPage(QWidget):
|
||||
tools_layout.addWidget(btn, row, col)
|
||||
tools_group.setLayout(tools_layout)
|
||||
main_layout.addWidget(tools_group)
|
||||
spacer = QFrame()
|
||||
spacer.setFixedHeight(4) # 可根据需要调整间隔高度
|
||||
spacer.setStyleSheet("background: transparent; border: none;")
|
||||
main_layout.addWidget(spacer)
|
||||
|
||||
# 开发/设计软件类
|
||||
dev_tools = [
|
||||
@ -757,8 +758,8 @@ class SerialAssistant(QWidget):
|
||||
# 新增:HEX模式复选框
|
||||
self.hex_send_chk = QCheckBox("HEX发送")
|
||||
self.hex_recv_chk = QCheckBox("HEX接收")
|
||||
self.hex_send_chk.setFont(QFont("微软雅黑", 10))
|
||||
self.hex_recv_chk.setFont(QFont("微软雅黑", 10))
|
||||
self.hex_send_chk.setFont(QFont("微软雅黑", 12))
|
||||
self.hex_recv_chk.setFont(QFont("微软雅黑", 12))
|
||||
self.hex_send_chk.setChecked(False)
|
||||
self.hex_recv_chk.setChecked(False)
|
||||
|
||||
@ -821,25 +822,29 @@ class SerialAssistant(QWidget):
|
||||
proto_group = QGroupBox("数据协议配置")
|
||||
proto_group.setFont(QFont("微软雅黑", 14, QFont.Bold))
|
||||
proto_group.setStyleSheet(config_group.styleSheet())
|
||||
proto_layout = QGridLayout()
|
||||
proto_layout.setSpacing(12)
|
||||
proto_layout = QHBoxLayout()
|
||||
proto_layout.setSpacing(18)
|
||||
proto_layout.setContentsMargins(16, 16, 16, 16)
|
||||
proto_layout.addWidget(QLabel("数据数量:"), 0, 0)
|
||||
proto_layout.addWidget(QLabel("数据数量:"))
|
||||
self.data_count_spin = QSpinBox()
|
||||
self.data_count_spin.setRange(1, 16)
|
||||
self.data_count_spin.setValue(self.data_count)
|
||||
self.data_count_spin.setFixedWidth(80)
|
||||
self.data_count_spin.valueChanged.connect(self.apply_proto_config)
|
||||
proto_layout.addWidget(self.data_count_spin, 0, 1)
|
||||
proto_layout.addWidget(QLabel("数据类型:"), 1, 0)
|
||||
proto_layout.addWidget(self.data_count_spin)
|
||||
proto_layout.addSpacing(18)
|
||||
proto_layout.addWidget(QLabel("数据类型:"))
|
||||
self.data_type_box = QComboBox()
|
||||
self.data_type_box.addItems(self.data_types)
|
||||
self.data_type_box.setCurrentText(self.data_type)
|
||||
self.data_type_box.setFixedWidth(100)
|
||||
self.data_type_box.currentTextChanged.connect(self.apply_proto_config)
|
||||
proto_layout.addWidget(self.data_type_box, 1, 1)
|
||||
proto_layout.addWidget(self.data_type_box)
|
||||
# proto_layout.addStretch(1)
|
||||
proto_group.setLayout(proto_layout)
|
||||
left_panel.addWidget(proto_group)
|
||||
|
||||
# 发送数据区
|
||||
# 发送数据区美化
|
||||
send_group = QGroupBox("发送数据")
|
||||
send_group.setFont(QFont("微软雅黑", 14, QFont.Bold))
|
||||
send_group.setStyleSheet("""
|
||||
@ -860,39 +865,37 @@ class SerialAssistant(QWidget):
|
||||
}
|
||||
""")
|
||||
send_layout = QVBoxLayout()
|
||||
send_layout.setSpacing(14)
|
||||
send_layout.setContentsMargins(12, 12, 12, 12)
|
||||
send_layout.setSpacing(16)
|
||||
send_layout.setContentsMargins(18, 18, 18, 18)
|
||||
|
||||
# 输入框(更大更高字体)
|
||||
self.send_edit = QLineEdit()
|
||||
self.send_edit.setFont(QFont("Consolas", 22))
|
||||
self.send_edit.setPlaceholderText("输入要发送的数据...")
|
||||
self.send_edit.setMinimumHeight(54)
|
||||
self.send_edit.setMinimumWidth(360)
|
||||
# 输入框(多行)
|
||||
self.send_edit = QTextEdit()
|
||||
self.send_edit.setFont(QFont("Consolas", 18))
|
||||
self.send_edit.setPlaceholderText("输入要发送的数据,可多行(支持HEX/文本)...")
|
||||
self.send_edit.setMinimumHeight(140)
|
||||
self.send_edit.setMaximumHeight(220)
|
||||
self.send_edit.setStyleSheet("""
|
||||
QLineEdit {
|
||||
QTextEdit {
|
||||
background: #f8fbfd;
|
||||
border-radius: 10px;
|
||||
border: 1.5px solid #d6eaf8;
|
||||
font-size: 22px;
|
||||
padding: 12px 18px;
|
||||
border-radius: 12px;
|
||||
border: 2px solid #d6eaf8;
|
||||
font-size: 18px;
|
||||
padding: 14px 20px;
|
||||
}
|
||||
""")
|
||||
send_layout.addWidget(self.send_edit)
|
||||
|
||||
# HEX复选框行(字体更小)
|
||||
hex_chk_row = QHBoxLayout()
|
||||
self.hex_send_chk.setFont(QFont("微软雅黑", 10))
|
||||
self.hex_recv_chk.setFont(QFont("微软雅黑", 10))
|
||||
for chk in [self.hex_send_chk, self.hex_recv_chk]:
|
||||
chk.setStyleSheet("""
|
||||
# HEX复选框和按钮行
|
||||
row1 = QHBoxLayout()
|
||||
row1.setSpacing(24)
|
||||
self.hex_send_chk.setStyleSheet("""
|
||||
QCheckBox {
|
||||
color: #2471a3;
|
||||
spacing: 12px;
|
||||
font-size: 15px;
|
||||
}
|
||||
QCheckBox::indicator {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
QCheckBox::indicator:checked {
|
||||
background-color: #2980b9;
|
||||
@ -903,34 +906,68 @@ class SerialAssistant(QWidget):
|
||||
border: 1.5px solid #b5d0ea;
|
||||
}
|
||||
""")
|
||||
self.hex_send_chk.setChecked(False)
|
||||
self.hex_recv_chk.setChecked(False)
|
||||
hex_chk_row.addWidget(self.hex_send_chk)
|
||||
hex_chk_row.addWidget(self.hex_recv_chk)
|
||||
hex_chk_row.addStretch(1)
|
||||
send_layout.addLayout(hex_chk_row)
|
||||
self.hex_recv_chk.setStyleSheet(self.hex_send_chk.styleSheet())
|
||||
row1.addWidget(self.hex_send_chk)
|
||||
row1.addWidget(self.hex_recv_chk)
|
||||
row1.addStretch(1)
|
||||
send_layout.addLayout(row1)
|
||||
|
||||
# 发送按钮
|
||||
send_btn_row = QHBoxLayout()
|
||||
# 发送和持续发送按钮+频率(优化为“每秒发送次数”)
|
||||
row2 = QHBoxLayout()
|
||||
row2.setSpacing(18)
|
||||
self.send_btn = QPushButton("发送")
|
||||
self.send_btn.clicked.connect(self.send_data)
|
||||
self.send_btn.setFont(QFont("微软雅黑", 16, QFont.Bold))
|
||||
self.send_btn.setFixedHeight(44)
|
||||
self.send_btn.setFixedWidth(120)
|
||||
self.send_btn.setStyleSheet(self._btn_style("#2980b9"))
|
||||
send_btn_row.addWidget(self.send_btn)
|
||||
send_layout.addLayout(send_btn_row)
|
||||
row2.addWidget(self.send_btn)
|
||||
|
||||
self.cont_send_btn = QPushButton("持续发送")
|
||||
self.cont_send_btn.setCheckable(True)
|
||||
self.cont_send_btn.setFont(QFont("微软雅黑", 15))
|
||||
self.cont_send_btn.setFixedHeight(44)
|
||||
self.cont_send_btn.setFixedWidth(120)
|
||||
self.cont_send_btn.setStyleSheet(self._btn_style("#f1c40f"))
|
||||
self.cont_send_btn.clicked.connect(self.toggle_cont_send)
|
||||
row2.addWidget(self.cont_send_btn)
|
||||
|
||||
freq_label = QLabel(" 每秒发送次数:")
|
||||
freq_label.setFont(QFont("微软雅黑", 8))
|
||||
freq_label.setFixedWidth(180)
|
||||
#文本居中
|
||||
# freq_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
|
||||
row2.addWidget(freq_label)
|
||||
|
||||
self.freq_input = QSpinBox()
|
||||
self.freq_input.setRange(1, 1000)
|
||||
self.freq_input.setValue(5)
|
||||
self.freq_input.setFont(QFont("Consolas", 14))
|
||||
self.freq_input.setFixedWidth(100)
|
||||
self.freq_input.setStyleSheet("""
|
||||
QSpinBox {
|
||||
background: #f8fbfd;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #d6eaf8;
|
||||
font-size: 15px;
|
||||
padding: 2px 8px;
|
||||
}
|
||||
""")
|
||||
row2.addWidget(self.freq_input)
|
||||
|
||||
# freq_unit = QLabel("次/秒")
|
||||
# freq_unit.setFont(QFont("微软雅黑", 13))
|
||||
# freq_unit.setFixedWidth(40)
|
||||
# row2.addWidget(freq_unit)
|
||||
row2.addStretch(1)
|
||||
send_layout.addLayout(row2)
|
||||
|
||||
self.cont_send_timer = QTimer(self)
|
||||
self.cont_send_timer.timeout.connect(self.send_data)
|
||||
|
||||
send_group.setLayout(send_layout)
|
||||
left_panel.addWidget(send_group, stretch=1) # 让发送区弹性填充
|
||||
|
||||
# 清空按钮
|
||||
self.clear_btn = QPushButton("清空接收和曲线")
|
||||
self.clear_btn.clicked.connect(self.clear_all)
|
||||
self.clear_btn.setStyleSheet(self._btn_style("#e74c3c"))
|
||||
left_panel.addWidget(self.clear_btn)
|
||||
|
||||
# 弹性空隙(动态扩展填满)
|
||||
left_panel.addStretch(1)
|
||||
|
||||
# 使用说明始终在最下方
|
||||
usage_group = QGroupBox("使用说明")
|
||||
usage_group.setFont(QFont("微软雅黑", 13, QFont.Bold))
|
||||
@ -953,14 +990,11 @@ class SerialAssistant(QWidget):
|
||||
""")
|
||||
usage_layout = QVBoxLayout()
|
||||
usage_label = QLabel(
|
||||
"1. 选择串口号和波特率,点击“打开串口”。\n"
|
||||
"2. 在“数据协议配置”中选择数据数量和数据类型。\n"
|
||||
"3. 下位机发送格式:\n"
|
||||
"1. 在“数据协议配置”中选择数据数量和数据类型。\n"
|
||||
"2. 下位机发送格式:\n"
|
||||
" 0x55 + 数据数量(1字节) + 数据 + 校验和(1字节)\n"
|
||||
" 校验和为包头到最后一个数据字节的累加和的低8位。\n"
|
||||
"4. 每包数据自动绘制曲线,X轴为采样点(或时间),Y轴为各通道数据。\n"
|
||||
"5. 支持float/int16/uint16/int8/uint8类型,最多16通道。\n"
|
||||
"6. 可点击“测试绘图”按钮模拟数据包接收效果。"
|
||||
"3. 每包数据自动绘制曲线,X轴为采样点(或时间),Y轴为各通道数据。\n"
|
||||
)
|
||||
usage_label.setWordWrap(True)
|
||||
usage_label.setFont(QFont("微软雅黑", 9))
|
||||
@ -968,6 +1002,13 @@ class SerialAssistant(QWidget):
|
||||
usage_group.setLayout(usage_layout)
|
||||
left_panel.addWidget(usage_group)
|
||||
|
||||
# 清空按钮紧贴使用说明
|
||||
self.clear_btn = QPushButton("清空接收和曲线")
|
||||
self.clear_btn.clicked.connect(self.clear_all)
|
||||
self.clear_btn.setStyleSheet(self._btn_style("#e74c3c"))
|
||||
self.clear_btn.setFixedHeight(38)
|
||||
left_panel.addWidget(self.clear_btn)
|
||||
|
||||
main_layout.addLayout(left_panel, 0)
|
||||
|
||||
# 右侧面板
|
||||
@ -1031,6 +1072,55 @@ class SerialAssistant(QWidget):
|
||||
# 默认配置
|
||||
self.apply_proto_config()
|
||||
|
||||
def parse_hex_string(self, s):
|
||||
"""支持 0x11 0x22 33 44 格式转bytes"""
|
||||
s = s.strip().replace(',', ' ').replace(';', ' ')
|
||||
parts = s.split()
|
||||
result = []
|
||||
for part in parts:
|
||||
if part.startswith('0x') or part.startswith('0X'):
|
||||
try:
|
||||
result.append(int(part, 16))
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
result.append(int(part, 16))
|
||||
except Exception:
|
||||
pass
|
||||
return bytes(result)
|
||||
|
||||
def send_data(self):
|
||||
if self.ser and self.ser.is_open:
|
||||
data = self.send_edit.text()
|
||||
try:
|
||||
if self.hex_send_chk.isChecked():
|
||||
# 支持 0x11 0x22 33 44 格式
|
||||
data_bytes = self.parse_hex_string(data)
|
||||
if not data_bytes:
|
||||
self.recv_box.append("HEX格式错误,未发送。")
|
||||
return
|
||||
self.ser.write(data_bytes)
|
||||
self.recv_box.append(f"发送(HEX): {' '.join(['%02X'%b for b in data_bytes])}")
|
||||
else:
|
||||
self.ser.write(data.encode('utf-8'))
|
||||
self.recv_box.append(f"发送: {data}")
|
||||
except Exception as e:
|
||||
self.recv_box.append(f"发送失败: {e}")
|
||||
else:
|
||||
self.recv_box.append("串口未打开,无法发送。")
|
||||
|
||||
def toggle_cont_send(self):
|
||||
if self.cont_send_btn.isChecked():
|
||||
try:
|
||||
interval = int(self.freq_box.currentText())
|
||||
except Exception:
|
||||
interval = 200
|
||||
self.cont_send_timer.start(interval)
|
||||
self.cont_send_btn.setText("停止发送")
|
||||
else:
|
||||
self.cont_send_timer.stop()
|
||||
self.cont_send_btn.setText("持续发送")
|
||||
|
||||
def simulate_data(self):
|
||||
"""模拟一包数据并自动解析绘图"""
|
||||
@ -1127,23 +1217,6 @@ class SerialAssistant(QWidget):
|
||||
self.recv_box.append("串口已关闭")
|
||||
self.timer.stop()
|
||||
|
||||
def send_data(self):
|
||||
if self.ser and self.ser.is_open:
|
||||
data = self.send_edit.text()
|
||||
try:
|
||||
if self.hex_send_chk.isChecked():
|
||||
# HEX模式发送
|
||||
data_bytes = bytes.fromhex(data.replace(' ', ''))
|
||||
self.ser.write(data_bytes)
|
||||
self.recv_box.append(f"发送(HEX): {data_bytes.hex(' ').upper()}")
|
||||
else:
|
||||
self.ser.write(data.encode('utf-8'))
|
||||
self.recv_box.append(f"发送: {data}")
|
||||
except Exception as e:
|
||||
self.recv_box.append(f"发送失败: {e}")
|
||||
else:
|
||||
self.recv_box.append("串口未打开,无法发送。")
|
||||
|
||||
def send_multi_data(self):
|
||||
if self.ser and self.ser.is_open:
|
||||
text = self.send_edit.text()
|
||||
@ -1152,9 +1225,9 @@ class SerialAssistant(QWidget):
|
||||
if line.strip():
|
||||
try:
|
||||
if self.hex_send_chk.isChecked():
|
||||
data_bytes = bytes.fromhex(line.strip().replace(' ', ''))
|
||||
data_bytes = self.parse_hex_string(line.strip())
|
||||
self.ser.write(data_bytes)
|
||||
self.recv_box.append(f"发送(HEX): {data_bytes.hex(' ').upper()}")
|
||||
self.recv_box.append(f"发送(HEX): {' '.join(['%02X'%b for b in data_bytes])}")
|
||||
else:
|
||||
self.ser.write(line.strip().encode('utf-8'))
|
||||
self.recv_box.append(f"发送: {line.strip()}")
|
||||
@ -1295,8 +1368,8 @@ class SerialAssistant(QWidget):
|
||||
def update_plot(self):
|
||||
self.figure.clear()
|
||||
ax = self.figure.add_subplot(111)
|
||||
ax.set_xlabel("采样点", fontsize=14)
|
||||
ax.set_ylabel("数据值", fontsize=14)
|
||||
ax.set_xlabel("Sample", fontsize=14)
|
||||
ax.set_ylabel("Value", fontsize=14)
|
||||
has_curve = False
|
||||
for idx in range(self.data_count):
|
||||
color = self.curve_colors[idx % len(self.curve_colors)]
|
||||
@ -1317,6 +1390,8 @@ class SerialAssistant(QWidget):
|
||||
|
||||
# --------- 功能四:MRobot架构生成 ---------
|
||||
class GenerateMRobotCode(QWidget):
|
||||
repo_ready_signal = pyqtSignal()
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.setFont(QFont("微软雅黑", 15))
|
||||
@ -1327,20 +1402,523 @@ class GenerateMRobotCode(QWidget):
|
||||
padding: 20px;
|
||||
}
|
||||
""")
|
||||
# 变量初始化
|
||||
self.repo_dir = "MRobot_repo"
|
||||
self.repo_url = "http://gitea.qutrobot.top/robofish/MRobot.git"
|
||||
self.header_file_vars = {}
|
||||
self.task_vars = []
|
||||
self.ioc_data = None
|
||||
self.add_gitignore = False
|
||||
self.auto_configure = False
|
||||
self.repo_ready = False # 标志:仓库是否已准备好
|
||||
self.init_ui()
|
||||
self.repo_ready_signal.connect(self.on_repo_ready)
|
||||
|
||||
def showEvent(self, event):
|
||||
super().showEvent(event)
|
||||
if not self.repo_ready:
|
||||
self.log("首次进入,正在克隆MRobot仓库...")
|
||||
self.clone_repo_and_refresh()
|
||||
|
||||
def clone_repo_and_refresh(self):
|
||||
import threading
|
||||
def do_clone():
|
||||
self.clone_repo()
|
||||
self.repo_ready = True
|
||||
self.ioc_data = self.find_and_read_ioc_file()
|
||||
self.repo_ready_signal.emit()
|
||||
threading.Thread(target=do_clone).start()
|
||||
|
||||
@pyqtSlot()
|
||||
def on_repo_ready(self):
|
||||
self.update_freertos_status()
|
||||
self.update_header_files()
|
||||
self.update_task_ui()
|
||||
self.log("仓库准备完成!")
|
||||
|
||||
def init_ui(self):
|
||||
main_layout = QVBoxLayout(self)
|
||||
main_layout.setContentsMargins(20, 20, 20, 20)
|
||||
main_layout.setSpacing(20)
|
||||
main_layout.setSpacing(18)
|
||||
main_layout.setContentsMargins(32, 32, 32, 32)
|
||||
self.setStyleSheet("""
|
||||
QWidget {
|
||||
background: qlineargradient(x1:0, y1:0, x2:1, y2:1,
|
||||
stop:0 #eaf6fb, stop:1 #d6eaf8);
|
||||
border-radius: 16px;
|
||||
}
|
||||
""")
|
||||
|
||||
# 功能说明
|
||||
desc_label = QLabel("MRobot架构生成工具,帮助您快速生成MRobot项目代码。")
|
||||
desc_label.setFont(QFont("微软雅黑", 14))
|
||||
desc_label.setAlignment(Qt.AlignCenter)
|
||||
main_layout.addWidget(desc_label)
|
||||
# 顶部标题区
|
||||
title = QLabel("MRobot 架构生成工具")
|
||||
title.setFont(QFont("微软雅黑", 22, QFont.Bold))
|
||||
title.setAlignment(Qt.AlignCenter)
|
||||
title.setStyleSheet("color: #2980b9; letter-spacing: 2px; margin-bottom: 2px;")
|
||||
main_layout.addWidget(title)
|
||||
|
||||
# 其他UI元素...
|
||||
desc = QLabel("快速生成 MRobot 项目代码,自动管理模块、任务与环境配置。")
|
||||
desc.setFont(QFont("微软雅黑", 13))
|
||||
desc.setAlignment(Qt.AlignCenter)
|
||||
desc.setStyleSheet("color: #34495e; margin-bottom: 8px;")
|
||||
main_layout.addWidget(desc)
|
||||
|
||||
# 状态与选项区
|
||||
status_opt_row = QHBoxLayout()
|
||||
status_opt_row.setSpacing(24)
|
||||
|
||||
# 状态区
|
||||
status_col = QVBoxLayout()
|
||||
self.freertos_status_label = QLabel("FreeRTOS 状态: 检测中...")
|
||||
self.freertos_status_label.setFont(QFont("微软雅黑", 12))
|
||||
self.freertos_status_label.setStyleSheet("color: #2471a3;")
|
||||
status_col.addWidget(self.freertos_status_label)
|
||||
status_col.addStretch(1)
|
||||
status_opt_row.addLayout(status_col, 1)
|
||||
|
||||
# 选项区
|
||||
option_col = QVBoxLayout()
|
||||
self.gitignore_chk = QCheckBox("生成 .gitignore")
|
||||
self.gitignore_chk.setFont(QFont("微软雅黑", 12))
|
||||
self.gitignore_chk.stateChanged.connect(lambda x: setattr(self, "add_gitignore", x == Qt.Checked))
|
||||
option_col.addWidget(self.gitignore_chk)
|
||||
self.auto_env_chk = QCheckBox("自动环境配置")
|
||||
self.auto_env_chk.setFont(QFont("微软雅黑", 12))
|
||||
self.auto_env_chk.stateChanged.connect(lambda x: setattr(self, "auto_configure", x == Qt.Checked))
|
||||
option_col.addWidget(self.auto_env_chk)
|
||||
option_col.addStretch(1)
|
||||
status_opt_row.addLayout(option_col, 1)
|
||||
|
||||
status_opt_row.addStretch(2)
|
||||
main_layout.addLayout(status_opt_row)
|
||||
|
||||
# 主体分区:左侧模块选择,右侧任务管理
|
||||
body_layout = QHBoxLayout()
|
||||
body_layout.setSpacing(24)
|
||||
|
||||
# 左侧:模块文件选择
|
||||
left_col = QVBoxLayout()
|
||||
self.header_group = QGroupBox("模块文件选择")
|
||||
self.header_group.setFont(QFont("微软雅黑", 15, QFont.Bold))
|
||||
self.header_group.setStyleSheet("""
|
||||
QGroupBox {
|
||||
border: 2px solid #b5d0ea;
|
||||
border-radius: 12px;
|
||||
margin-top: 8px;
|
||||
background: #f8fbfd;
|
||||
color: #2471a3;
|
||||
padding: 10px 0 0 0;
|
||||
}
|
||||
QGroupBox:title {
|
||||
subcontrol-origin: margin;
|
||||
left: 18px;
|
||||
top: -10px;
|
||||
background: transparent;
|
||||
padding: 0 8px;
|
||||
}
|
||||
""")
|
||||
self.header_layout = QVBoxLayout(self.header_group)
|
||||
self.header_layout.setSpacing(8)
|
||||
left_col.addWidget(self.header_group)
|
||||
left_col.addStretch(1)
|
||||
body_layout.addLayout(left_col, 2)
|
||||
|
||||
# 右侧:任务管理
|
||||
right_col = QVBoxLayout()
|
||||
self.task_group = QGroupBox("任务管理 (FreeRTOS)")
|
||||
self.task_group.setFont(QFont("微软雅黑", 15, QFont.Bold))
|
||||
self.task_group.setStyleSheet(self.header_group.styleSheet())
|
||||
self.task_layout = QVBoxLayout(self.task_group)
|
||||
self.task_layout.setSpacing(8)
|
||||
right_col.addWidget(self.task_group)
|
||||
right_col.addStretch(1)
|
||||
body_layout.addLayout(right_col, 2)
|
||||
|
||||
main_layout.addLayout(body_layout)
|
||||
|
||||
# 生成按钮区
|
||||
btn_row = QHBoxLayout()
|
||||
btn_row.addStretch(1)
|
||||
self.generate_btn = QPushButton("一键生成 MRobot 代码")
|
||||
self.generate_btn.setFont(QFont("微软雅黑", 18, QFont.Bold))
|
||||
self.generate_btn.setMinimumHeight(48)
|
||||
self.generate_btn.setStyleSheet("""
|
||||
QPushButton {
|
||||
background: qlineargradient(x1:0, y1:0, x2:1, y2:1,
|
||||
stop:0 #eaf6fb, stop:1 #d6eaf8);
|
||||
color: #2980b9;
|
||||
border-radius: 20px;
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
padding: 12px 0;
|
||||
border: 1px solid #d6eaf8;
|
||||
}
|
||||
QPushButton:hover {
|
||||
background: qlineargradient(x1:0, y1:0, x2:1, y2:1,
|
||||
stop:0 #f8fffe, stop:1 #cfe7fa);
|
||||
color: #1a6fae;
|
||||
border: 1.5px solid #b5d0ea;
|
||||
}
|
||||
QPushButton:pressed {
|
||||
background: #e3f0fa;
|
||||
color: #2471a3;
|
||||
border: 1.5px solid #a4cbe3;
|
||||
}
|
||||
""")
|
||||
self.generate_btn.clicked.connect(self.generate_action)
|
||||
btn_row.addWidget(self.generate_btn)
|
||||
btn_row.addStretch(1)
|
||||
main_layout.addLayout(btn_row)
|
||||
|
||||
# 日志输出区
|
||||
self.msg_box = QTextEdit()
|
||||
self.msg_box.setReadOnly(True)
|
||||
self.msg_box.setFont(QFont("Consolas", 13))
|
||||
self.msg_box.setMaximumHeight(100)
|
||||
self.msg_box.setStyleSheet("""
|
||||
QTextEdit {
|
||||
background: #f4f6f7;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #d6eaf8;
|
||||
font-size: 15px;
|
||||
color: #2c3e50;
|
||||
padding: 8px;
|
||||
}
|
||||
""")
|
||||
main_layout.addWidget(self.msg_box)
|
||||
|
||||
# 页脚
|
||||
footer = QLabel("如遇问题请反馈至 QUT 机器人战队")
|
||||
footer.setFont(QFont("微软雅黑", 11))
|
||||
footer.setAlignment(Qt.AlignCenter)
|
||||
footer.setStyleSheet("color: #b2bec3; margin-top: 6px;")
|
||||
main_layout.addWidget(footer)
|
||||
|
||||
# 初始化内容
|
||||
self.update_header_files()
|
||||
self.update_task_ui()
|
||||
|
||||
# ...其余方法保持不变...
|
||||
|
||||
def log(self, msg):
|
||||
self.msg_box.append(msg)
|
||||
|
||||
def clone_repo(self):
|
||||
import shutil
|
||||
from git import Repo
|
||||
if os.path.exists(self.repo_dir):
|
||||
shutil.rmtree(self.repo_dir)
|
||||
try:
|
||||
self.log("正在克隆仓库...")
|
||||
Repo.clone_from(self.repo_url, self.repo_dir, multi_options=["--depth=1"])
|
||||
self.log("仓库克隆成功!")
|
||||
except Exception as e:
|
||||
self.log(f"克隆仓库失败: {e}")
|
||||
|
||||
def find_and_read_ioc_file(self):
|
||||
for file in os.listdir("."):
|
||||
if file.endswith(".ioc"):
|
||||
with open(file, "r", encoding="utf-8") as f:
|
||||
return f.read()
|
||||
self.log("未找到 .ioc 文件!")
|
||||
return None
|
||||
|
||||
def check_freertos_enabled(self):
|
||||
import re
|
||||
if not self.ioc_data:
|
||||
return False
|
||||
return bool(re.search(r"Mcu\.IP\d+=FREERTOS", self.ioc_data))
|
||||
|
||||
def update_freertos_status(self):
|
||||
if self.ioc_data:
|
||||
status = "已启用" if self.check_freertos_enabled() else "未启用"
|
||||
else:
|
||||
status = "未检测到 .ioc 文件"
|
||||
self.freertos_status_label.setText(f"FreeRTOS 状态: {status}")
|
||||
|
||||
def update_header_files(self):
|
||||
for i in reversed(range(self.header_layout.count())):
|
||||
widget = self.header_layout.itemAt(i).widget()
|
||||
if widget:
|
||||
widget.deleteLater()
|
||||
if not self.repo_ready or not os.path.exists(self.repo_dir):
|
||||
return
|
||||
from collections import defaultdict
|
||||
import csv
|
||||
folders = ["bsp", "component", "device", "module"]
|
||||
dependencies = defaultdict(list)
|
||||
for folder in folders:
|
||||
folder_dir = os.path.join(self.repo_dir, "User", folder)
|
||||
dep_file = os.path.join(folder_dir, "dependencies.csv")
|
||||
if os.path.exists(dep_file):
|
||||
with open(dep_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(self.repo_dir, "User", folder)
|
||||
if os.path.exists(folder_dir):
|
||||
group = QGroupBox(folder)
|
||||
g_layout = QHBoxLayout(group)
|
||||
for file in os.listdir(folder_dir):
|
||||
file_base, file_ext = os.path.splitext(file)
|
||||
if file_ext == ".h" and file_base != folder:
|
||||
var = QCheckBox(file_base)
|
||||
var.stateChanged.connect(lambda x, fb=file_base: self.handle_dependencies(fb, dependencies))
|
||||
self.header_file_vars[file_base] = var
|
||||
g_layout.addWidget(var)
|
||||
self.header_layout.addWidget(group)
|
||||
|
||||
def handle_dependencies(self, file_base, dependencies):
|
||||
if file_base in self.header_file_vars and self.header_file_vars[file_base].isChecked():
|
||||
for dep in dependencies.get(file_base, []):
|
||||
dep_base = os.path.basename(dep)
|
||||
if dep_base in self.header_file_vars:
|
||||
self.header_file_vars[dep_base].setChecked(True)
|
||||
|
||||
def update_task_ui(self):
|
||||
for i in reversed(range(self.task_layout.count())):
|
||||
widget = self.task_layout.itemAt(i).widget()
|
||||
if widget:
|
||||
widget.deleteLater()
|
||||
if not self.repo_ready or not self.check_freertos_enabled():
|
||||
self.task_group.setVisible(False)
|
||||
return
|
||||
self.task_group.setVisible(True)
|
||||
for i, (task_var, freq_var) in enumerate(self.task_vars):
|
||||
row = QHBoxLayout()
|
||||
name_edit = QLineEdit(task_var)
|
||||
freq_spin = QSpinBox()
|
||||
freq_spin.setRange(1, 1000)
|
||||
freq_spin.setValue(freq_var)
|
||||
del_btn = QPushButton("删除")
|
||||
del_btn.clicked.connect(lambda _, idx=i: self.remove_task(idx))
|
||||
row.addWidget(name_edit)
|
||||
row.addWidget(QLabel("频率:"))
|
||||
row.addWidget(freq_spin)
|
||||
row.addWidget(del_btn)
|
||||
container = QWidget()
|
||||
container.setLayout(row)
|
||||
self.task_layout.addWidget(container)
|
||||
add_btn = QPushButton("添加任务")
|
||||
add_btn.clicked.connect(self.add_task)
|
||||
self.task_layout.addWidget(add_btn)
|
||||
|
||||
def add_task(self):
|
||||
self.task_vars.append([f"Task_{len(self.task_vars)+1}", 100])
|
||||
self.update_task_ui()
|
||||
|
||||
def remove_task(self, idx):
|
||||
if 0 <= idx < len(self.task_vars):
|
||||
self.task_vars.pop(idx)
|
||||
self.update_task_ui()
|
||||
|
||||
def copy_file_from_repo(self, src_path, dest_path):
|
||||
import shutil
|
||||
if src_path.startswith(self.repo_dir):
|
||||
full_src_path = src_path
|
||||
else:
|
||||
full_src_path = os.path.join(self.repo_dir, src_path.lstrip(os.sep))
|
||||
if not os.path.exists(full_src_path):
|
||||
self.log(f"文件 {full_src_path} 不存在!")
|
||||
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)
|
||||
self.log(f"已复制 {full_src_path} 到 {dest_path}")
|
||||
|
||||
def generate_action(self):
|
||||
import threading
|
||||
def task():
|
||||
self.create_directories()
|
||||
if self.add_gitignore:
|
||||
self.copy_file_from_repo(".gitignore", ".gitignore")
|
||||
if self.ioc_data and self.check_freertos_enabled():
|
||||
self.copy_file_from_repo("src/freertos.c", os.path.join("Core", "Src", "freertos.c"))
|
||||
folders = ["bsp", "component", "device", "module"]
|
||||
for folder in folders:
|
||||
folder_dir = os.path.join(self.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
|
||||
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].isChecked():
|
||||
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)
|
||||
if self.ioc_data and self.check_freertos_enabled():
|
||||
self.modify_user_task_file()
|
||||
self.generate_user_task_header()
|
||||
self.generate_init_file()
|
||||
self.generate_task_files()
|
||||
self.log("生成完成!")
|
||||
threading.Thread(target=task).start()
|
||||
|
||||
def create_directories(self):
|
||||
dirs = [
|
||||
"User/bsp",
|
||||
"User/component",
|
||||
"User/device",
|
||||
"User/module",
|
||||
]
|
||||
if self.ioc_data and self.check_freertos_enabled():
|
||||
dirs.append("User/task")
|
||||
for d in dirs:
|
||||
if not os.path.exists(d):
|
||||
os.makedirs(d, exist_ok=True)
|
||||
self.log(f"已创建目录: {d}")
|
||||
|
||||
def generate_task_files(self):
|
||||
try:
|
||||
import re
|
||||
template_file_path = os.path.join(self.repo_dir, "User", "task", "task.c.template")
|
||||
task_dir = os.path.join("User", "task")
|
||||
if not os.path.exists(template_file_path):
|
||||
self.log(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()
|
||||
for task in self.task_vars:
|
||||
if isinstance(task, (list, tuple)):
|
||||
task_name = str(task[0])
|
||||
else:
|
||||
task_name = str(task)
|
||||
task_file_path = os.path.join(task_dir, f"{task_name.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_name.upper()}"
|
||||
)
|
||||
task_content = task_content.replace("{{task_delay}}", f"TASK_INIT_DELAY_{task_name.upper()}")
|
||||
with open(task_file_path, "w", encoding="utf-8") as f2:
|
||||
f2.write(task_content)
|
||||
self.log(f"已成功生成 {task_file_path} 文件!")
|
||||
except Exception as e:
|
||||
self.log(f"生成 task.c 文件时出错: {e}")
|
||||
|
||||
def modify_user_task_file(self):
|
||||
try:
|
||||
import re
|
||||
template_file_path = os.path.join(self.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):
|
||||
self.log(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_{str(task[0]).lower()} = {{
|
||||
.name = "{str(task[0])}",
|
||||
.priority = osPriorityNormal,
|
||||
.stack_size = 128 * 4,
|
||||
}};"""
|
||||
for task 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 f2:
|
||||
f2.write(task_content)
|
||||
self.log(f"已成功生成 {generated_task_file_path} 文件!")
|
||||
except Exception as e:
|
||||
self.log(f"修改 user_task.c 文件时出错: {e}")
|
||||
|
||||
def generate_user_task_header(self):
|
||||
try:
|
||||
import re
|
||||
template_file_path = os.path.join(self.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):
|
||||
self.log(f"模板文件 {template_file_path} 不存在,无法生成 user_task.h 文件!")
|
||||
return
|
||||
os.makedirs(os.path.dirname(header_file_path), exist_ok=True)
|
||||
existing_msgq_content = ""
|
||||
if os.path.exists(header_file_path):
|
||||
with open(header_file_path, "r", encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
match = re.search(r"/\* USER MESSAGE BEGIN \*/\s*(.*?)\s*/\* USER MESSAGE END \*/", content, re.DOTALL)
|
||||
if match:
|
||||
existing_msgq_content = match.group(1).strip()
|
||||
self.log("已存在的 msgq 区域内容已保留")
|
||||
with open(template_file_path, "r", encoding="utf-8") as f:
|
||||
template_content = f.read()
|
||||
thread_definitions = "\n".join([f" osThreadId_t {str(task[0]).lower()};" for task in self.task_vars])
|
||||
msgq_definitions = existing_msgq_content if existing_msgq_content else " osMessageQueueId_t default_msgq;"
|
||||
freq_definitions = "\n".join([f" float {str(task[0]).lower()};" for task in self.task_vars])
|
||||
last_up_time_definitions = "\n".join([f" uint32_t {str(task[0]).lower()};" for task in self.task_vars])
|
||||
task_attr_declarations = "\n".join([f"extern const osThreadAttr_t attr_{str(task[0]).lower()};" for task in self.task_vars])
|
||||
task_function_declarations = "\n".join([f"void {str(task[0])}(void *argument);" for task in self.task_vars])
|
||||
task_frequency_definitions = "\n".join([
|
||||
f"#define TASK_FREQ_{str(task[0]).upper()} ({int(task[1])}u)"
|
||||
for task in self.task_vars
|
||||
])
|
||||
task_init_delay_definitions = "\n".join([f"#define TASK_INIT_DELAY_{str(task[0]).upper()} (0u)" for task in self.task_vars])
|
||||
task_handle_definitions = "\n".join([f" osThreadId_t {str(task[0]).lower()};" for task 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)
|
||||
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 f2:
|
||||
f2.write(header_content)
|
||||
self.log(f"已成功生成 {header_file_path} 文件!")
|
||||
except Exception as e:
|
||||
self.log(f"生成 user_task.h 文件时出错: {e}")
|
||||
|
||||
def generate_init_file(self):
|
||||
try:
|
||||
import re
|
||||
template_file_path = os.path.join(self.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):
|
||||
self.log(f"模板文件 {template_file_path} 不存在,无法生成 init.c 文件!")
|
||||
return
|
||||
os.makedirs(os.path.dirname(generated_file_path), exist_ok=True)
|
||||
existing_msgq_content = ""
|
||||
if os.path.exists(generated_file_path):
|
||||
with open(generated_file_path, "r", encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
match = re.search(r"/\* USER MESSAGE BEGIN \*/\s*(.*?)\s*/\* USER MESSAGE END \*/", content, re.DOTALL)
|
||||
if match:
|
||||
existing_msgq_content = match.group(1).strip()
|
||||
self.log("已存在的消息队列区域内容已保留")
|
||||
with open(template_file_path, "r", encoding="utf-8") as f:
|
||||
template_content = f.read()
|
||||
thread_creation_code = "\n".join([
|
||||
f" task_runtime.thread.{str(task[0]).lower()} = osThreadNew({str(task[0])}, NULL, &attr_{str(task[0]).lower()});"
|
||||
for task in self.task_vars
|
||||
])
|
||||
init_content = template_content.replace("{{thread_creation_code}}", thread_creation_code)
|
||||
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 f2:
|
||||
f2.write(init_content)
|
||||
self.log(f"已成功生成 {generated_file_path} 文件!")
|
||||
except Exception as e:
|
||||
self.log(f"生成 init.c 文件时出错: {e}")
|
||||
|
||||
# --------- 主工具箱UI ---------
|
||||
class ToolboxUI(QWidget):
|
||||
@ -1399,7 +1977,7 @@ class ToolboxUI(QWidget):
|
||||
left_layout.addWidget(logo_label)
|
||||
|
||||
# 按钮区
|
||||
self.button_names = ["主页", "曲线拟合", "Mini串口助手", "MR架构配置","软件指南"]
|
||||
self.button_names = ["主页", "曲线拟合", "Mini串口助手(BUG)", "MR架构配置(开发中)","软件指南"]
|
||||
self.buttons = []
|
||||
for idx, name in enumerate(self.button_names):
|
||||
btn = QPushButton(name)
|
||||
@ -1480,6 +2058,8 @@ class ToolboxUI(QWidget):
|
||||
|
||||
self.output_box.append("欢迎使用 MRobot 工具箱!请选择左侧功能。")
|
||||
|
||||
|
||||
|
||||
def placeholder_page(self, text):
|
||||
page = QWidget()
|
||||
layout = QVBoxLayout(page)
|
||||
|
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