This commit is contained in:
Robofish 2025-10-21 00:59:19 +08:00
parent 49545dd232
commit d1c3b2747a
13 changed files with 600 additions and 875 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@ -6,42 +6,8 @@ from app.tools.analyzing_ioc import analyzing_ioc
from app.tools.code_generator import CodeGenerator from app.tools.code_generator import CodeGenerator
import os import os
import csv import csv
import shutil
import re import re
def preserve_all_user_regions(new_code, old_code):
""" Preserves all user-defined regions in the new code based on the old code.
This function uses regex to find user-defined regions in the old code and replaces them in the new code.
Args:
new_code (str): The new code content.
old_code (str): The old code content.
Returns:
str: The new code with preserved user-defined regions.
"""
import re
pattern = re.compile(
r"/\*\s*(USER [A-Z0-9_ ]+)\s*BEGIN\s*\*/(.*?)/\*\s*\1\s*END\s*\*/",
re.DOTALL
)
old_regions = {m.group(1): m.group(2) for m in pattern.finditer(old_code or "")}
def repl(m):
region = m.group(1)
old_content = old_regions.get(region)
if old_content is not None:
return m.group(0).replace(m.group(2), old_content)
return m.group(0)
return pattern.sub(repl, new_code)
def save_with_preserve(path, new_code):
if os.path.exists(path):
with open(path, "r", encoding="utf-8") as f:
old_code = f.read()
new_code = preserve_all_user_regions(new_code, old_code)
# 确保目录存在
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path, "w", encoding="utf-8") as f:
f.write(new_code)
class BspSimplePeripheral(QWidget): class BspSimplePeripheral(QWidget):
def __init__(self, project_path, peripheral_name, template_names): def __init__(self, project_path, peripheral_name, template_names):
super().__init__() super().__init__()
@ -50,7 +16,7 @@ class BspSimplePeripheral(QWidget):
self.template_names = template_names self.template_names = template_names
# 加载描述 # 加载描述
describe_path = os.path.join(CodeGenerator.get_assets_dir("User_code/bsp"), "describe.csv") describe_path = os.path.join(CodeGenerator.get_assets_dir("User_code/bsp"), "describe.csv")
self.descriptions = load_descriptions(describe_path) self.descriptions = CodeGenerator.load_descriptions(describe_path)
self._init_ui() self._init_ui()
self._load_config() self._load_config()
@ -102,7 +68,7 @@ class BspSimplePeripheral(QWidget):
if not template_content: if not template_content:
return False return False
output_path = os.path.join(self.project_path, f"User/bsp/{filename}") output_path = os.path.join(self.project_path, f"User/bsp/{filename}")
save_with_preserve(output_path, template_content) # 使用保留用户区域的写入 CodeGenerator.save_with_preserve(output_path, template_content) # 使用保留用户区域的写入
self._save_config() self._save_config()
return True return True
@ -133,7 +99,7 @@ class BspPeripheralBase(QWidget):
self.available_list = [] self.available_list = []
# 新增:加载描述 # 新增:加载描述
describe_path = os.path.join(CodeGenerator.get_assets_dir("User_code/bsp"), "describe.csv") describe_path = os.path.join(CodeGenerator.get_assets_dir("User_code/bsp"), "describe.csv")
self.descriptions = load_descriptions(describe_path) self.descriptions = CodeGenerator.load_descriptions(describe_path)
self._init_ui() self._init_ui()
self._load_config() self._load_config()
@ -249,7 +215,7 @@ class BspPeripheralBase(QWidget):
template_content, f"AUTO GENERATED {self.enum_prefix}_NAME", "\n".join(enum_lines) template_content, f"AUTO GENERATED {self.enum_prefix}_NAME", "\n".join(enum_lines)
) )
output_path = os.path.join(self.project_path, f"User/bsp/{self.template_names['header']}") output_path = os.path.join(self.project_path, f"User/bsp/{self.template_names['header']}")
save_with_preserve(output_path, content) # 使用保留用户区域的写入 CodeGenerator.save_with_preserve(output_path, content) # 使用保留用户区域的写入
return True return True
def _generate_source_file(self, configs, template_dir): def _generate_source_file(self, configs, template_dir):
@ -283,7 +249,7 @@ class BspPeripheralBase(QWidget):
content, f"AUTO GENERATED {self.enum_prefix}_GET_HANDLE", "\n".join(handle_lines) content, f"AUTO GENERATED {self.enum_prefix}_GET_HANDLE", "\n".join(handle_lines)
) )
output_path = os.path.join(self.project_path, f"User/bsp/{self.template_names['source']}") output_path = os.path.join(self.project_path, f"User/bsp/{self.template_names['source']}")
save_with_preserve(output_path, content) # 使用保留用户区域的写入 CodeGenerator.save_with_preserve(output_path, content) # 使用保留用户区域的写入
return True return True
def _save_config(self, configs): def _save_config(self, configs):
@ -337,17 +303,6 @@ class BspPeripheralBase(QWidget):
self._save_config(configs) self._save_config(configs)
return True return True
def load_descriptions(csv_path):
descriptions = {}
if os.path.exists(csv_path):
with open(csv_path, encoding='utf-8') as f:
reader = csv.reader(f)
for row in reader:
if len(row) >= 2:
key, desc = row[0].strip(), row[1].strip()
descriptions[key.lower()] = desc
return descriptions
# 各外设的可用列表获取函数 # 各外设的可用列表获取函数
def get_available_i2c(project_path): def get_available_i2c(project_path):
ioc_files = [f for f in os.listdir(project_path) if f.endswith('.ioc')] ioc_files = [f for f in os.listdir(project_path) if f.endswith('.ioc')]
@ -474,7 +429,7 @@ class bsp_can(BspPeripheralBase):
) )
output_path = os.path.join(self.project_path, f"User/bsp/{self.template_names['source']}") output_path = os.path.join(self.project_path, f"User/bsp/{self.template_names['source']}")
save_with_preserve(output_path, content) CodeGenerator.save_with_preserve(output_path, content)
return True return True
def _generate_single_can_init(self, init_lines, configs, fifo_assignment): def _generate_single_can_init(self, init_lines, configs, fifo_assignment):
@ -744,7 +699,7 @@ class bsp_gpio(QWidget):
self.available_list = self._get_all_gpio_list() self.available_list = self._get_all_gpio_list()
# 加载描述 # 加载描述
describe_path = os.path.join(CodeGenerator.get_assets_dir("User_code/bsp"), "describe.csv") describe_path = os.path.join(CodeGenerator.get_assets_dir("User_code/bsp"), "describe.csv")
self.descriptions = load_descriptions(describe_path) self.descriptions = CodeGenerator.load_descriptions(describe_path)
self._init_ui() self._init_ui()
self._load_config() self._load_config()
@ -891,7 +846,7 @@ class bsp_gpio(QWidget):
) )
output_path = os.path.join(self.project_path, "User/bsp/gpio.h") output_path = os.path.join(self.project_path, "User/bsp/gpio.h")
save_with_preserve(output_path, content) CodeGenerator.save_with_preserve(output_path, content)
return True return True
def _generate_source_file(self, configs, template_dir): def _generate_source_file(self, configs, template_dir):
@ -932,7 +887,7 @@ class bsp_gpio(QWidget):
) )
output_path = os.path.join(self.project_path, "User/bsp/gpio.c") output_path = os.path.join(self.project_path, "User/bsp/gpio.c")
save_with_preserve(output_path, content) CodeGenerator.save_with_preserve(output_path, content)
return True return True
@ -983,7 +938,7 @@ class bsp_pwm(QWidget):
self.available_list = self._get_pwm_channels() self.available_list = self._get_pwm_channels()
# 加载描述 # 加载描述
describe_path = os.path.join(CodeGenerator.get_assets_dir("User_code/bsp"), "describe.csv") describe_path = os.path.join(CodeGenerator.get_assets_dir("User_code/bsp"), "describe.csv")
self.descriptions = load_descriptions(describe_path) self.descriptions = CodeGenerator.load_descriptions(describe_path)
self._init_ui() self._init_ui()
self._load_config() self._load_config()
@ -1148,7 +1103,7 @@ class bsp_pwm(QWidget):
) )
output_path = os.path.join(self.project_path, "User/bsp/pwm.h") output_path = os.path.join(self.project_path, "User/bsp/pwm.h")
save_with_preserve(output_path, content) CodeGenerator.save_with_preserve(output_path, content)
return True return True
def _generate_source_file(self, configs, template_dir): def _generate_source_file(self, configs, template_dir):
@ -1169,7 +1124,7 @@ class bsp_pwm(QWidget):
) )
output_path = os.path.join(self.project_path, "User/bsp/pwm.c") output_path = os.path.join(self.project_path, "User/bsp/pwm.c")
save_with_preserve(output_path, content) CodeGenerator.save_with_preserve(output_path, content)
return True return True
def _save_config(self, configs): def _save_config(self, configs):
@ -1245,7 +1200,7 @@ class bsp(QWidget):
if os.path.exists(src_bsp_h): if os.path.exists(src_bsp_h):
with open(src_bsp_h, 'r', encoding='utf-8') as f: with open(src_bsp_h, 'r', encoding='utf-8') as f:
content = f.read() content = f.read()
save_with_preserve(dst_bsp_h, content) CodeGenerator.save_with_preserve(dst_bsp_h, content)
total = 0 total = 0
success_count = 0 success_count = 0

