chr
2025-03-04 3f62d18e4361cd1d7a49c126765d95b2ad9c8246
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
using PdmSwPlugin.Common.Entity.Pdm;
using SolidWorks.Interop.sldworks;
using SolidWorks.Interop.swconst;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
 
namespace PdmSwPlugin.Common.Util.Pdm
{
    public class BomTreeReader
    {
        public BomTreeReader()
        {
            factory = LimitedConcurrencyLevelTaskSchedulerMsn.Factory;
        }
 
        public TaskFactory factory;
        /// <summary>
        /// 全部BOM的平铺List
        /// </summary>
        public ConcurrentQueue<PdmBom> CacheList { get; private set; } = new ConcurrentQueue<PdmBom>();
 
        /// <summary>
        /// 插件展示的TreeBom
        /// </summary>
        public ConcurrentQueue<PdmBom> TreeList { get; private set; } = new ConcurrentQueue<PdmBom>();
 
        /// <summary>
        /// 每次刷新时的缓存
        /// </summary>
        private ConcurrentDictionary<string, BomInfo> BomInfoCache { get; set; }
 
        private CancellationTokenSource CancellationSwitch { get; set; }
 
        public event EventHandler<PdmBom> SingleCompleted;
 
        private int bomOrder = 0;
 
        public ConcurrentQueue<string> needSaveList { get; set; } = new ConcurrentQueue<string>();
 
        /// <summary>
        /// 多线程从打开的根文档中读取BOM信息
        /// </summary>
        /// <returns>树型BOM列表</returns>
        public void GetBomsFromDocAsync(ModelDoc2 sldDoc)
        {
            bomOrder = 0;
            if (sldDoc == null)
            {
                return;
            }
 
            int drawingType = sldDoc.GetType();
            Configuration activeConfiguration = sldDoc.GetActiveConfiguration() as Configuration;
            if (activeConfiguration == null)
            {
                return;
            }
            Component2 rootComponent = activeConfiguration.GetRootComponent3(false);
            string path = rootComponent.GetPathName();
 
            CustomPropertyUtil.ClearAllCustomProperties(sldDoc, out bool needSave);
            if (needSave)
            {
                int err = 0, warn = 0;
                if (sldDoc.Save3((int)swSaveAsOptions_e.swSaveAsOptions_AvoidRebuildOnSave, ref err, ref warn))
                {
                    needSaveList.Enqueue(path);
                }
                else
                {
                    needSaveList.Enqueue($"{path}自动保存失败,异常代码:{err}");
                }
            }
 
            BomInfo bomInfo = GetBomInfoFromDoc(rootComponent);
            PdmBom bom = ConcatCacheBom(bomInfo, 0, null, rootComponent);
            // ConcatBom 可能会返回null,说明这是个模拟件
            if (bom == null)
            {
                return;
            }
            BomInfoCache[rootComponent.GetPathName()] = bomInfo;
            CacheList.Enqueue(bom);
            TreeList.Enqueue(bom);
            SingleCompleted?.Invoke(this, bom);
            bom.children = new ObservableCollection<PdmBom>();
            // 这里放个1,多线程说不准,就扣的快了
            CountdownEvent counter = new CountdownEvent(1);
            // counter.Add 要在每个子任务前执行,不能放到子任务中进行!!!
            // 防止任务数太少,来不及增加计数导致直接Wait结束
            counter.TryAddCount();
 
            factory.StartNew(() =>
            {
                GetChildrenBomAsync(bom, rootComponent, 0, bom.children, counter);
            });
            counter.Wait();
            counter.Dispose();
        }
 
