apsis-book/README.zh.md

215 lines
9.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# apsis-book
[English](./README.md) | 简体中文
ApsisRS 的 Web 服务层。它在浏览器中呈现面向商业航天与卫星互联网的
ApsisRS 计算内核:概念文档、知识图谱、从库源码中提取的接口文档,以及一组
在线工具——用户给定参数,工具在这些内核上实际计算。
服务构建为单一二进制,其中嵌入了它所服务的全部资源。运行时不读取内容目录,
也不读取前端构建产物;二进制旁边不需要存在这两者。
## 服务内容
| 板块 | 内容 | 路由前缀 |
|------|------|----------|
| 概念文档 | 关于时间尺度、参考系、元素格式、传播模型的文档,附各自的单位与适用范围 | `/api/docs` |
| 知识图谱 | 概念及其关系,以及库的 crate 结构与算法流程图 | `/api/graph` |
| 接口文档 | 服务所构建的 ApsisRS 各层的公共接口,从源码文档注释中提取 | `/api/api` |
| 在线工具 | TLE 解析、SGP4 传播、轨迹采样、时间尺度换算、坐标换算、参考系旋转、开普勒换算与霍曼转移规划 | `/api/tools` |
## 设计约束
服务遵循它所属工作区的约束。
- **一个运行时依赖。** `[dependencies]` 中只有 `apsis-rs` 一项,且是路径依赖。
HTTP 服务器、JSON 编解码器、路由器、Markdown 渲染器与图模型,全部基于标准库
编写。
- **不使用 `unsafe`。** 本 crate 不含任何 unsafe 代码块。
- **请求路径中不 panic。** 畸形请求、非法参数或计算失败都产生一个响应,而不是
panic。处理请求的工作线程会把处理函数中的 panic 捕获下来作为最后一道防线,
并返回服务器错误。
- **注释自足。** 每条注释都解释代码、算法、接口或项目本身;没有注释指向私有
文档、指令或计划。
## 构建
必须先构建前端,发布二进制才能嵌入其产物;发布构建会自动完成这一步。
每个构建出的资源,其文件名都带有内容哈希,因此替换二进制就替换了这些文件名。
浏览器因而不可能握着旧包:新包是在它从未见过的 URL 下请求的。入口页面以
`no-cache, must-revalidate` 提供,带哈希的资源则以
`public, max-age=31536000, immutable` 提供。服务不注册 service worker。
```sh
# 开发:调试构建嵌入 frontend/dist 中已有的内容,不运行前端构建。
cargo build
# 发布:build.rs 构建前端并嵌入结果。
cargo build --release
```
二进制位于 `target/release/apsis-book`。
环境变量:
| 变量 | 作用 |
|------|------|
| `APSIS_BOOK_HOST` | 绑定的网络接口,默认 `127.0.0.1` |
| `APSIS_BOOK_PORT` | 绑定的端口,默认 `8080` |
| `APSIS_BOOK_ADDRESS` | 以 `HOST:PORT` 表示的绑定地址,覆盖上面两项 |
| `APSIS_BOOK_WORKERS` | 工作线程数,默认 4 |
| `APSIS_BOOK_SKIP_FRONTEND` | 设为 `1` 时在发布构建中跳过前端构建 |
| `APSIS_BOOK_FRONTEND_DIR` | 前端目录,默认 `frontend` |
命令行选项优先于环境变量:
```sh
# 在 8080 端口绑定所有网络接口。
apsis-book --host 0.0.0.0 --port 8080
# 合并写法等价。
apsis-book --address 0.0.0.0:8080
```
`--host` 与 `--port` 可以同时给出。`--address` 不能与它们中的任何一个合用,
因为二者生效的先后顺序没有定义。主机可以是字面地址、方括号中的 IPv6 字面量,
或由操作系统解析的名字。
### 语言
界面为双语,默认以中文打开。读者用顶栏中的控件切换到英文;该选择保存在浏览器
中,对所有页面生效。
后端为每一条面向读者的字符串同时保存两种语言,因此切换无需请求即可生效。
一篇概念文档的英文写为 `content/concepts/<id>.md`,中文译为
`content/concepts/<id>.zh.md`。没有译文的文档对所有读者显示英文,并附一条说明。
## 运行
```sh
./target/release/apsis-book --address 127.0.0.1:8080
```
打开 `http://127.0.0.1:8080`。JSON 接口不依赖前端即可访问:
- `GET /health`
- `GET /api/docs`、`GET /api/docs/<id>`、`GET /api/docs/search?q=`
- `GET /api/graph`、`GET /api/graph/crates`、`GET /api/graph/flows`、`GET /api/graph/<flow>`
- `GET /api/api`、`GET /api/api/crates/<name>`、`GET /api/api/items/<name>`
- `GET /api/tools`、`GET /api/tools/<id>`、`POST /api/tools/<id>/run`
一次工具调用接受一个参数 JSON 对象,返回一个已整形的结果:
```sh
curl -s http://127.0.0.1:8080/api/tools/hohmann-transfer/run \
-H 'content-type: application/json' \
-d '{"from_radius": 6778, "to_radius": 42164}'
```
## 内容布局
内容是任何单一库所实现之物的一个超集,分为两组。
**概念 / Concepts** 描述这个领域。各层沿用库的分层,因为那是读者接触这个学科的
顺序:
| 层 | 主题 |
|----|------|
| 导论 | 范围、组织方式,以及如何阅读一页 |
| 基础层 | 单位、量纲、数值方法、误差 |
| 物理层 | 时间与坐标系是两个彼此无关的主题,故并列于此 |
| 轨道层 | 根数、元素格式、摄动、传播器、轨道机动 |
| 射频与传播 | 多普勒、链路预算、介质、干扰 |
| 星座与网络 | 星座、可见性、拓扑、切换、路由 |
| 运行层 | 遥测、跟踪、指令、规划 |
| 仿真层 | 场景、事件,以及结果的含义 |
**实现 / Implementation** 描述某一个特定的库对上述内容的实现情况,包括它如何
组织、以及它的算法从何而来。把它们分开,正是概念集保持独立的原因:一篇关于
链路预算的页面不会因为某个库变了而改变。
文档归属于它所在目录所命名的层,因此新增一篇文档就是在它所属的层下创建文件,
并在 `chapters.json` 中列出。
## 重新生成接口目录
接口文档由 ApsisRS 源码生成,而非手工撰写。那里的文档注释先写英文、再写中文,
提取器把两半拆开,因此两种语言都来自代码旁的同一段注释,谁也不会落后于它所描述的
代码。源码变化时在本目录运行提取脚本,
然后重新构建:
```sh
scripts/extract_api.sh
cargo build --release
```
脚本默认读取 `../apsis-rs` 的工作区;设置 `APSIS_RS_WORKSPACE` 可指向别处的
检出。它需要 Python 3.8 或更高版本。Rust 构建不运行它,也不依赖 Python。
## 编辑内容
| 路径 | 内容 |
|------|------|
| `content/concepts/chapters.json` | 分组、各层与阅读顺序,含双语标题 |
| `content/concepts/<layer>/*.md` | 文档,每层一个目录;目录给出层,文件名给出标识 |
| `content/concepts/<layer>/*.zh.md` | 中文译文,文件名对应其所译的文件 |
| `content/graph/concepts.json` | 概念图谱 |
| `content/flows/*.json` | 算法流程图 |
| `content/api.json` | 生成的接口目录,含两种语言;不要手工编辑 |
公式写在美元符号之间:独占一行用 `$$...$$`,行内用 `$...$`。这是各类
Markdown 数学渲染器都识别的写法,也是本文档所采用的写法:公式是数学,
代码块不是放它的地方。服务把该记法翻译为 MathML,由浏览器自行排版。
文档头部声明 `title`、`summary`、`tags` 与 `prerequisites`。标识与层不由头部
声明:标识即文件名,层即目录,二者无法与目录结构相抵触。交叉引用写作
`[[id]]` 或 `[[id|label]]`,渲染为链接,前端无须整页加载即可路由。
## 目录结构
```text
apsis-book/
├── build.rs 嵌入前端构建产物与文本内容
├── Cargo.toml 一个运行时依赖:apsis-rs
├── content/ 撰写的文档、图谱与生成的目录
│ └── concepts/<layer>/ 文档,每层一个目录
├── frontend/ Svelte 与 TypeScript,由 Vite 构建
│ ├── src/lib/ API 客户端、路由器、组件、格式化
│ ├── src/pages/ 每个路由一个组件
│ └── dist/ 构建产物,由 build.rs 嵌入
├── scripts/extract_api.sh 接口目录生成器
├── src/
│ ├── api/ 路由器与 JSON 载荷
│ ├── docs/ 内容、Markdown、文档、接口目录
│ ├── embedded/ 对嵌入式前端资源的访问
│ ├── graph/ 知识图谱与流程图
│ ├── http/ 请求、响应与服务器
│ ├── tools/ 工具注册表、处理函数、结果形状
│ ├── json.rs JSON 编解码器
│ ├── lib.rs 各模块,以库的形式
│ ├── testkit.rs 测试所需的少数内部项
│ ├── main.rs 进程入口
│ └── state.rs 解析后的内容,供每个工作线程共享
└── tests/ 全部测试;`src/` 中没有
├── service.rs 针对构建出的二进制的端到端测试
└── <module>.rs 各模块的测试
```
## 测试
```sh
cargo test # 单元测试与端到端测试
cargo clippy --all-targets
cargo fmt --check
cd frontend && npm run check
```
端到端测试在一个临时端口上启动构建出的二进制,并以普通 TCP 请求与之通信。
## 许可协议
GNU 通用公共许可证,第 3 版或更高版本,且仅此一种。见 `LICENSE`。
本服务依赖 `apsis-rs`,后者以 MIT 或 Apache-2.0 提供。这两种协议都允许在 GPL
作品内再分发,因此二者可以按本协议一并分发。本服务不提供任何其他许可。