View File

@ -4,66 +4,7 @@ from qfluentwidgets import InfoBar
from PyQt5.QtCore import Qt, pyqtSignal from PyQt5.QtCore import Qt, pyqtSignal
from app.tools.code_generator import CodeGenerator from app.tools.code_generator import CodeGenerator
import os import os
import csv
import shutil import shutil
import re
def preserve_all_user_regions(new_code, old_code):
""" Preserves all user-defined regions in the new code based on the old code.
This function uses regex to find user-defined regions in the old code and replaces them in the new code.
Args:
new_code (str): The new code content.
old_code (str): The old code content.
Returns:
str: The new code with preserved user-defined regions.
"""
pattern = re.compile(
r"/\*\s*(USER [A-Z0-9_ ]+)\s*BEGIN\s*\*/(.*?)/\*\s*\1\s*END\s*\*/",
re.DOTALL
)
old_regions = {m.group(1): m.group(2) for m in pattern.finditer(old_code or "")}
def repl(m):
region = m.group(1)
old_content = old_regions.get(region)
if old_content is not None:
return m.group(0).replace(m.group(2), old_content)
return m.group(0)
return pattern.sub(repl, new_code)
def save_with_preserve(path, new_code):
"""保存文件并保留用户区域"""
if os.path.exists(path):
with open(path, "r", encoding="utf-8") as f:
old_code = f.read()
new_code = preserve_all_user_regions(new_code, old_code)
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path, "w", encoding="utf-8") as f:
f.write(new_code)
def load_descriptions(csv_path):
"""加载组件描述信息"""
descriptions = {}
if os.path.exists(csv_path):
with open(csv_path, encoding='utf-8') as f:
reader = csv.reader(f)
for row in reader:
if len(row) >= 2:
key, desc = row[0].strip(), row[1].strip()
descriptions[key.lower()] = desc
return descriptions
def load_dependencies(csv_path):
"""加载组件依赖关系"""
dependencies = {}
if os.path.exists(csv_path):
with open(csv_path, encoding='utf-8') as f:
reader = csv.reader(f)
for row in reader:
if len(row) >= 2:
component = row[0].strip()
deps = [dep.strip() for dep in row[1:] if dep.strip()]
dependencies[component] = deps
return dependencies
def get_component_page(component_name, project_path, component_manager=None): def get_component_page(component_name, project_path, component_manager=None):
@ -108,8 +49,8 @@ class ComponentSimple(QWidget):
component_dir = CodeGenerator.get_assets_dir("User_code/component") component_dir = CodeGenerator.get_assets_dir("User_code/component")
describe_path = os.path.join(component_dir, "describe.csv") describe_path = os.path.join(component_dir, "describe.csv")
dependencies_path = os.path.join(component_dir, "dependencies.csv") dependencies_path = os.path.join(component_dir, "dependencies.csv")
self.descriptions = load_descriptions(describe_path) self.descriptions = CodeGenerator.load_descriptions(describe_path)
self.dependencies = load_dependencies(dependencies_path) self.dependencies = CodeGenerator.load_dependencies(dependencies_path)
self._init_ui() self._init_ui()
self._load_config() self._load_config()
@ -174,7 +115,7 @@ class ComponentSimple(QWidget):
print(f"模板文件不存在或为空: {template_path}") print(f"模板文件不存在或为空: {template_path}")
continue continue
output_path = os.path.join(self.project_path, f"User/component/{filename}") output_path = os.path.join(self.project_path, f"User/component/{filename}")
save_with_preserve(output_path, template_content) CodeGenerator.save_with_preserve(output_path, template_content)
self._save_config() self._save_config()
return True return True
@ -296,7 +237,7 @@ class component(QWidget):
if os.path.exists(src_component_h): if os.path.exists(src_component_h):
with open(src_component_h, 'r', encoding='utf-8') as f: with open(src_component_h, 'r', encoding='utf-8') as f:
content = f.read() content = f.read()
save_with_preserve(dst_component_h, content) CodeGenerator.save_with_preserve(dst_component_h, content)
# 收集所有需要生成的组件和它们的依赖 # 收集所有需要生成的组件和它们的依赖
components_to_generate = set() components_to_generate = set()
@ -373,7 +314,7 @@ class component(QWidget):
os.makedirs(os.path.dirname(dst_path), exist_ok=True) os.makedirs(os.path.dirname(dst_path), exist_ok=True)
with open(src_path, 'r', encoding='utf-8') as f: with open(src_path, 'r', encoding='utf-8') as f:
new_content = f.read() new_content = f.read()
save_with_preserve(dst_path, new_content) CodeGenerator.save_with_preserve(dst_path, new_content)
else: else:
# 如果既不是文件也不是目录,跳过 # 如果既不是文件也不是目录,跳过
print(f"跳过不存在的依赖: {dep_path}") print(f"跳过不存在的依赖: {dep_path}")

View File

