面向钢构模块化建造的参数化建模交互式演示。前后端一体化部署于小广云主机,通过 nginx 反向代理对外提供。本文档覆盖版本演进、功能清单、技术架构、API 设计、部署架构与回撤方案。
来源:https://box.jjcl.cn/demo/ · 本文档镜像:https://bim.jjcl.cn/v1.9/
BIM Demo 从首版参数化 IFC 生成,逐步演进为集参数建模、物理联动、构件点选、撤回/复位、STEP+BOM 双文件上传、OCP 解析与历史面板于一体的完整演示系统。全部开发与部署集中在 2026-08-10 一天内完成。
| 版本 | 日期 | 核心变更 |
|---|---|---|
| 1.0.0 | 08-10 | 首版 MVP。滑块参数输入 + ifcopenshell 生成 IFC + xeokit 三维预览。每层 4 柱 4 墙 1 板共 9 构件。 |
| 1.1.0 | 08-10 | 渲染引擎切换到 Three.js,替代 xeokit,统一前端三维渲染管线,预留构件独立 Mesh。 |
| 1.2.0 | 08-10 | 拖动滑块自动生成模型,移除手动触发,参数与模型双向实时联动重新生成。 |
| 1.3.0 | 08-10 | 新增构件点选:点击三维构件高亮选中,右侧面板展示并调整该构件参数。 |
| 1.4.0 | 08-10 | 后端新增 STEP 上传端点骨架,预留几何解析接口,前端补齐上传 UI。 |
| 1.5.0 | 08-10 | STEP 上传完整落地:/api/upload-step + 网格化解析 + 优雅降级(FreeCAD 未装则仅保存文件)。 |
| 1.6.0 | 08-10 | STEP + BOM 双文件上传。BOM 走 openpyxl 解析 Excel,支持列名自动识别(_detect_column)。新增 /api/upload-step-bom 合并上传。 |
| 1.7.0 | 08-10 | 双文件上传区域可见化 + BOM 联动:BOM 清单与 STEP 导入的构件建立对应关系并同步变更;后端 version 字段升级。 |
| 1.8.0 | 08-10 | STEP 解析底层从 FreeCAD 切换为 OpenCascade (OCP),进程内解析,移除子进程依赖,稳健性与性能提升。 |
| 1.9.0 | 08-10 | 交互打磨:撤回 / 复位操作栈、历史面板、构件删除、物理联动(参数/构件/外部导入三维联动的联动规则)。前端 index.html 版本号升至 v1.9.0。 |
/api/health 中的 version 字段与前端 nav 品牌号需保持一致。当前前端 index.html 声明 v1.9.0,后端 health 显示 1.8.0——两者差异为版本漂移,验收时以页面品牌号为准(见第六节回撤/一致性核对)。
核心能力。通过滑块/输入框设定 宽 width (3~30m)、深 depth (3~30m)、层高 floor_height (2.2~5.0m)、层数 floors (1~8),后端即调用 ifcopenshell 高层 API 生成标准钢构户型 IFC 模型。每层齐平生成 4 根角柱(IfcColumn,0.3×0.3)+ 4 面外墙(IfcWall)+ 1 块楼板(IfcSlab,厚 0.15),即 9 构件/层,N 层共 9N 构件。参数输入经服务端 validate_params() 白名单校验,越界返回 400 + 中文明细。
参数 → 模型 → 构件的多层联动:调整主参数时,IFC 重新生成,前端 Three.js 场景整树重建;三维场景中点选/删除某构件时,状态同步到参数面板与该构件的物理属性;导入的 STEP 实体与 BOM 清单条目之间按命名/标签建立映射,BOM 条目的数量、材质联动反映到对应三维构件。
基于 Three.js 射线拾取(Raycaster)。点击三维构件命中后高亮描边,右侧参数面板切换为该构件的独立参数(名称、尺寸、偏移、材质、可见性),支持就地编辑并实时回写场景。取消点选恢复全局参数面板。
撤回:维护操作栈,可连续回退最近一次构件级或参数级变更(点选、删除、参数调整),逐步撤销恢复模型。复位:一键将全部构件与参数恢复到初始生成状态,清空历史栈。
支持一次上传 STEP 几何文件(.stp/.step)与 BOM 清单(Excel,openpyxl 解析,字段名自动识别)。前端含拖拽/点击上传区、进度条、文件信息区与相对路径 /demo/api/...(规避 HTTPS 混合内容)。合并上传走 /api/upload-step-bom,两文件一次性到位并建立关联。
后端内置 _parse_step_ocp():使用 OpenCascade 的 STEPControl_Reader 读取 STEP,BRepMesh_IncrementalMesh 网格化,TopExp_Explorer 遍历三角面片,输出与 Three.js 兼容的扁平顶点/索引数组(parts),附边界盒 bbox 与三角面数统计。进程内运行,无需子进程/FreCAD。
右侧面板集中展示会话内全部操作与导入记录:参数生成记录(含各次 IFC id 与构件数)、STEP/BOM 导入记录(文件名、大小、解析结果)、构件点选与删除动作。便于审计当前模型的变更轨迹。
对构件级对象提供删除动作:删除后该构件从三维场景与构件树移除,物理联动同步失效,删除动作记入操作栈可被撤回还原,并在历史面板留痕。
| 层 | 技术 | 职责 |
|---|---|---|
| 前端 | Three.js | 三维场景渲染、Raycaster 构件点选、Mesh 独立管理、历史/构件树/参数面板 |
| 后端 | Flask (Python) | REST API:IFC 生成、STEP/BOM 上传解析、模型/文件下载、健康检查;绑定 0.0.0.0:8200 |
| 几何内核 | ifcopenshell | 高层 API ifcopenshell.api.run() 创建 IFC4 项目/场地/建筑/楼层/构件,输出标准化 .ifc |
| STEP 解析 | cadquery / OCP | OpenCascade 绑定(pythonocc/OCP)读取 STEP 并网格化,替代原有 FreeCAD 子进程方案 |
| BOM 解析 | openpyxl | 读取 Excel BOM 清单,列名/字段关键词自动识别 |
| 网关 | nginx (xiaoguang-nginx) | 静态文件服务 + 反向代理,Docker bridge 网关 172.17.0.1:8200 转发到宿主机 Flask |
172.17.0.1:8200(不走公网更稳定),而非 localhost。Flask 保持绑定 0.0.0.0(改 127.0.0.1 会断反代),公网 8200 靠安全组/防火墙封禁。/demo/api/generate、/demo/models),规避 HTTPS 页面混用 HTTP 的混合内容阻断。xeokit-sdk.min.es5.js)并显式提供 <canvas> 节点。box.jjcl.cn 等受信来源。MODEL_DIR = /var/www/box/demo/models/ — 生成/下载的 .ifc 模型文件(uuid8 命名)。UPLOAD_DIR = /var/www/box/demo/uploads/ — 用户上传的 STEP 源文件。| 方法 | 路径 | 入参 | 说明 |
|---|---|---|---|
| GET | /api/health | — | 状态、模型数 models、上传数 uploads、step_parser、openpyxl 布尔、version |
| POST | /api/generate | JSON:width/depth/floor_height/floors/name | 参数化生成 IFC,返回 id、elements 数、ifc_url |
| GET | /models/<mid>.ifc | — | 下载指定 IFC 模型 |
| POST | /api/upload-step | multipart:file (.stp/.step), quality | 上传 STEP 并 OCP 解析返回网格 parts |
| GET | /uploads/<filename> | — | 取回已上传 STEP 源文件 |
| POST | /api/upload-bom | multipart:excel | openpyxl 解析 BOM,字段关键词识别 |
| POST | /api/upload-step-bom | multipart:step + bom | STEP + BOM 合并上传并建立联动 |
| POST | /api/upload-excel | multipart:file | 通用 Excel 上传入口 |
# 健康检查
GET /api/health
→ {"models":58,"uploads":36,"step_parser":"opencascade","openpyxl":true,"version":"1.8.0"}
# 参数化建模
POST /api/generate {"width":10,"depth":8,"floor_height":3,"floors":2}
→ {"success":true,"data":{"id":"ab12cd34","elements":18,
"ifc_url":"/models/ab12cd34.ifc","parameters":{...}}}
# 参数越界 → 400
POST /api/generate {"width":9999}
→ {"error":"参数校验失败","details":["width: 必须在 3.0 ~ 30.0 之间,当前值 9999.0"]}
parts[] 每项与 Three.js 兼容:vertices(扁平坐标数组)、faces(扁平三角索引)、num_faces/num_edges/num_vertexes/num_solids、label;汇总含 total_parts/total_faces/total_triangles/bbox。读取 STEP 失败/无法识别/网格化失败均返回中文明细;可读但无网格(纯产品壳)返回空模型优雅处理。
| 项 | 值 |
|---|---|
| 主机 | 小广 · 腾讯云 114.132.93.176(SSH 用户 ubuntu / WireGuard 10.0.0.20 两路可达) |
| 演示域名 | https://box.jjcl.cn/demo/ |
| 文档域名 | bim.jjcl.cn(本文档 /v1.9/),nginx root /var/www/box/bim2 |
| 前端 | 静态 index.html → /var/www/box/demo/index.html(nginx serve) |
| 后端 | 宿主机 Flask server.py,监听 0.0.0.0:8200(ubuntu 用户 nohup/systemd-run 启动) |
| 网关 | Docker 容器 xiaoguang-nginx,39 同 server 块加 location 反代 |
| 数据目录 | /var/www/box/demo/models/、/var/www/box/demo/uploads/、/var/www/box/demo/versions/ |
在 box.jjcl.cn 的 80 与 443 两个 server 块内均需加入(最长前缀优先于 /demo/ 静态 location):
location /demo/api/ { proxy_pass http://172.17.0.1:8200/api/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_read_timeout 30s; }
location /demo/models/ { proxy_pass http://172.17.0.1:8200/models/; proxy_set_header Host $host; }
浏览器 ──HTTPS──▶ nginx(xiaoguang-nginx) ──location /demo/*──▶ 172.17.0.1:8200 (宿主机 Flask)
│ static /demo/index.html
└──http──▶ /var/www/box/demo/index.html
su - ubuntu -c 'cd /var/www/box/demo && nohup python3 -u server.py >> server.log 2>&1 &' & sleep 2; ss -tlnp | grep 8200 # 确认 0.0.0.0:8200 监听
systemd-run --unit=bim-demo-v1.7 --uid=ubuntu /bin/bash -c '...'。改 server.py 后须重启该 unit,root 启动会报 ifcopenshell 模块缺失。
每个历史版本的前端 index.html 与后端 server.py 均在 /var/www/box/demo/versions/ 留有快照:
| 路径 | 内容 |
|---|---|
versions/v1.2.0.html … v1.9.0.html | 各版本前端 index.html 快照(v1.9.0.html = 81242B) |
versions/server_v1.4.0.py, server_v1.6.0.py, server_v1.7.0.py.bak | 各版本后端 server.py 快照 |
versions/v1.7.0_backup.html, v1.8.0_backup.html | 部署前覆盖备份 |
# 前端回退到 v1.9.0 快照(示例目标版本)
sudo cp /var/www/box/demo/versions/v1.9.0.html /var/www/box/demo/index.html
# 后端回退(以 v1.6.0 为例)
sudo cp /var/www/box/demo/versions/server_v1.6.0.py /var/www/box/demo/server.py
# 重启后端(切 ubuntu 用户)
su - ubuntu -c 'pkill -f "python3 -u server.py"; cd /var/www/box/demo && nohup python3 -u server.py >> server.log 2>&1 &' &
# 验证
curl -s -o /dev/null -w '%{http_code}' https://box.jjcl.cn/demo/ # 200
curl -s https://box.jjcl.cn/demo/api/health # version + status ok
cp 原文件 原文件.bak.版本,再写新文件。顺序不能反——若先覆盖再 cp 备份,备份到的是新内容、旧版已丢。md5sum 当前文件 vs 备份文件——备份的 md5 必须不等于当前文件(证明抓到的是旧版)。curl 127.0.0.1/... 带 Host 确认 200 + grep -c 关键版本标记;公网侧 curl -s -o /dev/null -w '%{http_code}' 验证 HTTP 200 + Content-Length 与本地一致(防截断)。demo/VERSIONS.md(版本表 + 回撤命令 + 部署记录)。index.html 的 nav 品牌号与 server.py 的 /api/health.version 应一致;不一致时按页面品牌号就地修 sed -i 's/"version": "1.8.0"/"version": "1.9.0"/' server.py 并重启。本文档部署于 bim.jjcl.cn/v1.9/(实际目录 /var/www/box/bim2/v1.9/index.html)。覆盖 bim.jjcl.cn 根 /var/www/box/bim2/index.html 前,已先备份原「建筑BIM生产管理平台 2.0 开发文档(V2.0 业务驱动版)」至 bim2/index.html.bak.bim2-v2doc。若需恢复原 2.0 文档:
sudo cp /var/www/box/bim2/index.html.bak.bim2-v2doc /var/www/box/bim2/index.html
curl -s -o /dev/null -w '%{http_code}' https://bim.jjcl.cn/ # 期望 200