in devel
This commit is contained in:
parent
a0005ea478
commit
c157710a32
|
|
@ -0,0 +1,10 @@
|
|||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# 已忽略包含查询文件的默认文件夹
|
||||
/queries/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
# 基于编辑器的 HTTP 客户端请求
|
||||
/httpRequests/
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="jdk" jdkName="C:\ProgramData\anaconda3\envs\py310" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
<component name="PyDocumentationSettings">
|
||||
<option name="format" value="PLAIN" />
|
||||
<option name="myDocStringFormat" value="Plain" />
|
||||
</component>
|
||||
</module>
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
||||
<inspection_tool class="PyBroadExceptionInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="PyStubPackagesAdvertiser" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignoredPackages">
|
||||
<list>
|
||||
<option value="pyspark-stubs==3.0.0.post1" />
|
||||
</list>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
<inspection_tool class="PyUnresolvedReferencesInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="ignoredIdentifiers">
|
||||
<list>
|
||||
<option value="bin.test.*" />
|
||||
<option value="pdbx.Rpdb" />
|
||||
</list>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
<inspection_tool class="ShellCheck" enabled="true" level="ERROR" enabled_by_default="true">
|
||||
<shellcheck_settings value="SC1090,SC2166" />
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
# ════════════════════════════════════════════════════════════════
|
||||
# config/config.yaml — Agent 系统全局配置文件
|
||||
# ════════════════════════════════════════════════════════════════
|
||||
|
||||
# ── LLM 模型配置 ───────────────────────────────────────────────
|
||||
llm:
|
||||
provider: "openai"
|
||||
model_name: "gpt-4o"
|
||||
api_key: "sk-AUmOuFI731Ty5Nob38jY26d8lydfDT-QkE2giqb0sCuPCAE2JH6zjLM4lZLpvL5WMYPOocaMe2FwVDmqM_9KimmKACjR" # 优先读取环境变量 LLM_API_KEY
|
||||
api_base_url: "https://openapi.monica.im/v1" # 自定义代理地址,留空使用官方
|
||||
max_tokens: 4096
|
||||
temperature: 0.7
|
||||
timeout: 60
|
||||
max_retries: 3
|
||||
function_calling: true
|
||||
stream: false
|
||||
model_path: ""
|
||||
ollama_host: "http://localhost:11434"
|
||||
database:
|
||||
type: "sqlite"
|
||||
url: "sqlite:///skills.db"
|
||||
skills_directory: "./" # 新增:SKILL.md 文件所在目录
|
||||
# ── 本地 MCP Server 配置 ───────────────────────────────────────
|
||||
mcp:
|
||||
server_name: "MCPServer"
|
||||
transport: "stdio"
|
||||
host: "localhost"
|
||||
port: 3000
|
||||
tools_directory: "./agent/tools"
|
||||
# 本地注册的工具列表
|
||||
enabled_tools:
|
||||
- uav_self_check
|
||||
- uav_control
|
||||
- get_uav_state
|
||||
|
||||
|
||||
# ── 在线 MCP Skill 配置 ────────────────────────────────────────
|
||||
# 每一项代表一个远端 MCP Server,其暴露的所有工具将作为 skill 注册到 Agent
|
||||
mcp_skills:
|
||||
# ── 工具配置 ───────────────────────────────────────────────────
|
||||
tools:
|
||||
web_search:
|
||||
max_results: 5
|
||||
timeout: 10
|
||||
api_key: "7917bef5e46044af5209fdb78518be98be394f3fe763bbce3fbb503280408bd9"
|
||||
uav_control:
|
||||
# ── 记忆配置 ───────────────────────────────────────────────────
|
||||
memory:
|
||||
max_history: 20
|
||||
enable_long_term: false
|
||||
vector_db_url: ""
|
||||
|
||||
# ── 日志配置 ───────────────────────────────────────────────────
|
||||
logging:
|
||||
level: "DEBUG"
|
||||
enable_file: true
|
||||
log_dir: "./logs"
|
||||
log_file: "agent.log"
|
||||
|
||||
# ── Agent 行为配置 ─────────────────────────────────────────────
|
||||
agent:
|
||||
max_chain_steps: 10
|
||||
enable_multi_step: true
|
||||
session_timeout: 3600
|
||||
fallback_to_rules: true
|
||||
|
||||
device:
|
||||
type: "uav"
|
||||
device_id: "UAV-001"
|
||||
protocol: "mavlink"
|
||||
params:
|
||||
connection_string: "udp:127.0.0.1:14550"
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit 83337796dcfb8cfbf733bd24d0b2c7e4f0732790
|
||||
Subproject commit 6fdbd74e2eea693d03d8b737fc494b9eeb135635
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
"""
|
||||
tools/web_search.py
|
||||
网络搜索工具 —— google搜索
|
||||
配置通过 settings.tools['web_search'] 读取
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass
|
||||
|
||||
from config.settings import settings
|
||||
from tools.base_tool import BaseTool
|
||||
from utils.logger import get_logger
|
||||
from serpapi import SerpApiClient
|
||||
|
||||
logger = get_logger("TOOL.WebSearch")
|
||||
|
||||
|
||||
def _cfg(key: str, fallback=None):
|
||||
return settings.tools['web_search'].get(key, fallback)
|
||||
|
||||
|
||||
@dataclass
|
||||
class SearchResult:
|
||||
title: str
|
||||
url: str
|
||||
snippet: str
|
||||
rank: int = 0
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"[{self.rank}] {self.title}\n {self.url}\n {self.snippet}"
|
||||
|
||||
|
||||
class Tool(BaseTool):
|
||||
name = "web_search"
|
||||
description = (
|
||||
"在互联网上搜索信息,返回相关网页的标题、链接和摘要。"
|
||||
"适用于需要实时信息、最新资讯或不确定的知识查询。"
|
||||
)
|
||||
parameters = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"query": {
|
||||
"type": "string",
|
||||
"description": "搜索关键词或问题,例如: 'Python 3.12 新特性'",
|
||||
}
|
||||
},
|
||||
"required": ["query"],
|
||||
}
|
||||
|
||||
def execute(self, query: str = "", **_) -> str:
|
||||
"""
|
||||
一个基于SerpApi的实战网页搜索引擎工具。
|
||||
它会智能地解析搜索结果,优先返回直接答案或知识图谱信息。
|
||||
"""
|
||||
print(f"🔍 正在执行 [SerpApi] 网页搜索: {query}")
|
||||
try:
|
||||
api_key = _cfg("api_key")
|
||||
if not api_key:
|
||||
return "错误:ApiKey未配置。"
|
||||
|
||||
params = {
|
||||
"engine": "google",
|
||||
"q": query,
|
||||
"api_key": api_key,
|
||||
"gl": "cn", # 国家代码
|
||||
"hl": "zh-cn", # 语言代码
|
||||
}
|
||||
|
||||
client = SerpApiClient(params)
|
||||
results = client.get_dict()
|
||||
|
||||
# 智能解析:优先寻找最直接的答案
|
||||
if "answer_box_list" in results:
|
||||
return "\n".join(results["answer_box_list"])
|
||||
if "answer_box" in results and "answer" in results["answer_box"]:
|
||||
return results["answer_box"]["answer"]
|
||||
if "knowledge_graph" in results and "description" in results["knowledge_graph"]:
|
||||
return results["knowledge_graph"]["description"]
|
||||
if "organic_results" in results and results["organic_results"]:
|
||||
# 如果没有直接答案,则返回前三个有机结果的摘要
|
||||
snippets = [
|
||||
f"[{i + 1}] {res.get('title', '')}\n{res.get('snippet', '')}"
|
||||
for i, res in enumerate(results["organic_results"][:3])
|
||||
]
|
||||
return "\n\n".join(snippets)
|
||||
|
||||
return f"对不起,没有找到关于 '{query}' 的信息。"
|
||||
|
||||
except Exception as e:
|
||||
return f"搜索时发生错误: {e}"
|
||||
434
uas/agent.py
434
uas/agent.py
|
|
@ -1,434 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import queue
|
||||
import threading
|
||||
import traceback
|
||||
from enum import Enum
|
||||
from time import sleep
|
||||
from typing import Optional, Tuple
|
||||
|
||||
from PyQt5.QtCore import QObject, pyqtSlot, pyqtSignal, QThread
|
||||
|
||||
from uas.action.action_executor import action_executor
|
||||
from uas.event.event_bus import event_bus
|
||||
from uas.event.handlers.device_handlers import DeviceErrorHandler, DeviceTimeoutHandler, EquipmentFaultHandler
|
||||
from uas.task.task_executor import task_executor
|
||||
from uas.task.task_model import Task, TaskState
|
||||
from uas.uas_control.base_controller import BaseUASController
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class AgentState(Enum):
|
||||
IDLE = "IDLE"
|
||||
READY = "READY"
|
||||
RUNNING = "RUNNING"
|
||||
PAUSED = "PAUSED"
|
||||
STOPPED = "STOPPED"
|
||||
SWITCHING = "SWITCHING"
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
# 装备模型抽象基类
|
||||
# ─────────────────────────────────────────────────────────────────
|
||||
|
||||
class AgentTaskWorker(QObject):
|
||||
# 信号:用于向主线程发送结果
|
||||
finished = pyqtSignal()
|
||||
result = pyqtSignal(object) # 可传数据
|
||||
|
||||
def __init__(self, agent:"Agent", dt:int):
|
||||
super().__init__()
|
||||
self.agent = agent
|
||||
self.dt = dt
|
||||
|
||||
@pyqtSlot()
|
||||
def do_work(self):
|
||||
"""真正的耗时任务"""
|
||||
try:
|
||||
# 执行你的任务
|
||||
print("线程开始执行")
|
||||
res = self._start_task()
|
||||
# 发送结果
|
||||
self.result.emit(res)
|
||||
finally:
|
||||
self.finished.emit()
|
||||
|
||||
def _start_task(self):
|
||||
self.agent._model.start()
|
||||
self.agent.state = AgentState.RUNNING
|
||||
while self.agent.state != AgentState.STOPPED and not self.agent._task_queue.empty():
|
||||
self.agent.current_task = self.agent._task_queue.get()
|
||||
priority, task = self.agent.current_task
|
||||
assert isinstance(task, Task)
|
||||
self.agent._planning_task(priority, task)
|
||||
task_executor.submit(task)
|
||||
|
||||
while task.state in(TaskState.RUNNING, TaskState.PLANNED, TaskState.READY):
|
||||
# 如果需要切换任务
|
||||
if self.agent.from_to(AgentState.RUNNING, AgentState.SWITCHING):
|
||||
self.agent._save_task(priority, task)
|
||||
task_executor.cancel(task.task_id)
|
||||
break
|
||||
# 如果需要停止任务
|
||||
elif self.agent.from_to(AgentState.RUNNING, AgentState.STOPPED):
|
||||
task_executor.cancel(task.task_id)
|
||||
break
|
||||
elif self.agent.from_to(AgentState.RUNNING, AgentState.PAUSED):
|
||||
task_executor.pause(task.task_id)
|
||||
elif self.agent.from_to(AgentState.PAUSED, AgentState.RUNNING):
|
||||
task_executor.resume(task.task_id)
|
||||
try:
|
||||
if task.state == TaskState.RUNNING and self.agent.state == AgentState.RUNNING:
|
||||
result = self.agent._model.step(self.dt)
|
||||
# 控制仿真速率(实时:sleep = dt;加速:sleep < dt)
|
||||
sleep(max(.050, (self.dt/1000) * 0.200))
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
print(f"[SimThread] 仿真异常: {e}")
|
||||
break
|
||||
self.agent.state = AgentState.IDLE
|
||||
self.agent.current_task = None
|
||||
self.agent._model.stop()
|
||||
|
||||
|
||||
def _start_task2(self):
|
||||
self.agent.state = AgentState.RUNNING
|
||||
while self.agent.state != AgentState.STOPPED and not self.agent._task_queue.empty():
|
||||
self.agent.current_task = self.agent._task_queue.get()
|
||||
priority, task = self.agent.current_task
|
||||
assert isinstance(task, Task)
|
||||
self.agent._planning_task(priority, task)
|
||||
task_executor.submit(task)
|
||||
|
||||
route = task.route
|
||||
for seg_index, segment in enumerate(task.route):
|
||||
pass
|
||||
|
||||
while task.state in(TaskState.RUNNING, TaskState.PLANNED, TaskState.READY):
|
||||
|
||||
# 如果需要切换任务
|
||||
if self.agent.from_to(AgentState.RUNNING, AgentState.SWITCHING):
|
||||
self.agent._save_task(priority, task)
|
||||
task_executor.cancel(task.task_id)
|
||||
break
|
||||
# 如果需要停止任务
|
||||
elif self.agent.from_to(AgentState.RUNNING, AgentState.STOPPED):
|
||||
task_executor.cancel(task.task_id)
|
||||
break
|
||||
elif self.agent.from_to(AgentState.RUNNING, AgentState.PAUSED):
|
||||
task_executor.pause(task.task_id)
|
||||
elif self.agent.from_to(AgentState.PAUSED, AgentState.RUNNING):
|
||||
task_executor.resume(task.task_id)
|
||||
try:
|
||||
if task.state == TaskState.RUNNING and self.agent.state == AgentState.RUNNING:
|
||||
result = self.agent._model.step(self.dt)
|
||||
# 控制仿真速率(实时:sleep = dt;加速:sleep < dt)
|
||||
sleep(max(.050, (self.dt/1000) * 0.200))
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
print(f"[SimThread] 仿真异常: {e}")
|
||||
break
|
||||
self.agent.state = AgentState.IDLE
|
||||
self.agent.current_task = None
|
||||
self.agent._model.stop()
|
||||
|
||||
class Agent(QObject):
|
||||
state_updated = pyqtSignal(object)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
controller: BaseUASController,
|
||||
parent: QObject = None,
|
||||
):
|
||||
super().__init__(parent)
|
||||
self._controller = controller
|
||||
# self._model = model
|
||||
# 日志
|
||||
self.logger = logging.getLogger(
|
||||
f"{self.__class__.__name__}[{self._model.equipment_id}]"
|
||||
)
|
||||
|
||||
self._prev_state: object = AgentState.IDLE
|
||||
self._state: object = AgentState.IDLE
|
||||
|
||||
self._current_task: Optional[Tuple[int, Task]] = None
|
||||
self._task_lock = threading.Lock()
|
||||
self._task_queue = queue.PriorityQueue()
|
||||
self._task_thread = None
|
||||
|
||||
self._setup()
|
||||
|
||||
def _setup(self):
|
||||
# 注册事件类处理器
|
||||
event_bus.register_handler(DeviceErrorHandler())
|
||||
event_bus.register_handler(DeviceTimeoutHandler())
|
||||
event_bus.register_handler(EquipmentFaultHandler())
|
||||
# 扫描 @on_event 装饰器处理器
|
||||
count = event_bus.scan_decorators()
|
||||
event_bus.event_submitted.connect(self._on_event_submitted)
|
||||
event_bus.event_handled.connect(self._on_event_handled)
|
||||
event_bus.event_failed.connect(self._on_event_failed)
|
||||
event_bus.event_ignored.connect(self._on_event_ignored)
|
||||
event_bus.stats_updated.connect(self._on_stats_updated)
|
||||
|
||||
# ── 连接 ActionExecutor 信号 ──────────────────────────────
|
||||
action_executor.action_completed.connect(self._on_action_completed)
|
||||
action_executor.action_progress.connect(self._on_action_progress)
|
||||
action_executor.events_produced.connect(self._on_events_produced)
|
||||
|
||||
# ── 连接 TaskExecutor 信号 ────────────────────────────────
|
||||
task_executor.task_submitted.connect(self._on_task_submitted)
|
||||
task_executor.task_started.connect(self._on_task_started)
|
||||
task_executor.task_completed.connect(self._on_task_completed)
|
||||
task_executor.task_failed.connect(self._on_task_failed)
|
||||
task_executor.task_cancelled.connect(self._on_task_cancelled)
|
||||
task_executor.task_paused.connect(self._on_task_paused)
|
||||
task_executor.task_resumed.connect(self._on_task_resumed)
|
||||
task_executor.segment_entered.connect(self._on_segment_entered)
|
||||
task_executor.segment_exited.connect(self._on_segment_exited)
|
||||
action_executor.set_event_bus(event_bus)
|
||||
task_executor.set_action_executor(action_executor)
|
||||
|
||||
def _state_trans_handler(self):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
def put_task(self, priority: int, task: Task):
|
||||
with self._task_lock:
|
||||
self._task_queue.put((priority, task))
|
||||
if self._state == AgentState.IDLE:
|
||||
self._state = AgentState.READY
|
||||
self.state_updated.emit(self._state)
|
||||
|
||||
def remove_task(self, task: Optional[Task|str]):
|
||||
with self._task_lock:
|
||||
temp_queue = queue.PriorityQueue()
|
||||
while not self._task_queue.empty():
|
||||
priorty, _task = self._task_queue.get()
|
||||
assert isinstance(_task, Task)
|
||||
if isinstance(task, Task):
|
||||
if _task != task:
|
||||
temp_queue.put((priorty, task))
|
||||
elif isinstance(task, str):
|
||||
if _task.task_id != task:
|
||||
temp_queue.put((priorty, _task))
|
||||
self._task_queue = temp_queue
|
||||
if self._task_queue.empty() and self._state == AgentState.READY:
|
||||
self._state = AgentState.IDLE
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
with self._task_lock:
|
||||
return self._state
|
||||
|
||||
@state.setter
|
||||
def state(self, state: AgentState):
|
||||
with self._task_lock:
|
||||
self._prev_state = self._state
|
||||
self._state = state
|
||||
self.state_updated.emit(state)
|
||||
|
||||
@property
|
||||
def current_task(self):
|
||||
with self._task_lock:
|
||||
return self._current_task
|
||||
|
||||
@current_task.setter
|
||||
def current_task(self, task):
|
||||
with self._task_lock:
|
||||
self._current_task = task
|
||||
|
||||
def schedule_task(self):
|
||||
self.state = AgentState.SWITCHING
|
||||
|
||||
def stop(self):
|
||||
self.state = AgentState.STOPPED
|
||||
|
||||
def pause(self):
|
||||
self.state = AgentState.PAUSED
|
||||
|
||||
def resume(self):
|
||||
self.state = AgentState.RUNNING
|
||||
|
||||
def from_to(self, from_state, to_state):
|
||||
with self._task_lock:
|
||||
if self._prev_state == from_state and self._state == to_state:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def _process_task_queue(self, dt: float):
|
||||
if self.state == AgentState.RUNNING:
|
||||
return
|
||||
self._model.start()
|
||||
self.state = AgentState.RUNNING
|
||||
while self.state != AgentState.STOPPED and not self._task_queue.empty():
|
||||
self.current_task = self._task_queue.get()
|
||||
priority, task = self.current_task
|
||||
assert isinstance(task, Task)
|
||||
self._planning_task(priority, task)
|
||||
task_executor.submit(task)
|
||||
|
||||
while task.state in(TaskState.RUNNING, TaskState.PLANNED, TaskState.READY):
|
||||
# 如果需要切换任务
|
||||
if self.from_to(AgentState.RUNNING, AgentState.SWITCHING):
|
||||
self._save_task(priority, task)
|
||||
task_executor.cancel(task.task_id)
|
||||
break
|
||||
# 如果需要停止任务
|
||||
elif self.from_to(AgentState.RUNNING, AgentState.STOPPED):
|
||||
task_executor.cancel(task.task_id)
|
||||
break
|
||||
elif self.from_to(AgentState.RUNNING, AgentState.PAUSED):
|
||||
task_executor.pause(task.task_id)
|
||||
elif self.from_to(AgentState.PAUSED, AgentState.RUNNING):
|
||||
task_executor.resume(task.task_id)
|
||||
try:
|
||||
if task.state == TaskState.RUNNING and self.state == AgentState.RUNNING:
|
||||
result = self._model.step(dt)
|
||||
# 控制仿真速率(实时:sleep = dt;加速:sleep < dt)
|
||||
sleep(max(.050, (dt/1000) * 0.200))
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
print(f"[SimThread] 仿真异常: {e}")
|
||||
break
|
||||
self.state = AgentState.IDLE
|
||||
self.current_task = None
|
||||
self._model.stop()
|
||||
|
||||
def start(self, dt):
|
||||
if self.state == AgentState.RUNNING:
|
||||
return
|
||||
self._task_thread = QThread()
|
||||
self._worker = AgentTaskWorker(self, dt)
|
||||
self._worker.moveToThread(self._task_thread)
|
||||
self._task_thread.started.connect(self._worker.do_work)
|
||||
self._worker.finished.connect(self._task_thread.quit)
|
||||
self._worker.finished.connect(self._task_thread.quit) # 任务结束 → 退出线程
|
||||
self._worker.finished.connect(self._worker.deleteLater) # 自动释放
|
||||
self._task_thread.finished.connect(self._task_thread.deleteLater) # 自动释放
|
||||
self._task_thread.finished.connect(self._on_worker_finished) # 任务结束后恢复按钮
|
||||
self._task_thread.start()
|
||||
|
||||
@pyqtSlot()
|
||||
def _on_worker_finished(self):
|
||||
self._task_thread = None
|
||||
|
||||
# ── 属性(只读)──────────────────────────────────────────────
|
||||
def _planning_task(self, priority, task):
|
||||
pass
|
||||
|
||||
def _log(self, message, style):
|
||||
print(message)
|
||||
|
||||
def _save_task(self, priority, task):
|
||||
pass
|
||||
|
||||
@pyqtSlot(object)
|
||||
def _on_event_submitted(self, event):
|
||||
self._log(f"📥 提交 | {event.type_key} | src={event.source}", "#569cd6")
|
||||
|
||||
@pyqtSlot(object)
|
||||
def _on_event_handled(self, event):
|
||||
results_str = " | ".join(str(r) for r in event.results)
|
||||
self._log(
|
||||
f"✅ 完成 | {event.type_key} | {event.elapsed_ms:.1f}ms | {results_str}",
|
||||
"#4ec9b0"
|
||||
)
|
||||
|
||||
@pyqtSlot(object, str)
|
||||
def _on_event_failed(self, event, error):
|
||||
self._log(f"❌ 失败 | {event.type_key} | {error}", "#f44747")
|
||||
|
||||
@pyqtSlot(object)
|
||||
def _on_event_ignored(self, event):
|
||||
self._log(f"⚪ 忽略 | {event.type_key} | 无处理器", "#808080")
|
||||
|
||||
@pyqtSlot(int, int, int)
|
||||
def _on_stats_updated(self, total: int, handled: int, failed: int):
|
||||
pass
|
||||
|
||||
|
||||
# ── TaskExecutor 信号槽 ───────────────────────────────────────
|
||||
@pyqtSlot(object)
|
||||
def _on_task_submitted(self, task):
|
||||
self._log(
|
||||
f"📥 任务提交: {task.name} [{task.task_id[:8]}]", "#569cd6"
|
||||
)
|
||||
|
||||
@pyqtSlot(object)
|
||||
def _on_task_started(self, task):
|
||||
self._log(f"▶ 任务开始执行: {task.name}", "#4ec9b0")
|
||||
|
||||
@pyqtSlot(object)
|
||||
def _on_task_completed(self, task):
|
||||
self._log(
|
||||
f"✅ 任务完成: {task.name} 耗时={task.elapsed_s:.1f}s",
|
||||
"#4CAF50"
|
||||
)
|
||||
|
||||
@pyqtSlot(object, str)
|
||||
def _on_task_failed(self, task, error):
|
||||
self._log(f"❌ 任务失败: {task.name} {error}", "#f44747")
|
||||
|
||||
@pyqtSlot(object)
|
||||
def _on_task_cancelled(self, task):
|
||||
self._log(f"🚫 任务取消: {task.name}", "#FF9800")
|
||||
|
||||
@pyqtSlot(object)
|
||||
def _on_task_paused(self, task):
|
||||
self._log(f"⏸ 任务暂停: {task.name}", "#9E9E9E")
|
||||
# self._add_rec_row("state_change", "任务已暂停", "")
|
||||
|
||||
@pyqtSlot(object)
|
||||
def _on_task_resumed(self, task):
|
||||
self._log(f"▶ 任务恢复: {task.name}", "#4ec9b0")
|
||||
# self._add_rec_row("state_change", "任务已恢复", "")
|
||||
|
||||
@pyqtSlot(object, int)
|
||||
def _on_segment_entered(self, task, seg_index):
|
||||
if task.has_route and seg_index < len(task.route.segments):
|
||||
seg = task.route.segments[seg_index]
|
||||
msg = (f"进入航段[{seg_index}] "
|
||||
f"{seg.wp_from.identifier}→{seg.wp_to.identifier} "
|
||||
f"({seg.distance / 1000:.1f}km)")
|
||||
else:
|
||||
msg = f"进入航段[{seg_index}]"
|
||||
self._log(f"✈️ {msg}", "#dcdcaa")
|
||||
# self._add_rec_row("segment", msg, f"seg_index={seg_index}")
|
||||
|
||||
@pyqtSlot(object, int)
|
||||
def _on_segment_exited(self, task, seg_index):
|
||||
self._log(f"🏁 离开航段[{seg_index}]", "#808080")
|
||||
|
||||
# ── ActionExecutor 信号槽 ─────────────────────────────────────
|
||||
|
||||
@pyqtSlot(object)
|
||||
def _on_action_completed(self, action):
|
||||
self._log(
|
||||
f" ✅ Action完成: {action.type_key} "
|
||||
f"src={action.source} {action.elapsed_ms:.0f}ms",
|
||||
"#6a9955",
|
||||
)
|
||||
|
||||
@pyqtSlot(str, int, str)
|
||||
def _on_action_progress(self, action_id: str, percent: int, message: str):
|
||||
pass
|
||||
|
||||
@pyqtSlot(object, list)
|
||||
def _on_events_produced(self, action, events):
|
||||
# self._event_count += len(events)
|
||||
for ev in events:
|
||||
self._log(
|
||||
f" ⚡ Event产生: {ev.type_key} src={ev.source}",
|
||||
"#c586c0",
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (f"{self.__class__.__name__}("
|
||||
f"id={self.equipment_id}, "
|
||||
f"name={self.name}, "
|
||||
f"lc={self.lifecycle.value})")
|
||||
|
||||
|
|
@ -23,7 +23,6 @@ from uas.route.base_params import GroundParams
|
|||
from uas.route.base_route import BaseRoute
|
||||
from uas.route.base_segment import BaseSegment, BaseTransition, SegmentKind
|
||||
from uas.route.base_waypoint import EquipmentDomain
|
||||
from uas.utils import geo_utils
|
||||
from uas.utils.geo_utils import GeoUtils
|
||||
|
||||
|
||||
|
|
@ -77,7 +76,7 @@ def _ground_seg_interpolate(seg: GroundSegment, t: float) -> GroundParams:
|
|||
dist = seg.v_start * dt + 0.5 * seg.acceleration * dt * dt
|
||||
dist = max(0.0, min(dist, seg.distance))
|
||||
|
||||
new_lat, new_lon = geo_utils.offset_position(
|
||||
new_lat, new_lon = GeoUtils.offset_position(
|
||||
seg.start_lat, seg.start_lon, seg.bearing, dist
|
||||
)
|
||||
speed = max(seg.v_start + seg.acceleration * dt, 0.0)
|
||||
|
|
|
|||
|
|
@ -10,30 +10,6 @@ EARTH_RADIUS = 6_371_000.0
|
|||
DEG2RAD = math.pi / 180.0
|
||||
RAD2DEG = 180.0 / math.pi
|
||||
|
||||
|
||||
# def determine_phase1(alt_start, alt_end, vs):
|
||||
# if alt_start < 500 and alt_end < 500:
|
||||
# return FlightPhase.TAKEOFF if vs > 0.5 else FlightPhase.LAND
|
||||
# if alt_start < 500: return FlightPhase.TAKEOFF
|
||||
# if alt_end < 500: return FlightPhase.APPROACH
|
||||
# if vs > 1.0: return FlightPhase.CLIMB
|
||||
# if vs < -1.0: return FlightPhase.DESCENT
|
||||
# return FlightPhase.CRUISE
|
||||
#
|
||||
# def determine_phase2(altitude, vs, airspeed):
|
||||
# """判断飞行阶段(同 v1.0)"""
|
||||
# if altitude < 50.0:
|
||||
# return FlightPhase.LAND
|
||||
# if vs > 2.0:
|
||||
# return FlightPhase.CLIMB
|
||||
# if vs < -2.0:
|
||||
# if altitude < 3000.0 and airspeed < 103.0:
|
||||
# return FlightPhase.APPROACH
|
||||
# return FlightPhase.DESCENT
|
||||
# if altitude < 3000.0:
|
||||
# return FlightPhase.APPROACH
|
||||
# return FlightPhase.CRUISE
|
||||
|
||||
def determine_phase(alt: float, vs: float,
|
||||
min_alt: float, max_alt: float):
|
||||
if alt <= min_alt + 50:
|
||||
|
|
|
|||
Loading…
Reference in New Issue