@ -3,49 +3,9 @@ from qfluentwidgets import BodyLabel, CheckBox, ComboBox, SubtitleLabel
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt
from app.tools.code_generator import CodeGenerator from app.tools.code_generator import CodeGenerator
import os import os
import shutil
import yaml import yaml
import re import re
def preserve_all_user_regions(new_code, old_code):
""" Preserves all user-defined regions in the new code based on the old code.
This function uses regex to find user-defined regions in the old code and replaces them in the new code.
Args:
new_code (str): The new code content.
old_code (str): The old code content.
Returns:
str: The new code with preserved user-defined regions.
"""
pattern = re.compile(
r"/\*\s*(USER [A-Z0-9_ ]+)\s*BEGIN\s*\*/(.*?)/\*\s*\1\s*END\s*\*/",
re.DOTALL
)
old_regions = {m.group(1): m.group(2) for m in pattern.finditer(old_code or "")}
def repl(m):
region = m.group(1)
old_content = old_regions.get(region)
if old_content is not None:
return m.group(0).replace(m.group(2), old_content)
return m.group(0)
return pattern.sub(repl, new_code)
def save_with_preserve(path, new_code):
"""保存文件并保留用户区域"""
if os.path.exists(path):
with open(path, "r", encoding="utf-8") as f:
old_code = f.read()
new_code = preserve_all_user_regions(new_code, old_code)
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path, "w", encoding="utf-8") as f:
f.write(new_code)
def load_device_config(config_path):
"""加载设备配置"""
if os.path.exists(config_path):
with open(config_path, 'r', encoding='utf-8') as f:
return yaml.safe_load(f)
return {}
def get_available_bsp_devices(project_path, bsp_type, gpio_type=None): def get_available_bsp_devices(project_path, bsp_type, gpio_type=None):
"""获取可用的BSP设备GPIO可选类型过滤""" """获取可用的BSP设备GPIO可选类型过滤"""
bsp_config_path = os.path.join(project_path, "User/bsp/bsp_config.yaml") bsp_config_path = os.path.join(project_path, "User/bsp/bsp_config.yaml")
@ -94,7 +54,7 @@ def generate_device_header(project_path, enabled_devices):
# 加载设备配置来获取信号信息 # 加载设备配置来获取信号信息
config_path = os.path.join(device_dir, "config.yaml") config_path = os.path.join(device_dir, "config.yaml")
device_configs = load_device_config(config_path) device_configs = CodeGenerator.load_device_config(config_path)
for device_name in enabled_devices: for device_name in enabled_devices:
device_key = device_name.lower() device_key = device_name.lower()
@ -305,7 +265,7 @@ class DeviceSimple(QWidget):
os.makedirs(os.path.dirname(dst_path), exist_ok=True) os.makedirs(os.path.dirname(dst_path), exist_ok=True)
with open(src_path, 'r', encoding='utf-8') as f: with open(src_path, 'r', encoding='utf-8') as f:
content = f.read() content = f.read()
save_with_preserve(dst_path, content) CodeGenerator.save_with_preserve(dst_path, content)
elif file_type == 'source': elif file_type == 'source':
# 源文件需要替换BSP设备名称 # 源文件需要替换BSP设备名称
@ -362,7 +322,7 @@ def get_device_page(device_name, project_path):
from app.tools.code_generator import CodeGenerator from app.tools.code_generator import CodeGenerator
device_dir = CodeGenerator.get_assets_dir("User_code/device") device_dir = CodeGenerator.get_assets_dir("User_code/device")
config_path = os.path.join(device_dir, "config.yaml") config_path = os.path.join(device_dir, "config.yaml")
device_configs = load_device_config(config_path) device_configs = CodeGenerator.load_device_config(config_path)
devices = device_configs.get('devices', {}) devices = device_configs.get('devices', {})
device_key = device_name.lower() device_key = device_name.lower()

View File

@ -1,9 +1,10 @@
import os import os
import yaml import yaml
import shutil import shutil
from typing import Dict, List, Tuple from typing import Dict, List, Tuple, Optional
import sys import sys
import os import re
import csv
class CodeGenerator: class CodeGenerator:
"""通用代码生成器""" """通用代码生成器"""
@ -163,4 +164,304 @@ class CodeGenerator:
print(f"警告:资源目录不存在: {full_path}") print(f"警告:资源目录不存在: {full_path}")
setattr(CodeGenerator, warning_key, True) setattr(CodeGenerator, warning_key, True)
return full_path return full_path
@staticmethod
def preserve_all_user_regions(new_code: str, old_code: str) -> str:
"""保留用户定义的代码区域
在新代码中保留旧代码中所有用户定义的区域
用户区域使用如下格式标记
/* USER REGION_NAME BEGIN */
用户代码...
/* USER REGION_NAME END */
Args:
new_code: 新的代码内容
old_code: 旧的代码内容
Returns:
str: 保留了用户区域的新代码
"""
if not old_code:
return new_code
pattern = re.compile(
r"/\*\s*(USER [A-Z0-9_ ]+)\s*BEGIN\s*\*/(.*?)/\*\s*\1\s*END\s*\*/",
re.DOTALL
)
# 提取旧代码中的所有用户区域
old_regions = {m.group(1): m.group(2) for m in pattern.finditer(old_code)}
def repl(m):
region_name = m.group(1)
old_content = old_regions.get(region_name)
if old_content is not None:
# 替换为旧的用户内容
return m.group(0).replace(m.group(2), old_content)
return m.group(0)
return pattern.sub(repl, new_code)
@staticmethod
def save_with_preserve(file_path: str, new_code: str) -> bool:
"""保存文件并保留用户代码区域
如果文件已存在会先读取旧文件内容保留其中的用户代码区域
然后将新代码与保留的用户区域合并后保存
Args:
file_path: 文件路径
new_code: 新的代码内容
Returns:
bool: 保存是否成功
"""
try:
# 如果文件已存在,先读取旧内容
old_code = ""
if os.path.exists(file_path):
with open(file_path, "r", encoding="utf-8") as f:
old_code = f.read()
# 保留用户区域
final_code = CodeGenerator.preserve_all_user_regions(new_code, old_code)
# 确保目录存在
os.makedirs(os.path.dirname(file_path), exist_ok=True)
# 保存文件
with open(file_path, "w", encoding="utf-8") as f:
f.write(final_code)
return True
except Exception as e:
print(f"保存文件失败: {file_path}, 错误: {e}")
return False
@staticmethod
def load_descriptions(csv_path: str) -> Dict[str, str]:
"""从CSV文件加载组件或设备的描述信息
CSV格式第一列为组件/设备名称第二列为描述
Args:
csv_path: CSV文件路径
Returns:
Dict[str, str]: 名称到描述的映射字典
"""
descriptions = {}
if os.path.exists(csv_path):
try:
with open(csv_path, encoding='utf-8') as f:
reader = csv.reader(f)
for row in reader:
if len(row) >= 2:
key, desc = row[0].strip(), row[1].strip()
descriptions[key.lower()] = desc
except Exception as e:
print(f"加载描述文件失败: {csv_path}, 错误: {e}")
return descriptions
@staticmethod
def load_dependencies(csv_path: str) -> Dict[str, List[str]]:
"""从CSV文件加载组件依赖关系
CSV格式第一列为组件名后续列为依赖的组件
Args:
csv_path: CSV文件路径
Returns:
Dict[str, List[str]]: 组件名到依赖列表的映射字典
"""
dependencies = {}
if os.path.exists(csv_path):
try:
with open(csv_path, encoding='utf-8') as f:
reader = csv.reader(f)
for row in reader:
if len(row) >= 2:
component = row[0].strip()
deps = [dep.strip() for dep in row[1:] if dep.strip()]
dependencies[component] = deps
except Exception as e:
print(f"加载依赖文件失败: {csv_path}, 错误: {e}")
return dependencies
@staticmethod
def load_device_config(config_path: str) -> Dict:
"""加载设备配置文件
Args:
config_path: YAML配置文件路径
Returns:
Dict: 配置数据字典
"""
return CodeGenerator.load_config(config_path)
@staticmethod
def copy_dependency_file(src_path: str, dst_path: str) -> bool:
"""复制依赖文件
Args:
src_path: 源文件路径
dst_path: 目标文件路径
Returns:
bool: 复制是否成功
"""
try:
os.makedirs(os.path.dirname(dst_path), exist_ok=True)
shutil.copy2(src_path, dst_path)
return True
except Exception as e:
print(f"复制文件失败: {src_path} -> {dst_path}, 错误: {e}")
return False
@staticmethod
def generate_code_from_template(template_path: str, output_path: str,
replacements: Optional[Dict[str, str]] = None,
preserve_user_code: bool = True) -> bool:
"""从模板生成代码文件
Args:
template_path: 模板文件路径
output_path: 输出文件路径
replacements: 要替换的标记字典 {'MARKER': 'replacement_content'}
preserve_user_code: 是否保留用户代码区域
Returns:
bool: 生成是否成功
"""
try:
# 加载模板
template_content = CodeGenerator.load_template(template_path)
if not template_content:
print(f"模板文件不存在或为空: {template_path}")
return False
# 执行替换
if replacements:
for marker, replacement in replacements.items():
template_content = CodeGenerator.replace_auto_generated(
template_content, marker, replacement
)
# 保存文件
if preserve_user_code:
return CodeGenerator.save_with_preserve(output_path, template_content)
else:
return CodeGenerator.save_file(template_content, output_path)
except Exception as e:
print(f"从模板生成代码失败: {template_path} -> {output_path}, 错误: {e}")
return False
@staticmethod
def read_file_content(file_path: str) -> Optional[str]:
"""读取文件内容
Args:
file_path: 文件路径
Returns:
str: 文件内容如果失败返回None
"""
try:
with open(file_path, 'r', encoding='utf-8') as f:
return f.read()
except Exception as e:
print(f"读取文件失败: {file_path}, 错误: {e}")
return None
@staticmethod
def write_file_content(file_path: str, content: str) -> bool:
"""写入文件内容
Args:
file_path: 文件路径
content: 文件内容
Returns:
bool: 写入是否成功
"""
try:
os.makedirs(os.path.dirname(file_path), exist_ok=True)
with open(file_path, 'w', encoding='utf-8') as f:
f.write(content)
return True
except Exception as e:
print(f"写入文件失败: {file_path}, 错误: {e}")
return False
@staticmethod
def update_file_with_pattern(file_path: str, pattern: str, replacement: str,
use_regex: bool = True) -> bool:
"""更新文件中匹配模式的内容
Args:
file_path: 文件路径
pattern: 要匹配的模式
replacement: 替换内容
use_regex: 是否使用正则表达式
Returns:
bool: 更新是否成功
"""
try:
content = CodeGenerator.read_file_content(file_path)
if content is None:
return False
if use_regex:
import re
updated_content = re.sub(pattern, replacement, content, flags=re.DOTALL)
else:
updated_content = content.replace(pattern, replacement)
return CodeGenerator.write_file_content(file_path, updated_content)
except Exception as e:
print(f"更新文件失败: {file_path}, 错误: {e}")
return False
@staticmethod
def replace_multiple_markers(content: str, replacements: Dict[str, str]) -> str:
"""批量替换内容中的多个标记
Args:
content: 要处理的内容
replacements: 替换字典 {'MARKER1': 'content1', 'MARKER2': 'content2'}
Returns:
str: 替换后的内容
"""
result = content
for marker, replacement in replacements.items():
result = CodeGenerator.replace_auto_generated(result, marker, replacement)
return result
@staticmethod
def extract_user_regions(code: str) -> Dict[str, str]:
"""从代码中提取所有用户区域
Args:
code: 要提取的代码内容
Returns:
Dict[str, str]: 区域名称到区域内容的映射
"""
if not code:
return {}
pattern = re.compile(
r"/\*\s*USER CODE BEGIN ([A-Za-z0-9_ ]+)\s*\*/(.*?)/\*\s*USER CODE END \1\s*\*/",
re.DOTALL
)
return {m.group(1): m.group(2) for m in pattern.finditer(code)}

View File

@ -1,387 +0,0 @@
/*
*/
#include "cmd.h"
#include <string.h>
/* USER INCLUDE BEGIN */
/* USER INCLUDE END */
/* USER DEFINE BEGIN */
/* USER DEFINE END */
/**
* @brief
*
* @param cmd
* @param behavior
* @return uint16_t
*/
static inline CMD_KeyValue_t CMD_BehaviorToKey(CMD_t *cmd,
CMD_Behavior_t behavior) {
return cmd->param->map.key_map[behavior].key;
}
static inline CMD_ActiveType_t CMD_BehaviorToActive(CMD_t *cmd,
CMD_Behavior_t behavior) {
return cmd->param->map.key_map[behavior].active;
}
/**
* @brief
*
* @param rc
* @param key
* @param stateful
* @return true
* @return false
*/
static bool CMD_KeyPressedRc(const CMD_RC_t *rc, CMD_KeyValue_t key) {
/* 按下按键为鼠标左、右键 */
if (key == CMD_L_CLICK) {
return rc->mouse.l_click;
}
if (key == CMD_R_CLICK) {
return rc->mouse.r_click;
}
return rc->key & (1u << key);
}
static bool CMD_BehaviorOccurredRc(const CMD_RC_t *rc, CMD_t *cmd,
CMD_Behavior_t behavior) {
CMD_KeyValue_t key = CMD_BehaviorToKey(cmd, behavior);
CMD_ActiveType_t active = CMD_BehaviorToActive(cmd, behavior);
bool now_key_pressed, last_key_pressed;
/* 按下按键为鼠标左、右键 */
if (key == CMD_L_CLICK) {
now_key_pressed = rc->mouse.l_click;
last_key_pressed = cmd->mouse_last.l_click;
} else if (key == CMD_R_CLICK) {
now_key_pressed = rc->mouse.r_click;
last_key_pressed = cmd->mouse_last.r_click;
} else {
now_key_pressed = rc->key & (1u << key);
last_key_pressed = cmd->key_last & (1u << key);
}
switch (active) {
case CMD_ACTIVE_PRESSING:
return now_key_pressed && !last_key_pressed;
case CMD_ACTIVE_RASING:
return !now_key_pressed && last_key_pressed;
case CMD_ACTIVE_PRESSED:
return now_key_pressed;
}
}
/**
* @brief pc行为逻辑
*
* @param rc
* @param cmd
* @param dt_sec
*/
static void CMD_PcLogic(const CMD_RC_t *rc, CMD_t *cmd, float dt_sec) {
cmd->gimbal.mode = GIMBAL_MODE_ABSOLUTE;
/* 云台设置为鼠标控制欧拉角的变化,底盘的控制向量设置为零 */
cmd->gimbal.delta_eulr.yaw =
(float)rc->mouse.x * dt_sec * cmd->param->sens_mouse;
cmd->gimbal.delta_eulr.pit =
(float)(-rc->mouse.y) * dt_sec * cmd->param->sens_mouse;
cmd->chassis.ctrl_vec.vx = cmd->chassis.ctrl_vec.vy = 0.0f;
cmd->shoot.reverse_trig = false;
/* 按键行为映射相关逻辑 */
if (CMD_BehaviorOccurredRc(rc, cmd, CMD_BEHAVIOR_FORE)) {
cmd->chassis.ctrl_vec.vy += cmd->param->move.move_sense;
}
if (CMD_BehaviorOccurredRc(rc, cmd, CMD_BEHAVIOR_BACK)) {
cmd->chassis.ctrl_vec.vy -= cmd->param->move.move_sense;
}
if (CMD_BehaviorOccurredRc(rc, cmd, CMD_BEHAVIOR_LEFT)) {
cmd->chassis.ctrl_vec.vx -= cmd->param->move.move_sense;
}
if (CMD_BehaviorOccurredRc(rc, cmd, CMD_BEHAVIOR_RIGHT)) {
cmd->chassis.ctrl_vec.vx += cmd->param->move.move_sense;
}
if (CMD_BehaviorOccurredRc(rc, cmd, CMD_BEHAVIOR_ACCELERATE)) {
cmd->chassis.ctrl_vec.vx *= cmd->param->move.move_fast_sense;
cmd->chassis.ctrl_vec.vy *= cmd->param->move.move_fast_sense;
}
if (CMD_BehaviorOccurredRc(rc, cmd, CMD_BEHAVIOR_DECELEBRATE)) {
cmd->chassis.ctrl_vec.vx *= cmd->param->move.move_slow_sense;
cmd->chassis.ctrl_vec.vy *= cmd->param->move.move_slow_sense;
}
if (CMD_BehaviorOccurredRc(rc, cmd, CMD_BEHAVIOR_FIRE)) {
/* 切换至开火模式,设置相应的射击频率和弹丸初速度 */
cmd->shoot.mode = SHOOT_MODE_LOADED;
cmd->shoot.fire = true;
} else {
/* 切换至准备模式,停止射击 */
cmd->shoot.mode = SHOOT_MODE_LOADED;
cmd->shoot.fire = false;
}
if (CMD_BehaviorOccurredRc(rc, cmd, CMD_BEHAVIOR_FIRE_MODE)) {
/* 每按一次依次切换开火下一个模式 */
cmd->shoot.fire_mode++;
cmd->shoot.fire_mode %= FIRE_MODE_NUM;
}
if (CMD_BehaviorOccurredRc(rc, cmd, CMD_BEHAVIOR_ROTOR)) {
/* 切换到小陀螺模式 */
cmd->chassis.mode = CHASSIS_MODE_ROTOR;
cmd->chassis.mode_rotor = ROTOR_MODE_RAND;
}
if (CMD_BehaviorOccurredRc(rc, cmd, CMD_BEHAVIOR_OPENCOVER)) {
/* 每按一次开、关弹舱盖 */
cmd->shoot.cover_open = !cmd->shoot.cover_open;
}
if (CMD_BehaviorOccurredRc(rc, cmd, CMD_BEHAVIOR_BUFF)) {
if (cmd->ai_status == AI_STATUS_HITSWITCH) {
/* 停止ai的打符模式停用host控制 */
CMD_RefereeAdd(&(cmd->referee), CMD_UI_HIT_SWITCH_STOP);
cmd->host_overwrite = false;
cmd->ai_status = AI_STATUS_STOP;
} else if (cmd->ai_status == AI_STATUS_AUTOAIM) {
/* 自瞄模式中切换失败提醒 */
} else {
/* ai切换至打符模式启用host控制 */
CMD_RefereeAdd(&(cmd->referee), CMD_UI_HIT_SWITCH_START);
cmd->ai_status = AI_STATUS_HITSWITCH;
cmd->host_overwrite = true;
}
}
if (CMD_BehaviorOccurredRc(rc, cmd, CMD_BEHAVIOR_AUTOAIM)) {
if (cmd->ai_status == AI_STATUS_AUTOAIM) {
/* 停止ai的自瞄模式停用host控制 */
cmd->host_overwrite = false;
cmd->ai_status = AI_STATUS_STOP;
CMD_RefereeAdd(&(cmd->referee), CMD_UI_AUTO_AIM_STOP);
} else {
/* ai切换至自瞄模式启用host控制 */
cmd->ai_status = AI_STATUS_AUTOAIM;
cmd->host_overwrite = true;
CMD_RefereeAdd(&(cmd->referee), CMD_UI_AUTO_AIM_START);
}
} else {
cmd->host_overwrite = false;
// TODO: 修复逻辑
}
if (CMD_BehaviorOccurredRc(rc, cmd, CMD_BEHAVIOR_REVTRIG)) {
/* 按下拨弹反转 */
cmd->shoot.reverse_trig = true;
}
if (CMD_BehaviorOccurredRc(rc, cmd, CMD_BEHAVIOR_FOLLOWGIMBAL35)) {
cmd->chassis.mode = CHASSIS_MODE_FOLLOW_GIMBAL_35;
}
/* 保存当前按下的键位状态 */
cmd->key_last = rc->key;
memcpy(&(cmd->mouse_last), &(rc->mouse), sizeof(cmd->mouse_last));
}
/**
* @brief rc行为逻辑
*
* @param rc
* @param cmd
* @param dt_sec
*/
static void CMD_RcLogic(const CMD_RC_t *rc, CMD_t *cmd, float dt_sec) {
switch (rc->sw_l) {
/* 左拨杆相应行为选择和解析 */
case CMD_SW_UP:
cmd->chassis.mode = CHASSIS_MODE_BREAK;
break;
case CMD_SW_MID:
cmd->chassis.mode = CHASSIS_MODE_FOLLOW_GIMBAL;
break;
case CMD_SW_DOWN:
cmd->chassis.mode = CHASSIS_MODE_ROTOR;
cmd->chassis.mode_rotor = ROTOR_MODE_CW;
break;
case CMD_SW_ERR:
cmd->chassis.mode = CHASSIS_MODE_RELAX;
break;
}
switch (rc->sw_r) {
/* 右拨杆相应行为选择和解析*/
case CMD_SW_UP:
cmd->gimbal.mode = GIMBAL_MODE_ABSOLUTE;
cmd->shoot.mode = SHOOT_MODE_SAFE;
break;
case CMD_SW_MID:
cmd->gimbal.mode = GIMBAL_MODE_ABSOLUTE;
cmd->shoot.fire = false;
cmd->shoot.mode = SHOOT_MODE_LOADED;
break;
case CMD_SW_DOWN:
cmd->gimbal.mode = GIMBAL_MODE_ABSOLUTE;
cmd->shoot.mode = SHOOT_MODE_LOADED;
cmd->shoot.fire_mode = FIRE_MODE_SINGLE;
cmd->shoot.fire = true;
break;
/*
case CMD_SW_UP:
cmd->gimbal.mode = GIMBAL_MODE_RELAX;
cmd->shoot.mode = SHOOT_MODE_SAFE;
break;
case CMD_SW_MID:
cmd->gimbal.mode = GIMBAL_MODE_RELAX;
cmd->shoot.fire = false;
cmd->shoot.mode = SHOOT_MODE_LOADED;
break;
case CMD_SW_DOWN:
cmd->gimbal.mode = GIMBAL_MODE_RELAX;
cmd->shoot.mode = SHOOT_MODE_LOADED;
cmd->shoot.fire_mode = FIRE_MODE_SINGLE;
cmd->shoot.fire = true;
break;
*/
case CMD_SW_ERR:
cmd->gimbal.mode = GIMBAL_MODE_RELAX;
cmd->shoot.mode = SHOOT_MODE_RELAX;
}
/* 将操纵杆的对应值转换为底盘的控制向量和云台变化的欧拉角 */
cmd->chassis.ctrl_vec.vx = rc->ch_l_x;
cmd->chassis.ctrl_vec.vy = rc->ch_l_y;
cmd->gimbal.delta_eulr.yaw = rc->ch_r_x * dt_sec * cmd->param->sens_rc;
cmd->gimbal.delta_eulr.pit = rc->ch_r_y * dt_sec * cmd->param->sens_rc;
}
/**
* @brief rc失控时机器人恢复放松模式
*
* @param cmd
*/
static void CMD_RcLostLogic(CMD_t *cmd) {
/* 机器人底盘、云台、射击运行模式恢复至放松模式 */
cmd->chassis.mode = CHASSIS_MODE_RELAX;
cmd->gimbal.mode = GIMBAL_MODE_RELAX;
cmd->shoot.mode = SHOOT_MODE_RELAX;
}
/**
* @brief
*
* @param cmd
* @param param
* @return int8_t 0
*/
int8_t CMD_Init(CMD_t *cmd, const CMD_Params_t *param) {
/* 指针检测 */
if (cmd == NULL) return -1;
if (param == NULL) return -1;
/* 设置机器人的命令参数初始化控制方式为rc控制 */
cmd->pc_ctrl = false;
cmd->param = param;
return 0;
}
/**
* @brief
*
* @param cmd
* @return true
* @return false
*/
inline bool CMD_CheckHostOverwrite(CMD_t *cmd) { return cmd->host_overwrite; }
/**
* @brief
*
* @param rc
* @param cmd
* @param dt_sec
* @return int8_t 0
*/
int8_t CMD_ParseRc(CMD_RC_t *rc, CMD_t *cmd, float dt_sec) {
/* 指针检测 */
if (rc == NULL) return -1;
if (cmd == NULL) return -1;
/* 在pc控制和rc控制间切换 */
if (CMD_KeyPressedRc(rc, CMD_KEY_SHIFT) &&
CMD_KeyPressedRc(rc, CMD_KEY_CTRL) && CMD_KeyPressedRc(rc, CMD_KEY_Q))
cmd->pc_ctrl = true;
if (CMD_KeyPressedRc(rc, CMD_KEY_SHIFT) &&
CMD_KeyPressedRc(rc, CMD_KEY_CTRL) && CMD_KeyPressedRc(rc, CMD_KEY_E))
cmd->pc_ctrl = false;
/*c当rc丢控时恢复机器人至默认状态 */
if ((rc->sw_l == CMD_SW_ERR) || (rc->sw_r == CMD_SW_ERR)) {
CMD_RcLostLogic(cmd);
} else {
if (cmd->pc_ctrl) {
CMD_PcLogic(rc, cmd, dt_sec);
} else {
CMD_RcLogic(rc, cmd, dt_sec);
}
}
return 0;
}
/**
* @brief
*
* @param host host数据
* @param cmd
* @param dt_sec
* @return int8_t 0
*/
int8_t CMD_ParseHost(const CMD_Host_t *host, CMD_t *cmd, float dt_sec) {
(void)dt_sec; /* 未使用dt_sec消除警告 */
/* 指针检测 */
if (host == NULL) return -1;
if (cmd == NULL) return -1;
/* 云台欧拉角设置为host相应的变化的欧拉角 */
cmd->gimbal.delta_eulr.yaw = host->gimbal_delta.yaw;
cmd->gimbal.delta_eulr.pit = host->gimbal_delta.pit;
/* host射击命令设置不同的射击频率和弹丸初速度 */
if (host->fire) {
cmd->shoot.mode = SHOOT_MODE_LOADED;
cmd->shoot.fire = true;
} else {
cmd->shoot.mode = SHOOT_MODE_SAFE;
}
return 0;
}
/**
* @brief Referee发送的命令
*
* @param ref
* @param cmd
* @return int8_t 0
*/
int8_t CMD_RefereeAdd(CMD_RefereeCmd_t *ref, CMD_UI_t cmd) {
/* 指针检测 */
if (ref == NULL) return -1;
/* 越界检测 */
if (ref->counter >= CMD_REFEREE_MAX_NUM || ref->counter < 0) return -1;
/* 添加机器人当前行为状态到画图的命令队列中 */
ref->cmd[ref->counter] = cmd;
ref->counter++;
return 0;
}
/* USER FUNCTION BEGIN */
/* USER FUNCTION END */

