diff --git a/.DS_Store b/.DS_Store index ded3be5..414c9f2 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/app/code_page/bsp_interface.py b/app/code_page/bsp_interface.py index f1ad7c7..6244ce8 100644 --- a/app/code_page/bsp_interface.py +++ b/app/code_page/bsp_interface.py @@ -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 diff --git a/app/code_page/component_interface.py b/app/code_page/component_interface.py index 0983027..5ced305 100644 --- a/app/code_page/component_interface.py +++ b/app/code_page/component_interface.py @@ -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}") diff --git a/app/code_page/device_interface.py b/app/code_page/device_interface.py index fa63661..4ccf3f9 100644 --- a/app/code_page/device_interface.py +++ b/app/code_page/device_interface.py @@ -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() diff --git a/app/tools/code_generator.py b/app/tools/code_generator.py index 492abe1..627bc9d 100644 --- a/app/tools/code_generator.py +++ b/app/tools/code_generator.py @@ -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: """通用代码生成器""" @@ -163,4 +164,304 @@ class CodeGenerator: print(f"警告:资源目录不存在: {full_path}") setattr(CodeGenerator, warning_key, True) - return full_path \ No newline at end of file + 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)} \ No newline at end of file diff --git a/assets/User_code/component/cmd.c b/assets/User_code/component/cmd.c deleted file mode 100644 index 1effe69..0000000 --- a/assets/User_code/component/cmd.c +++ /dev/null @@ -1,387 +0,0 @@ -/* - 控制命令 -*/ - -#include "cmd.h" - -#include - -/* 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 */ diff --git a/assets/User_code/component/cmd.h b/assets/User_code/component/cmd.h deleted file mode 100644 index df84538..0000000 --- a/assets/User_code/component/cmd.h +++ /dev/null @@ -1,318 +0,0 @@ -/* - 控制命令 -*/ - -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include - -#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 diff --git a/assets/User_code/config.csv b/assets/User_code/config.csv index 32290e1..db5a049 100644 --- a/assets/User_code/config.csv +++ b/assets/User_code/config.csv @@ -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, \ No newline at end of file diff --git a/assets/User_code/device/ai.c b/assets/User_code/device/ai.c new file mode 100644 index 0000000..2632536 --- /dev/null +++ b/assets/User_code/device/ai.c @@ -0,0 +1,142 @@ +/* +AI +*/ + +/* Includes ----------------------------------------------------------------- */ +#include "ai.h" + +#include +#include + +#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; + } +} + diff --git a/assets/User_code/device/ai.h b/assets/User_code/device/ai.h new file mode 100644 index 0000000..24b2a31 --- /dev/null +++ b/assets/User_code/device/ai.h @@ -0,0 +1,131 @@ +/* + AI +*/ + +#pragma once + +#include +#ifdef __cplusplus +extern "C" { +#endif + +/* Includes ----------------------------------------------------------------- */ +#include "component/ahrs.h" +#include "component/filter.h" +#include "component/user_math.h" +#include "device/device.h" +#include +#include +#include + +/* 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 diff --git a/assets/User_code/module/2_axis_gimbal.c b/assets/User_code/module/gimbal/2_axis_gimbal/2_axis_gimbal.c similarity index 100% rename from assets/User_code/module/2_axis_gimbal.c rename to assets/User_code/module/gimbal/2_axis_gimbal/2_axis_gimbal.c diff --git a/assets/User_code/module/2_axis_gimbal.h b/assets/User_code/module/gimbal/2_axis_gimbal/2_axis_gimbal.h similarity index 100% rename from assets/User_code/module/2_axis_gimbal.h rename to assets/User_code/module/gimbal/2_axis_gimbal/2_axis_gimbal.h diff --git a/assets/User_code/module/gimbal/2_axis_gimbal/config.yaml b/assets/User_code/module/gimbal/2_axis_gimbal/config.yaml new file mode 100644 index 0000000..e69de29