Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
8b70c8e
精简不必要功能后的基础版本
Aug 17, 2026
fb84926
v1.0.1
Aug 17, 2026
4ed9885
v1.0.2
Aug 17, 2026
1b643af
v1.0.3
Aug 17, 2026
f9703ac
v1.0.4
Aug 17, 2026
e7adb15
v1.0.5
Aug 17, 2026
fc47187
v1.0.6
Aug 17, 2026
401fb5d
v1.0.7
Aug 17, 2026
9f4dc08
v1.0.8
Aug 18, 2026
1501e7d
v1.0.9
Aug 18, 2026
6421cdc
v1.0.10
Aug 18, 2026
5161a18
重装系统后的第一次提交,已根据新系统重新配置了环境,并添加了Prompt.md文件
Aug 22, 2026
3140d95
测试幽灵飞机和新的UI框架
Aug 25, 2026
0acf1a6
幽灵飞机轨迹测试正常
Aug 25, 2026
097d9b1
优化显示界面
Aug 26, 2026
3f341b4
修改启动界面图标和初始页布局
Aug 26, 2026
8d8c4af
图表chart逻辑单独提出后第一次烟测通过
Aug 26, 2026
9b195fe
覆盖8d8c4af5677a6bd6828f29997a696538641c8413提交
Aug 26, 2026
c78f399
重构图表工作逻辑
Aug 27, 2026
3963330
重构跟飞面板UI和启动逻辑
Aug 27, 2026
453c8ad
主界面UI调整完成
Aug 27, 2026
d565191
数据刷新方式大规模重构前的提交
Aug 27, 2026
922bd41
重构状态机后首次烟测成功
Aug 27, 2026
e99a301
状态机相关变量名优化并烟测成功
Aug 27, 2026
29821a4
更换图标
Aug 28, 2026
3622549
优化主界面UI按钮显示样式的修改
Aug 28, 2026
0eab9b8
修正圈时标志位置错误问题
Aug 29, 2026
b8b31ba
实现基础的历史数据对比功能
Aug 30, 2026
0fe9384
优化不必要的透传,补充代码注释
Aug 30, 2026
b076a1c
重构了数据字段的工作方式,将数据字段从原来的字典方式改为Dataframe方式
Aug 30, 2026
39d7926
实现摇杆监测基础功能
Aug 30, 2026
ba85c45
实现航迹图
Sep 1, 2026
7885c04
优化图表对比逻辑和UI显示样式
Sep 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 0 additions & 25 deletions .github/workflows/test-and-build.yml

This file was deleted.

Binary file added .wheels/numpy-2.5.2-cp314-cp314-win_amd64.whl
Binary file not shown.
Binary file added .wheels/pandas-3.0.5-cp314-cp314-win_amd64.whl
Binary file not shown.
Binary file not shown.
Binary file added .wheels/six-1.17.0-py2.py3-none-any.whl
Binary file not shown.
Binary file added .wheels/tzdata-2026.3-py2.py3-none-any.whl
Binary file not shown.
116 changes: 116 additions & 0 deletions Docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# Flight Assistant

## 图表模块架构

重构后图表功能拆为两层:

- **`models/`**:数据与配置(与 Qt / UI 无关)
- **`charts/`**:Qt Charts 渲染与交互

```
app_controller
└── ChartHistoryBuffer ← models/chart.py(写数据)
└── read_aq_snapshot() ← models/data_bridge.py(单拍 AQ)

models/data_bridge.py
└── FieldDef / build_overlay_config() / load_track_bundle()

charts/
├── LineChart ← 读 ChartConfig + field_data
├── HealthSpanOverlay ← 读 overlay spec + health_span_kind
└── TrendChart (facade) ← 组装为页面可直接使用的 widget
```

页面侧用法:

```python
from charts import TrendChart # 成品趋势图(推荐)
from charts.container import ChartSlot # 自行拼装时才需要
from charts.base.line_chart import LineChart
```

---

## `models/chart.py` — 数据层

| 内容 | 作用 | 谁在用 |
|------|------|--------|
| `ChartFieldSpec` / `ChartConfig` | 字段定义、Y 轴范围、视窗时长 | `LineChart`、`TrendChart`、`ChartHistoryBuffer` |
| `ChartHistoryBuffer` | 存时间序列;`append` / `slice_view` | **`app_controller.py`** 写数据,图表只读 |
| `TRAINING_CHART` 等预置 | 训练页默认图表配置 | controller、facade |
| `health_band` / `health_span_kind` | 健康度业务阈值判定(96.5 / 97.5 / 99.5) | `app_frame` 指标颜色、`HealthSpanOverlay` 色带 |
| `view_bounds` / `relative_times` / `series_points` | 视窗切片、坐标换算 | `ChartSlot`、`LineChart` |

数据流:`read_aq_snapshot` → `ChartHistoryBuffer.append()` + 轨迹录制 → 图表 `update(history)` 读切片渲染。不经过 Qt。

---

## `models/data_bridge.py` — 字段、蒙版与轨迹出入

| 内容 | 作用 | 谁在用 |
|------|------|--------|
| `FieldDef` / `FIELDS` | 图表字段出数 + 轴/线型/蒙版元数据 | `extract`、`frame_to_history` |
| `read_aq_snapshot` | 单拍 SimConnect 快照(图表+录制共用) | `app_controller`、`shadow_plane` |
| `load_track_bundle` | CSV 一次加载 → `(Trajectory, ChartHistoryBuffer)` | `track_compare` |
| `ThresholdSpanOverlaySpec` / `LapMarkerOverlaySpec` | 蒙版规格 | `registry.create_overlays()` |
| `build_overlay_config` / `load_overlay_config` | 从 FieldDef 组装蒙版 | `TrendChart` 初始化 |
| `kind_resolver` | `"health_span_kind"` → `models.chart.health_span_kind` | `HealthSpanOverlay` |

字段蒙版与图级 `lap_marker` 由 `FIELDS` / `CHART_LEVEL_OVERLAYS` 定义(不再使用 JSON)。

兼容:`models.chart_overlay` 仍 re-export 上述符号。

---

## `charts/` 目录结构

```
charts/
qtcharts.py # PySide2 QtCharts 兼容导入
context.py # ChartRenderContext(一次 render 的共享上下文)
theme.py # 配色适配(委托 UITheme)
container.py # ChartDisplayArea / ChartPane / ChartSlot
base/ # 基底图表:LineChart、BarChart(速度带可选附图)
overlay/ # 蒙版图层:HealthSpanOverlay、LapMarkerOverlay、registry
facade/ # 对外成品 widget(见下文)
widgets/ # PanChartView、ChartToolbar、TrackMapPane、SpeedBandPane、StickInput…
```

### 三层职责

1. **容器层**(`container.py`):总显示区域 + 可嵌套二级容器;统一 `update` / 平移 / 布局
2. **基底图表**(`base/`):按类型只渲染业务数据(折线 / 柱),不含蒙版
3. **蒙版图层**(`overlay/`):按阈值或标注规则覆盖基底(色带、LAP 竖线等)

### `charts/facade/` — 外观层

Facade 对外提供简单 API,内部组装 container + base + overlay + toolbar。

当前仅有 `TrendChart`:

```
TrainingAssistPage
└── TrendChart ← facade
├── ChartToolbar
├── ChartDisplayArea
│ ├── ChartSlot(LineChart + overlays) 主图
│ └── TrackMapPane + StickInputPane 底部附图
├── TimelineScrubber
└── update / add_lap_marker 等 API
```

页面无需了解 `ChartSlot`、`HealthSpanOverlay` 等细节。

可选附图:`charts/widgets/speed_band_pane.py`(`BarChart` + 速度带累计)仍保留,可按需挂到 `bottom_widgets`;默认 UI 使用航迹图。

---

## 修改指南

| 要改的内容 | 文件 |
|------------|------|
| 扭矩 / 健康 Y 轴范围、视窗 150s | `models/chart.py` 或 `TRAINING_CHART` |
| 健康阈值 96.5 / 97.5 / 99.5 | `models/chart.py` 中 `HEALTH_BOUND_*` |
| 色带透明度 / 颜色、LAP 标签文案 | `models/data_bridge.py`(`FieldDef.overlays` / `CHART_LEVEL_OVERLAYS`) |
| 折线样式、拖拽、布局、SAVE | `charts/` |
| SimVar / 弧度转度 | `services/simulator.py` |
Binary file added Docs/Simulation Variables.pdf
Binary file not shown.
192 changes: 192 additions & 0 deletions Docs/_regen_zh.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
# -*- coding: utf-8 -*-
"""重新生成带中文说明的模拟器参数清单。"""

import re
from collections import OrderedDict, defaultdict
from pathlib import Path

from simvar_translate import (
FUEL_ENUM_ZH,
best_chinese,
translate_units,
)

ROOT = Path(__file__).resolve().parent.parent
text = (ROOT / "SimConnect" / "RequestList.py").read_text(encoding="utf-8")

cat_map = OrderedDict(
[
("__AircraftEngineData", "一、Aircraft Engine Data(发动机)"),
("__AircraftFuelData", "二、Aircraft Fuel Data(燃油)"),
("__AircraftLightsData", "三、Aircraft Lights Data(灯光)"),
("__AircraftPositionandSpeedData", "四、Aircraft Position and Speed Data(位置与速度)"),
("__AircraftFlightInstrumentationData", "五、Aircraft Flight Instrumentation Data(飞行仪表)"),
("__AircraftAvionicsData", "六、Aircraft Avionics Data(航电)"),
("__AircraftControlsData", "七、Aircraft Controls Data(操纵/控制)"),
("__AircraftAutopilotData", "八、Aircraft Autopilot Data(自动驾驶)"),
("__AircraftLandingGearData", "九、Aircraft Landing Gear Data(起落架)"),
("__AircraftEnvironmentData", "十、Aircraft Environment Data(飞机环境)"),
("__HelicopterSpecificData", "十一、Helicopter Specific Data(直升机)"),
("__SlingsandHoists", "十二、Slings and Hoists(吊索/绞车)"),
("__AircraftMiscellaneousSystemsData", "十三、Aircraft Miscellaneous Systems Data(杂项系统)"),
("__AircraftMiscellaneousData", "十四、Miscellaneous Data(杂项)"),
("__AircraftStringData", "十五、Aircraft String Data(字符串)"),
("__AIControlledAircraft", "十六、AI Controlled Aircraft(AI 飞机)"),
("__CarrierOperations", "十七、Carrier Operations(航母作业)"),
("__Racing", "十八、Racing(竞速)"),
("__EnvironmentData", "十九、Environment Data(全局环境)"),
]
)

items = []
pos = 0
while True:
m = re.search(r"class (__\w+)\(RequestHelper\):\s*\n\s*list = \{", text[pos:])
if not m:
break
cname = m.group(1)
start = pos + m.end() - 1
depth = 0
i = start
while i < len(text):
if text[i] == "{":
depth += 1
elif text[i] == "}":
depth -= 1
if depth == 0:
break
i += 1
d = eval(text[start : i + 1], {"__builtins__": {}})
for _key, val in d.items():
desc, simname, units, sett = val
sim = simname.decode() if isinstance(simname, bytes) else simname
u = units.decode() if isinstance(units, bytes) else units
items.append((cname, sim, desc, u, sett == "Y"))
pos = i + 1

by = defaultdict(list)
seen = set()
for c, sim, desc, u, sett in items:
if (c, sim) in seen:
continue
seen.add((c, sim))
by[c].append((sim, desc, u, sett))

total = sum(len(v) for v in by.values())
writable = sum(1 for rows in by.values() for r in rows if r[3])
readonly = total - writable

lines = []
lines.append("# 模拟器 Simulation Variables 参数清单")
lines.append("")
lines.append(
"> 来源:`Docs/Simulation Variables.pdf`(FSX/SimConnect 官方仿真变量表),"
"并与项目 `SimConnect/RequestList.py` 中的结构化定义对齐。"
)
lines.append("> 补充说明见 `Docs/项目原理.md`。")
lines.append("")
lines.append("## 读写规则说明")
lines.append("")
lines.append("| 标记 | 含义 | 本项目用法 |")
lines.append("|---|---|---|")
lines.append(
"| **只读 (N)** | 只能通过 get / request_data 订阅读取 | "
"`AircraftRequests.get(...)` 或 REST `GET /datapoint/<name>/get` |"
)
lines.append(
"| **可写 (Y)** | 可通过 set_data 直接写入数值 | "
"`AircraftRequests.set(...)` 或 REST `POST /datapoint/<name>/set` |"
)
lines.append("")
lines.append("**交互补充(非变量写入):**")
lines.append("")
lines.append(
"- 许多开关/手柄(如自动驾驶开关、灯光开关)在变量表中为 **只读**,"
"需通过 **SimConnect 事件**(`EventList.py` / `POST /event/<name>/trigger`)触发才能交互。"
)
lines.append("- 带 `:index` 的变量需要指定系统索引(发动机从 0,通讯设备从 1)。")
lines.append(
"- PDF 另有 **Program Data**(`P:`,如 `SIMULATION RATE`、`UNITS OF MEASURE`)"
"与 **Units of Measurement** 单位表;本清单主体为 Aircraft/Environment 的 `A:` 仿真变量。"
)
lines.append("")
lines.append("## 总览")
lines.append("")
lines.append(f"- **变量总数**:{total}")
lines.append(f"- **可直接写入 (Y)**:{writable}")
lines.append(f"- **只能读取 (N)**:{readonly}")
lines.append("")
lines.append("| 分类 | 总数 | 可写 | 只读 |")
lines.append("|---|---:|---:|---:|")
for c, title in cat_map.items():
rows = by.get(c, [])
y = sum(1 for r in rows if r[3])
lines.append(f"| {title} | {len(rows)} | {y} | {len(rows) - y} |")
lines.append("")
lines.append("---")
lines.append("")