View File

@ -1,318 +0,0 @@
/*
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stdint.h>
#include "component/ahrs.h"
/* USER INCLUDE BEGIN */
/* USER INCLUDE END */
#define CMD_REFEREE_MAX_NUM (3) /* Lines 16 omitted */
/* USER DEFINE BEGIN */
/* USER DEFINE END */
/* 机器人型号 */
typedef enum {
ROBOT_MODEL_INFANTRY = 0, /* 步兵机器人 */
ROBOT_MODEL_HERO, /* 英雄机器人 */
ROBOT_MODEL_ENGINEER, /* 工程机器人 */
ROBOT_MODEL_DRONE, /* 空中机器人 */
ROBOT_MODEL_SENTRY, /* 哨兵机器人 */
ROBOT_MODEL_NUM, /* 型号数量 */
} CMD_RobotModel_t;
/* 底盘运行模式 */
typedef enum {
CHASSIS_MODE_RELAX, /* 放松模式,电机不输出。一般情况底盘初始化之后的模式 */
CHASSIS_MODE_BREAK, /* 刹车模式,电机闭环控制保持静止。用于机器人停止状态 */
CHASSIS_MODE_FOLLOW_GIMBAL, /* 通过闭环控制使车头方向跟随云台 */
CHASSIS_MODE_FOLLOW_GIMBAL_35, /* 通过闭环控制使车头方向35度跟随云台 */
CHASSIS_MODE_ROTOR, /* 小陀螺模式,通过闭环控制使底盘不停旋转 */
CHASSIS_MODE_INDENPENDENT, /* 独立模式。底盘运行不受云台影响 */
CHASSIS_MODE_OPEN, /* 开环模式。底盘运行不受PID控制直接输出到电机 */
} CMD_ChassisMode_t;
/* 云台运行模式 */
typedef enum {
GIMBAL_MODE_RELAX, /* 放松模式,电机不输出。一般情况云台初始化之后的模式 */
GIMBAL_MODE_ABSOLUTE, /* 绝对坐标系控制,控制在空间内的绝对姿态 */
GIMBAL_MODE_RELATIVE, /* 相对坐标系控制,控制相对于底盘的姿态 */
} CMD_GimbalMode_t;
/* 射击运行模式 */
typedef enum {
SHOOT_MODE_RELAX, /* 放松模式,电机不输出 */
SHOOT_MODE_SAFE, /* 保险模式,电机闭环控制保持静止 */
SHOOT_MODE_LOADED, /* 上膛模式,摩擦轮开启。随时准备开火 */
} CMD_ShootMode_t;
typedef enum {
FIRE_MODE_SINGLE, /* 单发开火模式 */
FIRE_MODE_BURST, /* N连发开火模式 */
FIRE_MODE_CONT, /* 持续开火模式 */
FIRE_MODE_NUM,
} CMD_FireMode_t;
/* 小陀螺转动模式 */
typedef enum {
ROTOR_MODE_CW, /* 顺时针转动 */
ROTOR_MODE_CCW, /* 逆时针转动 */
ROTOR_MODE_RAND, /* 随机转动 */
} CMD_RotorMode_t;
/* 底盘控制命令 */
typedef struct {
CMD_ChassisMode_t mode; /* 底盘运行模式 */
CMD_RotorMode_t mode_rotor; /* 小陀螺转动模式 */
MoveVector_t ctrl_vec; /* 底盘控制向量 */
} CMD_ChassisCmd_t;
/* 云台控制命令 */
typedef struct {
CMD_GimbalMode_t mode; /* 云台运行模式 */
AHRS_Eulr_t delta_eulr; /* 欧拉角变化角度 */
} CMD_GimbalCmd_t;
/* 射击控制命令 */
typedef struct {
CMD_ShootMode_t mode; /* 射击运行模式 */
CMD_FireMode_t fire_mode; /* 开火模式 */
bool fire; /*开火*/
bool cover_open; /* 弹舱盖开关 */
bool reverse_trig; /* 拨弹电机状态 */
} CMD_ShootCmd_t;
/* 拨杆位置 */
typedef enum {
CMD_SW_ERR = 0,
CMD_SW_UP = 1,
CMD_SW_MID = 3,
CMD_SW_DOWN = 2,
} CMD_SwitchPos_t;
/* 键盘按键值 */
typedef enum {
CMD_KEY_W = 0,
CMD_KEY_S,
CMD_KEY_A,
CMD_KEY_D,
CMD_KEY_SHIFT,
CMD_KEY_CTRL,
CMD_KEY_Q,
CMD_KEY_E,
CMD_KEY_R,
CMD_KEY_F,
CMD_KEY_G,
CMD_KEY_Z,
CMD_KEY_X,
CMD_KEY_C,
CMD_KEY_V,
CMD_KEY_B,
CMD_L_CLICK,
CMD_R_CLICK,
CMD_KEY_NUM,
} CMD_KeyValue_t;
/* 行为值序列 */
typedef enum {
CMD_BEHAVIOR_FORE = 0, /* 向前 */
CMD_BEHAVIOR_BACK, /* 向后 */
CMD_BEHAVIOR_LEFT, /* 向左 */
CMD_BEHAVIOR_RIGHT, /* 向右 */
CMD_BEHAVIOR_ACCELERATE, /* 加速 */
CMD_BEHAVIOR_DECELEBRATE, /* 减速 */
CMD_BEHAVIOR_FIRE, /* 开火 */
CMD_BEHAVIOR_FIRE_MODE, /* 切换开火模式 */
CMD_BEHAVIOR_BUFF, /* 打符模式 */
CMD_BEHAVIOR_AUTOAIM, /* 自瞄模式 */
CMD_BEHAVIOR_OPENCOVER, /* 弹舱盖开关 */
CMD_BEHAVIOR_ROTOR, /* 小陀螺模式 */
CMD_BEHAVIOR_REVTRIG, /* 反转拨弹 */
CMD_BEHAVIOR_FOLLOWGIMBAL35, /* 跟随云台呈35度 */
CMD_BEHAVIOR_NUM,
} CMD_Behavior_t;
typedef enum {
CMD_ACTIVE_PRESSING, /* 按下时触发 */
CMD_ACTIVE_RASING, /* 抬起时触发 */
CMD_ACTIVE_PRESSED, /* 按住时触发 */
} CMD_ActiveType_t;
typedef struct {
CMD_ActiveType_t active;
CMD_KeyValue_t key;
} CMD_KeyMapItem_t;
/* 行为映射的对应按键数组 */
typedef struct {
CMD_KeyMapItem_t key_map[CMD_BEHAVIOR_NUM];
} CMD_KeyMap_Params_t;
/* 位移灵敏度参数 */
typedef struct {
float move_sense; /* 移动灵敏度 */
float move_fast_sense; /* 加速灵敏度 */
float move_slow_sense; /* 减速灵敏度 */
} CMD_Move_Params_t;
typedef struct {
uint16_t width;
uint16_t height;
} CMD_Screen_t;
/* 命令参数 */
typedef struct {
float sens_mouse; /* 鼠标灵敏度 */
float sens_rc; /* 遥控器摇杆灵敏度 */
CMD_KeyMap_Params_t map; /* 按键映射行为命令 */
CMD_Move_Params_t move; /* 位移灵敏度参数 */
CMD_Screen_t screen; /* 屏幕分辨率参数 */
} CMD_Params_t;
/* AI行为状态 */
typedef enum {
AI_STATUS_STOP, /* 停止状态 */
AI_STATUS_AUTOAIM, /* 自瞄状态 */
AI_STATUS_HITSWITCH, /* 打符状态 */
AI_STATUS_AUTOMATIC /* 自动状态 */
} CMD_AI_Status_t;
/* UI所用行为状态 */
typedef enum {
CMD_UI_NOTHING, /* 当前无状态 */
CMD_UI_AUTO_AIM_START, /* 自瞄状态开启 */
CMD_UI_AUTO_AIM_STOP, /* 自瞄状态关闭 */
CMD_UI_HIT_SWITCH_START, /* 打符状态开启 */
CMD_UI_HIT_SWITCH_STOP /* 打符状态关闭 */
} CMD_UI_t;
/*裁判系统发送的命令*/
typedef struct {
CMD_UI_t cmd[CMD_REFEREE_MAX_NUM]; /* 命令数组 */
uint8_t counter; /* 命令计数 */
} CMD_RefereeCmd_t;
typedef struct {
bool pc_ctrl; /* 是否使用键鼠控制 */
bool host_overwrite; /* 是否Host控制 */
uint16_t key_last; /* 上次按键键值 */
struct {
int16_t x;
int16_t y;
int16_t z;
bool l_click; /* 左键 */
bool r_click; /* 右键 */
} mouse_last; /* 鼠标值 */
CMD_AI_Status_t ai_status; /* AI状态 */
const CMD_Params_t *param; /* 命令参数 */
CMD_ChassisCmd_t chassis; /* 底盘控制命令 */
CMD_GimbalCmd_t gimbal; /* 云台控制命令 */
CMD_ShootCmd_t shoot; /* 射击控制命令 */
CMD_RefereeCmd_t referee; /* 裁判系统发送命令 */
} CMD_t;
typedef struct {
float ch_l_x; /* 遥控器左侧摇杆横轴值,上为正 */
float ch_l_y; /* 遥控器左侧摇杆纵轴值,右为正 */
float ch_r_x; /* 遥控器右侧摇杆横轴值,上为正 */
float ch_r_y; /* 遥控器右侧摇杆纵轴值,右为正 */
float ch_res; /* 第五通道值 */
CMD_SwitchPos_t sw_r; /* 右侧拨杆位置 */
CMD_SwitchPos_t sw_l; /* 左侧拨杆位置 */
struct {
int16_t x;
int16_t y;
int16_t z;
bool l_click; /* 左键 */
bool r_click; /* 右键 */
} mouse; /* 鼠标值 */
uint16_t key; /* 按键值 */
uint16_t res; /* 保留,未启用 */
} CMD_RC_t;
typedef struct {
AHRS_Eulr_t gimbal_delta; /* 欧拉角的变化量 */
struct {
float vx; /* x轴移动速度 */
float vy; /* y轴移动速度 */
float wz; /* z轴转动速度 */
} chassis_move_vec; /* 底盘移动向量 */
bool fire; /* 开火状态 */
} CMD_Host_t;
/**
* @brief
*
* @param rc
* @param cmd
*/
int8_t CMD_Init(CMD_t *cmd, const CMD_Params_t *param);
/**
* @brief
*
* @param cmd
* @return true
* @return false
*/
bool CMD_CheckHostOverwrite(CMD_t *cmd);
/**
* @brief
*
* @param rc
* @param cmd
* @param dt_sec
* @return int8_t 0
*/
int8_t CMD_ParseRc(CMD_RC_t *rc, CMD_t *cmd, float dt_sec);
/**
* @brief
*
* @param host host数据
* @param cmd
* @param dt_sec
* @return int8_t 0
*/
int8_t CMD_ParseHost(const CMD_Host_t *host, CMD_t *cmd, float dt_sec);
/**
* @brief Referee发送的命令
*
* @param ref
* @param cmd
* @return int8_t 0
*/
int8_t CMD_RefereeAdd(CMD_RefereeCmd_t *ref, CMD_UI_t cmd);
/* USER FUNCTION BEGIN */
/* USER FUNCTION END */
#ifdef __cplusplus
}
#endif

