# EZGCode **Repository Path**: openez/ezgcode ## Basic Information - **Project Name**: EZGCode - **Description**: 基于igH实现的Ethercat服务端, 部署于X86 - **Primary Language**: C - **License**: GPL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-08-28 - **Last Updated**: 2026-09-04 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # EZGCode 控制系统网关 EZGCode 是运行在 Debian/Ubuntu(PREEMPT_RT 实时内核)上的 EtherCAT + LinuxCNC 一体化运动控制系统网关,包含: - **OpenPnP 兼容 G 代码网关**:TCP 5010(OpenPnP GcodeDriver / GcodeAsyncDriver) - **Web 控制面板**(端口 8000):控制面板 / IO 面板 / 设备监控(数字字典、SDO 读写、报警、脚本、配方、趋势图)/ 宏编辑器 / 激光、写字、点胶等应用前端 - **NML 直连 LinuxCNC 2.10**(milltask/emcsvr),经 IgH EtherCAT 主站驱动雷赛伺服(2CL3-EC507 双轴 / CL3-EC507 单轴)与 IO 扩展模块(EM32DX-E4) ``` RT 实时内核 (PREEMPT_RT) └─ IgH EtherCAT 主站 (ethercat.service, ec_master) └─ LinuxCNC 2.10 (milltask/linuxcncsvr, NML 共享内存) ├─ lcec / hal-cia402 (HAL 侧 EtherCAT 从站驱动) └─ EZGCode 网关 (NML 直连 milltask, TCP 5010 / Web 8000) └─ 前端页面 / OpenPnP 客户端 / 监控脚本 (python3 + sdk) ``` --- ## 1. 界面截图 ### 登录页 ![登录页](screenshots/login.png) ### 控制面板 ![控制面板 - 日志Tab](screenshots/index.png) ![控制面板 - G代码Tab](screenshots/index_gcode.png) ![控制面板 - 宏Tab](screenshots/index_micro.png) ![控制面板 - 文件Tab](screenshots/index_file.png) ### 设备监控 ![EtherCat状态监控](screenshots/monitor.png) ### IO面板 ![IO面板](screenshots/io.png) ### 宏编辑器 ![宏编辑器](screenshots/micro_edit.png) ### 宏帮助文档 ![宏帮助文档](screenshots/micro_help.png) --- ## 2. 代码结构 ``` EZGCode/ ├── Makefile # 构建 (debug/release/asan/libsdk.a/test/install) ├── src/ │ ├── main.c # 入口: 启动依赖检查(deps_check)/后台初始化/服务链/线程 │ ├── config.h util.c # 配置结构 + config.ini 解析 / 驱动加载 / 路径解析 │ ├── backend.c backend.h # 控制卡后端抽象 (linuxcnc/grblHAL/marlin/klipper/fluidnc) │ ├── ezgcode_ctl.c # 服务控制 (svc 链 ethercat→app, 模拟模式) │ ├── ethercat/ │ │ ├── lcec/ # lcec.c: NML 客户端 (lcec_cmd/mdi/enable_seq/重连) │ │ └── io/ # io.c: IO 别名 P0-P15/P100-P115 → halcmd 读写 (EM32DX-E4) │ ├── nml/ # nml_bridge.cpp/h: NML 直连桥 (C++, 依赖 LinuxCNC 源码树头 + libnml) │ ├── gcode/ # gcode.c(解析/async 队列/急停) + gcmd_native.c(G0/G1/M 码) │ ├── server/ # TCP 5010 (OpenPnP) + /api + WS (api.c tcp.c websocket.c) │ ├── web/ # web_server.c(HTTP/WS 路由) ctrl.c(控制面板) │ │ # monitor.c(设备监控) microedit.c teach.c upgrade.c │ │ # mdview.c motionctl.c login.c iopanel.c ai.c │ ├── plugins/ │ │ ├── pnp/ # OpenPnP 插件 (custom/grblhal/marlin/micro/eeprom/board/serial) │ │ └── servo/ # 舵机插件 │ └── sdk/ # sdk.h + sdk_hal/motor/io.c (状态数据源) │ └── python/ # pysdk.c + build.sh → sdk*.so (监控脚本功能) ├── conf/ │ └── ezgcode/ # 运行配置 (config.ini 主配置, sim_mode=on 进入模拟模式) │ ├── drivers/<类型>/ # driver.ini lcec.xml sdo.conf dict.conf (雷赛驱动) │ ├── scripts/ # ezg.py 辅助库 + 预置监控脚本 (s1_status 等) │ ├── cia402/ eeprom.ini remap/ serial_board.ini zlog.conf ├── data/ # micros/*.cfg 宏定义 ├── web/ │ └── ezgcode/ # Web 前端 (html/css/js) ├── service/services.ini # 服务链定义 (type=all: ethercat+app 全链) └── lib/ # libezgcode.so 共享库 (含示例/公共头, 见 §9) ``` **关键源文件速查** | 文件 | 职责 | |---|---| | `src/main.c` | 启动依赖检查、后台初始化、服务链 auto_start | | `src/nml/nml_bridge.cpp` | LinuxCNC NML 直连(状态/命令/错误通道),替代 linuxcncrsh TCP | | `src/ethercat/lcec/lcec.c` | MDI 发送、使能序列、G 代码接口命令、断线重连 | | `src/gcode/gcode.c` | G 代码解析、async 队列、宏引擎、急停 | | `src/web/web_server.c` | HTTP/WS 路由、页面生成、`/api/v1/*` 分发 | | `src/web/monitor.c` | 设备监控 API(状态/字典/SDO/报警/脚本/配方) | | `src/sdk/python/pysdk.c` | CPython 扩展(脚本内 `import sdk`) | --- ## 3. 编译环境 **系统要求**:Debian 13(bookworm)或 Ubuntu 22.04+ / amd64 / PREEMPT_RT 内核(运行 EtherCAT 需要)。 **工具链**:`gcc/g++`(推荐 gcc-14)、`make`、`pkg-config`、`python3` + `python3-config`(构建 SDK 扩展)。 **依赖开发包**(Debian 包名): ```sh apt install build-essential pkg-config libevent-dev libwebsockets-dev \ libuv1-dev libllhttp-dev libssl-dev ``` `zlog` 日志库无标准 apt 包,需源码安装(或随系统镜像预装),Makefile 链接 `-lzlog`。 **LinuxCNC 2.10 源码树**(必需,仅编译期使用 NML 头文件): - Makefile 中 `LCNC_SRC := /root/src/deps_src/linuxcnc/src`,按实际源码位置修改 - 提供 `emc.hh / emc_nml.hh / nml.hh / emc.cc` 等头/源(NML 桥接层 C++ 编译用) - 运行库 `-lnml -lposemath` 来自 `/usr/lib/linuxcnc`(LinuxCNC 安装提供) **构建目标**(Makefile): | 目标 | 产物 | 说明 | |---|---|---| | `make` / `make all` | `Build/debug/` + `Build/release/` | Debug + Release | | `make release` | `Build/ezgcode` | Release 版(-O2 -DNDEBUG),**正式运行用** | | `make debug` | `Build/debug/ezgcode` | Debug 版(-g -O0 -DDEBUG) | | `make asan` | `Build/asan/ezgcode` | ASan/UBSan 验证版(越界/泄露/UAF 检测) | | `make libsdk.a` | `Build/sdk/libsdk.a` | 独立 SDK 静态库 | | `make test` | — | 单元/集成测试 | | `make clean` | — | 清理 Build/ | | `make install PREFIX=...` | `$PREFIX/` | 安装到前缀(conf/ezgcode/web/ezgcode/data/service) | --- ## 4. 系统依赖(详细) 启动时网关会执行**依赖检查**(`src/main.c deps_check`),缺失项以 `LL_ERR` 写入日志并在 stderr 提示。 | # | 依赖 | 作用 | 检查命令 | 缺失处理 | |---|---|---|---|---| | 1 | **RT 实时内核** (PREEMPT_RT) | EtherCAT 主站运行前提 | `uname -r`(含 `-rt`) | `apt install linux-image-*-rt` 并重启 | | 2 | **IgH EtherCAT 主站** 1.6.10 | 总线主站驱动,`ethercat.service` 由 systemd 管理 | `systemctl is-active ethercat.service` | 安装/加载 `ec_master.ko` 并 `systemctl start ethercat` | | 3 | **LinuxCNC 2.10** (milltask/linuxcncsvr) | 运动控制核心;网关经 **NML 共享内存直连**,`milltask` 未运行则所有 MDI 命令失败 | `pidof milltask`、`systemctl is-active linuxcnc` | 启动 LinuxCNC(服务链 `ethercat→app` 会自动拉起);网关启动后 60s 内持续重连,超时输出"依赖未就绪"告警,连接恢复后命令自动可用 | | 4 | **lcec + hal-cia402**(HAL 侧模块) | HAL 与 EtherCAT 从站(驱动/IO)的桥接 | `ls /usr/lib/linuxcnc/modules/lcec.so` | halcompile 编译安装 | | 5 | **从站驱动配置** `conf/drivers/<类型>/` | 雷赛驱动与 IO 模块的 SDO/PDO/字典定义 | `ls conf/drivers/` | 缺失时该从站无法配置(设备监控字典为空) | | 6 | **python3 + sdk 扩展** (`src/sdk/python/sdk*.so`) | 设备监控脚本功能(`import sdk` 读取轴/IO 状态) | `python3 -c "import sys;print(sys.version)"`、`ls src/sdk/python/*.so` | 运行 `cd src/sdk/python && ./build.sh` 生成 | | 7 | **系统工具** halcmd/ss/nc/systemctl/pkill/pidof | 启动依赖检查与运行期管理 | `which halcmd ss nc` | `apt install linuxcnc-uspace halcmd iproute2 netcat-openbsd` | | 8 | **Web/编译库** libevent/libwebsockets/libuv/libllhttp/openssl/zlog | 网关运行库 | `pkg-config --exists libevent libwebsockets libuv` | 见 §3 依赖安装 | **NML 就绪等待**:`gcode_bg_init` 后台线程每 0.5s 探测 `milltask/emcsvr` 的 NML 通道,最长 60s;超时输出 `LL_ERR`"依赖未就绪 - linuxcnc NML",网关按命令惰性重连,`linuxcnc` 启动后自动恢复——无需重启网关。 --- ## 5. 运行方式 ### 5.1 配置(`conf/ezgcode/config.ini`,关键段) | 段 | 关键项 | 说明 | |---|---|---| | `[server]` | `listen_port = 5010` | OpenPnP TCP G 代码端口 | | `[api]` | `token = ezgcode-api` | API token(`/api/v1/gcode?token=...&cmd=...`),请修改 | | `[gcode]` | `mode = async/sync` | 异步(流水线)/同步(GcodeDriver) | | `[motion]` | `feed_default = 1500` | G1 缺省进给率 mm/min(无 F 字时注入) | | `[startup]` | `auto_enable / auto_home / auto_start` | 启动自动使能(NML)/自动回零/自动启动服务链 | | `[web]` | `port = 8000` | Web 端口(HTTP + `/ws/gcode` + `/api/v1/`) | | `[svc_builtin]` | `slave_ = <类型>:<名称>` | EtherCAT 从站登记(2CL3-EC507/CL3-EC507/EM32DX-E4) | ### 5.2 启动 ```sh # 无硬件冒烟: 模拟模式 (config.ini 设 sim_mode=on, 跳过 EtherCAT/HAL) ./Build/ezgcode -c conf/ezgcode/config.ini # 真实环境 (配置文件所在目录即 conf_dir, 相对路径配置以此为基准) ./Build/ezgcode -c conf/ezgcode/config.ini # 可选: -p 覆盖监听端口; -v 详细日志; -V 版本 ``` 启动序列:依赖检查(deps_check)→ 后端初始化 → Web 线程(8000)→ NML 后台初始化(使能序列:estop off + machine on + mdi,2s 重试)→ `auto_start` 服务链(ethercat→app,`service/services.ini` `type=all`)。 ### 5.3 日志 - 配置文件 `conf/zlog.conf`,`[log]` 段控制等级,默认输出 `/var/log/ezgcode.log` - 启动后检查:`tail -f /var/log/ezgcode.log`(关注"启动依赖检查"、"后台初始化完成: NML 直连已就绪"、"auto_start" 行) ### 5.4 停止 `SIGTERM`(优雅退出:若服务链由本网关启动则一并停止;systemd 部署 `kill %1` 或 `systemctl stop ezgcode`)。 --- ## 6. AI 完整编译与启动测试流程 以下步骤可由 AI 代理逐条执行完成"依赖检查 → 编译 → 启动 → 冒烟测试"全流程。 ### Step 0 环境检查 ```sh uname -r # 应含 -rt (实时内核) gcc --version && make --version # 工具链 pkg-config --exists libevent libwebsockets libuv && echo deps-ok systemctl is-active ethercat.service # EtherCAT 主站 (真实环境) which halcmd ss nc python3 # 系统工具 ``` 若 ethercat 未激活:真实环境需先 `systemctl start ethercat`;无硬件测试可直接用模拟配置(Step 6)。 ### Step 1 安装构建依赖 ```sh apt install -y build-essential pkg-config libevent-dev libwebsockets-dev \ libuv1-dev libllhttp-dev libssl-dev # zlog 库 (无 apt 包): 确认 -lzlog 可链接, 缺失则源码安装 zlog 并 make install ``` ### Step 2 确认 LinuxCNC 源码树与运行库 ```sh grep '^LCNC_SRC' Makefile # 指向存在的 linuxcnc 源码树 (NML 头文件) ls /usr/lib/linuxcnc/libnml* /usr/lib/linuxcnc/libposemath* # 运行库 ``` 路径不符时修改 Makefile 的 `LCNC_SRC` 后再编译。 ### Step 3 编译 ```sh make release # 产物 Build/ezgcode file Build/ezgcode # 确认 ELF 可执行 # 可选: make test (单元/集成测试); make asan (ASan/UBSan 消毒版, 较慢) ``` ### Step 4 前端校验(可选,需 node) ```sh for f in web/ezgcode/js/*.js; do node --check "$f" || exit 1; done; echo js-ok ``` ### Step 5 SDK 扩展(脚本功能) ```sh cd src/sdk/python && ./build.sh && cd - # 生成 sdk*.so (gcc 直编, 不依赖 setuptools) ls src/sdk/python/*.so ``` ### Step 6 启动 ```sh # 无硬件: 模拟模式 (config.ini 设 sim_mode=on, 跳过 EtherCAT/HAL, web 前端可完整测试) ./Build/ezgcode -c conf/ezgcode/config.ini >/tmp/ezgcode.out 2>&1 & # 真实环境: -c conf/ezgcode/config.ini (linuxcnc/ethercat 须已就绪) sleep 3 tail -20 /var/log/ezgcode.log # 依赖检查 / 后台初始化 / auto_start ``` ### Step 7 冒烟测试 ```sh # Web 页面 curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8000/ # 期望 200 # 设备监控状态 (本机 loopback 免 token) curl -s http://localhost:8000/api/v1/monitor/status | head -c 300 # API G 代码通道 (api token) curl -s "http://localhost:8000/api/v1/gcode?token=ezgcode-api&cmd=?" ``` 真实环境还应验证:控制面板发送 `G1 X10 F1000` 后日志出现"使能: ... -> ..."与位置更新;若失败,错误 resp 会附带 linuxcnc 原因文本(未回零/软限位/任务态)。 ### Step 8 停止 ```sh kill %1 # 或 pkill -TERM -f 'Build/ezgcode' ``` ### 冒烟判据 - 启动日志:出现"启动依赖检查: 全部依赖就绪" + 模拟模式"模拟模式,跳过 EtherCAT/HAL"或真实环境"后台初始化完成: NML 直连已就绪" + "auto_start: 服务链已全部就绪,无需启动" - `curl /` 返回 HTTP 200;`/api/v1/monitor/status` 返回 `{"ok":1,...}` JSON;日志无未处理的 `LL_ERR` --- ## 7. 常见问题 | 现象 | 原因 | 处理 | |---|---|---| | make 报 nml.hh/emc.hh 等头文件缺失 | Makefile `LCNC_SRC` 路径不对或源码树不全 | 修正指向完整 LinuxCNC 2.10 源码树 | | 链接 `-lnml` 失败 | LinuxCNC 未安装到 `/usr/lib/linuxcnc` | 安装 linuxcnc-uspace(2.10) | | `ethercat.service` 未激活 | RT 内核/主站未安装 | 安装 RT 内核后 `systemctl start ethercat` | | 启动日志"依赖未就绪 - linuxcnc NML" | milltask/emcsvr 未运行 | 启动 LinuxCNC;网关每 2s 重连,恢复后自动可用,无需重启 | | `G1` 返回 `error: linuxcnc mdi failed (linuxcnc: ...)` | 未回零 / 软限位 / 任务 error 态 | 按错误文本处理(G28 回零 / 检查行程 / M999 恢复) | | 脚本运行失败 / `import sdk` 报错 | sdk*.so 未构建或 PYTHONPATH 不对 | `cd src/sdk/python && ./build.sh` | | Web 页面 `WS_HOST` 未替换 | 页面未由网关生成(web/ezgcode 直开) | 通过网关端口访问(页面含 `__WS_HOST__` 占位,由 `wb_gen_page` 生成) | --- ## 8. rt.sh — RT/CPU 亲和与 IRQ 绑定优化脚本 `rt.sh` 是 EZGCode 配套的实时性优化脚本,用于降低 EtherCAT DC 同步抖动(面向 PREEMPT_RT 实时内核 + IgH 主站场景)。 **核心思路**:将网卡中断处理(CPU0)与 RT 控制线程(CPU2-3)分离,避免中断抢占 RT 控制链,从而降低 EtherCAT 周期抖动。 **CPU 拓扑假设**(i3-3120M 双物理核 4 线程为例): | CPU | 用途 | |---|---| | CPU0 | enp2s0 EtherCAT 网卡 IRQ(+ 系统定时器旁路) | | CPU1 | 系统中断/杂务(timer 等) | | CPU2-3 | EZGCode + rtapi_app + milltask + linuxcncsvr 服务链(RT 控制) | **用法**: ```sh ./rt.sh apply # 应用所有亲和/IRQ 绑定(幂等,可重复执行) ./rt.sh show # 查看当前绑定状态(进程亲和 / IRQ / 中断节流) ``` `apply` 共执行 5 步: 1. **cpufreq 切 performance**:消除调频切换延迟; 2. **禁用深度 C-state(C6/C7)**:C6/C7 唤醒延迟大,是抖动尖峰来源; 3. **绑定服务链到 CPU2-3**:`ezgcode`/`rtapi_app`/`milltask`/`linuxcncsvr`/`halcmd` 进程亲和绑定; 4. **网卡 IRQ 绑定**:enp2s0(EtherCAT)IRQ 绑到 CPU0,其余局域网卡 MSI-X IRQ 绑到 CPU0/1,并关闭 igb 中断节流(`ethtool -C rx-usecs 0 tx-usecs 0`); 5. **内核线程/IRQ 线程绑定 CPU0/1**:kworker/rcu/ksoftirqd 等不占用 RT 核,timer IRQ 绑 CPU1。 **持久化**(建议):在 `/etc/systemd/system/ezgcode.service` 的 `[Service]` 段添加: ```ini ExecStartPre=/root/src/EZGCode/rt.sh apply ``` --- ## 9. libezgcode 共享库:功能与其它项目集成 ### 9.1 它是什么 `lib/libezgcode.so` 把 EZGCode 网关**除 `main.c` 入口与 Python 扩展外的全部核心源码**(含 NML C++ 桥接)打包为共享库(soname `libezgcode.so.1`,`nm -D` 导出 350+ 符号)。其它项目不需要再通过 TCP 5010 / WebSocket 与网关进程通信,而是**直接链接库、在进程内调用同一套 G 代码引擎与硬件访问接口**。 库内嵌完整 G 代码处理链:命令分派(`cmd_execute`)、解析(`gcode_parse`)、异步队列(`q_*`)、软限位/急停、EEPROM(`eeprom_*`)、IO(`io_m42`/`M62-65`)、Klipper 宏(`micro_*`)、飞拍(`FLYCAM`/`M740-742`)、服务链控制(`@status` 等)、后端抽象(linuxcnc/grblHAL/marlin/klipper/fluidnc)、Web/API 监听(可选启动)。 ### 9.2 公共接口 - **统一入口** `lib/include/ezgcode/ezgcode.h`(一个头包含全部模块头,C/C++ 通用): `libezgcode_init / libezgcode_start / libezgcode_exec / libezgcode_running / libezgcode_stop / libezgcode_version` - **单命令同步执行** `libezgcode_exec("G0 X10 Y20 F300", buf, n)`:返回 `0=已处理 / -1=错误 / 2=已消费`,resp 已填。 - **直接调用任意模块**:`gcode_parse`、`cmd_execute`、`sdk_motor_get`、`io_m42`、`eeprom_set/save`、`micro_verify`、`svc_status_all` 等。 - **Python 绑定** `lib/examples/python/ezlib.py`(ctypes,零依赖):`load()/init/start/stop/exec_cmd` + 结构体对齐 + 全原型 + `LCEC_MAX_RESP` 常量;`EZGCODE_LIB` 环境变量或 `LD_LIBRARY_PATH` 可覆盖库路径。 ### 9.3 构建 ```sh cd lib make # 生成 libezgcode.so.1.0.0 -> libezgcode.so.1 -> libezgcode.so make examples-c # 编译 10 个 C 示例 -> examples/c/*.bin make py-examples # 校验 10 个 Python 示例 make install PREFIX=/usr/local/ezgcode # 可选: 安装头 + 库 ``` ### 9.4 在其它项目中的集成方法 **C/C++(方式 A:本地源码树)** ```sh gcc app.c -I/lib/include -L/lib -lezgcode -o app export LD_LIBRARY_PATH=/lib # 或编译时 -Wl,-rpath,/lib ``` **C/C++(方式 B:已 make install)** ```sh gcc app.c -lezgcode -o app ``` **Python(ctypes)** ```sh export LD_LIBRARY_PATH=/lib:$LD_LIBRARY_PATH # 或用 EZGCODE_LIB=/lib/libezgcode.so python3 - <<'PY' import sys; sys.path.insert(0, "/lib/examples/python") import ezlib ezlib.init("/conf/ezgcode/config.ini", 1) # 配置路径是唯一锚点 ezlib.start(ezlib.EZGCODE_START_ALL) rc, resp = ezlib.exec_cmd("G0 X10 Y20 F300") print(resp) ezlib.stop() PY ``` ### 9.5 配置文件与数据目录(集成要点) `libezgcode_init(cfg_path)` 的 cfg_path 是唯一锚点:`cfg_load` 取 config.ini 所在目录 `realpath` 为 `conf_dir`,其余相对路径自动派生(eeprom.ini/M500 的"DB"、`data/micros` 宏、`service/services.ini`、`web/ezgcode` 静态页)。外部项目两种引用方式: - **继承仓库布局**:直接传 `/conf/ezgcode/config.ini`(eeprom 落盘 `/conf/ezgcode/`)。 - **自包含目录**:把 config.ini 放进自己项目的 `conf/ezgcode/`(或任意目录)后传绝对路径,eeprom.ini 自动落在 conf_dir 且需可写;若后端/宏/Web 与仓库解耦,可自行配置相应相对路径。 详细路径派生规则见《ezgcode类库使用手册》附录 C 与 `lib/README.md`。 ### 9.6 示例清单(`lib/examples/`,C/Python 一一对应,均可离线验证) | # | 示例 | 覆盖接口 | |---|---|---| | 01 | lifecycle | `libezgcode_init/start/stop/running/version` | | 02 | gcode_basic | `libezgcode_exec` `G28/G0/G1/M400/?/M114/M115/G4` | | 03 | parse_inspect | `gcode_parse` `gcode_check_limits`(软限位预校验,离线可跑) | | 04 | io | `M42/M62-65` `io_*` `sdk_io_*` | | 05 | axis_state | `sdk_axes_count` `sdk_motor_get` `M114` | | 06 | async_queue | `MODE_ASYNC` `q_*` `ctx_start`(Python 版为 M301/M302 批量传送) | | 07 | eeprom_params | `M92/M220/M221/M280` `eeprom_*` | | 08 | macro | `micro_count/micro_get_copy/micro_verify` | | 09 | flycam | `M740-742` `FLYCAM` `sdk_flycam_*` | | 10 | service_backend | `backend_type` `svc_status_all` `@status` | > 20 个示例均已在新目录布局(`conf/ezgcode`、`web/ezgcode`)下编译运行验证 exit=0。 或放入 `rc.local` / udev 规则,详见《RT系统与igH优化》文档。