using log4net; using Microsoft.Win32; using NPOI.SS.Formula.PTG; using NPOI.SS.UserModel; using NPOI.Util; using NPOI.XSSF.UserModel; using PdmSwPlugin.Commmon.Control; using PdmSwPlugin.Commmon.Util.UI; using PdmSwPlugin.Common; using PdmSwPlugin.Common.Constants; using PdmSwPlugin.Common.Control.TreeGrid; using PdmSwPlugin.Common.Entity.DrawAudit; using PdmSwPlugin.Common.Entity.Pdm; using PdmSwPlugin.Common.Entity.System; using PdmSwPlugin.Common.Interface; using PdmSwPlugin.Common.Setting; using PdmSwPlugin.Common.Util; using PdmSwPlugin.Common.Util.Http; using PdmSwPlugin.Common.Util.UI; using PdmSwPlugin.PDM.Constant; using PdmSwPlugin.PDM.Model; using SolidWorks.Interop.sldworks; using SolidWorks.Interop.swconst; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; using System.IO; using System.Linq; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Interop; using static PdmSwPlugin.PDM.Event.PdmEvent; namespace PdmSwPlugin.PDM { /// /// MainWindow.xaml 的交互逻辑 /// [PdmSwPlugin(Title = "PDM")] public partial class PdmControl : UserControl, IActiveDocChangeHandler { private static ILog Logger = LogManager.GetLogger("PDM"); SldWorks SwApp; private Model.PdmControlModel model; private readonly HttpClient Client; private string CurrentUserName; private readonly int MaxThreads; private ModelDoc2 OpenDoc { get; set; } private HttpClientCreator clientCreator { get; set; } private volatile PdmStatus workStatus; private TaskFactory taskFactory = LimitedConcurrencyLevelTaskSchedulerMsn.Factory; SldWorks ISwAppSetter.SwApp => SwApp; public PdmControl() : this(null) { } public PdmControl(SldWorks swAddin) { SwApp = swAddin; InitializeComponent(); // 滚动事件 // RaiseWheelEvent(); model = new Model.PdmControlModel(); model.bomTreeReader.SingleCompleted += BomTreeReader_SingleCompleted; DataContext = model; clientCreator = new HttpClientCreator(new HttpConfig( PluginSetting.Instance.TimeOut, PluginSetting.Instance.BaseAddress)); Client = clientCreator.GetClient(); CurrentUserName = System.Environment.UserName; PdmBom.InBomChanged += PdmBom_InBomChanged; InitDemonThread(); } private void PdmBom_InBomChanged(object sender, bool e) { //if (e) //{ // MaskAdorner.ShowMask(content, "请稍后..."); //} //else { // MaskAdorner.HideMask(content); //} MaskAdorner.ShowMask(content, "请稍后..."); Task.Run(() => { try { workStatus = PdmStatus.READING; PdmBom bom = sender as PdmBom; bom.UpdateInBom(e); RefreshBomTree(); } finally { MaskAdorner.HideMask(content); workStatus = PdmStatus.FREE; } }); } private void RefreshBomTree() { var cache = model.BomTree; model.BomTree = new ObservableCollection(); model.BomTree = cache; } /// /// 加载BOM结构时,单个BOM完成事件 /// /// /// private void BomTreeReader_SingleCompleted(object sender, PdmBom e) { MaskAdorner.ShowMessage(content, e.d3FilePath); } /// /// 刷新BOM列表 /// 内部消化异常,不往外发 /// public ObservableCollection RefreshBomList(ModelDoc2 doc) { OpenDoc = doc; try { if (doc == null) { this.Warning("请打开一张图纸"); return new ObservableCollection(); } //if (7 == doc.GetBlockingState()) //{ // this.Warning("请等待文档加载完毕"); // return new ObservableCollection(); //} DateTime beforeDT = DateTime.Now; model.DoRefresh(Client, SwApp, doc); DateTime now = DateTime.Now; double times = DateTime.Now.Subtract(beforeDT).TotalMilliseconds; return model.BomTree; } catch (Exception e) { // FillBomInfo(boms, bomInfos, drawInfos); model.BomTree = new ObservableCollection(); Logger.Error($"V{PdmUser.LoginUser.pluginVersion}, Refresh Bom List Failed.", e); this.Error($"V{PdmUser.LoginUser.pluginVersion},刷新失败:{e.Message}"); return new ObservableCollection(); } } public void AutoSaveAlert(List msgs) { //Dispatcher.Invoke(() => //{ // MultiExWindow exWin = new MultiExWindow(this, "以下文档因删除特定配置而被保存", msgs, "文件路径"); // ShowExWindow(exWin); //}); } /// /// 刷新按钮 /// /// /// private async void Refresh_Click(object sender, RoutedEventArgs e) { MaskAdorner.ShowMask(content, "请求中,请稍后..."); try { workStatus = PdmStatus.READING; Logger.Debug("PDM插件,刷新列表..."); // !!! Task.Run中不能抛出异常,不然会中断程序 // 这里一定要用Task.Run,不然会阻塞UI线程,导致遮罩层显示不出来 // 垃圾WPF await Task.Run(() => { RefreshBomList(SwApp.IActiveDoc2); if (model.bomTreeReader.needSaveList.Count > 0) { List msgs = new List(model.bomTreeReader.needSaveList); AutoSaveAlert(msgs); } }); Logger.Debug("PDM插件,列表刷新完成!"); } finally { workStatus = PdmStatus.FREE; MaskAdorner.HideMask(content); } } public void ValidateBoms(out string error) { error = null; ObservableCollection boms = model.BomTree; if (boms == null || boms.Count <= 0) { error = string.Empty; } List lostBoms = model.CacheList.Where(b => b.lost).Select(b => b.partModel).ToList(); if (lostBoms != null && lostBoms.Count > 0) { error = "[" + string.Join(",", lostBoms) + "]文件不存在!请检查"; } } /// /// 检出操作,同步方法,不关注校验,校验放外边 /// private object DoCheckOut() { ObservableCollection boms = model.BomTree; string bomId = boms[0].id; PdmBomParam param = new PdmBomParam { id = bomId, checkUserName = CurrentUserName }; Result res = Client.PostSyncAction(param, "wpf/bom/openApi/checkOut"); return res.HandleResult(); } /// /// 检出操作,同步方法,不关注校验,校验放外边 /// private Exception DoCheckOutAndRefresh() { try { _ = DoCheckOut(); RefreshBomList(SwApp.IActiveDoc2); return null; } catch (Exception e) { return e; } } /// /// 检入操作,同步方法 /// private object DoCheckIn() { Dictionary param = new Dictionary { { "treeList",model.BomTree }, { "list",model.CacheList}, { "checkInUsername",CurrentUserName} }; Result result = Client.PostSyncAction(param, "wpf/bom/openApi/checkIn"); return result.HandleResult(); } private Exception DoCheckInAndRefresh() { try { _ = DoCheckIn(); RefreshBomList(SwApp.IActiveDoc2); return null; } catch (Exception e) { return e; } } private bool SameDoc(out string message) { message = null; ModelDoc2 currentDoc = SwApp.IActiveDoc2; if (currentDoc.GetSaveFlag()) { message = "请先保存当前文档"; return false; } // 傻逼C#,直接=赋值的对象==返回false,qnmd弱智东西 if (OpenDoc == currentDoc) { return true; } string sb1 = OpenDoc?.GetPathName(); string sb2 = currentDoc?.GetPathName(); if (sb1 != sb2) { message = "当前BOM与图纸不对应,请刷新BOM列表"; return false; } return true; } private async void CheckOut_Click(object sender, RoutedEventArgs e) { try { MaskAdorner.ShowMask(content); if (!SameDoc(out string ErrMsg)) { this.Warning(ErrMsg); return; } ValidateBoms(out string message); if (message != null) { if (message != string.Empty) { this.Show(message, true); } return; } Logger.Debug("PDM插件,开始检出..."); ObservableCollection boms = model.BomTree; string bomId = boms[0].id; if (string.IsNullOrEmpty(bomId)) { this.Show("该BOM并未检入!", true); return; } // Task.Run中不能有未处理的异常,所以返回出来,放到主函数做异常处理 Exception error = await Task.Run(() => { return DoCheckOutAndRefresh(); }); if (error != null) { throw error; } Logger.Debug("PDM插件,检出完成!"); this.Show("检出成功", true); } catch (Exception ex) { Logger.Error($"V{PdmUser.LoginUser.pluginVersion},Check In Bom Failed.", ex); this.Error($"V{PdmUser.LoginUser.pluginVersion},检入Bom失败!{ex.Message}"); } finally { MaskAdorner.HideMask(content); } } private async void CheckIn_Click(object sender, RoutedEventArgs e) { try { Logger.Debug("PDM插件,开始检入..."); MaskAdorner.ShowMask(content); if (!SameDoc(out string ErrMsg)) { this.Warning(ErrMsg); return; } ValidateBoms(out string message); if (message != null) { if (message != string.Empty) { this.Show(message, true); } return; } Exception error = await Task.Run(() => { return DoCheckInAndRefresh(); }); if (error != null) { throw error; } Logger.Debug("PDM插件,检入完成!"); this.Show("检入成功", true); } catch (Exception ex) { Logger.Error($"V{PdmUser.LoginUser.pluginVersion}, Check In BOM Failed.", ex); this.Error($"V{PdmUser.LoginUser.pluginVersion},检入BOM失败!{ex.Message}"); } finally { MaskAdorner.HideMask(content); } } private async void CheckIn_Click2(object sender, RoutedEventArgs e) { try { MaskAdorner.ShowMask(content); ValidateBoms(out string message); if (message != null) { if (message != string.Empty) { this.Show(message, true); } return; } Dictionary param = new Dictionary { { "treeList",model.BomTree }, { "list",model.CacheList}, { "checkInUsername",CurrentUserName} }; MultipartFormDataContent httpContent = new MultipartFormDataContent { { new StringContent(JsonUtil.Serialize(param)), "sb" } }; List boms = model.GetModifiedBoms(); FileInfo fileInfo; foreach (PdmBom bom in boms) { fileInfo = new FileInfo(bom.d3FilePath); httpContent.Add(new ByteArrayContent(FileUtil.toByteArray(bom.d3FilePath)), bom.partModel + "_3d", fileInfo.Name); if (!string.IsNullOrEmpty(bom.d2FilePath) && File.Exists(bom.d2FilePath)) { fileInfo = new FileInfo(bom.d2FilePath); httpContent.Add(new ByteArrayContent(FileUtil.toByteArray(bom.d2FilePath)), bom.partModel + "_2d", fileInfo.Name); } if (!string.IsNullOrEmpty(bom.pdfFilePath) && File.Exists(bom.pdfFilePath)) { fileInfo = new FileInfo(bom.pdfFilePath); httpContent.Add(new ByteArrayContent(FileUtil.toByteArray(bom.pdfFilePath)), bom.partModel + "_pdf", fileInfo.Name); } } Result result = await Client.PostAsyncAction("wpf/bom/openApi/checkInWithFile", httpContent); object res = result.HandleResult(); RefreshBomList(SwApp.IActiveDoc2); this.Show("检入成功", true); } catch (Exception ex) { Logger.Error($"V{PdmUser.LoginUser.pluginVersion}, Check In Bom With File Failed.", ex); this.Error($"V{PdmUser.LoginUser.pluginVersion},检入失败!{ex.Message}"); } finally { MaskAdorner.HideMask(content); } } private async void CheckIn_Click3(object sender, RoutedEventArgs e) { try { MaskAdorner.ShowMask(content); if (!SameDoc(out string ErrMsg)) { this.Warning(ErrMsg); return; } ValidateBoms(out string message); if (message != null) { if (message != string.Empty) { this.Show(message, true); } return; } Logger.Debug("PDM插件,带图纸的检入开始..."); // 图纸变更过的BOM,已经按照型号筛选过 List modifiedBoms = model.GetModifiedBoms(); if (modifiedBoms.Count <= 0) { // 没有变更的图纸就直接检入,这里要用Task,否则蒙版不起作用 Exception err = await Task.Run(() => { return DoCheckInAndRefresh(); }); if (err != null) { throw err; } this.Show("检入成功", true); return; } // 多线程计数器 CountdownEvent counter = new CountdownEvent(modifiedBoms.Count); // 异常收集 Dictionary errors = new Dictionary(); if (MaxThreads <= 0) { await Task.Run(() => { SendFileToServerBatch(new object[] { modifiedBoms, counter, errors }); }); } else { // 线程数 int taskCount = MaxThreads; // 任务总数 int totalCount = modifiedBoms.Count; // 每个线程处理的任务数 int eachCount = totalCount / taskCount; // 余数 int otherCount = totalCount % taskCount; int handledCount = 0; for (int i = 0; i < taskCount; i++) { IEnumerable boms; if (i + 1 <= otherCount) { boms = modifiedBoms.Skip(handledCount).Take(eachCount + 1); } else { boms = modifiedBoms.Skip(handledCount).Take(eachCount); } handledCount += boms.Count(); await Task.Run(() => { SendFileToServerBatch(new object[] { boms, counter, errors }); }); } } //foreach (var bom in modifiedBoms) //{ // ThreadPool.QueueUserWorkItem(new WaitCallback(SendFileToServer), new object[] { bom, counter, errors }); //} counter.Wait(); counter.Dispose(); if (errors.Count > 0) { string Msg = ""; foreach (PdmBom bom in errors.Keys) { Exception err = errors[bom]; Logger.Error($"V{PdmUser.LoginUser.pluginVersion},[{bom.partModel}] Upload Draw Failed.", err); Msg += $"[{bom.partModel}]上传图纸错误!{err.Message}\r\n"; } this.Show(Msg); return; } Logger.Debug("PDM插件,带图纸的检入,图纸上传完成!正在检入..."); // Task.Run中不能有未处理的异常,所以返回出来,放到主函数做异常处理 Exception error = await Task.Run(() => { return DoCheckInAndRefresh(); }); if (error != null) { throw error; } Logger.Debug("PDM插件,带图纸的检入,检入完成!"); this.Show("检入成功", true); } catch (Exception ex) { Logger.Error($"V{PdmUser.LoginUser.pluginVersion}, Check In BOM With File Failed.", ex); this.Error($"V{PdmUser.LoginUser.pluginVersion},检入失败!{ex.Message}"); } finally { MaskAdorner.HideMask(content); } } private async void SendFileToServer(object state) { object[] args = (object[])state; PdmBom mainBom = (PdmBom)args[0]; BomInfo param = new BomInfo() { partModel = mainBom.partModel, d3FileId = mainBom.d3FileId, d3FilePath = mainBom.d3FilePath, d2FileId = mainBom.d2FileId, d2FilePath = mainBom.d2FilePath, pdfFileId = mainBom.pdfFileId, pdfFilePath = mainBom.pdfFilePath, checkUserName = CurrentUserName }; CountdownEvent counter = (CountdownEvent)args[1]; Dictionary errors = (Dictionary)args[2]; try { FileInfo fileInfo; var httpContent = new MultipartFormDataContent(); httpContent.Add(new StringContent(JsonUtil.Serialize(param)), "bomStr"); fileInfo = new FileInfo(param.d3FilePath); httpContent.Add(new ByteArrayContent(FileUtil.toByteArray(param.d3FilePath)), param.partModel + "_3d", fileInfo.Name); if (!string.IsNullOrEmpty(param.d2FilePath) && File.Exists(param.d2FilePath)) { fileInfo = new FileInfo(param.d2FilePath); httpContent.Add(new ByteArrayContent(FileUtil.toByteArray(param.d2FilePath)), param.partModel + "_2d", fileInfo.Name); } if (!string.IsNullOrEmpty(param.pdfFilePath) && File.Exists(param.pdfFilePath)) { fileInfo = new FileInfo(param.pdfFilePath); httpContent.Add(new ByteArrayContent(FileUtil.toByteArray(param.pdfFilePath)), param.partModel + "_pdf", fileInfo.Name); } Result result = await Client.PostAsyncAction("wpf/bom/openApi/updateBomFile", httpContent); BomInfo res = result.HandleResult(); mainBom._drawInfo.d3Md5 = res.d3Md5; mainBom._drawInfo.d2Md5 = res.d2Md5; mainBom._drawInfo.pdfMd5 = res.pdfMd5; mainBom._drawInfo.d3FileId = res.d3FileId; mainBom._drawInfo.d2FileId = res.d2FileId; mainBom._drawInfo.pdfFileId = res.pdfFileId; } catch (Exception e) { errors.Add(mainBom, e); } finally { _ = counter.Signal(); } } /// /// 批量上传图纸到PDM服务器 /// /// 参数 private void SendFileToServerBatch(object state) { object[] args = (object[])state; IEnumerable mainBoms = (IEnumerable)args[0]; CountdownEvent counter = (CountdownEvent)args[1]; Dictionary errors = (Dictionary)args[2]; foreach (PdmBom mainBom in mainBoms) { BomInfo param = new BomInfo() { partModel = mainBom.partModel, d3FileId = mainBom.d3FileId, d3FilePath = mainBom.d3FilePath, d2FileId = mainBom.d2FileId, d2FilePath = mainBom.d2FilePath, pdfFileId = mainBom.pdfFileId, pdfFilePath = mainBom.pdfFilePath, checkUserName = CurrentUserName }; try { string d3FilePath = param.d3FilePath; if (string.IsNullOrEmpty(d3FilePath) || !File.Exists(d3FilePath)) { throw new Exception($"未找到3D图纸"); } string d2FilePath = Path.Combine(Path.GetDirectoryName(d3FilePath), Path.GetFileNameWithoutExtension(d3FilePath) + ".SLDDRW"); if (string.IsNullOrEmpty(d2FilePath) || !File.Exists(d2FilePath)) { throw new Exception($"未找到工程图文件"); } FileInfo fileInfo; MultipartFormDataContent httpContent = new MultipartFormDataContent(); httpContent.Add(new StringContent(JsonUtil.Serialize(param)), "bomStr"); fileInfo = new FileInfo(d3FilePath); httpContent.Add(new ByteArrayContent(FileUtil.toByteArray(d3FilePath)), param.partModel + "_3d", fileInfo.Name); fileInfo = new FileInfo(d2FilePath); httpContent.Add(new ByteArrayContent(FileUtil.toByteArray(d2FilePath)), param.partModel + "_2d", fileInfo.Name); if (!string.IsNullOrEmpty(param.pdfFilePath) && File.Exists(param.pdfFilePath)) { fileInfo = new FileInfo(param.pdfFilePath); httpContent.Add(new ByteArrayContent(FileUtil.toByteArray(param.pdfFilePath)), param.partModel + "_pdf", fileInfo.Name); } Result result = Client.PostSyncAction("wpf/bom/openApi/updateBomFile", httpContent); BomInfo res = result.HandleResult(); mainBom._drawInfo.d3Md5 = res.d3Md5; mainBom._drawInfo.d2Md5 = res.d2Md5; mainBom._drawInfo.pdfMd5 = res.pdfMd5; mainBom._drawInfo.d3FileId = res.d3FileId; mainBom._drawInfo.d2FileId = res.d2FileId; mainBom._drawInfo.pdfFileId = res.pdfFileId; } catch (Exception e) { errors.Add(mainBom, e); } finally { _ = counter.Signal(); } } } /// /// 为未建料的物料生成建料Excel /// /// /// private void Excel_Click(object sender, RoutedEventArgs e) { if (!SameDoc(out string ErrMsg)) { this.Warning(ErrMsg); return; } List nos = model.CacheList.Where(b => b.needOrdered && b.inDb != true).ToList(); if (nos.Count <= 0) { this.Show("没有需要建料的物料", true); return; } try { Logger.Debug("PDM插件,正在导出新建物料..."); string targetPath; SaveFileDialog dialog = new SaveFileDialog(); dialog.Filter = "文件|*.xlsx"; dialog.Title = "建料申请"; dialog.DefaultExt = "文件|*.xlsx"; dialog.FileName = "建料申请.xlsx"; if (dialog.ShowDialog() == true) { targetPath = dialog.FileName; } else { Logger.Debug("PDM插件,用户取消导出"); return; } // 从资源中获取模板 byte[] template = Properties.Resources.Template; IWorkbook wb; using (ByteArrayInputStream rfs = new ByteArrayInputStream(template)) { wb = new XSSFWorkbook(rfs); rfs.Close(); } var sh = wb.GetSheetAt(0); HashSet models = new HashSet(); int startRow = 6; for (int i = 0; i < nos.Count; i++) { PdmBom bom = nos[i]; if (models.Contains(bom.partModel)) { continue; } models.Add(bom.partModel); sh.ShiftRows(startRow, startRow + 22, 1); IRow row = sh.CreateRow(startRow); IRow styleRow = sh.GetRow(startRow - 1); row.Height = styleRow.Height; row.CreateCell(0).SetCellValue(i + 1); row.CreateCell(2).SetCellValue("pcs"); row.CreateCell(5).SetCellValue(bom.partModel); row.CreateCell(6).SetCellValue(""); if (bom.jgj == true) { row.CreateCell(1).SetCellValue("A65"); string des = bom.partModel; if (bom.properties.ContainsKey(PropertyName.JGJ_TYPE)) { des += "-" + bom.properties[PropertyName.JGJ_TYPE]; } if (bom.properties.ContainsKey(PropertyName.STUFF_TYPE)) { des += "-" + bom.properties[PropertyName.STUFF_TYPE]; } row.CreateCell(3).SetCellValue(des); row.CreateCell(4).SetCellValue("加工件"); } else { row.CreateCell(1).SetCellValue(""); row.CreateCell(3).SetCellValue(""); row.CreateCell(4).SetCellValue(bom.partBrandName); } for (int j = 0; j <= 6; j++) { row.GetCell(j).CellStyle = styleRow.GetCell(j).CellStyle; } startRow++; } FileStream fs = new FileStream(targetPath, FileMode.Create); wb.Write(fs, false); wb.Close(); Logger.Debug("PDM插件,新建物料导出完成!"); System.Diagnostics.Process.Start(targetPath); } catch (Exception ex) { Logger.Error($"V{PdmUser.LoginUser.pluginVersion}, Export Material Excel Failed.", ex); this.Error($"V{PdmUser.LoginUser.pluginVersion},导出建料文档失败!{ex.Message}"); } } /// /// 从服务器获取BOM信息 /// /// 顶层BOM型号 /// 型号-Bom字典 private Dictionary GetBomsFromWeb(PdmBomParam param) { Result> result = Client.PostSyncAction>(param, "wpf/bom/openApi/bomInfo"); Dictionary bomInfo = result.HandleResult(); return bomInfo; } public void OnSwActiveDocChange(ModelDoc2 lastDoc, ModelDoc2 doc, Component2 comp) { //Dispatcher.Invoke(delegate //{ // logger.Debug("PDM插件,切换文档事件触发..."); // Button_Click(null, null); //}); } public void SetSwApp(SldWorks SwApp) { this.SwApp = SwApp; } private void GlobalCheckBox_Checked(object sender, RoutedEventArgs e) { CheckBox cb = sender as CheckBox; //PdmBom bom = cb.DataContext as PdmBom; //bom.selected = cb.IsChecked.Value; //List allBoms = model.bomTreeReader.CacheList.ToList(); //foreach (PdmBom bom in allBoms) //{ // if (bom.checkBoxEnabled) // { // bom.selected = cb.IsChecked.Value; // } //} List treeBoms = model.bomTreeReader.TreeList.ToList(); if (treeBoms == null || treeBoms.Count <= 0) return; treeBoms[0].selected = cb.IsChecked.Value; model.RefreshSelectedBomCount(); } private void SingleCheckBox_Event(object sender, RoutedEventArgs e) { model.RefreshSelectedBomCount(); } /// /// SolidWorks文档保存后触发 /// 下面俩参数我也没搞懂有啥用 /// /// 文档 /// 组件 public void OnSwActiveDocSaved(ModelDoc2 doc, Component2 comp) { // RefreshBomList(SwApp.IActiveDoc2); } private void treeDataGrid_LoadingRow(object sender, DataGridRowEventArgs e) { TreeItemData row = e.Row.Item as TreeItemData; PdmBom bom = row.Data as PdmBom; // 型材子料不可选 // 丢失的BOM不可选 e.Row.IsEnabled = !(bom.xcChild == true || bom.lost); } public void OnCustomPropertyChange(string propName, string Configuration, string oldValue, string NewValue, int valueType) { //throw new NotImplementedException(); } /// /// 处理质量,保留6位小数 /// /// /// public static string HandleMass(double number) { return Math.Round(number, 6).ToString("0.000000"); } public DrawInfo GetSingleDrawInfo(PdmBom bom) { ModelDoc2 sldDoc = bom.doc; Component2 component = bom.component; Dictionary allProperties = new Dictionary(); allProperties["物料型号"] = bom.partModel; DrawInfo drawInfo = bom._drawInfo; drawInfo.isHidden = bom.isHidden; drawInfo.allProperties = allProperties; allProperties["压缩"] = bom.suppressed.ToString(); allProperties["轻量化"] = bom.lightWeight.ToString(); string filepath = component.GetPathName(); // 文件是否存在 allProperties["图纸是否存在"] = "true"; // 带后缀的全名 allProperties["文件全称"] = Path.GetFileName(filepath); // 不带后缀的文件名 allProperties["文件名"] = Path.GetFileNameWithoutExtension(filepath); // 文件后缀 allProperties["文件后缀"] = Path.GetExtension(filepath); // 文件路径 allProperties["文件路径"] = filepath; allProperties["历史图纸"] = drawInfo.HistoryData.ToString(); string drawFilePath = filepath.Replace(Path.GetExtension(filepath), ".slddrw"); bool drawExists = File.Exists(drawFilePath); // 工程图一致性检查 allProperties["工程图是否存在"] = drawExists.ToString(); // 不跳过检查的加工件,如果工程图不存在,设置noDrw为true if (!drawExists && !bom.skipCheck && bom.produceWay == "加工件") { drawInfo.noDrw = true; } allProperties["md5"] = bom.localD3Md5; if (bom.lost || bom.BomInfo.isHidden || sldDoc == null) { allProperties["图纸是否存在"] = "false"; return drawInfo; } // 文件保存状态 allProperties["图纸保存状态"] = sldDoc.GetSaveFlag().ToString(); Dictionary properties = CustomPropertyUtil.GetCustomProperties2(sldDoc, true, new HashSet { "重量" }, null); // 图纸属性 if (properties != null) { foreach (string key in properties.Keys) { allProperties[key] = properties[key]; } } if (drawExists) { string[] refs = SwDMUtil.GetDrawingRef2(drawFilePath, out string err, out int[] status); string refPath = ""; if (refs != null && refs.Length > 0) { foreach (var rf in refs) { if (string.Equals(rf, filepath, StringComparison.CurrentCultureIgnoreCase)) { refPath = rf; refPath = Path.GetFileName(refPath); break; } } } else { Logger.Error($"V{PdmUser.LoginUser.pluginVersion}, [{drawFilePath}] Read slddrw Ref Path Failed.[{err}]"); } //string refPath = SwDMUtil.GetDrawingRef(drawFilePath, out string errMsg); //if (refPath == null) //{ // Logger.Error($"V{PdmUser.LoginUser.pluginVersion}, [{drawFilePath}] Read slddrw Ref Path Failed.[{errMsg}]"); //} //else //{ // refPath = Path.GetFileName(refPath); //} Logger.Debug($"V{PdmUser.LoginUser.pluginVersion},文档路径:【{filepath}】\r\n" + $"工程图路径:【{drawFilePath}】\r\n" + $"文件全称:【{allProperties["文件全称"]}】\r\n" + $"Result:【{string.Join(",", refs ?? new string[] { })}】\r\n" + $"Status:【{string.Join(",", status ?? new int[] { })}】\r\n" + $"引用路径:【{refPath}】"); allProperties["工程图引用文件"] = refPath; } // 质量属性 try { // IModelDocExtension ext = sldDoc.Extension; // double[] massPropertys = ext.GetMassProperties2(0, out int status, false); double[] massPropertys = SwDMUtil.GetMassProperty(filepath, out string errMSg); if (massPropertys == null) { throw new Exception(errMSg); } allProperties["体积"] = HandleMass(massPropertys[3]); allProperties["表面积"] = HandleMass(massPropertys[4]); allProperties["质量"] = HandleMass(massPropertys[5]); Logger.Debug(JsonUtil.Serialize(massPropertys)); } catch (Exception e) { Logger.Error($"V{PdmUser.LoginUser.pluginVersion},[{filepath}] Read Mass Properties Failed.", e); } allProperties["图纸类型"] = bom.produceWay;//bom.BomInfo.jgj; bom.BomInfo.properties = allProperties; return drawInfo; } public Dictionary GetAllPropertiesByTask(List ErrMsgs) { ConcurrentDictionary result = new ConcurrentDictionary(); PdmBom topBom = model.BomTree[0]; CountdownEvent counter = new CountdownEvent(1); GetAllByTask(new object[] { topBom, result, counter, ErrMsgs, 0 }); counter.Wait(); return new Dictionary(result); } public void GetAllByTask(object param) { object[] datas = param as object[]; PdmBom bom = (PdmBom)datas[0]; ModelDoc2 sldDoc = bom.doc; Component2 component = bom.component; ConcurrentDictionary result = (ConcurrentDictionary)datas[1]; CountdownEvent counter = (CountdownEvent)datas[2]; List ErrMsgs = (List)datas[3]; int level = (int)datas[4]; try { counter.AddCount(); string filepath = bom.partNo; lock (result) { if (result.ContainsKey(filepath)) { if (bom.isHidden) { result[filepath].isHidden = true; } return; } if (!bom.skipCheck) { result[filepath] = GetSingleDrawInfo(bom); if (bom.BomInfo.inKeeDeeDb == true) result[filepath]._inKeeDeeDb = true; } } ObservableCollection children = bom.modules; if (children != null) { foreach (PdmBom child in children) { taskFactory.StartNew(() => { GetAllByTask(new object[] { child, result, counter, ErrMsgs, 1 }); }); } } MaskAdorner.ShowMessage(content, filepath); } catch (Exception e) { bom.drawInfo.checkRuleResult = 2; bom.drawInfo.errMessages = new List { e.Message }; Logger.Error($"V{PdmUser.LoginUser.pluginVersion},[{bom.filePath}] Read MASS Properies Failed.", e); } finally { counter.Signal(); if (level == 0) { counter.Signal(); } } } public bool DoCheckRuleAsync(out List messages) { var temp = new List(); bool allSuccess = true; Dictionary properties = GetAllPropertiesByTask(temp); MaskAdorner.ShowMessage(content, "正在请求数据..."); // var factory = LimitedConcurrencyLevelTaskScheduler.Factory; CountdownEvent counter = new CountdownEvent(properties.Count); int iii = 0; var start = DateTime.Now; Logger.Debug("DoCheckRuleAsync Start..."); foreach (KeyValuePair property in properties) { /// Task.Run 这个方法最快,但不好管控,因为不知道属性字典有多大 /// taskFactory.StartNew 稳定一点吗? taskFactory.StartNew(() => { string key = property.Key; iii++; try { Logger.Debug($"{key} checking rule..."); DrawInfo checkResult; Result result = Client.PostSyncAction(property.Value, "wpf/bom/openApi/checkRuleSingle"); checkResult = result.HandleResult(); properties[key].checkRuleResult = checkResult.checkRuleResult; properties[key].errMessages = checkResult.errMessages; var msgs = checkResult.errMessages; if (property.Value.noDrw || property.Value.isHidden || (!properties[key].HistoryData && (!checkResult.checkRuleOk))) { allSuccess = false; } lock (temp) { if (property.Value.isHidden) { temp.Add($"物料【{key}】被设置为隐藏!"); } if (property.Value.noDrw && property.Value.HistoryData) { temp.Add($"物料【{key}】未找到工程图!"); } if (!properties[key].HistoryData) { if (msgs != null && msgs.Count > 0) { foreach (string msg in msgs) { temp.Add($"物料【{key}】未通过规则检查!{msg}"); } } } } MaskAdorner.ShowMessage(content, $"物料【{key}】检查完成"); } catch (Exception ex) { properties[key].checkRuleResult = 2; properties[key].errMessages = new List { "发起检查失败,请重试" }; lock (temp) { temp.Add($"物料【{key}】发起检查失败,请重试"); } allSuccess = false; MaskAdorner.ShowMessage(content, $"物料【{key}】检查完成"); } finally { counter.Signal(); } }); //LimitedConcurrencyLevelTaskScheduler scheduler = new LimitedConcurrencyLevelTaskScheduler(int.MaxValue); //Task.Factory.StartNew(() => { // string key = property.Key; // iii++; // try // { // Logger.Error($"{key} checking rule..."); // Result result = Client.PostSyncAction(property.Value, "wpf/bom/openApi/checkRuleSingle"); // DrawInfo checkResult = result.HandleResult(); // properties[key].checkRuleResult = checkResult.checkRuleResult; // properties[key].errMessages = checkResult.errMessages; // var msgs = checkResult.errMessages; // if (!properties[key].HistoryData && !checkResult.checkRuleOk) // { // allSuccess = false; // } // if (!properties[key].HistoryData && msgs != null && msgs.Count > 0) // { // lock (temp) // { // foreach (string msg in msgs) // { // temp.Add($"物料【{key}】未通过规则检查!{msg}"); // } // } // } // MaskAdorner.ShowMessage(content, $"物料【{key}】检查完成"); // } // catch (Exception ex) // { // properties[key].checkRuleResult = 2; // properties[key].errMessages = new List { "发起检查失败,请重试" }; // lock (temp) // { // temp.Add($"物料【{key}】发起检查失败,请重试"); // } // allSuccess = false; // MaskAdorner.ShowMessage(content, $"物料【{key}】检查完成"); // } // finally // { // counter.Signal(); // } //}, CancellationToken.None, TaskCreationOptions.None, scheduler); /// 下面两个都在管控Task数量,但还是很慢 /*string key = property.Key; _ = ThreadPool.QueueUserWorkItem( (WaitCallback)delegate { iii++; try { Logger.Error($"{key} checking rule..."); Result result = Client.PostSyncAction(property.Value, "wpf/bom/openApi/checkRuleSingle"); DrawInfo checkResult = result.HandleResult(); properties[key].checkRuleResult = checkResult.checkRuleResult; properties[key].errMessages = checkResult.errMessages; var msgs = checkResult.errMessages; if (!properties[key].HistoryData && !checkResult.checkRuleOk) { allSuccess = false; } if (!properties[key].HistoryData && msgs != null && msgs.Count > 0) { lock (temp) { foreach (string msg in msgs) { temp.Add($"物料【{key}】未通过规则检查!{msg}"); } } } MaskAdorner.ShowMessage(content, $"物料【{key}】检查完成"); } catch (Exception ex) { properties[key].checkRuleResult = 2; properties[key].errMessages = new List { "发起检查失败,请重试" }; lock (temp) { temp.Add($"物料【{key}】发起检查失败,请重试"); } allSuccess = false; MaskAdorner.ShowMessage(content, $"物料【{key}】检查完成"); } finally { counter.Signal(); } });*/ //factory.StartNew(() => //{ // string key = property.Key; // iii++; // try // { // Logger.Error($"{key} checking rule..."); // Result result = Client.PostSyncAction(property.Value, "wpf/bom/openApi/checkRuleSingle"); // DrawInfo checkResult = result.HandleResult(); // properties[key].checkRuleResult = checkResult.checkRuleResult; // properties[key].errMessages = checkResult.errMessages; // var msgs = checkResult.errMessages; // if (!properties[key].HistoryData && !checkResult.checkRuleOk) // { // allSuccess = false; // } // if (!properties[key].HistoryData && msgs != null && msgs.Count > 0) // { // lock (temp) // { // foreach (string msg in msgs) // { // temp.Add($"物料【{key}】未通过规则检查!{msg}"); // } // } // } // MaskAdorner.ShowMessage(content, $"物料【{key}】检查完成"); // } // catch (Exception ex) // { // properties[key].checkRuleResult = 2; // properties[key].errMessages = new List { "发起检查失败,请重试" }; // lock (temp) // { // temp.Add($"物料【{key}】发起检查失败,请重试"); // } // allSuccess = false; // MaskAdorner.ShowMessage(content, $"物料【{key}】检查完成"); // } // finally // { // counter.Signal(); // } //}); } counter.Wait(); counter.Dispose(); messages = temp; Logger.Debug($"DoCheckRuleAsync Ended in {(DateTime.Now - start).TotalMilliseconds}ms."); return allSuccess; } private void ShowExWindow(MultiExWindow exWin) { IntPtr winformWindow = Process.GetCurrentProcess().MainWindowHandle; if (winformWindow != null) new WindowInteropHelper(exWin) { Owner = winformWindow }; exWin.ShowDialog(); } /// /// 规则检查按钮点击事件 /// /// /// private void CheckRule_Click(object sender, RoutedEventArgs e) { if (SwApp.IActiveDoc2 == null) { RefreshBomList(null); return; } workStatus = PdmStatus.CHECKING; MaskAdorner.ShowMask(content, "检查中,请稍后..."); Task.Run(() => { try { RefreshBomList(SwApp.IActiveDoc2); if (model.bomTreeReader.needSaveList.Count > 0) { List infos = new List(model.bomTreeReader.needSaveList); AutoSaveAlert(infos); } if (!DoCheckRuleAsync(out List msgs)) { Dispatcher.Invoke(() => { MultiExWindow exWin = new MultiExWindow(this, "检查结果", msgs); ShowExWindow(exWin); }); } } catch (Exception ex) { Logger.Error($"V{PdmUser.LoginUser.pluginVersion}, PDM check rule failed.", ex); this.Error($"V{PdmUser.LoginUser.pluginVersion},图纸检查执行失败!{ex.Message}"); } finally { workStatus = PdmStatus.FREE; MaskAdorner.HideMask(content); } }); } /// /// 刷新WEB数据 /// public void RefreshWebBomInfo() { model.RefreshWebInfo(Client); } /// /// 发起审批按钮点击 /// /// /// public void DrawAudit_Click(object sender, RoutedEventArgs e) { if (SwApp.IActiveDoc2 == null) { RefreshBomList(null); return; } workStatus = PdmStatus.UPLOADING; MaskAdorner.ShowMask(content, "请求中,请稍等..."); Task.Run(() => { try { // 对所有的BOM做规则检查 bool allSuccess = DoCheckRuleAsync(out _); // 先获取所有的BOM HashSet cache = new HashSet(); // 然后筛选选中的BOM List boms = model.bomTreeReader.CacheList.Where(b => b.selected).ToList(); if (boms == null || boms.Count <= 0) { // 如果没有选中的BOM,那就是所有BOM boms = model.bomTreeReader.CacheList.ToList(); } // 筛选有图纸的,在物料明细中,未提交的,在物料明细表中的零件体加工件进行上传 boms = boms.Where(b => { if (!cache.Contains(b.partModel)) { cache.Add(b.partModel); return b.NeedAudit; } return false; }).ToList(); if (boms == null || boms.Count <= 0) { this.Warning("只有未提交过或发生变更的加工件图纸可以进行审核"); return; } if (!allSuccess) { // 如果有未通过的图纸,需要提示 var err = boms.Where(b => b.noDrw || b.isHidden || (!b.IsHistoryData && (!b.checkRuleOk))).ToList(); if (err != null && err.Count > 0) { List msgs = new List(); foreach (var ee in err) { if (ee.noDrw && ee.IsHistoryData) { msgs.Add($"物料【{ee.partNo}】未通过规则检查!未找到工程图"); } if (ee.drawInfo.isHidden) { msgs.Add($"物料【{ee.partNo}】未通过规则检查!被设为隐藏"); } if (ee.drawInfo.errMessages == null) { msgs.Add($"物料【{ee.partNo}】未通过规则检查!"); continue; } foreach (string msg in ee.drawInfo.errMessages) { msgs.Add($"物料【{ee.partNo}】未通过规则检查!{msg}"); } } Dispatcher.Invoke(() => { MultiExWindow exWin = new MultiExWindow(this, "多个待提交的图纸未通过规则检查,请确认", msgs); ShowExWindow(exWin); }); return; } } // 都通过了,再进行审批 // 筛选非特殊的审核 boms = boms.Where(b => !b._drawInfo.isSpecial).ToList(); if (boms == null || boms.Count <= 0) return; // 先申请单号 DrawAuditOrder daOrder = new DrawAuditOrder { requestUserId = PdmUser.LoginUser.id, requestUser = PdmUser.LoginUser.realname }; try { Result res = Client.PostSyncAction(daOrder, "drawAudit/order/create"); daOrder = res.HandleResult(); } catch (Exception ex) { Logger.Error($"V{PdmUser.LoginUser.pluginVersion}, Request AuditOrder Code Failed.", ex); this.Error($"V{PdmUser.LoginUser.pluginVersion},请求审批单号失败! {ex.Message}"); return; } ConcurrentQueue errs = new ConcurrentQueue(); CountdownEvent counter = new CountdownEvent(boms.Count); int successCount = 0; ConcurrentQueue pathCache = new ConcurrentQueue(boms.Select(b => b.filePath).ToList()); foreach (PdmBom bom in boms) { taskFactory.StartNew(() => { try { MaskAdorner.ShowMessage(content, $"正在上传【{bom.partNo}】..."); UploadDraw(daOrder, bom, pathCache); Interlocked.Increment(ref successCount); } catch (Exception ex) { Logger.Error($"V{PdmUser.LoginUser.pluginVersion}, [{bom.partModel}] Request Audit Failed.", ex); errs.Enqueue($"物料【{bom.partModel}】发起审批失败!{ex.Message}"); } finally { counter.Signal(); } }); } counter.Wait(); counter.Dispose(); if (successCount > 0) { try { var param = new DrawAudit { orderId = daOrder.id, count = successCount }; var msgRes = Client.PostSyncAction(param, "drawAudit/sendToAuditor"); msgRes.HandleResult(); } catch (Exception ig) { Logger.Error($"V{PdmUser.LoginUser.pluginVersion}, Send Audit Socket Failed.", ig); } } // 刷新服务器上的BOM信息 RefreshWebBomInfo(); if (errs.Count > 0) { Dispatcher.Invoke(() => { MultiExWindow exWindow = new MultiExWindow(this, "审批失败", errs.ToList()); exWindow.ShowDialog(); }); } } catch (Exception ex) { Logger.Error($"V{PdmUser.LoginUser.pluginVersion}, Request audit failed", ex); this.Error($"V{PdmUser.LoginUser.pluginVersion},发起审批异常!{ex}"); } finally { workStatus = PdmStatus.FREE; MaskAdorner.HideMask(content); } }); } /// /// 上传图纸操作 /// /// /// private void UploadDraw(DrawAuditOrder daOrder, PdmBom bom, ConcurrentQueue pathCache) { if (bom.BomInfo.drawingType == (int)swDocumentTypes_e.swDocASSEMBLY && bom.children != null && bom.children.Count > 0) { foreach (PdmBom child in bom.children) { UploadChildDraw(daOrder, child, true, pathCache); } } UploadSingleDraw(daOrder, bom, false); } /// /// 上传子零件图纸,有递归 /// /// /// /// private void UploadChildDraw(DrawAuditOrder daOrder, PdmBom bom, bool justUpload, ConcurrentQueue pathCache) { if (bom.BomInfo.drawingType == (int)swDocumentTypes_e.swDocASSEMBLY && bom.children != null && bom.children.Count > 0) { foreach (PdmBom child in bom.children) { UploadChildDraw(daOrder, child, justUpload, pathCache); } } if (!pathCache.Contains(bom.filePath)) { UploadSingleDraw(daOrder, bom, justUpload); pathCache.Append(bom.filePath); } } /// /// 上传单张图纸 /// /// /// /// /// private void UploadSingleDraw(DrawAuditOrder daOrder, PdmBom bom, bool justUpload) { try { string d3FilePath = bom.d3FilePath; string d2FilePath = d3FilePath.Replace(Path.GetExtension(d3FilePath), ".slddrw"); if (string.IsNullOrEmpty(d3FilePath) || !File.Exists(d3FilePath)) { throw new Exception($"[{bom.partModel}]未找到3D图纸"); } // !justUpload(需要审核)要判断2D图纸存不存在 if (!justUpload && (string.IsNullOrEmpty(d2FilePath) || !File.Exists(d2FilePath))) { throw new Exception($"[{bom.partModel}]未找到工程图纸"); } MultipartFormDataContent httpContent = new MultipartFormDataContent(); httpContent.Add(new StringContent(bom.partNo), "materialCode"); httpContent.Add(new StringContent(bom.partNo), "materialModel"); httpContent.Add(new StringContent(daOrder.id), "orderId"); httpContent.Add(new StringContent(justUpload.ToString()), "justUpload"); FileInfo fileInfo = new FileInfo(d3FilePath); httpContent.Add(new ByteArrayContent(FileUtil.toByteArray(d3FilePath)), "d3File", fileInfo.Name); // !justUpload(需要审核)要上传2D图 if (!justUpload) { FileInfo file2Info = new FileInfo(d2FilePath); httpContent.Add(new ByteArrayContent(FileUtil.toByteArray(d2FilePath)), "d2File", file2Info.Name); } Result res = Client.PostSyncAction("drawAudit/add", httpContent); DrawAudit da = res.HandleResult(); } catch (NullReferenceException nex) { Logger.Error($"UploadSingleDraw NullReferenceException.", nex); } } private void Exclude_Click(object sender, RoutedEventArgs e) { if (SwApp.IActiveDoc2 == null) { RefreshBomList(null); return; } if (SwApp.SendMsgToUser2("确定将未通过的物料全部排除物料明细表吗?", (int)swMessageBoxIcon_e.swMbWarning , (int)swMessageBoxBtn_e.swMbYesNo) == (int)swMessageBoxResult_e.swMbHitYes) { MaskAdorner.ShowMask(content, "操作中,请稍后..."); Task.Run(() => { try { workStatus = PdmStatus.READING; List boms = model.bomTreeReader.CacheList.Where(b => b.BomInfo.status == "rejected") .ToList(); if (boms == null || boms.Count <= 0) { this.Warning("没有未通过的物料"); return; } Dispatcher.Invoke(() => { foreach (PdmBom bom in boms) { bom.UpdateInBom(true); } RefreshBomTree(); }); } finally { workStatus = PdmStatus.FREE; MaskAdorner.HideMask(content); } }); } } /// /// 树数据展示过滤 /// /// /// private void CollectionViewSource_Filter(object sender, FilterEventArgs e) { if (e.Item is TreeItemData data) { e.Accepted = data.IsVisible; } } /// /// 树展开/关闭事件 /// /// /// private void treeDataGrid_ExpandedChange(object sender, bool e) { CollectionViewSource cvs = this.Resources["TreeGridFilter"] as CollectionViewSource; cvs?.View.Refresh(); } /// /// 双击打开检查结果 /// /// /// private void TextBox_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e) { try { // 检查是否是双击 if (e.ClickCount == 2) { TextBlock textBlock = sender as TextBlock; PdmBom bom = textBlock.DataContext as PdmBom; if (bom.checkRuleResult == 0) { this.Warning("该图纸还未检查"); return; } List errs = new List(); if (bom.noDrw && bom.IsHistoryData) { errs.Add("工程图不存在 "); } if (bom.isHidden) { errs.Add("文档被设置为隐藏"); } if (bom.drawInfo.errMessages != null) { errs.AddRange(bom.drawInfo.errMessages); } if (errs.Count > 0) { MultiExWindow exWin = new MultiExWindow(this, "检查结果", errs); ShowExWindow(exWin); } else if (bom.checkRuleResult == 3) { string title = string.Empty; if (bom.IsHistoryData) { title = "历史图纸无需检查"; } else if (bom.ExcludeFromBOM) { title = "不在物料明细表中的物料无需检查"; } else if (bom.component?.IsVirtual == true) { title = "虚拟件无需检查"; } else { title = "无需检查"; } this.Info(title); return; } else if (bom.checkRuleOk && !bom.isHidden) { this.Show("规则检查通过"); } } } catch (Exception ex) { Logger.Error("Double Click CheckRule Exception.", ex); this.Error($"未知异常:{ex.Message}"); } } /// /// 双击打开审核详情 /// /// /// private void AuditTextBox_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e) { try { // 检查是否是双击 if (e.ClickCount == 2) { TextBlock textBlock = sender as TextBlock; PdmBom bom = textBlock.DataContext as PdmBom; if (bom._drawInfo.isSpecial) { this.Info("无需审核"); return; } if (bom.BomInfo.inKeeDeeDb == true) { this.Info("已入系统"); return; } if (string.IsNullOrEmpty(bom.BomInfo.status)) { if (bom.produceWay == "模组" || bom.produceWay == "标准件") { this.Info($"{bom.produceWay}无需审核"); return; } if (bom.IsHistoryData && !bom.localDocChanged) { this.Info("历史图纸无需审核"); return; } if (bom.IsHistoryData && bom.localDocChanged) { this.Info("历史图纸一致性变更,需审核"); return; } this.Warning("该图纸未审核"); return; } MaskAdorner.ShowMask(content, "请求中,请稍后..."); Task.Run(() => { try { Result> res = Client.GetSyncAction>("drawAudit/listTaskHis", new DrawAudit { id = bom.BomInfo.id }); var datas = res.HandleResult(); Dispatcher.Invoke(() => { //DrawAuditHisWindow window = new DrawAuditHisWindow(this, $"【{bom.partModel}】审核详情", datas); RichHisWindow window = new RichHisWindow(this, $"【{bom.partModel}】审核详情", datas); window.ShowDialog(); }); } catch (Exception ex) { Logger.Error($"V{PdmUser.LoginUser.pluginVersion}, Get draw audit history failed.", ex); this.Error($"V{PdmUser.LoginUser.pluginVersion},获取审核历史失败!{ex.Message}"); } finally { MaskAdorner.HideMask(content); } }); } } catch (Exception ex) { Logger.Error("Double Click Audit Status Exception.", ex); this.Error($"未知异常:{ex.Message}"); } } private volatile IntPtr importPtr = IntPtr.Zero; private volatile IntPtr checkInPtr = IntPtr.Zero; private readonly object importLock = new object(); private readonly object checkInLock = new object(); // 捕捉到插件启动事件 private event PtrHandler PtrCheckHandler; /// /// 搜索是否有导入窗口,有就一直最小化,并执行规则检查 /// /// private async Task ListenImportHandle() { IntPtr ptr = WindowIntPtrUtil.FindWindow(null, PluginSetting.Instance.ImportListenName); if (ptr == IntPtr.Zero) { return; } if (ptr != importPtr) { WindowIntPtrUtil.ShowWindow(ptr, 6); lock (importLock) { importPtr = ptr; PtrCheckHandler += ImportHandler; } await PtrHandler2(); } else if (workStatus == PdmStatus.DO_LISTENING) { WindowIntPtrUtil.ShowWindow(ptr, 6); } } /// /// 规则检查执行完之后的操作,成功就激活导入窗口,失败就关闭导入窗口 /// /// /// /// private void ImportHandler(List errs, Exception ex, bool success) { lock (importLock) { if (success) { WindowIntPtrUtil.ShowWindow(importPtr, 1); } else { WindowIntPtrUtil.KillProcessByIntPtr(importPtr); importPtr = IntPtr.Zero; } PtrCheckHandler -= ImportHandler; } } /// /// 搜索是否有检入窗口,有就一直最小化,并执行规则检查 /// /// private async Task ListenCheckInHandle() { IntPtr ptr = WindowIntPtrUtil.FindWindow(null, PluginSetting.Instance.CheckInListenName); if (ptr == IntPtr.Zero) { return; } if (ptr != checkInPtr) { WindowIntPtrUtil.ShowWindow(ptr, 6); lock (checkInLock) { checkInPtr = ptr; PtrCheckHandler += CheckInWindowHandler; } await PtrHandler2(); } else if (workStatus == PdmStatus.DO_LISTENING) { WindowIntPtrUtil.ShowWindow(ptr, 6); } } /// /// 规则检查执行完之后的操作,成功就激活导入窗口,失败就关闭导入窗口 /// /// /// /// private void CheckInWindowHandler(List errs, Exception ex, bool success) { lock (checkInLock) { if (success) { WindowIntPtrUtil.ShowWindow(checkInPtr, 1); } else { WindowIntPtrUtil.KillProcessByIntPtr(checkInPtr); checkInPtr = IntPtr.Zero; } PtrCheckHandler -= CheckInWindowHandler; } } private async Task PtrHandler2() { if (workStatus == PdmStatus.FREE) { workStatus = PdmStatus.DO_LISTENING; MaskAdorner.ShowMask(content, "检查中,请稍后..."); await Task.Run(() => { try { // 执行规则检查并获取结果 RefreshBomList(SwApp.IActiveDoc2); if (model.bomTreeReader.CacheList.Count <= 0) { throw new CantCheckInException(new List { "请打开一张图纸" }); } if (model.bomTreeReader.needSaveList.Count > 0) { List infos = new List(model.bomTreeReader.needSaveList); AutoSaveAlert(infos); } DoCheckRuleAsync(out _); // 筛选所有在物料明细表中,不跳过检查并且检查未通过的,类型是加工件的未通过审核的,加工件审核通过的但图纸变更的 List errs = model.bomTreeReader.CacheList.Select(b => b.NeedStopCheckIn()) .Where(s => s != null).ToList(); if (errs != null && errs.Count > 0) { throw new CantCheckInException(errs); } PtrCheckHandler?.Invoke(null, null, true); } catch (CantCheckInException cex) { PtrCheckHandler?.Invoke(cex.errs, null, false); Dispatcher.Invoke(() => { MultiExWindow exWin = new MultiExWindow(this, "检入停止", cex.errs); exWin.ShowDialog(); }); } catch (Exception ex) { PtrCheckHandler?.Invoke(null, ex, false); Logger.Error($"V{PdmUser.LoginUser.pluginVersion}, PDM check rule failed.", ex); this.Error($"V{PdmUser.LoginUser.pluginVersion},图纸检查执行失败:{ex.Message}"); } finally { workStatus = PdmStatus.FREE; MaskAdorner.HideMask(content); } }); } else if (workStatus != PdmStatus.DO_LISTENING) { PtrCheckHandler?.Invoke(null, null, false); if (workStatus == PdmStatus.CHECKING) { this.Info("正在进行规则检查,请勿检入"); } else if (workStatus == PdmStatus.READING) { this.Info("正在加载,请勿检入"); } else if (workStatus == PdmStatus.UPLOADING) { this.Info("正在上传图纸,请勿检入"); } } } private Task importListenTask; private Task checkInListenTask; /// /// 开启插件监听线程 /// public void InitDemonThread() { importListenTask = Task.Run(() => { while (true) { // 监听导入插件的启动 ListenImportHandle(); Thread.Sleep(PluginSetting.Instance.ScanInterval); } }); checkInListenTask = Task.Run(() => { while (true) { // 监听检入插件的启动 ListenCheckInHandle(); Thread.Sleep(PluginSetting.Instance.ScanInterval); } }); } public void OnDocDestroy(ModelDoc2 doc) { } public void AfterDocDestroy() { } public void DisabledHandler() { } } }