View File

@ -1,4 +1,4 @@
bsp,can,dwt,gpio,i2c,mm,spi,uart,pwm,time bsp,can,dwt,gpio,i2c,mm,spi,uart,pwm,time
component,ahrs,capacity,cmd,crc8,crc16,error_detect,filter,FreeRTOS_CLI,limiter,mixer,pid,ui,user_math component,ahrs,capacity,crc8,crc16,error_detect,filter,FreeRTOS_CLI,limiter,mixer,pid,ui,user_math
device,dr16,bmi088,ist8310,motor,motor_rm,motor_dm,motor_vesc,motor_lk,motor_lz,motor_odrive,dm_imu,rc_can,servo,buzzer,led,ws2812,vofa,ops9 device,dr16,bmi088,ist8310,motor,motor_rm,motor_dm,motor_vesc,motor_lk,motor_lz,motor_odrive,dm_imu,rc_can,servo,buzzer,led,ws2812,vofa,ops9,ai
module,config, module,config,
1 bsp,can,dwt,gpio,i2c,mm,spi,uart,pwm,time
2 component,ahrs,capacity,cmd,crc8,crc16,error_detect,filter,FreeRTOS_CLI,limiter,mixer,pid,ui,user_math component,ahrs,capacity,crc8,crc16,error_detect,filter,FreeRTOS_CLI,limiter,mixer,pid,ui,user_math
3 device,dr16,bmi088,ist8310,motor,motor_rm,motor_dm,motor_vesc,motor_lk,motor_lz,motor_odrive,dm_imu,rc_can,servo,buzzer,led,ws2812,vofa,ops9 device,dr16,bmi088,ist8310,motor,motor_rm,motor_dm,motor_vesc,motor_lk,motor_lz,motor_odrive,dm_imu,rc_can,servo,buzzer,led,ws2812,vofa,ops9,ai
4 module,config,

