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_route import BaseRoute
|
||||||
from uas.route.base_segment import BaseSegment, BaseTransition, SegmentKind
|
from uas.route.base_segment import BaseSegment, BaseTransition, SegmentKind
|
||||||
from uas.route.base_waypoint import EquipmentDomain
|
from uas.route.base_waypoint import EquipmentDomain
|
||||||
from uas.utils import geo_utils
|
|
||||||
from uas.utils.geo_utils import GeoUtils
|
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 = seg.v_start * dt + 0.5 * seg.acceleration * dt * dt
|
||||||
dist = max(0.0, min(dist, seg.distance))
|
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
|
seg.start_lat, seg.start_lon, seg.bearing, dist
|
||||||
)
|
)
|
||||||
speed = max(seg.v_start + seg.acceleration * dt, 0.0)
|
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
|
DEG2RAD = math.pi / 180.0
|
||||||
RAD2DEG = 180.0 / math.pi
|
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,
|
def determine_phase(alt: float, vs: float,
|
||||||
min_alt: float, max_alt: float):
|
min_alt: float, max_alt: float):
|
||||||
if alt <= min_alt + 50:
|
if alt <= min_alt + 50:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue