一份 JSON 支撑四类交付:nVisual DCDesigner 的数据模型与机房设计成果一致性
摘要:机房工勘成果「对不上」的根因,通常是图与表各自独立保存。DCDesigner 把项目保存为一份结构化 JSON:毫米坐标 + 原点约定 + 显式依附关系 + 工程属性,平面图、DXF、CSV 清单、3D 模型、校验报告与平台回传全部由它派生。本文解释这份数据模型的结构、约束与派生关系,说明「改一处、处处同步」是怎么实现的。一、一致性问题:不是工具不好,是数据有两份
一个典型的机房工勘交付物包含:平面图、CAD 图、设备清单、房间与设备参数表。如果它们分别由不同软件、不同时间点生成,就会出现典型的不一致:
- 图上 42 台机柜,清单里 40 台(两台是后加的);
- CAD 图里通道宽 1200,表格里写 1000(改了图没改表);
- 平面图某个门的位置对,但清单里的门数量对不上;
- 复盘时想知道「A 排第 3 台功率多少」,需要三份文件交叉查找。
DCDesigner 的做法是只保留一份数据:项目文件是一个 JSON,包含项目名、工勘与机房信息(meta)、对象数组(objects)。所有输出都是这份数据的「视图」,而不是独立的「副本」。
二、数据模型的结构2.1 总体形状
{"name": "XX大楼3F机房","meta": { "surveyor": "张三", "date": "2026-08-28" },"objects": [ /* 墙体、门窗、机柜、UPS、桥架…… */ ]- 单位:全部毫米;
- 原点:房间西南角内表面 (0,0),X 向东、Y 向北;
meta除勘测人/日期外,还可记录机房整体信息:结构层高roomHeight、吊顶面高ceilHeight、架空地板高floorHeight、机房等级tier、所属园区/楼宇site、总供电容量powerCap、制冷架构coolArch、电力模块容量powerModule、地面承重floorLoad、温湿度要求climateremark
对象按类型区分,每类有各自的几何字段与props。几个代表性的结构(摘自仓库文档的数据格式说明):
// 矩形对象(机柜):中心坐标 + 旋转 + 宽深高 + 电气参数{ "id": "o…", "type": "cabinet", "x": 1500, "y": 2100, "rotation": 0,"props": { "name": "A01", "w": 600, "d": 1200, "h": 2000,"u": 42, "power": 4,"rpdu": 2, "voltage": 220, "current": 32,"sockets": [ { "type": "IEC C13", "count": 16 },{ "type": "IEC C19", "count": 6 } ] } }// 线段对象(墙、桥架):两端点 + 属性{ "id": "o…", "type": "wall", "x1": 0, "y1": 0, "x2": 12000, "y2": 0,"props": { "name": "墙-1", "thickness": 240, "height": 3000, "material": "砖墙" } }{ "id": "o…", "type": "tray", "x1": 1000, "y1": 2100, "x2": 11300, "y2": 2100,"props": { "name": "桥架-1", "width": 300, "height": 2500, "kind": "弱电" } }// 依附对象(门窗/格栅):不存坐标,挂在墙上{ "id": "o…", "type": "door", "wallId": "o…", "t": 0.5,"props": { "name": "门-1", "width": 1000, "height": 2100,"leaf": "double", "swing": "left", "side": "out" } }从工程视角看,这份模型有几个刻意的选择:
设计选择工程含义坐标存毫米、原点在西南角内表面与卷尺读数同口径,现场复核无需换算门窗/格栅存wallId + t而非绝对坐标依附关系显式化:墙动门动、删墙级联,不可能出现「孤儿门窗」通道带与机柜不建立持久关联通道宽联动按几何(朝向+边缘贴合)实时识别,旧项目零迁移电气、制冷、消防等属性字段化属性参与渲染、统计、自检、导出与平台提交,而不是备注文字枚举值以中文为数据键(如"kind":"弱电"任意界面语言互相打开兼容;界面只做显示翻译2.3 类型清单与「实体」的边界
当前建模对象覆盖机房平面记录的主要范围:墙体、机房门、窗、格栅(风墙送/回风口)、柱子、框架梁、机柜、列头柜、冷/热通道、桥架、精密空调、UPS、蓄电池、灭火器、地板出风口。
其中机柜、柱子、空调、UPS、蓄电池、灭火器、风口、列头柜被定义为实体对象——彼此不允许重叠,放置与拖动时自动推挤让位。墙体、桥架、门窗、通道带属于结构或标注对象,不参与推挤。这个边界同样是数据模型的一部分:它决定了碰撞检测、自检与联动分别在哪些对象之间生效。
三、数据进入系统时的清洗与迁移
手工编辑过的 JSON、旧版本保存的文件、从别处拼装的数据,都会在打开项目与草稿恢复时经过一次清洗:
- 剔除无
idtype的残缺对象; - 剔除丢失所属墙体的门窗/格栅(防止悬空对象);
- 坐标与尺寸类字段(
x/y/x1…/rotation/tprops中的宽高深、厚度、电气参数等)强制数值化; - 非负物理量负值归零(功率/尺寸/制冷量等,温度除外);
- 旧版字段自动迁移(门的
side: left/rightout/in;旧版机柜单类型socketsockets列表)。
这意味着两件事:
- 异常数据不会静默进入画布:手工改坏 JSON 不会让属性面板出现字符串或负数,也不会污染统计与自检;
- 旧项目零成本升级:字段演进而非格式推翻,历史项目可继续使用。
数据模型的真正价值体现在「谁消费它」。DCDesigner 内部各输出有一致的派生路径:
输出数据来源关键实现2D 平面图objects坐标与propspublic/js/render.js3D 机房预览同一份objects派生三维模型public/js/3d/builders3d.jsDXF 图纸同一坐标逐对象转图层图元(1:1 毫米)public/js/dxf.jsCSV 工程量清单按类别+规格聚合 + 逐台明细public/js/csv.jsbuildBOQ纯函数)合规校验报告runChecks(project, cfg)纯函数public/js/checks.jsAI 建模/Agent 指令读写同一份 state 与撤销栈public/js/agent.jspublic/js/ai/*平台回传包buildSurveyPayload(深拷贝 + 与保存同清洗)public/js/nvsubmit_core.js
其中三个是纯函数:给定项目数据,输出确定,不依赖界面状态,因此可以直接在 Node 里单测(tests/csv.test.mjs、tests/checks.test.mjs、tests/nvsubmit.test.mjs)。这也是「一致性」可以被验证而不只是被宣称的原因。
反过来看变更成本:现场改一台机柜的功率,改的是数据;下一次导出,PNG/DXF/CSV/3D/报告/平台提交包全部自动反映这个新值。没有「同步」这个动作,因为没有第二份数据。
五、文件、草稿与工作现场:数据的三种存储层
一个容易混淆的地方是「数据存在哪」。DCDesigner 实际有三层存储,各自职责不同:
存储键/位置内容生命周期项目文件本机 JSON(系统文件对话框保存/打开)namemetaobjects长期,可备份/拷贝/入库本地草稿localStorageidc2d_draft_v1当前编辑中的项目刷新/误关页面后自动恢复;保存后标记为与文件一致界面状态sessionStorageidc2d_uistate_v1向导开合与步骤、自检面板、属性面板、2D 缩放位置、3D 相机跟随标签页会话,新开页回到默认布局用户偏好localStorage(语言、属性面板宽度、默认值注册表等)与项目数据无关的习惯设置跨会话保留,不参与撤销
这层区分解决两个常见痛点:
- 「刷新一下,辛苦摆的东西没了」——项目数据有草稿兜底;
- 「刷新一下,面板全开了、视图也回到原点」——界面状态单独记忆,刷新后回到刷新前的样子(
tests/uistate.cdp.mjs专门验证「连续刷新 3 次视野不漂移」)。
保存交互采用标准文件对话框:首次保存弹出「另存为」,默认文件名取机房名称(未填写时用时间戳);保存过一次后再次保存直接覆盖原文件(文件句柄存于浏览器 IndexedDB,刷新后仍可直接保存);文件选择框未关闭时重复触发会被拦截并提示,不把浏览器技术错误抛给使用者。
六、边界与注意事项
- JSON 不是数据库:没有多用户并发锁、没有版本历史、没有权限控制。它面向的是「一个人在现场/办公室记录一个机房」的场景;
- 服务端项目存取 API 仍保留但界面默认不使用:数据以
data/*.json形式存放时是共享命名空间,同名项目会互相覆盖、无鉴权;多人协作建议以文件方式各自保存、通过平台回传汇总; - 删除是级联的:删除墙体时其上门窗/格栅一并删除,这是数据模型的必然结果,操作前应有心理预期;
- 属性值以中文为数据键:跨语言兼容,但也意味着导出的 CSV 清单内容固定中文(与界面语言无关),这是为「清单作为工程文件」的一致性做的取舍。
附:事实来源与核验方式
- JSON 字段与示例:
README.md「数据格式」节(含机柜/墙/门窗/通道/桥架/空调/UPS 等字段说明) - 清洗与旧字段迁移:
public/js/state.jssanitize);项目对象定义见同文件 state 结构 - 派生纯函数:
public/js/csv.jsbuildBOQpublic/js/checks.jsrunCheckspublic/js/nvsubmit_core.jsbuildSurveyPayload - 存储键:
idc2d_draft_v1public/js/state.jsidc2d_uistate_v1public/js/uistate.jsidc2d_locale_v1idc2d_props_w_v1idc2d_lastprops_v1public/js/i18n.jspublic/js/props.jspublic/js/state.js - 保存/打开与并发锁:
public/js/main.js;端到端回归node tests/qa-20260907-verify.cdp.mjs - 可复现核验:
node tests/csv.test.mjsnode tests/checks.test.mjsnode tests/nvsubmit.test.mjsnode tests/uistate.cdp.mjs
热门跟贴