        /// <summary>
        /// 多线程递归获取子BOM
        /// </summary>
        /// <param name="param"></param>
        private void GetChildrenBomAsync(PdmBom parent,
            Component2 parentComponent,
            int level,
            Collection<PdmBom> children,
            CountdownEvent counter)
        {
            try
            {
                if (parentComponent == null)
                {
                    parent.modules = new ObservableCollection<PdmBom>();
                    return;
                }
 
                object[] childrenComponents = parentComponent.GetChildren();
                if (childrenComponents == null)
                {
                    parent.modules = new ObservableCollection<PdmBom>();
                    return;
                }
 
                // 计算每个子料数量的缓存
                Dictionary<string, PdmBom> numCache = new Dictionary<string, PdmBom>();
                List<PdmBom> modules = new List<PdmBom>();
                int order = -1;
                foreach (object child in childrenComponents)
                {
                    Component2 childComponent = child as Component2;
                    ModelDoc2 childDoc = childComponent.GetModelDoc2();
                    string childPath = childComponent.GetPathName();
                    // 清除格式不对的属性
                    CustomPropertyUtil.ClearAllCustomProperties(childDoc, out bool needSave);
                    if (needSave)
                    {
                        int err = 0, warn = 0;
                        if (childDoc.Save3((int)swSaveAsOptions_e.swSaveAsOptions_AvoidRebuildOnSave, ref err, ref warn))
                        {
                            needSaveList.Enqueue(childPath);
                        }
                        else
                        {
                            needSaveList.Enqueue($"{childPath}自动保存失败,异常代码:{err}");
                        }
                    }
 
                    PdmBom bom = null;
                    if (BomInfoCache.ContainsKey(childPath))
                    {
                        BomInfo info = BomInfoCache[childPath];
                        bom = ConcatCacheBom(info, level + 1, parent, childComponent);
                        // 就算有缓存,也要把新bom的treeId更新了,id在这里不一定唯一,tree是唯一的
                        bom.treeId = Guid.NewGuid().ToString();
                    }
                    else
                    {
                        BomInfo info = GetBomInfoFromDoc(childComponent);
                        if (info == null)
                        {
                            // 可能会返回null,说明这是个模拟件,直接跳过
                            continue;
                        }
                        BomInfoCache[childPath] = info;
                        bom = ConcatCacheBom(info, level + 1, parent, childComponent);
                    }
                    if (!bom.lost)
                    {
                        // // 如果bom没有丢失,处理子料
                        if (bom.drawingType == (int)swDocumentTypes_e.swDocASSEMBLY)
                        {
                            bom.children = new ObservableCollection<PdmBom>();
                            counter.TryAddCount();
                            factory.StartNew(() =>
                            {
                                GetChildrenBomAsync(bom, childComponent, bom.level, bom.children, counter);
                            });
                        }
                        else
                        {
                            bom.modules = new ObservableCollection<PdmBom>();
                        }
                    }
                    // 列表缓存
                    bom.order = ++order;
                    CacheList.Enqueue(bom);
                    modules.Add(bom);
                    SingleCompleted?.Invoke(this, bom);
 
                    // 丢没丢都要处理数量
                    if (numCache.ContainsKey(bom.name))
                    {
                        numCache[bom.name].quantity++;
                    }
                    else
                    {
                        numCache[bom.name] = bom;
                        children.Add(bom);
                    }
                }
 
                if (modules != null && modules.Count > 0)
                {
                    parent.modules = new ObservableCollection<PdmBom>(modules.OrderBy(e => e.name).ToList());
                    return;
                }
                parent.modules = new ObservableCollection<PdmBom>();
            }
            finally
            {
                counter.Signal();
                if (level == 0) counter.Signal();
            }
        }
 
        public List<PdmBom> Refresh(SldWorks swApp, ModelDoc2 doc)
        {
            if (swApp == null || doc == null)
            {
                return null;
            }
            CacheList = new ConcurrentQueue<PdmBom>();
            TreeList = new ConcurrentQueue<PdmBom>();
            BomInfoCache = new ConcurrentDictionary<string, BomInfo>();
            needSaveList = new ConcurrentQueue<string>();
            GetBomsFromDocAsync(doc);
            return TreeList.OrderBy(e => e.name).ToList(); ;
        }
        /// <summary>
        /// 从WEB获取与Component2对应的数据库中的物料信息
        /// </summary>
        /// <param name="component"></param>
        /// <returns></returns>
        private static BomInfo GetBomInfoFromDoc(Component2 component)
        {
            ModelDoc2 doc = component.GetModelDoc2();
            // 直接用文件名,以免 SolidWorks 因为有多个相同文件而把 Name2 加了 -N 的后缀名
            string childPath = component.GetPathName();
            string childName = System.IO.Path.GetFileNameWithoutExtension(childPath);
 
            if (ModelUtil.Is_Mn(childName))
            {
                // 如果是个模拟件,直接跳过了
                return null;
            }
            BomInfo bomInfo;
 
            // id先用uuid填充
            if (doc == null || string.IsNullOrEmpty(childPath) || !File.Exists(childPath))
            {
                bomInfo = new BomInfo
                {
                    id = Guid.NewGuid().ToString(),
                    doc = doc,
                    lost = true,
                    suppressed = component.IsSuppressed(),
                    lightWeight = (component.GetSuppression2() == (int)swComponentSuppressionState_e.swComponentLightweight),
                };
                return bomInfo;
            }
            int drawingType = doc.GetType();
            bomInfo = new BomInfo
            {
                id = Guid.NewGuid().ToString(),
                doc = doc,
                drawingType = drawingType,
                properties = CustomPropertyUtil.GetCustomProperties2(doc, true, null, null),
                d3FilePath = childPath,
            };
            return bomInfo;
        }
 
        /// <summary>
        /// 拼接WEB中BomInfo信息和本地图纸中的Pdmbom信息
        /// </summary>
        /// <param name="info"></param>
        /// <param name="level"></param>
        /// <param name="parent"></param>
        /// <returns></returns>
        private static PdmBom ConcatCacheBom(BomInfo info, int level, PdmBom parent, Component2 component)
        {
            if (info == null) return null;
            //if (component.IsHidden(true))
            //{
            //    info.isHidden = true;
            //}
            PdmBom bom = new PdmBom
            {
                BomInfo = info,
                treeId = info.id,
                level = level,
                quantity = 1,
                parent = parent,
                component = component,
                isHidden = component.IsHidden(true)
            };
            // 封装Bom信息
            bom.Init();
            return bom;
        }
    }
}