diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..b6b1ecf
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,10 @@
+# 默认忽略的文件
+/shelf/
+/workspace.xml
+# 已忽略包含查询文件的默认文件夹
+/queries/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
+# 基于编辑器的 HTTP 客户端请求
+/httpRequests/
diff --git a/.idea/base_agent.iml b/.idea/base_agent.iml
new file mode 100644
index 0000000..cbfcb6b
--- /dev/null
+++ b/.idea/base_agent.iml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..317bc4d
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/config.yaml b/config.yaml
new file mode 100644
index 0000000..260abe9
--- /dev/null
+++ b/config.yaml
@@ -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"
diff --git a/skills.db b/skills.db
new file mode 100644
index 0000000..6771bce
Binary files /dev/null and b/skills.db differ
diff --git a/skills/hexstrike-ai b/skills/hexstrike-ai
index 8333779..6fdbd74 160000
--- a/skills/hexstrike-ai
+++ b/skills/hexstrike-ai
@@ -1 +1 @@
-Subproject commit 83337796dcfb8cfbf733bd24d0b2c7e4f0732790
+Subproject commit 6fdbd74e2eea693d03d8b737fc494b9eeb135635
diff --git a/tools/web_search.py b/tools/web_search.py
deleted file mode 100644
index 190753c..0000000
--- a/tools/web_search.py
+++ /dev/null
@@ -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}"
\ No newline at end of file
diff --git a/uas/agent.py b/uas/agent.py
deleted file mode 100644
index fff7704..0000000
--- a/uas/agent.py
+++ /dev/null
@@ -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})")
-
diff --git a/uas/route/routes/ground_route.py b/uas/route/routes/ground_route.py
index e27e1e5..fa530f1 100644
--- a/uas/route/routes/ground_route.py
+++ b/uas/route/routes/ground_route.py
@@ -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)
diff --git a/uas/utils/geo_utils.py b/uas/utils/geo_utils.py
index 8698f36..a27a5d7 100644
--- a/uas/utils/geo_utils.py
+++ b/uas/utils/geo_utils.py
@@ -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: