更新
This commit is contained in:
parent
49545dd232
commit
d1c3b2747a
@ -6,42 +6,8 @@ from app.tools.analyzing_ioc import analyzing_ioc
|
||||
from app.tools.code_generator import CodeGenerator
|
||||
import os
|
||||
import csv
|
||||
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.
|
||||
"""
|
||||
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):
|
||||
def __init__(self, project_path, peripheral_name, template_names):
|
||||
super().__init__()
|
||||
@ -50,7 +16,7 @@ class BspSimplePeripheral(QWidget):
|
||||
self.template_names = template_names
|
||||
# 加载描述
|
||||
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._load_config()
|
||||
|
||||
@ -102,7 +68,7 @@ class BspSimplePeripheral(QWidget):
|
||||
if not template_content:
|
||||
return False
|
||||
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()
|
||||
return True
|
||||
|
||||
@ -133,7 +99,7 @@ class BspPeripheralBase(QWidget):
|
||||
self.available_list = []
|
||||
# 新增:加载描述
|
||||
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._load_config()
|
||||
|
||||
@ -249,7 +215,7 @@ class BspPeripheralBase(QWidget):
|
||||
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']}")
|
||||
save_with_preserve(output_path, content) # 使用保留用户区域的写入
|
||||
CodeGenerator.save_with_preserve(output_path, content) # 使用保留用户区域的写入
|
||||
return True
|
||||
|
||||
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)
|
||||
)
|
||||
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
|
||||
|
||||
def _save_config(self, configs):
|
||||
@ -337,17 +303,6 @@ class BspPeripheralBase(QWidget):
|
||||
self._save_config(configs)
|
||||
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):
|
||||
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']}")
|
||||
save_with_preserve(output_path, content)
|
||||
CodeGenerator.save_with_preserve(output_path, content)
|
||||
return True
|
||||
|
||||
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()
|
||||
# 加载描述
|
||||
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._load_config()
|
||||
|
||||
@ -891,7 +846,7 @@ class bsp_gpio(QWidget):
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
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")
|
||||
save_with_preserve(output_path, content)
|
||||
CodeGenerator.save_with_preserve(output_path, content)
|
||||
return True
|
||||
|
||||
|
||||
@ -983,7 +938,7 @@ class bsp_pwm(QWidget):
|
||||
self.available_list = self._get_pwm_channels()
|
||||
# 加载描述
|
||||
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._load_config()
|
||||
|
||||
@ -1148,7 +1103,7 @@ class bsp_pwm(QWidget):
|
||||
)
|
||||
|
||||
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
|
||||
|
||||
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")
|
||||
save_with_preserve(output_path, content)
|
||||
CodeGenerator.save_with_preserve(output_path, content)
|
||||
return True
|
||||
|
||||
def _save_config(self, configs):
|
||||
@ -1245,7 +1200,7 @@ class bsp(QWidget):
|
||||
if os.path.exists(src_bsp_h):
|
||||
with open(src_bsp_h, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
save_with_preserve(dst_bsp_h, content)
|
||||
CodeGenerator.save_with_preserve(dst_bsp_h, content)
|
||||
|
||||
total = 0
|
||||
success_count = 0
|
||||
|
||||
@ -4,66 +4,7 @@ from qfluentwidgets import InfoBar
|
||||
from PyQt5.QtCore import Qt, pyqtSignal
|
||||
from app.tools.code_generator import CodeGenerator
|
||||
import os
|
||||
import csv
|
||||
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):
|
||||
@ -108,8 +49,8 @@ class ComponentSimple(QWidget):
|
||||
component_dir = CodeGenerator.get_assets_dir("User_code/component")
|
||||
describe_path = os.path.join(component_dir, "describe.csv")
|
||||
dependencies_path = os.path.join(component_dir, "dependencies.csv")
|
||||
self.descriptions = load_descriptions(describe_path)
|
||||
self.dependencies = load_dependencies(dependencies_path)
|
||||
self.descriptions = CodeGenerator.load_descriptions(describe_path)
|
||||
self.dependencies = CodeGenerator.load_dependencies(dependencies_path)
|
||||
|
||||
self._init_ui()
|
||||
self._load_config()
|
||||
@ -174,7 +115,7 @@ class ComponentSimple(QWidget):
|
||||
print(f"模板文件不存在或为空: {template_path}")
|
||||
continue
|
||||
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()
|
||||
return True
|
||||
|
||||
@ -296,7 +237,7 @@ class component(QWidget):
|
||||
if os.path.exists(src_component_h):
|
||||
with open(src_component_h, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
save_with_preserve(dst_component_h, content)
|
||||
CodeGenerator.save_with_preserve(dst_component_h, content)
|
||||
|
||||
# 收集所有需要生成的组件和它们的依赖
|
||||
components_to_generate = set()
|
||||
@ -373,7 +314,7 @@ class component(QWidget):
|
||||
os.makedirs(os.path.dirname(dst_path), exist_ok=True)
|
||||
with open(src_path, 'r', encoding='utf-8') as f:
|
||||
new_content = f.read()
|
||||
save_with_preserve(dst_path, new_content)
|
||||
CodeGenerator.save_with_preserve(dst_path, new_content)
|
||||
else:
|
||||
# 如果既不是文件也不是目录,跳过
|
||||
print(f"跳过不存在的依赖: {dep_path}")
|
||||
|
||||
@ -3,49 +3,9 @@ from qfluentwidgets import BodyLabel, CheckBox, ComboBox, SubtitleLabel
|
||||
from PyQt5.QtCore import Qt
|
||||
from app.tools.code_generator import CodeGenerator
|
||||
import os
|
||||
import shutil
|
||||
import yaml
|
||||
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):
|
||||
"""获取可用的BSP设备,GPIO可选类型过滤"""
|
||||
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")
|
||||
device_configs = load_device_config(config_path)
|
||||
device_configs = CodeGenerator.load_device_config(config_path)
|
||||
|
||||
for device_name in enabled_devices:
|
||||
device_key = device_name.lower()
|
||||
@ -305,7 +265,7 @@ class DeviceSimple(QWidget):
|
||||
os.makedirs(os.path.dirname(dst_path), exist_ok=True)
|
||||
with open(src_path, 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
save_with_preserve(dst_path, content)
|
||||
CodeGenerator.save_with_preserve(dst_path, content)
|
||||
|
||||
elif file_type == 'source':
|
||||
# 源文件需要替换BSP设备名称
|
||||
@ -362,7 +322,7 @@ def get_device_page(device_name, project_path):
|
||||
from app.tools.code_generator import CodeGenerator
|
||||
device_dir = CodeGenerator.get_assets_dir("User_code/device")
|
||||
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', {})
|
||||
device_key = device_name.lower()
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
import os
|
||||
import yaml
|
||||
import shutil
|
||||
from typing import Dict, List, Tuple
|
||||
from typing import Dict, List, Tuple, Optional
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import csv
|
||||
class CodeGenerator:
|
||||
"""通用代码生成器"""
|
||||
|
||||
@ -164,3 +165,303 @@ class CodeGenerator:
|
||||
setattr(CodeGenerator, warning_key, True)
|
||||
|
||||
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)}
|
||||
@ -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 */
|
||||
@ -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
|
||||
@ -1,4 +1,4 @@
|
||||
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
|
||||
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
|
||||
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,ai
|
||||
module,config,
|
||||
|
142
assets/User_code/device/ai.c
Normal file
142
assets/User_code/device/ai.c
Normal 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;
|
||||
}
|
||||
}
|
||||
|
||||
131
assets/User_code/device/ai.h
Normal file
131
assets/User_code/device/ai.h
Normal 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
|
||||
Loading…
Reference in New Issue
Block a user