From f6d701a661ac0f1ba6860a01f215e77709d0db8d Mon Sep 17 00:00:00 2001 From: company-desktop-computer Date: Sun, 25 Oct 2026 10:30:19 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=AD=98=E7=9B=98=E4=B8=8D=E5=86=8D?= =?UTF-8?q?=E6=B8=85=E7=A9=BA=E7=BB=84=E5=90=88=E8=AE=BE=E5=A4=87=E7=9A=84?= =?UTF-8?q?=E5=AD=90=E5=8D=95=E5=85=83=EF=BC=88m=5FCombins=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dwgOutFieldsForXPrim 里的容错条件写成 m_eType != kRevitCombination || m_eType != kRevitCombination 恒为真,任何单元存盘前都会执行 m_Combins.clear(): - 组合设备(kCombination)写出去就是 0 个子单元,存盘一次几何永久丢失 - clear() 只清 vector 不释放指针,内存里的 XPrimData 全部泄漏 - 存盘是只读语义,原代码却在写盘路径里修改了对象状态 改为按实际有效数量写出,不修改对象状态;跳过 NULL 并单独计数,避免 "写了数量但循环少写一条"造成读写错位。同时删掉定义后未使用的 int num。 没有按原方案把条件改成 &&,也没有在读入端加过滤:m_Combins 只有组合 单元才会非空,写端正确后不存在"非组合类型带子单元"的数据,保留枚举 判断等于把正确性押在类型比较上,和原来的错误同源。 受影响的图纸里组合设备的子单元已经被写没了,无法从 dwg 恢复,不做数据 迁移。文档同步更新 3.1、5.2、5.4、6、8。 --- AecDbEwEquipment-analysis.md | 37 ++++++++++++------- .../Code2026/CoreLibrary/AecDbEwEquipment.cpp | 24 ++++++++---- 2 files changed, 39 insertions(+), 22 deletions(-) diff --git a/AecDbEwEquipment-analysis.md b/AecDbEwEquipment-analysis.md index 1d74944..5015856 100644 --- a/AecDbEwEquipment-analysis.md +++ b/AecDbEwEquipment-analysis.md @@ -269,7 +269,7 @@ delete pEnt; // :4049 只删了 exp ## 3. 潜在缺陷 -### 3.1 P0:存盘时把组合设备的子单元清空 +### 3.1 P0:存盘时把组合设备的子单元清空(已修复,PR #48) ```cpp void dwgOutFieldsForXPrim(AecEqui::XPrimData *pSolid, ...) // :239 @@ -292,7 +292,15 @@ void dwgOutFieldsForXPrim(AecEqui::XPrimData *pSolid, ...) // :239 - 因为是就地 `clear()`,**当前内存里的对象也被破坏**,存完盘再画就没了 - 这段位于 `const` 的 `dwgOutFields` 调用链上,等于在"只读"函数里改数据 -影响面取决于图纸里是否还有 `kCombination` 单元(Revit 组合走的是 `kRevitCombination`,数据在 `m_pData` 里,不受影响)。但这段逻辑本身是明确的错误,应先修成 `&&`,并且把"容错"挪到读入或创建时,不要在存盘路径里改数据。 +**影响面(2026-10-23 核实)**:`kCombination` 仍在用,`PipelineCmd/EquipmentMakeDlg.cpp:4357` 创建组合设备时会把被组合设备的子单元逐个 `push_back` 进 `m_Combins`,`toJson:789` 与 `Draw3dSolid:3844` 也都按这个列表递归。也就是说这条不是"可能有用",而是**新建的组合设备只要存盘一次,几何就永久丢失**(Revit 组合走 `kRevitCombination`,数据在 `m_pData` 里,不受影响)。 + +**已修复(PR #48)**:删掉整段容错,改为按实际数量写出,不再修改对象状态。没有按原方案"修成 `&&`",也没有把容错挪到读入,原因: + +- `m_Combins` 只有组合单元才会非空,写端正确后读端不可能读到"非组合类型带子单元"的数据,容错没有作用对象 +- 写端一旦按 `m_eType` 过滤,就会重现"类型判断错→丢数据"的同类风险 +- 存盘函数改对象状态本身就是错的,无论条件对不对 + +顺带处理:跳过 `NULL` 指针并单独计数,避免"写了数量但循环少写一条"导致读写错位;删掉原来定义后未使用的 `int num`。 ### 3.2 P1:`subErase` 里 `Find` 当布尔用 @@ -549,16 +557,16 @@ Draw3dSolid(&dc, nColor); // :3062 拖拽时每帧都走到这里 另外补做的 4 处内存修复(2.6、2.7、2.9、2.10、2.11、`GetDraw3dables` 合并泄漏、`dwgInFields` 的 `RemoveAll`)见 2.13。 -### 5.2 第二批:`m_Combins` 存盘清空(需要确认影响面) +### 5.2 第二批:`m_Combins` 存盘清空(已完成) -| 步骤 | 内容 | -| --- | --- | -| 1 | 统计现有图纸里 `kCombination` 单元的实际使用(与阶段三那次一样,先看数据) | -| 2 | 修 `:279` 的恒真条件为 `!= kRevitCombination && != kCombination` | -| 3 | 把"容错清理"从 `dwgOutFields` 挪到 `dwgInFields` 读完之后 | -| 4 | 用含组合设备的样例做"存盘→重开→几何完整"验证 | +| 步骤 | 内容 | 状态 | +| --- | --- | --- | +| 1 | 确认 `kCombination` 的使用:`EquipmentMakeDlg.cpp:4357` 创建时填充,`toJson:789` / `Draw3dSolid:3844` 递归使用 | 已确认在用 | +| 2 | 删掉 `:279` 的恒真条件与 `m_Combins.clear()`,按实际有效数量写出 | 已完成(PR #48) | +| 3 | 不做读入容错:写端正确后不存在"非组合类型带子单元"的数据,加过滤反而可能误删 | 已取消 | +| 4 | 用含组合设备的样例做"存盘→重开→几何完整"验证 | 待实测 | -若确认图纸里已无 `kCombination`,可以只做 2、3,不做数据迁移。 +没有按原计划"修成 `&&`":`m_eType` 判断本身是多余的,`m_Combins` 只有组合单元才会非空;保留判断等于把正确性押在枚举比较上,和原来的错误同源。也没有数据迁移——受影响的图纸里组合设备的子单元已经被写没了,无法从 dwg 恢复,只能重建。 ### 5.3 第三批:性能(先量后改) @@ -575,7 +583,7 @@ Draw3dSolid(&dc, nColor); // :3062 拖拽时每帧都走到这里 1. ~~`booleanOper` 是否删除入参(3.15)~~ —— 2026-10-23 按「不删除入参」处理,理由见 3.15 2. `GetSectionDraw` 的返回值调用方到底期望什么(3.5) 3. `PreprocessForHlr` 的 `mat4` 双次变换是否刻意(3.12) -4. `kCombination` 单元的实际存量(5.2) +4. ~~`kCombination` 单元的实际存量(5.2)~~ —— 2026-10-25 确认仍在用,见 3.1 --- @@ -585,6 +593,7 @@ Draw3dSolid(&dc, nColor); // :3062 拖拽时每帧都走到这里 | --- | --- | | 夹点数组 / 析构释放 | 打开含 1000+ 设备的图纸,任务管理器看私有字节;关图后应回落 | | `releaseData` 统一 `delete[]` | 各类单元(box/cylinder/cone/Revit/组合)各删一个,确认无崩溃;开图→改参数→存盘→重开,几何完整 | +| `m_Combins` 存盘 | 新建组合设备 → 存盘 → 关图 → 重开,组合几何完整;再存盘一次仍完整(验证不是"只活一次") | | 剖切 / 2D 导出 | 对含阀门、法兰、普通设备的图各剖一次,反复开关剖切对话框,内存不持续上涨 | | `subDMesh2NewSolid` | 含 SubDMesh 的样例图,开图后单元数量与类型正确,绘制不缺体 | | `subErase` | 删除阀门/接头/普通设备各一次,确认只有阀门走合并管线 | @@ -608,7 +617,7 @@ Draw3dSolid(&dc, nColor); // :3062 拖拽时每帧都走到这里 ## 8. 实施顺序 1. ~~第一批 1~5(内存),单独一个 PR,可独立验证~~ —— 已完成,范围扩到 2.1~2.11 全部内存项,见 2.13 -2. 第一批 6~11(逻辑),单独一个 PR -3. 确认 5.4 剩下的三个问题 -4. 第二批(`m_Combins`),视存量决定是否做 +2. ~~第二批(`m_Combins` 存盘清空)~~ —— 已完成(PR #48),见 3.1、5.2 +3. 第一批 6~11(逻辑),单独一个 PR +4. 确认 5.4 剩下的两个问题 5. 第三批按 4.7 度量结果排序,一项一个 PR diff --git a/SourceCode/Code2026/CoreLibrary/AecDbEwEquipment.cpp b/SourceCode/Code2026/CoreLibrary/AecDbEwEquipment.cpp index ad15060..ae6ce47 100644 --- a/SourceCode/Code2026/CoreLibrary/AecDbEwEquipment.cpp +++ b/SourceCode/Code2026/CoreLibrary/AecDbEwEquipment.cpp @@ -275,16 +275,24 @@ namespace if (CurrentVer >= 5) { - // 做一下容错 - if (pSolid->m_eType != AecEqui::kRevitCombination || pSolid->m_eType != AecEqui::kRevitCombination) - pSolid->m_Combins.clear(); - - int num(pSolid->m_Combins.size()); - pFiler->writeInt32((Adesk::Int32)pSolid->m_Combins.size()); + //只有组合单元(kCombination)会带子单元。原来这里的判断写成 + //m_eType != kRevitCombination || m_eType != kRevitCombination,恒为真, + //任何单元存盘前都会被 clear()——组合设备写出去就是 0 个子单元, + //而且 clear() 不释放指针,内存里的对象也被破坏。 + //存盘是只读语义,这里只按实际数量写出,不修改对象状态。 + //(写端不再产生"非组合类型带子单元"的数据,读端不需要额外容错) + int nCount = 0; for (size_t i(0); i < pSolid->m_Combins.size(); ++i) { - AecEqui::XPrimData *pp(pSolid->m_Combins[i]); - dwgOutFieldsForXPrim(pSolid->m_Combins[i], pFiler, CurrentVer); + if (pSolid->m_Combins[i] != NULL) + ++nCount; + } + + pFiler->writeInt32((Adesk::Int32)nCount); + for (size_t i(0); i < pSolid->m_Combins.size(); ++i) + { + if (pSolid->m_Combins[i] != NULL) + dwgOutFieldsForXPrim(pSolid->m_Combins[i], pFiler, CurrentVer); } pFiler->writeString((LPCTSTR)pSolid->m_sName); } -- 2.54.0