球標自動化:從依賴鏈到批量創(chuàng)建的完整實踐)
做了這么多年Creo二次開發(fā)我最大的感觸是出圖環(huán)節(jié)里最磨人的往往不是建模而是那些看起來毫無技術(shù)含量的球標標注。一個三百來件的大裝配BOM表生成出來很快但球標一個一個手動點、一個一個挪位置能讓人從下午三點坐到晚上十點。這也是我當初下定決心寫球標函數(shù)工具的原因。這篇東西我不打算寫成一個API手冊式的流水賬而是想聊聊我自己對球標自動化這件事的理解球標在Creo里到底是什么、調(diào)用鏈該怎么設(shè)計、批量創(chuàng)建時有哪些坑是文檔里不會告訴你的。如果你是剛接觸Creo二次開發(fā)的工程師或者正被裝配圖球標折磨得夠嗆的工藝/設(shè)計同事這篇文章應(yīng)該能幫你少走不少彎路。1. 球標標注為什么是工程圖出圖的頭號耗時項先說清楚球標是什么。在工程圖上球標就是那個帶引線的圓圈圓圈里寫序號引線指向某個零件。它的序號并不是隨便編的而是和BOM表在Creo里通常叫重復(fù)區(qū)域表里的行號一一對應(yīng)。也就是說圖上每個球標背后都對應(yīng)著明細欄里的某一行零件信息。手動放球標的問題在于它不是放完就完事了而是放了以后基本都要再調(diào)一頓。調(diào)整的理由五花八門引線交叉零件多了球標扎堆引線繞成一團圖面沒法看。序號與BOM行對不上模型改了一版某個零件刪掉了后面的序號全部順延手動改一批圓圈里的數(shù)字。標準和風(fēng)格不統(tǒng)一有的球標是圓的有的是橢圓的有的引線連在邊上有的連在面上。看著鬧心審圖的人也挑毛病。視圖更新后球標漂移或者丟失模型改了視圖重新生成辛辛苦苦擺好的位置全部白費。我見過很多公司對球標的處理方式就是認了因為覺得改球標是不可避免的重復(fù)勞動。但仔細想想這些操作其實高度規(guī)律根據(jù)BOM表行號確定序號、根據(jù)零件位置確定引線連接點、根據(jù)規(guī)則確定擺放區(qū)域。只要是規(guī)律性的操作就有自動化的空間。所以球標函數(shù)要解決的核心問題可以概括成一句話把人工逐個創(chuàng)建球標人工整理位置變成程序按規(guī)則批量生成一鍵刷新。這種自動化的收益在50個零件以上的裝配圖里非常明顯零件越多收益越夸張。我自己做的工具在三百件左右的裝配圖上原先手動搞一個多小時現(xiàn)在程序批量跑一遍加檢查和修改大概一首歌的時間。2. 球標不是獨立注釋和視圖、BOM表的依賴關(guān)系很多剛接觸球標開發(fā)的人第一個誤區(qū)就是把它當成普通注釋note來對待。普通注釋是獨立實體隨便放哪都行。球標完全不是這么回事。在Creo的工程圖數(shù)據(jù)模型里球標是一個掛在視圖對象下面的子實體它必須依賴兩個東西才能活第一必須有一個視圖。球標不是滿天亂飛的注釋它必須屬于某個視圖而且它的引線連接參照連到哪個面、哪條邊、哪個軸也是針對該視圖里的模型幾何而言的。第二必須關(guān)聯(lián)到BOM重復(fù)區(qū)域表。球標序號不是自己隨便編的數(shù)字它是從重復(fù)區(qū)域表的某個列一般是索引列取來的。如果沒有綁定重復(fù)區(qū)域你創(chuàng)建出來的球標基本就是個空圈序號永遠填不進去。所以完整的依賴鏈路是這樣的三維裝配模型 → 工程圖繪圖 → 視圖對象 → BOM重復(fù)區(qū)域表 → 球標對象在開發(fā)層面對應(yīng)的對象層級大致是ProDrawing工程圖 └── ProSheet圖頁 └── ProView視圖 └── ProBalloon球標我建議所有準備寫球標函數(shù)的人先把這個層級背下來因為后面90%的bug都是層級理解不到位造成的后面我會專門講。另外提一下開發(fā)方式的選擇。Creo二次開發(fā)常見的有三種路子基于C的Pro/TOOLKIT、基于Java的J-Link、以及VB/C#那套API。球標這種底層依賴視圖和表的操作我個人最推薦C的Pro/TOOLKIT。原因很簡單C接口離內(nèi)核最近函數(shù)覆蓋最全尤其是批處理場景下性能優(yōu)勢明顯。J-Link也能做但有些底層枚舉和回調(diào)處理不如C接口順手。VB/C#適合做小工具跑一些簡單的界面交互還可以做大型裝配批量球標處理我不太建議性能和維護性都吃虧。3. 球標函數(shù)的調(diào)用鏈路從視圖收集到球標落位這一節(jié)是核心。我講的是我實際封裝后的調(diào)用流程而不是某個版本的具體API手冊。有一點先說明Creo不同版本、不同小版本的PRO/TOOLKIT接口聲明多多少少有差異函數(shù)名這種東西一定要以你自己機器上安裝目錄里protoolkit/inc下面的頭文件為準。我下面代碼里的命名是我自己封裝層的命名風(fēng)格上盡量貼近官方但你直接復(fù)制是不行的得對著自己的頭文件改。整個球標創(chuàng)建鏈路由五個環(huán)節(jié)組成缺一不可。第一步拿到工程圖。這個比較簡單打開當前文檔確定它是繪圖類型就行。如果做批處理就是遍歷指定目錄下的所有繪圖文件。第二步收集目標視圖。通過視圖收集接口把這個圖頁里所有視圖列出來。這里有個小技巧不是每個視圖都需要球標我一般只處理常規(guī)視圖general view類型投影視圖、剖視圖這些通常不需要重復(fù)放球標。過濾條件不同版本寫法不同但思路是固定的。// 示意代碼收集當前圖頁中的全部視圖 #include ProDrawing.h #include ProView.h ProError CollectTargetViews(ProDrawing drawing, ProView *views, int *count) { ProError status ProDrawingViewCollect(drawing, views); if (status ! PRO_TK_NO_ERROR) return status; int n 0; for (int i 0; i *count; i) { ProViewType type; if (ProViewTypeGet(views[i], type) PRO_TK_NO_ERROR) { if (type PRO_GENERAL_VIEW) { views[n] views[i]; // 只保留常規(guī)視圖 } } } *count n; return PRO_TK_NO_ERROR; }第三步檢查BOM重復(fù)區(qū)域表。這一步很多人會跳過但我要說別跳。球標能不能顯示序號完全取決于重復(fù)區(qū)域表是否健在、是否已經(jīng)更新。判斷方式也是遍歷繪圖上的表對象找到類型為重復(fù)區(qū)域的表再檢查表里是否有一個可用作索引的列。// 示意代碼確認視圖對應(yīng)當前圖頁中存在重復(fù)區(qū)域表 ProError CheckRepeatRegion(ProDrawing drawing, int *region_exists) { *region_exists 0; ProDwgTable tables; int n; ProDwgTableCollect(drawing, tables); ProArraySizeGet(tables, n); for (int i 0; i n; i) { if (ProRepeatRegionIsRepeatRegion(tables[i]) PRO_TK_NO_ERROR) { *region_exists 1; break; } } ProArrayFree(tables); return PRO_TK_NO_ERROR; }第四步創(chuàng)建球標并設(shè)置數(shù)據(jù)。球標創(chuàng)建接口通常需要一個視圖句柄、一個放置點、一個連接參照和一個參數(shù)結(jié)構(gòu)。參數(shù)結(jié)構(gòu)里最主要的東西包括球標類型圓/橢圓/旗形/自定義符號、文本高度、引線樣式、連接方向等。有一個必須注意的細節(jié)放置點坐標必須是當前工程圖頁的絕對坐標繪圖坐標不是相對于視圖的偏移。這個坐標一般用視圖中某個點的坐標加上一個偏移量算出來。// 示意代碼創(chuàng)建一個圓型球標并指定連接參照 ProError CreateOneBalloon( ProView view, ProSelection attach_ref, // 引線連接參照邊/面/軸/點 double sheet_x, double sheet_y, // 繪圖坐標系的放置位置 int balloon_type // 0圓 1橢圓 2旗形 ) { // 構(gòu)造參數(shù)結(jié)構(gòu)不同版本字段略有差別務(wù)必以頭文件為準 ProBalloonCreateData data; PRO_BALLOON_CREATE_DATA_DEF(data); data.type balloon_type; data.height 3.0; // 球標字高取決于繪圖標準 data.place_point[0] sheet_x; data.place_point[1] sheet_y; data.attach attach_ref; // 調(diào)用球標創(chuàng)建接口 return ProViewBalloonCreate(view, data, NULL); }如果你的調(diào)用連這個都報錯先別急著懷疑球標接口有問題回頭檢查上面兩步視圖是否有效、重復(fù)區(qū)域表是否存在。我在項目里見過太多次最后查出來根本不是球標函數(shù)的問題。第五步創(chuàng)建完成后刷新重繪。批量創(chuàng)建完一堆球標以后會有個很隱蔽的問題球標實體在數(shù)據(jù)里有了但圖面上沒刷出來。所以最后一定要調(diào)用一次視圖更新和圖形重繪的接口強制把球標顯示出來。這也是看起來創(chuàng)建失敗其實成功了的一個重要原因。4. 球標樣式和引線控制參數(shù)才是細節(jié)里的魔鬼球標函數(shù)寫起來不難難的是樣式和引線的控制。工程圖有工程圖的規(guī)矩尤其是外發(fā)圖紙球標樣式必須符合企業(yè)標準。Creo里球標樣式常見的有這么幾類樣式外觀典型場景踩坑提示圓形圓圈內(nèi)序號最通用絕大多數(shù)企業(yè)默認序號超過兩位數(shù)時圓圈可能顯得擠橢圓形橢圓內(nèi)序號部分歐洲客戶圖紙規(guī)范長軸方向受文本影響重新生成會變旗形小矩形加折線引線局部放大圖的零件標注引線起點位置難控制自定義符號公司標準符號有企業(yè)圖標的單位符號實例名必須事先加載到工程圖模板三角等其他樣式略小眾標準非內(nèi)置樣式需要額外定義我平時用圓形最多。但圓形球標有一個容易被忽略的問題序號位數(shù)變了圓的視野是不夠的。9號球標和10號球標圓的顯示效果差不少。程序里如果只是設(shè)置高度而不考慮文本寬度兩位數(shù)序號在球標里容易碰到圓圈邊打出來的圖紙?zhí)貏e難看。解決方式是在創(chuàng)建時預(yù)留文本寬度余量或者直接改用自定義符號并把符號做得大一些。引線控制是另一個大坑。球標引線的連接參照可以選邊、面、軸、基準點、坐標系每種參照在程序里構(gòu)造的Selection類型都不同。用邊做參照引線端點是邊的某個位置用面做參照引線端點在面內(nèi)某處如果用軸球標引線甚至可以是帶箭頭的折線。這些選擇直接影響出圖質(zhì)量對于鈑金件連在邊上最清晰工程上默認也是邊優(yōu)先。對于只有一個面暴露在視圖上的零件連面比連邊穩(wěn)。對于回轉(zhuǎn)體零件連軸更專業(yè)球標能體現(xiàn)中心線特征?;鶞庶c連接適合特殊情況比如在剖視圖里標注孔位。程序里批量處理時我不建議對不同零件做智能判斷連邊還是連面??此浦悄軐崉t難維護。我的做法是統(tǒng)一優(yōu)先連邊如果某個零件在該視圖中沒有可用邊再降級連面。這個邏輯用簡單的判斷就能實現(xiàn)穩(wěn)定可靠。還有引線折線的參數(shù)。引線樣式是否帶折線引線短線長度多少這些在Creo里都有對應(yīng)配置不同模板默認值不一樣。我踩過的坑是程序創(chuàng)建的球標引線到了某個裝配圖上突然變得特別短引線箭頭直接扎進零件里檢查了整整半天才發(fā)現(xiàn)是模板里設(shè)置的引線短線長度參數(shù)有問題。所以批量處理前先確認繪圖模板里的引線參數(shù)否則你代碼寫得再好也白搭。5. 批量創(chuàng)建球標時最容易踩的五個坑與完整排查過程這一節(jié)我重點講一個真實場景。某次我寫了一個批量球標工具輸入是一堆裝配圖輸出是全部自動加好球標的圖紙。第一次跑批量三百多張圖結(jié)果有七八十張球標少了一半有的圖甚至一個球標都沒有。當時第一反應(yīng)是函數(shù)調(diào)用失敗了于是花了一個小時查創(chuàng)建接口參數(shù)一無所獲。后來冷靜下來按數(shù)據(jù)鏈路從頭排查才找到真相。我把那次排查的完整思路寫出來以后你們遇到類似問題可以照這個順序走。排查第一步確認球標是不是被隱藏了。球標和圖元一樣有顯示和拭除的狀態(tài)。有些繪圖模板默認把球標層BALL層隱藏了這時候球標數(shù)據(jù)是存在的只是看不見。判斷方法很簡單手動在視圖上右鍵看球標是否在顯示狀態(tài)或者程序里查視圖下球標的顯示狀態(tài)。我當時這一關(guān)過了因為模板是公司統(tǒng)一的之前手動放球標是正常顯示的。排查第二步檢查重復(fù)區(qū)域表是否更新。這是最要命的一步。Creo里球標序號從重復(fù)區(qū)域取但重復(fù)區(qū)域表如果沒做更新表里可能只有空的骨架球標自然填不上序號。批量處理時我先刪了舊的球標又重建了重復(fù)區(qū)域然后創(chuàng)建新球標——問題就出在這我的代碼里只調(diào)用了更新重復(fù)區(qū)域但沒檢查它是否真的更新成功。有些圖中的BOM表因為參數(shù)缺失比如某個零件沒有材料參數(shù)更新過程被中斷后面的球標創(chuàng)建就成了無源之水。排查到這里我用一個笨辦法驗證手動打開出問題的圖重新更新BOM表如果球標還是不顯示再往下走。排查第三步查看球標放置坐標是否超出邊界。球標是放在繪圖坐標系里的。如果程序計算出的放置點落在圖框之外或者離視圖太遠Creo有可能直接放棄創(chuàng)建該球標而且不報錯。我那次批量失敗有一部分圖就是這個原因不同圖框的幅面不一樣而我寫死了一個偏移量。A3圖上剛好A4圖上就超出去了。這個問題最隱蔽因為它時好時壞特別容易讓人誤判成隨機bug。解決辦法是動態(tài)讀取當前圖頁的幅面大小再計算安全的放置區(qū)域絕不能用固定偏移。排查第四步檢查簡化表示。有些裝配視圖用了簡化表示視圖里壓根沒有某些零件。如果你對一個不存在的零件創(chuàng)建球標接口不報錯但球標就是不會出現(xiàn)。這也是批量場景的高發(fā)問題。處理方式很簡單創(chuàng)建前先判斷零件是否在當前視圖的顯示范圍里用一個零件是否可見的判斷過濾掉不可見對象。排查第五步檢查創(chuàng)建順序。球標必須在重復(fù)區(qū)域更新之后創(chuàng)建不能之前。如果先創(chuàng)建球標、后更新重復(fù)區(qū)域球標雖然建立起來了但序號文本還是空的你再去重新關(guān)聯(lián)會發(fā)現(xiàn)球標和表行的綁定已經(jīng)錯亂只能刪了重建。這是我最開始代碼里的順序失誤改掉之后成功率立刻上了好幾個臺階。把這五個環(huán)節(jié)走完那次批量失敗的真實原因基本定位了一半的圖是坐標偏移越界剩下的是簡化表示未過濾。修復(fù)后重新跑成圖率從82%提到98%。剩下2%是圖紙本身模型就有問題屬于人工介入范疇不能怪程序。6. 把球標能力封裝成可復(fù)用的組件球標函數(shù)寫一次容易寫得好用的關(guān)鍵是封裝。我最終沉淀下來的封裝思路是這樣的對外暴露兩個最基本的操作——創(chuàng)建球標和刷新球標。創(chuàng)建球標是從無到有傳入視圖、傳入需要標注的零件列表、傳入放置規(guī)則程序自動生成球標。刷新球標是從舊到新模型變更后球標序號、位置、連接參照可能失效程序重新遍歷所有球標把序號按當前BOM順序重排把引線連接點重新吸附到最新幾何上。這兩個操作為什么要分開因為實際出圖流程是不斷迭代的模型改了BOM變了圖紙要重新出。如果每次都把球標全部刪掉重建一是慢二是會把工程師手工微調(diào)過的好位置全部丟掉。刷新只更新序號和連接參照保留位置和引線走向這才是工程上最舒服的使用方式。封裝的輸入?yún)?shù)我建議設(shè)計成結(jié)構(gòu)體而不是一長串零散參數(shù)typedef struct { int balloon_type; // 球標類型 double text_height; // 字高 int attach_type; // 0邊 1面 2軸 3點 double margin_x; // 視圖外水平偏移 double margin_y; // 視圖外垂直偏移 int skip_invisible; // 是否跳過不可見零件 } BalloonToolConfig;用結(jié)構(gòu)體的好處是以后企業(yè)標準變了——比如從圓形改旗形——只需要改一處配置所有圖紙重新跑一遍刷新即可不用動業(yè)務(wù)代碼。性能方面也說一個經(jīng)驗。批量創(chuàng)建幾百個球標時千萬別每創(chuàng)建一個球標就調(diào)用一次界面刷新那會卡到你懷疑人生。正確做法是批量創(chuàng)建期間關(guān)閉視圖自動更新全部創(chuàng)建完成后統(tǒng)一刷新一次。我當時優(yōu)化這一步把整套圖紙的處理時間從三分鐘壓到了二十秒以內(nèi)。這一點對大批量工程圖自動化是實打?qū)嵉男阅芗t利。最后分享一個小技巧球標調(diào)試階段把創(chuàng)建接口的返回值完整打印到日志里不要只打成功失敗。返回碼只是第一步我一般會連放置點坐標、連接參照標識、視圖名稱一起打出來。跑完一批圖紙再統(tǒng)一拉日志看哪些球標被丟棄、被跳過一目了然。配合上面的排查思路能少熬不少夜。工具是越用越趁手的但前提是你把球標這條數(shù)據(jù)鏈路徹底想明白。上面這些坑我基本都是在實際項目里一個一個填過來的。如果你也在寫類似的球標自動化工具希望這篇東西能讓你少填幾個。