lines.append("## 附录 A:Fuel Tank Selection(油箱选择枚举值)")
lines.append("")
lines.append(
"以下不是独立 SimVar,而是 `FUEL TANK SELECTOR` / "
"`RECIP ENG FUEL TANK SELECTOR` 等枚举取值(文档章节 *Fuel Tank Selection*):"
)
lines.append("")
lines.append("| 枚举值 | 英文含义 | 中文含义 |")
lines.append("|---:|---|---|")
fuel_en = [
(0, "Off"), (1, "All"), (2, "Left"), (3, "Right"), (4, "Left auxiliary"),
(5, "Right auxiliary"), (6, "Center"), (7, "Center2"), (8, "Center3"),
(9, "External1"), (10, "External2"), (11, "Right tip"), (12, "Left tip"),
(13, "Crossfeed"), (14, "Crossfeed left to right"), (15, "Crossfeed right to left"),
(16, "Both"), (17, "External"), (18, "Isolate"), (19, "Left main"), (20, "Right main"),
]
for v, en in fuel_en:
lines.append(f"| {v} | {en} | {FUEL_ENUM_ZH[v]} |")
lines.append("")

lines.append("## 附录 B:Program Data(程序数据,PDF 有、RequestList 未收录)")
lines.append("")
lines.append("| 变量名 | 英文说明 | 中文说明 | 单位 | 读写 |")
lines.append("|---|---|---|---|---|")
lines.append(
"| `SIMULATION RATE` | Time acceleration factor | 时间加速倍率 | Number | 只读 |"
)
lines.append(
"| `UNITS OF MEASURE` | Units of measure: 0=English; 1=Metric(alt feet); 2=Metric(alt meters) | "
"单位制:0=英制;1=公制(高度英尺);2=公制(高度米) | Enum | 只读 |"
)
lines.append("")
lines.append("---")
lines.append("")

for c, title in cat_map.items():
rows = by.get(c, [])
lines.append(f"## {title}")
lines.append("")
y = sum(1 for r in rows if r[3])
lines.append(f"共 **{len(rows)}** 项(可写 {y} / 只读 {len(rows) - y})")
lines.append("")
lines.append("| 变量名 | 英文说明 | 中文说明 | 单位 | 读写 |")
lines.append("|---|---|---|---|---|")
for sim, desc, u, sett in rows:
flag = "**可写**" if sett else "只读"
zh = best_chinese(sim, desc)
desc_esc = desc.replace("|", "\\|").replace("\n", " ")
zh_esc = zh.replace("|", "\\|")
u_zh = translate_units(u)
u_cell = f"{u}({u_zh})" if u_zh != u else u
lines.append(f"| `{sim}` | {desc_esc} | {zh_esc} | {u_cell} | {flag} |")
lines.append("")

out = ROOT / "Docs" / "模拟器参数清单.md"
out.write_text("\n".join(lines), encoding="utf-8")
print("wrote", out)
print("bytes", out.stat().st_size)

# quality check
content = out.read_text(encoding="utf-8")
rows = [ln for ln in content.splitlines() if ln.startswith("| `")]
still_en = sum(1 for ln in rows if len(re.findall(r"[\u4e00-\u9fff]", ln.split("|")[3])) < 2)
print("rows", len(rows), "low_zh_quality", still_en)
Loading