View File

@ -0,0 +1,142 @@
/*
AI
*/
/* Includes ----------------------------------------------------------------- */
#include "ai.h"
#include <stdbool.h>
#include <string.h>
#include "bsp/time.h"
#include "bsp/uart.h"
#include "component/ahrs.h"
#include "component/crc16.h"
#include "component/crc8.h"
#include "component/user_math.h"
#include "component/filter.h"
/* Private define ----------------------------------------------------------- */
#define AI_LEN_RX_BUFF (sizeof(AI_DownPackage_t))
/* Private macro ------------------------------------------------------------ */
/* Private typedef ---------------------------------------------------------- */
/* Private variables -------------------------------------------------------- */
static uint8_t rxbuf[AI_LEN_RX_BUFF];
static bool inited = false;
static osThreadId_t thread_alert;
static uint32_t drop_message = 0;
// uint16_t crc16;
/* Private function -------------------------------------------------------- */
static void Ai_RxCpltCallback(void) {
osThreadFlagsSet(thread_alert, SIGNAL_AI_RAW_REDY);
}
static void Ai_IdleLineCallback(void) {
osThreadFlagsSet(thread_alert, SIGNAL_AI_RAW_REDY);
}
/* Exported functions ------------------------------------------------------- */
int8_t AI_Init(AI_t *ai) {
UNUSED(ai);
if (inited) return DEVICE_ERR_INITED;
thread_alert = osThreadGetId();
BSP_UART_RegisterCallback(BSP_UART_AI, BSP_UART_RX_CPLT_CB,
Ai_RxCpltCallback);
BSP_UART_RegisterCallback(BSP_UART_AI, BSP_UART_IDLE_LINE_CB,
Ai_IdleLineCallback);
inited = true;
return 0;
}
int8_t AI_Restart(AI_t *ai) {
UNUSED(ai);
__HAL_UART_DISABLE(BSP_UART_GetHandle(BSP_UART_AI));
__HAL_UART_ENABLE(BSP_UART_GetHandle(BSP_UART_AI));
return DEVICE_OK;
}
int8_t AI_StartReceiving(AI_t *ai) {
UNUSED(ai);
// if (HAL_UART_Receive_DMA(BSP_UART_GetHandle(BSP_UART_AI), rxbuf,
// AI_LEN_RX_BUFF) == HAL_OK)
if (BSP_UART_Receive(BSP_UART_AI, rxbuf,
AI_LEN_RX_BUFF, true) == HAL_OK)
return DEVICE_OK;
return DEVICE_ERR;
}
bool AI_WaitDmaCplt(void) {
return (osThreadFlagsWait(SIGNAL_AI_RAW_REDY, osFlagsWaitAll,0) ==
SIGNAL_AI_RAW_REDY);
}
int8_t AI_ParseHost(AI_t *ai) {
// crc16 = CRC16_Calc((const uint8_t *)&(rxbuf), sizeof(ai->from_host) - 2, CRC16_INIT);
if (!CRC16_Verify((const uint8_t *)&(rxbuf), sizeof(ai->from_host)))
goto error;
ai->header.online = true;
ai->header.last_online_time = BSP_TIME_Get();
memcpy(&(ai->from_host), rxbuf, sizeof(ai->from_host));
memset(rxbuf, 0, AI_LEN_RX_BUFF);
return DEVICE_OK;
error:
drop_message++;
return DEVICE_ERR;
}
int8_t AI_PackMCU(AI_t *ai, const AHRS_Quaternion_t *data){
if (ai == NULL || data == NULL) return DEVICE_ERR_NULL;
ai->to_host.mcu.id = AI_ID_MCU;
ai->to_host.mcu.package.quat=*data;
ai->to_host.mcu.package.notice = ai->status;
ai->to_host.mcu.crc16 = CRC16_Calc((const uint8_t *)&(ai->to_host.mcu), sizeof(AI_UpPackageMCU_t) - 2, CRC16_INIT);
return DEVICE_OK;
}
int8_t AI_PackRef(AI_t *ai, const AI_UpPackageReferee_t *data) {
if (ai == NULL || data == NULL) return DEVICE_ERR_NULL;
ai->to_host.ref = *data;
return DEVICE_OK;
}
int8_t AI_HandleOffline(AI_t *ai) {
if (ai == NULL) return DEVICE_ERR_NULL;
if (BSP_TIME_Get() - ai->header.last_online_time >
100000) {
ai->header.online = false;
}
return DEVICE_OK;
}
int8_t AI_StartSend(AI_t *ai, bool ref_online){
if (ai == NULL) return DEVICE_ERR_NULL;
if (ref_online) {
// 发送裁判系统数据和MCU数据
if (BSP_UART_Transmit(BSP_UART_AI, (uint8_t *)&(ai->to_host),
sizeof(ai->to_host.ref) + sizeof(ai->to_host.mcu), true) == HAL_OK)
return DEVICE_OK;
else
return DEVICE_ERR;
} else {
// 只发送MCU数据
if (BSP_UART_Transmit(BSP_UART_AI, (uint8_t *)&(ai->to_host.mcu),
sizeof(ai->to_host.mcu), true) == HAL_OK)
return DEVICE_OK;
else
return DEVICE_ERR;
}
}

View File

@ -0,0 +1,131 @@
/*
AI
*/
#pragma once
#include <sys/cdefs.h>
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ----------------------------------------------------------------- */
#include "component/ahrs.h"
#include "component/filter.h"
#include "component/user_math.h"
#include "device/device.h"
#include <cmsis_os2.h>
#include <stdbool.h>
#include <stdint.h>
/* Exported constants ------------------------------------------------------- */
/* Exported macro ----------------------------------------------------------- */
#define AI_ID_MCU (0xC4)
#define AI_ID_REF (0xA8)
#define AI_ID_AI (0xA1)
/* Exported types ----------------------------------------------------------- */
typedef enum {
AI_ARMOR_HERO = 0, /*英雄机器人*/
AI_ARMOR_INFANTRY, /*步兵机器人*/
AI_ARMOR_SENTRY, /*哨兵机器人*/
AI_ARMOR_ENGINEER, /*工程机器人*/
AI_ARMOR_OUTPOST, /*前哨占*/
AI_ARMOR_BASE, /*基地*/
AI_ARMOR_NORMAL, /*由AI自动选择*/
} AI_ArmorsType_t;
typedef enum {
AI_STATUS_OFF = 0, /* 关闭 */
AI_STATUS_AUTOAIM, /* 自瞄 */
AI_STATUS_AUTOPICK, /* 自动取矿 */
AI_STATUS_AUTOPUT, /* 自动兑矿 */
AI_STATUS_AUTOHITBUFF, /* 自动打符 */
AI_STATUS_AUTONAV,
} AI_Status_t;
typedef enum {
AI_NOTICE_NONE = 0,
AI_NOTICE_SEARCH,
AI_NOTICE_FIRE,
}AI_Notice_t;
/* 电控 -> 视觉 MCU数据结构体*/
typedef struct __packed {
AHRS_Quaternion_t quat; /* 四元数 */
// struct {
// AI_ArmorsType_t armor_type;
// AI_Status_t status;
// }notice; /* 控制命令 */
uint8_t notice;
} AI_Protucol_UpDataMCU_t;
/* 电控 -> 视觉 裁判系统数据结构体*/
typedef struct __packed {
/* USER REFEREE BEGIN */
uint16_t team; /* 本身队伍 */
uint16_t time; /* 比赛开始时间 */
/* USER REFEREE END */
} AI_Protocol_UpDataReferee_t;
/* 视觉 -> 电控 数据包结构体*/
typedef struct __packed {
AHRS_Eulr_t eulr; /* 欧拉角 */
MoveVector_t move_vec; /* 运动向量 */
uint8_t notice; /* 控制命令 */
} AI_Protocol_DownData_t;
/* 电控 -> 视觉 裁判系统数据包 */
typedef struct __packed {
uint8_t id; /* 包ID */
AI_Protocol_UpDataReferee_t package; /* 数据包 */
uint16_t crc16; /* CRC16校验 */
} AI_UpPackageReferee_t;
/* 电控 -> 视觉 MUC数据包 */
typedef struct __packed {
uint8_t id;
AI_Protucol_UpDataMCU_t package;
uint16_t crc16;
} AI_UpPackageMCU_t;
/* 视觉 -> 电控 数据包 */
typedef struct __packed {
uint8_t id; /* 包ID */
AI_Protocol_DownData_t package; /* 数据包 */
uint16_t crc16; /* CRC16校验 */
} AI_DownPackage_t;
typedef struct __packed {
DEVICE_Header_t header; /* 设备通用头部 */
AI_DownPackage_t from_host;
AI_Status_t status;
struct {
AI_UpPackageReferee_t ref;
AI_UpPackageMCU_t mcu;
} to_host;
} AI_t;
/* Exported functions prototypes -------------------------------------------- */
int8_t AI_Init(AI_t *ai);
int8_t AI_Restart(AI_t *ai);
int8_t AI_StartReceiving(AI_t *ai);
bool AI_WaitDmaCplt(void);
int8_t AI_ParseHost(AI_t *ai);
int8_t AI_PackMCU(AI_t *ai, const AHRS_Quaternion_t *quat);
int8_t AI_PackRef(AI_t *ai, const AI_UpPackageReferee_t *data);
int8_t AI_HandleOffline(AI_t *ai);
int8_t AI_StartSend(AI_t *ai, bool ref_online);
#ifdef __cplusplus
}
#endif