mirror of
https://github.com/goldenfishs/MRobot.git
synced 2025-07-04 22:44:16 +08:00
添加了可选运行频率方案
This commit is contained in:
parent
606bd7e054
commit
e9eb169547
60
MRobot.py
60
MRobot.py
@ -712,7 +712,7 @@ class DataInterface(BaseInterface):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def open_task_config_dialog(self):
|
def open_task_config_dialog(self):
|
||||||
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QSpinBox, QPushButton, QTableWidget, QTableWidgetItem, QHeaderView
|
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QSpinBox, QPushButton, QTableWidget, QTableWidgetItem, QHeaderView, QCheckBox
|
||||||
import yaml
|
import yaml
|
||||||
import os
|
import os
|
||||||
|
|
||||||
@ -720,15 +720,16 @@ class DataInterface(BaseInterface):
|
|||||||
def __init__(self, parent=None, config_path=None):
|
def __init__(self, parent=None, config_path=None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.setWindowTitle("任务配置")
|
self.setWindowTitle("任务配置")
|
||||||
self.resize(800, 420)
|
self.resize(900, 420)
|
||||||
layout = QVBoxLayout(self)
|
layout = QVBoxLayout(self)
|
||||||
self.table = QTableWidget(0, 5)
|
self.table = QTableWidget(0, 6)
|
||||||
self.table.setHorizontalHeaderLabels(["任务名称", "运行频率", "初始化延迟", "堆栈大小", "任务描述"])
|
self.table.setHorizontalHeaderLabels(["任务名称", "运行频率", "初始化延迟", "堆栈大小", "任务描述", "频率控制"])
|
||||||
self.table.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch)
|
self.table.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch)
|
||||||
self.table.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch)
|
self.table.horizontalHeader().setSectionResizeMode(1, QHeaderView.Stretch)
|
||||||
self.table.horizontalHeader().setSectionResizeMode(2, QHeaderView.Stretch)
|
self.table.horizontalHeader().setSectionResizeMode(2, QHeaderView.Stretch)
|
||||||
self.table.horizontalHeader().setSectionResizeMode(3, QHeaderView.Stretch)
|
self.table.horizontalHeader().setSectionResizeMode(3, QHeaderView.Stretch)
|
||||||
self.table.horizontalHeader().setSectionResizeMode(4, QHeaderView.Stretch)
|
self.table.horizontalHeader().setSectionResizeMode(4, QHeaderView.Stretch)
|
||||||
|
self.table.horizontalHeader().setSectionResizeMode(5, QHeaderView.ResizeToContents)
|
||||||
self.table.setColumnWidth(4, 320) # 任务描述更宽
|
self.table.setColumnWidth(4, 320) # 任务描述更宽
|
||||||
layout.addWidget(self.table)
|
layout.addWidget(self.table)
|
||||||
btn_layout = QHBoxLayout()
|
btn_layout = QHBoxLayout()
|
||||||
@ -748,6 +749,8 @@ class DataInterface(BaseInterface):
|
|||||||
cancel_btn.clicked.connect(self.reject)
|
cancel_btn.clicked.connect(self.reject)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# 自动读取配置文件
|
# 自动读取配置文件
|
||||||
if config_path and os.path.exists(config_path):
|
if config_path and os.path.exists(config_path):
|
||||||
try:
|
try:
|
||||||
@ -761,6 +764,10 @@ class DataInterface(BaseInterface):
|
|||||||
item = QTableWidgetItem(str(t.get(key, "")))
|
item = QTableWidgetItem(str(t.get(key, "")))
|
||||||
item.setTextAlignment(Qt.AlignCenter)
|
item.setTextAlignment(Qt.AlignCenter)
|
||||||
self.table.setItem(row, col, item)
|
self.table.setItem(row, col, item)
|
||||||
|
# 新增频率控制复选框
|
||||||
|
freq_ctrl = QCheckBox()
|
||||||
|
freq_ctrl.setChecked(t.get("freq_control", True))
|
||||||
|
self.table.setCellWidget(row, 5, freq_ctrl)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
pass # 配置文件损坏时忽略
|
pass # 配置文件损坏时忽略
|
||||||
|
|
||||||
@ -774,6 +781,9 @@ class DataInterface(BaseInterface):
|
|||||||
item = QTableWidgetItem(val)
|
item = QTableWidgetItem(val)
|
||||||
item.setTextAlignment(Qt.AlignCenter)
|
item.setTextAlignment(Qt.AlignCenter)
|
||||||
self.table.setItem(row, col, item)
|
self.table.setItem(row, col, item)
|
||||||
|
freq_ctrl = QCheckBox()
|
||||||
|
freq_ctrl.setChecked(True)
|
||||||
|
self.table.setCellWidget(row, 5, freq_ctrl)
|
||||||
|
|
||||||
def del_row(self):
|
def del_row(self):
|
||||||
rows = set([i.row() for i in self.table.selectedItems()])
|
rows = set([i.row() for i in self.table.selectedItems()])
|
||||||
@ -784,23 +794,31 @@ class DataInterface(BaseInterface):
|
|||||||
tasks = []
|
tasks = []
|
||||||
for row in range(self.table.rowCount()):
|
for row in range(self.table.rowCount()):
|
||||||
name = self.table.item(row, 0).text().strip()
|
name = self.table.item(row, 0).text().strip()
|
||||||
freq = int(self.table.item(row, 1).text())
|
freq = self.table.item(row, 1).text()
|
||||||
delay = int(self.table.item(row, 2).text())
|
delay = int(self.table.item(row, 2).text())
|
||||||
stack = int(self.table.item(row, 3).text())
|
stack = int(self.table.item(row, 3).text())
|
||||||
desc = self.table.item(row, 4).text().strip()
|
desc = self.table.item(row, 4).text().strip()
|
||||||
|
freq_ctrl = self.table.cellWidget(row, 5).isChecked()
|
||||||
# 校验 stack 必须为 128*2^n
|
# 校验 stack 必须为 128*2^n
|
||||||
if stack < 128 or (stack & (stack - 1)) != 0 or stack % 128 != 0:
|
if stack < 128 or (stack & (stack - 1)) != 0 or stack % 128 != 0:
|
||||||
raise ValueError(f"第{row+1}行任务“{name}”的堆栈大小必须为128、256、512、1024等(128*2^n)")
|
raise ValueError(f"第{row+1}行任务“{name}”的堆栈大小必须为128、256、512、1024等(128*2^n)")
|
||||||
tasks.append({
|
task = {
|
||||||
"name": name,
|
"name": name,
|
||||||
"function": f"Task_{name}",
|
"function": f"Task_{name}",
|
||||||
"frequency": freq,
|
|
||||||
"delay": delay,
|
"delay": delay,
|
||||||
"stack": stack,
|
"stack": stack,
|
||||||
"description": desc
|
"description": desc,
|
||||||
})
|
"freq_control": freq_ctrl
|
||||||
|
}
|
||||||
|
if freq_ctrl:
|
||||||
|
task["frequency"] = int(freq)
|
||||||
|
tasks.append(task)
|
||||||
return tasks
|
return tasks
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
config_path = os.path.join(self.project_path, "User", "task", "config.yaml")
|
config_path = os.path.join(self.project_path, "User", "task", "config.yaml")
|
||||||
dlg = TaskConfigDialog(self, config_path=config_path)
|
dlg = TaskConfigDialog(self, config_path=config_path)
|
||||||
if dlg.exec() == QDialog.Accepted:
|
if dlg.exec() == QDialog.Accepted:
|
||||||
@ -856,19 +874,21 @@ class DataInterface(BaseInterface):
|
|||||||
init_c_tpl = os.path.join(template_dir, "init.c.template")
|
init_c_tpl = os.path.join(template_dir, "init.c.template")
|
||||||
task_c_tpl = os.path.join(template_dir, "task.c.template")
|
task_c_tpl = os.path.join(template_dir, "task.c.template")
|
||||||
|
|
||||||
|
# 只统计需要频率控制的任务
|
||||||
|
freq_tasks = [t for t in task_list if t.get("freq_control", True)]
|
||||||
|
|
||||||
def render_template(path, context):
|
def render_template(path, context):
|
||||||
with open(path, encoding="utf-8") as f:
|
with open(path, encoding="utf-8") as f:
|
||||||
tpl = Template(f.read())
|
tpl = Template(f.read())
|
||||||
return tpl.render(**context)
|
return tpl.render(**context)
|
||||||
|
|
||||||
|
|
||||||
# 构造模板上下文
|
# 构造模板上下文
|
||||||
context_h = {
|
context_h = {
|
||||||
"thread_definitions": "\n".join([f" osThreadId_t {t['name']};" for t in task_list]),
|
"thread_definitions": "\n".join([f" osThreadId_t {t['name']};" for t in task_list]),
|
||||||
"freq_definitions": "\n".join([f" float {t['name']};" for t in task_list]),
|
"freq_definitions": "\n".join([f" float {t['name']};" for t in freq_tasks]),
|
||||||
"stack_definitions": "\n".join([f" UBaseType_t {t['name']};" for t in task_list]),
|
"stack_definitions": "\n".join([f" UBaseType_t {t['name']};" for t in task_list]),
|
||||||
"last_up_time_definitions": "\n".join([f" float {t['name']};" for t in task_list]),
|
"last_up_time_definitions": "\n".join([f" float {t['name']};" for t in freq_tasks]),
|
||||||
"task_frequency_definitions": "\n".join([f"#define {t['name'].upper()}_FREQ ({t['frequency']})" for t in task_list]),
|
"task_frequency_definitions": "\n".join([f"#define {t['name'].upper()}_FREQ ({t['frequency']})" for t in freq_tasks]),
|
||||||
"task_init_delay_definitions": "\n".join([f"#define {t['name'].upper()}_INIT_DELAY ({t['delay']})" for t in task_list]),
|
"task_init_delay_definitions": "\n".join([f"#define {t['name'].upper()}_INIT_DELAY ({t['delay']})" for t in task_list]),
|
||||||
"task_attr_declarations": "\n".join([f"extern const osThreadAttr_t attr_{t['name']};" for t in task_list]),
|
"task_attr_declarations": "\n".join([f"extern const osThreadAttr_t attr_{t['name']};" for t in task_list]),
|
||||||
"task_function_declarations": "\n".join([f"void {t['function']}(void *argument);" for t in task_list]),
|
"task_function_declarations": "\n".join([f"void {t['function']}(void *argument);" for t in task_list]),
|
||||||
@ -969,17 +989,16 @@ class DataInterface(BaseInterface):
|
|||||||
f.write(init_c)
|
f.write(init_c)
|
||||||
|
|
||||||
# ----------- 生成 task.c -----------
|
# ----------- 生成 task.c -----------
|
||||||
task_c_tpl = os.path.join(template_dir, "task.c.template")
|
|
||||||
for t in task_list:
|
for t in task_list:
|
||||||
# 自动换行任务描述
|
|
||||||
desc = t.get("description", "")
|
desc = t.get("description", "")
|
||||||
desc_wrapped = "\n ".join(textwrap.wrap(desc, 20))
|
desc_wrapped = "\n ".join(textwrap.wrap(desc, 20))
|
||||||
context_task = {
|
context_task = {
|
||||||
"task_name": t["name"],
|
"task_name": t["name"],
|
||||||
"task_function": t["function"],
|
"task_function": t["function"],
|
||||||
"task_frequency": f"{t['name'].upper()}_FREQ", # 使用宏定义
|
"task_frequency": f"{t['name'].upper()}_FREQ" if t.get("freq_control", True) else None,
|
||||||
"task_delay": f"{t['name'].upper()}_INIT_DELAY", # 使用宏定义
|
"task_delay": f"{t['name'].upper()}_INIT_DELAY",
|
||||||
"task_description": desc_wrapped
|
"task_description": desc_wrapped,
|
||||||
|
"freq_control": t.get("freq_control", True)
|
||||||
}
|
}
|
||||||
# 渲染模板
|
# 渲染模板
|
||||||
with open(task_c_tpl, encoding="utf-8") as f:
|
with open(task_c_tpl, encoding="utf-8") as f:
|
||||||
@ -990,7 +1009,6 @@ class DataInterface(BaseInterface):
|
|||||||
if os.path.exists(task_c_path):
|
if os.path.exists(task_c_path):
|
||||||
with open(task_c_path, "r", encoding="utf-8") as f:
|
with open(task_c_path, "r", encoding="utf-8") as f:
|
||||||
old_code = f.read()
|
old_code = f.read()
|
||||||
# 只保留USER区域
|
|
||||||
def preserve_user_region(new_code, old_code, region_name):
|
def preserve_user_region(new_code, old_code, region_name):
|
||||||
pattern = re.compile(
|
pattern = re.compile(
|
||||||
rf"/\*\s*{region_name}\s*BEGIN\s*\*/(.*?)/\*\s*{region_name}\s*END\s*\*/",
|
rf"/\*\s*{region_name}\s*BEGIN\s*\*/(.*?)/\*\s*{region_name}\s*END\s*\*/",
|
||||||
@ -1007,10 +1025,6 @@ class DataInterface(BaseInterface):
|
|||||||
code = preserve_user_region(code, old_code, region)
|
code = preserve_user_region(code, old_code, region)
|
||||||
with open(task_c_path, "w", encoding="utf-8") as f:
|
with open(task_c_path, "w", encoding="utf-8") as f:
|
||||||
f.write(code)
|
f.write(code)
|
||||||
# ----------- 保存任务配置到 config.yaml -----------
|
|
||||||
config_path = os.path.join(output_dir, "config.yaml")
|
|
||||||
with open(config_path, "w", encoding="utf-8") as f:
|
|
||||||
yaml.dump(task_list, f, allow_unicode=True)
|
|
||||||
|
|
||||||
# ===================== 串口终端界面 =====================
|
# ===================== 串口终端界面 =====================
|
||||||
class SerialReadThread(QThread):
|
class SerialReadThread(QThread):
|
||||||
|
@ -22,13 +22,16 @@
|
|||||||
void {{task_function}}(void *argument) {
|
void {{task_function}}(void *argument) {
|
||||||
(void)argument; /* 未使用argument,消除警告 */
|
(void)argument; /* 未使用argument,消除警告 */
|
||||||
|
|
||||||
|
{% if freq_control %}
|
||||||
/* 计算任务运行到指定频率需要等待的tick数 */
|
/* 计算任务运行到指定频率需要等待的tick数 */
|
||||||
const uint32_t delay_tick = osKernelGetTickFreq() / {{task_frequency}};
|
const uint32_t delay_tick = osKernelGetTickFreq() / {{task_frequency}};
|
||||||
|
|
||||||
osDelay({{task_delay}}); /* 延时一段时间再开启任务 */
|
osDelay({{task_delay}}); /* 延时一段时间再开启任务 */
|
||||||
/* USER CODE INIT BEGIN */
|
|
||||||
|
|
||||||
/* USER CODE INIT END */
|
/* USER CODE INIT BEGIN*/
|
||||||
|
|
||||||
|
/* USER CODE INIT END*/
|
||||||
|
|
||||||
uint32_t tick = osKernelGetTickCount(); /* 控制任务运行频率的计时 */
|
uint32_t tick = osKernelGetTickCount(); /* 控制任务运行频率的计时 */
|
||||||
while (1) {
|
while (1) {
|
||||||
tick += delay_tick; /* 计算下一个唤醒时刻 */
|
tick += delay_tick; /* 计算下一个唤醒时刻 */
|
||||||
@ -37,4 +40,18 @@ void {{task_function}}(void *argument) {
|
|||||||
/* USER CODE END */
|
/* USER CODE END */
|
||||||
osDelayUntil(tick); /* 运行结束,等待下一次唤醒 */
|
osDelayUntil(tick); /* 运行结束,等待下一次唤醒 */
|
||||||
}
|
}
|
||||||
|
{% else %}
|
||||||
|
osDelay({{task_delay}}); /* 延时一段时间再开启任务 */
|
||||||
|
|
||||||
|
/* USER CODE INIT BEGIN*/
|
||||||
|
|
||||||
|
/* USER CODE INIT END*/
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
/* USER CODE BEGIN */
|
||||||
|
|
||||||
|
/* USER CODE END */
|
||||||
|
osDelay(1); /* 默认1ms延时,防止死循环卡死CPU */
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user