換與G代碼生成)
簡(jiǎn)介在數(shù)控加工與智能制造領(lǐng)域不同設(shè)備間的數(shù)據(jù)交換格式差異是系統(tǒng)集成的核心痛點(diǎn)。傳統(tǒng)G代碼因廠商擴(kuò)展不兼容而難以通用后置處理器作為連接CAM與機(jī)床的橋梁其設(shè)計(jì)直接影響編程效率與設(shè)備適配性。XML以其結(jié)構(gòu)化、可擴(kuò)展的特性成為異構(gòu)數(shù)據(jù)轉(zhuǎn)換的理想載體XSLT則通過(guò)樣式表機(jī)制將數(shù)據(jù)解析與格式輸出解耦實(shí)現(xiàn)一套邏輯多設(shè)備復(fù)用。從STEP-NC的P21文本到ISO 10303-28定義的P28 XML表達(dá)再借助XSLT映射為具體控制器的指令序列這一鏈路為數(shù)控系統(tǒng)、機(jī)器人離線編程及自定義文本格式輸出提供了統(tǒng)一解決方案。文章圍繞數(shù)據(jù)轉(zhuǎn)換原理、樣式表設(shè)計(jì)、設(shè)備參數(shù)化配置及Saxon驗(yàn)證流程展開幫助工程師快速構(gòu)建面向多目標(biāo)設(shè)備的通用后置處理通道降低集成成本并提升代碼可維護(hù)性。1. 為什么說(shuō)“通用后置處理器”的關(guān)鍵卡在數(shù)據(jù)交換格式上傳統(tǒng)數(shù)控系統(tǒng)大多只認(rèn) G 代碼而 STEP-NC 期望用面向?qū)ο蟮姆绞桨鸭庸ぬ卣?、工藝方法和刀具路徑全部封裝進(jìn)一個(gè)標(biāo)準(zhǔn)數(shù)據(jù)模型里。兩者之間的鴻溝不是簡(jiǎn)單寫個(gè)翻譯器就能填平的——如果每個(gè)控制系統(tǒng)都單獨(dú)開發(fā)一套 STEP-NC 解析器工作量會(huì)隨設(shè)備種類線性膨脹而且 G 代碼本身在不同廠商之間還存在 NURBS 插補(bǔ)指令不兼容、坐標(biāo)格式不一致等問(wèn)題。這篇論文給出的思路很直接把 STEP-NC 代碼先轉(zhuǎn)換成 XML 表達(dá)P28 格式再用 XSLT 樣式表去定義目標(biāo)機(jī)床的數(shù)據(jù)接口格式這樣一套 XSLT 處理機(jī)就能適配多種控制系統(tǒng)。對(duì)正在做數(shù)控系統(tǒng)集成或機(jī)器人離線編程的人來(lái)說(shuō)這個(gè)方案最大的價(jià)值在于把“設(shè)備差異”從程序代碼里剝離出來(lái)變成純配置問(wèn)題。下面我會(huì)沿著論文的轉(zhuǎn)換鏈路把 P21 到 P28 的轉(zhuǎn)換、XSLT 樣式表設(shè)計(jì)、以及面向非 G 代碼設(shè)備的擴(kuò)展方法拆開講最后給出一個(gè)用 Saxon 可跑通的驗(yàn)證流程。2. STEP-NC 數(shù)據(jù)鏈路P21 到 P28 的轉(zhuǎn)換原理與實(shí)現(xiàn)2.1 為什么不能直接拿 P21 喂給 XSLT 處理機(jī)STEP-NC 最經(jīng)典的文本表達(dá)是 ISO 10303-21也就是常說(shuō)的 P21 文件。它的實(shí)體實(shí)例用#66...這種帶編號(hào)的方式表示屬性值可以是字符串、實(shí)數(shù)、枚舉也可以是嵌套的列表和實(shí)體引用。XSLT 1.0 只接受 XML 輸入XSLT 2.0 雖然理論上支持非 XML 文本并用正則表達(dá)式解析但 P21 文件的嵌套結(jié)構(gòu)比如一個(gè)實(shí)體屬性里又是一個(gè)列表列表里還有實(shí)體引用需要遞歸正則才能處理而目前主流 XSLT 處理機(jī)對(duì)遞歸正則的支持并不可靠。論文里的做法是加了一個(gè)預(yù)處理程序把 P21 把轉(zhuǎn)換成一種簡(jiǎn)單的 XML 中間格式再交給 XSLT 去生成 P28。這個(gè)中間格式不復(fù)雜本質(zhì)上就是把實(shí)體編號(hào)、實(shí)體名、屬性列表拆成標(biāo)簽。比如一個(gè)笛卡爾坐標(biāo)點(diǎn)在 P21 里寫為#66CARTESIAN_POINT(CLAMPING_POSITION1,(0.000,20.000,25.000));預(yù)處理后的 XML 中間格式大致如下entity_instance idid66 entity_nameCARTESIAN_POINT/entity_name attributes items itemCLAMPING_POSITION1/item /items items item0.000/item item20.000/item item25.000/item /items /attributes /entity_instance這里的邏輯是第一層items對(duì)應(yīng)CARTESIAN_POINT的第一個(gè)屬性名稱第二層items對(duì)應(yīng)第二個(gè)屬性坐標(biāo)列表。預(yù)處理程序需要區(qū)分“單值屬性”和“列表屬性”否則后續(xù) XSLT 匹配時(shí)無(wú)法知道某個(gè)item是實(shí)體名還是坐標(biāo)分量。實(shí)際開發(fā)中我會(huì)用 Python 寫這個(gè)預(yù)處理按分號(hào)切分實(shí)體聲明后再對(duì)括號(hào)做配對(duì)解析而不是用正則硬剝離。2.2 用 flex 和 bison 解析 EXPRESS 模式P28 文件不是憑空生成的它需要遵循 ISO 10303-28 定義的 XML 模式而這個(gè)模式來(lái)自 EXPRESS 信息模型。如果手動(dòng)把大量 EXPRESS 文件改寫成 XML Schema 或 DTD正確性很難保證。論文采用 flex 和 bison 做詞法/語(yǔ)法分析ex.l和ex.y分別定義記號(hào)規(guī)則和語(yǔ)法規(guī)則編譯生成 C 源碼后與符號(hào)表管理、語(yǔ)義分析、代碼生成模塊聯(lián)合編譯最終得到 EXPRESS-X 轉(zhuǎn)換器。這個(gè)轉(zhuǎn)換器把ENTITY cartesian_point這樣的 EXPRESS 聲明轉(zhuǎn)成entity_decl元素。ISO 10303-28 里給出了 EXPRESS-X 的 DTD 約束比如entity_decl可以包含entity_id、supertype_of、subtype_of、explicit_attr_block等子元素順序有嚴(yán)格要求。因此 EXPRESS-X 轉(zhuǎn)換器不只是做標(biāo)簽替換還要維護(hù)符號(hào)表來(lái)校驗(yàn)類型引用是否有效。對(duì)于只想實(shí)現(xiàn) P21 到 P28 轉(zhuǎn)換的應(yīng)用場(chǎng)景其實(shí)可以跳過(guò) EXPRESS-X 生成步驟直接套用已有應(yīng)用協(xié)議的 XML Schema但如果你要支持自定義的 STEP-NC 擴(kuò)展實(shí)體就得把 EXPRESS 解析器這條鏈路打通。NIST 的開源 SCLSTEP Class Library提供了現(xiàn)成的.l和.y文件建議基于它修改而不是從零寫語(yǔ)法。2.3 P28 三種編碼方式的選擇ISO 10303-28 定義了三種 XML 表達(dá)方式論文里稱為早聯(lián)編和晚聯(lián)編。早聯(lián)編early binding的 P28 文件按照實(shí)體類型直接生成對(duì)應(yīng)元素例如cartesian_point idid66優(yōu)點(diǎn)是文件緊湊、解析快晚聯(lián)編late binding則使用通用的entity_instance express_entity_namecartesian_point idid66結(jié)構(gòu)屬性統(tǒng)一用attribute_instance包裹。晚聯(lián)編雖然文件體積更大但它的元素類型不隨實(shí)體種類變化任何 EXPRESS 模式都能映射到同一套 XML 模板上通用性最好。對(duì)于目標(biāo)是“一個(gè)后置處理器適配多種設(shè)備”的場(chǎng)景晚聯(lián)編是更穩(wěn)妥的選擇。下面是一個(gè)晚聯(lián)編 P28 中坐標(biāo)點(diǎn)的示例entity_instance express_entity_namecartesian_point idid66 attribute_instance express_attribute_namename type_literal express_type_namelabel string_literalCLAMPING_POSITION1/string_literal /type_literal /attribute_instance attribute_instance express_attribute_namecoordinates list_literal type_literal express_type_namelength_measure real_literal0.000/real_literal /type_literal type_literal express_type_namelength_measure real_literal20.000/real_literal /type_literal type_literal express_type_namelength_measure real_literal25.000/real_literal /type_literal /list_literal /attribute_instance /entity_instance注意這里嵌套關(guān)系是entity_instance attribute_instance list_literal type_literal real_literal每一層都有對(duì)應(yīng)的express_*_name屬性。XSLT 樣式表在做匹配時(shí)可以利用這些屬性名做精確選擇而不是依賴元素名。這一點(diǎn)在后置處理時(shí)特別重要因?yàn)?STEP-NC 的實(shí)體種類很多但屬性訪問(wèn)路徑是有規(guī)律可循的。3. 基于 XSLT 的機(jī)床接口樣式表與 G 代碼輸出3.1 把 G 代碼格式差異收斂到樣式表通用后置處理器的核心假設(shè)是STEP-NC 的語(yǔ)義是統(tǒng)一的設(shè)備差異只體現(xiàn)在代碼格式上。因此 XSLT 樣式表需要完成兩件事一是從 P28 文件里提取刀具路徑點(diǎn)、進(jìn)給率、主軸轉(zhuǎn)速等工藝數(shù)據(jù)二是把這些數(shù)據(jù)按目標(biāo)控制器的語(yǔ)法組裝成文本。以 G01 為例不同的數(shù)控系統(tǒng)對(duì)坐標(biāo)精度、前置零、行號(hào)格式都有各自要求通過(guò)樣式表模板即可控制。下面是一個(gè)面向三軸銑床的 G01 模板Saxon 擴(kuò)展函數(shù)調(diào)用xsl:template matchentity_instance[express_entity_namemachining_workingstep] xsl:variable nametoolpath select.//entity_instance[express_entity_nametoolpath_list]/attribute_instance[express_attribute_nametoolpath]/ xsl:for-each select$toolpath//entity_instance[express_entity_namecartesian_point] xsl:variable namecoords selectattribute_instance[express_attribute_namecoordinates]/ xsl:value-of selectconcat(N, ex:GetIndexNumber(), G01 , ex:CoordinatesFormat(string($coords)), )/ /xsl:for-each /xsl:template這段模板匹配每個(gè)加工工步遍歷其中的刀位點(diǎn)生成N1 G01 X... Y... Z...格式的 G 代碼行。ex:GetIndexNumber()負(fù)責(zé)生成遞增行號(hào)ex:CoordinatesFormat()負(fù)責(zé)把 P28 里的坐標(biāo)列表轉(zhuǎn)成指定格式的 X/Y/Z 字符串。需要說(shuō)明的是string($coords)拿到的可能是一長(zhǎng)串0.000,20.000,25.000所以擴(kuò)展函數(shù)里要按分隔符切分后再格式化。對(duì)應(yīng)的擴(kuò)展函數(shù)實(shí)現(xiàn)Java 語(yǔ)言部署在 Saxon 環(huán)境public class ToolpathFormatter { private int index 0; public String getIndexNumber() { index; return N index; } public String coordinatesFormat(String coordinates) { String[] temp coordinates.split(,); if (temp.length 3) { return ; } // 注意P28 中可能帶空格這里先 trim 再轉(zhuǎn) double double x Double.parseDouble(temp[1].trim()); double y Double.parseDouble(temp[0].trim()); double z Double.parseDouble(temp[2].trim()) - 14.7; return String.format(X%.4f Y%.4f Z%.4f, x, y, z); } }這里的坐標(biāo)順序映射是個(gè)大坑STEP-NC 中cartesian_point的坐標(biāo)列表通常按 X、Y、Z 排列但在某些工藝數(shù)據(jù)里可能是 Y、X、Z 或者包含冗余分量。論文中的CoordinatesFormat是把第二個(gè)值當(dāng) X、第一個(gè)值當(dāng) Y說(shuō)明目標(biāo)機(jī)器人的坐標(biāo)系定義與 STEP-NC 默認(rèn)順序存在轉(zhuǎn)置關(guān)系。實(shí)際項(xiàng)目中我會(huì)在配置表里增加一個(gè)坐標(biāo)交換標(biāo)志避免為每臺(tái)設(shè)備重寫擴(kuò)展函數(shù)。3.2 行號(hào)、單位與進(jìn)給率的參數(shù)化處理G 代碼輸出不能只處理坐標(biāo)還要解決行號(hào)格式、單位換算、進(jìn)給率模式這幾個(gè)問(wèn)題。論文中的樣式表通過(guò)兩個(gè)內(nèi)嵌函數(shù)分別處理行號(hào)和坐標(biāo)這屬于“邏輯內(nèi)聚”的設(shè)計(jì)。更常見的做法是定義一個(gè)設(shè)備參數(shù) XML 文件把行號(hào)增量、小數(shù)位數(shù)、是否輸出行號(hào)等作為參數(shù)傳入樣式表。設(shè)備參數(shù)示例device-interface param nameline-number-prefix valueN/ param nameline-number-increment value5/ param namecoordinate-precision value0.0001/ param namefeed-mode valueF/ param namecoordinate-order valueY,X,Z/ param namez-offset value-14.7/ /device-interface在 XSLT 里可以通過(guò)document(device.xml)讀取這些參數(shù)xsl:variable namezOffset selectnumber(document(device.xml)/device-interface/param[namez-offset]/value)/這樣修改設(shè)備接口時(shí)只需編輯 XML不需要重新編譯 Java 擴(kuò)展函數(shù)。對(duì)于新手來(lái)說(shuō)要注意 XSLT 的變量作用域是模板層級(jí)的不能在同一模板內(nèi)重復(fù)定義同名變量對(duì)于熟手來(lái)說(shuō)參數(shù)化 XML 可以與 XSLT 的xsl:param機(jī)制結(jié)合實(shí)現(xiàn)命令行傳參覆蓋。3.3 機(jī)床結(jié)構(gòu)無(wú)關(guān)的刀軌遍歷模式STEP-NC 的 CC1/CC2 一致性分類下刀具路徑是顯式給出的后置處理器不需要做刀軌規(guī)劃。但路徑數(shù)據(jù)在 P28 里的嵌套結(jié)構(gòu)可能很深machining_workingstep下可能有machining_toolpath其下又掛多個(gè)cartesian_point。如果每個(gè)實(shí)體都寫死模板匹配會(huì)導(dǎo)致樣式表難以維護(hù)。推薦做法是用統(tǒng)一定位規(guī)則xsl:template match//entity_instance[(express_entity_namecartesian_point) and ancestor::entity_instance[express_entity_namemachining_toolpath]]這條 XPath 表達(dá)式的含義是匹配所有machining_toolpath后代中的cartesian_point實(shí)體。這樣無(wú)論工作步與刀軌之間的中間層是什么模板都能命中。實(shí)際調(diào)試時(shí)先用一個(gè)小型 P28 文件打印出所有entity_instance的名稱和層級(jí)關(guān)系確認(rèn)路徑再寫模板能省去大量試錯(cuò)時(shí)間。設(shè)備類型坐標(biāo)順序輸出格式擴(kuò)展函數(shù)職責(zé)三軸銑床X,Y,ZG01 X... Y... Z...行號(hào)生成、G 代碼格式化切削機(jī)器人Y,X,Z帶偏置MOVE P1 X... Y... Z...坐標(biāo)轉(zhuǎn)置、機(jī)器人指令封裝自定義測(cè)試系統(tǒng)X,Y,Z空格分隔純文本僅做數(shù)據(jù)提取不做格式化表格中的三種情況展示了同一套 P28 輸入如何通過(guò)不同樣式表和擴(kuò)展函數(shù)輸出差異化結(jié)果。熟手在評(píng)估工作量時(shí)可以據(jù)此快速估算坐標(biāo)順序映射、偏置、行號(hào)規(guī)則是三個(gè)最常見的改動(dòng)點(diǎn)。4. 面向機(jī)器人與非傳統(tǒng)控制器的后置處理擴(kuò)展4.1 從 G 代碼到機(jī)器人語(yǔ)言的語(yǔ)義遷移論文的第二臺(tái)驗(yàn)證設(shè)備是切削加工機(jī)器人輸出目標(biāo)是機(jī)器人語(yǔ)言。相比數(shù)控機(jī)床機(jī)器人程序不僅有坐標(biāo)還有姿態(tài)信息、運(yùn)動(dòng)指令類型關(guān)節(jié)運(yùn)動(dòng)、直線運(yùn)動(dòng)、以及速度與加速度參數(shù)。STEP-NC 的標(biāo)準(zhǔn)數(shù)據(jù)模型里刀具路徑的每個(gè)刀位點(diǎn)可能包含axis_point和tool_direction等屬性這些可以映射到機(jī)器人位姿。但這里有個(gè)實(shí)際困難機(jī)器人控制器通常要求給出工具坐標(biāo)系的姿態(tài)歐拉角而 STEP-NC 中的刀具方向向量并不直接等價(jià)于歐拉角需要額外的姿態(tài)求解模塊。在 XSLT 中處理姿態(tài)的方式是把方向向量的三個(gè)分量提取出來(lái)用 Java 擴(kuò)展函數(shù)計(jì)算歐拉角然后輸出到機(jī)器人代碼。以下是一個(gè)簡(jiǎn)化的模板片段xsl:template matchentity_instance[express_entity_nametool_direction] xsl:variable namei selectattribute_instance[express_attribute_namedirection_ratios]/list_literal/type_literal[1]/real_literal/ xsl:variable namej selectattribute_instance[express_attribute_namedirection_ratios]/list_literal/type_literal[2]/real_literal/ xsl:variable namek selectattribute_instance[express_attribute_namedirection_ratios]/list_literal/type_literal[3]/real_literal/ xsl:value-of selectconcat( ORIENT , ex:OrientFromVector(number($i), number($j), number($k)), )/ /xsl:template這里ex:OrientFromVector返回一個(gè)RX RY RZ字符串。注意direction_ratios在 P28 中可能不是list_literal而是數(shù)組表達(dá)式所以模板里用了[1]、[2]、[3]作為位置限定。如果方向向量未歸一化擴(kuò)展函數(shù)里要先做歸一化再計(jì)算歐拉角否則機(jī)器人控制器會(huì)報(bào)姿態(tài)錯(cuò)誤。4.2 自定義文本格式與 INI 輸出的實(shí)現(xiàn)要點(diǎn)論文特別提到用戶自定義代碼可能是純文本、INI 或二進(jìn)制。對(duì)于純文本格式XSLT 的輸出方法設(shè)為text即可。對(duì)于 INI 格式可以利用 XSLT 生成簡(jiǎn)單的鍵值對(duì)但要小心特殊字符轉(zhuǎn)義。二進(jìn)制輸出不建議直接用 XSLT 做一方面是編碼處理麻煩另一方面是二進(jìn)制格式往往依賴控制器底層數(shù)據(jù)結(jié)構(gòu)更適合在擴(kuò)展函數(shù)里用 Java 完成字節(jié)寫入。一個(gè)輸出 INI 風(fēng)格刀位文件的樣式表聲明xsl:output methodtext encodingUTF-8/模板中生成N#...這樣的內(nèi)容時(shí)如果目標(biāo)系統(tǒng)需要\r\n換行而 XSLT 默認(rèn)輸出\n會(huì)導(dǎo)致某些 Windows 控制器解析異常。解決辦法是在擴(kuò)展函數(shù)里統(tǒng)一處理?yè)Q行符或者用xsl:text disable-output-escapingyes#13;#10;/xsl:text強(qiáng)制輸出回車。這里提醒一點(diǎn)disable-output-escaping只在部分處理機(jī)中有效Saxon HE 對(duì)它的支持有限更可靠的方式是輸出后再用腳本做換行轉(zhuǎn)換。4.3 多設(shè)備共存的樣式表組織方式當(dāng)一臺(tái)后置處理器需要同時(shí)輸出 G 代碼和機(jī)器人語(yǔ)言時(shí)不能把兩種設(shè)備的邏輯寫在一個(gè)大樣式表里。論文的“通用”體現(xiàn)在輸入統(tǒng)一、轉(zhuǎn)換框架統(tǒng)一而不是樣式表統(tǒng)一。推薦的做法是給每類設(shè)備維護(hù)獨(dú)立的 XSLT 文件和擴(kuò)展函數(shù)類然后定義一個(gè)調(diào)度層根據(jù)運(yùn)行參數(shù)選擇具體樣式表。調(diào)度層可以用一個(gè)簡(jiǎn)單的 bash 腳本實(shí)現(xiàn)#!/bin/bash DEVICE$1 INPUT_P28$2 if [ $DEVICE mill ]; then java -cp saxon9he.jar:. net.sf.saxon.Transform -s:$INPUT_P28 -xsl:mill.xsl -o:output.cnc elif [ $DEVICE robot ]; then java -cp saxon9he.jar:. net.sf.saxon.Transform -s:$INPUT_P28 -xsl:robot.xsl -o:output.robot else echo Unsupported device: $DEVICE exit 1 fi這個(gè)腳本展示了后置處理器的“驅(qū)動(dòng)層”如何與 XSLT 解耦。實(shí)際生產(chǎn)環(huán)境中調(diào)度層往往需要處理錯(cuò)誤碼、日志記錄、以及多文件批量處理但核心思路不變?cè)O(shè)備差異在樣式表流程控制在腳本或程序。對(duì)于“微型機(jī)器”這類小型設(shè)備比如桌面數(shù)控機(jī)床它們的控制器通常只支持極簡(jiǎn) G 代碼子集此時(shí)可以把mill.xsl簡(jiǎn)化為只輸出 G00/G01/G02/G03 的模板省去換刀和固定循環(huán)邏輯。4.4 排錯(cuò)模板匹配不到任何節(jié)點(diǎn)怎么辦遇到 XSLT 輸出為空的情況第一步先確認(rèn)輸入 XML 能被正確解析。用下面的 XPath 檢查 P28 里是否存在目標(biāo)實(shí)體xmllint --xpath //entity_instance[express_entity_namemachining_toolpath] input.p28.xml如果沒(méi)有輸出說(shuō)明預(yù)處理生成的 P28 里實(shí)體名不是預(yù)期值或者命名空間有問(wèn)題。在 XML 模式下ISO 10303-28 允許帶命名空間很多 XSLT 初學(xué)者會(huì)在xsl:template match...里忘記加命名空間前綴導(dǎo)致匹配不上。此時(shí)可以查看 P28 文件的根元素是否帶xmlns聲明如果有樣式表要相應(yīng)聲明命名空間并在 XPath 中使用前綴。另一種情況是晚聯(lián)編 P28 中express_entity_name的值帶連字符或點(diǎn)號(hào)XPath 匹配時(shí)用express_entity_namecartesian_point沒(méi)問(wèn)題但如果值包含特殊字符建議用contains()做模糊匹配不過(guò)會(huì)犧牲一點(diǎn)性能。5. 用 Saxon 直接跑通整條后置處理流水線5.1 準(zhǔn)備測(cè)試環(huán)境與最小 P21 樣例不用裝專用 CAD/CAM 軟件只需 Java 環(huán)境和 Saxon HE 9.3 以上版本配合一個(gè)手寫的 P21 文件就能驗(yàn)證論文的核心流程。先準(zhǔn)備一個(gè)只含單個(gè)刀具路徑的最小 P21 文件內(nèi)容如下ISO-10303-21; HEADER; FILE_DESCRIPTION((),1); FILE_NAME(demo.stp,,(),(),,,); FILE_SCHEMA((machining_schema)); ENDSEC; DATA; #1MACHINING_WORKINGSTEP(WS1,#2,#3); #2CARTESIAN_POINT(START_POINT,(0.000,0.000,20.000)); #3MACHINING_TOOLPATH(TP1,(#4,#5,#6)); #4CARTESIAN_POINT(P1,(10.000,5.000,20.000)); #5CARTESIAN_POINT(P2,(20.000,10.000,18.000)); #6CARTESIAN_POINT(P3,(30.000,15.000,16.000)); ENDSEC; END-ISO-10303-21;這個(gè)文件包含一個(gè)加工工步、一條刀軌和三個(gè)刀位點(diǎn)。注意MACHINING_WORKINGSTEP的第二、第三屬性是實(shí)體引用預(yù)處理程序要把#2、#3解析成引用關(guān)系。我習(xí)慣在預(yù)處理輸出里用refid2這樣的屬性表示引用這樣 P28 中可以直接映射為entity_ref。5.2 預(yù)處理腳本與 P28 生成預(yù)處理腳本可以用 Python 實(shí)現(xiàn)核心代碼如下import re p21_text open(demo.stp, r, encodingutf-8).read() data_section p21_text.split(DATA;)[1].split(ENDSEC;)[0] lines [l.strip() for l in data_section.strip().splitlines() if l.strip()] xml_parts [?xml version1.0 encodingUTF-8?] xml_parts.append(step_root) for line in lines: if not line.startswith(#): continue match re.match(r#(\d)([A-Z_])\((.*)\);, line) if not match: continue entity_id, entity_name, attr_text match.groups() xml_parts.append(fentity_instance idid{entity_id}) xml_parts.append(fentity_name{entity_name}/entity_name) xml_parts.append(attributes) # 按逗號(hào)切分屬性但不切分括號(hào)內(nèi)的逗號(hào) depth 0 current [] for ch in attr_text: if ch (: depth 1 elif ch ): depth - 1 elif ch , and depth 0: current.append(.join(current_parts)) current_parts [] continue current_parts.append(ch) if current_parts: current.append(.join(current_parts)) for attr in current: if attr.startswith(() and attr.endswith()): items attr[1:-1].split(,) xml_parts.append(items) for it in items: xml_parts.append(fitem{it.strip()}/item) xml_parts.append(/items) else: xml_parts.append(fitem{attr.strip()}/item) xml_parts.append(/attributes) xml_parts.append(/entity_instance) xml_parts.append(/step_root) open(demo_intermediate.xml, w, encodingutf-8).write(\n.join(xml_parts))這段腳本里關(guān)鍵的邏輯是depth計(jì)數(shù)器用來(lái)處理坐標(biāo)列表(0.000,20.000,25.000)內(nèi)部的逗號(hào)避免把坐標(biāo)分量誤拆成獨(dú)立屬性。代碼中的current_parts需要在外層循環(huán)前初始化實(shí)際寫的時(shí)候要記得每處理完一個(gè)屬性重置一次。生成的demo_intermediate.xml可以直接用 XSLT 2.0 處理但這里還不是標(biāo)準(zhǔn) P28只是緊挨著 P28 的中間格式。如果你希望直接生成符合 ISO 10303-28 的晚聯(lián)編 P28需要在預(yù)處理后增加一個(gè) XSLT 轉(zhuǎn)換把entity_name映射到express_entity_name把item按類型封裝成type_literal。這一步可以復(fù)用上一章提到的模板思路核心是把中間格式里的值按照目標(biāo)實(shí)體的定義包裝成帶類型的字面量。5.3 用 Java Saxon 測(cè)試 G 代碼輸出寫一個(gè)最小的 Java 入口來(lái)調(diào)用 Saxon避免每次都用命令行拼參數(shù)import net.sf.saxon.s9api.Processor; import net.sf.saxon.s9api.Xslt30Transformer; import net.sf.saxon.s9api.XdmNode; import net.sf.saxon.s9api.DocumentBuilder; public class PostProcessorRunner { public static void main(String[] args) throws Exception { Processor processor new Processor(true); Xslt30Transformer transformer processor.newXsltCompiler().compile( new java.io.File(mill.xsl) ).load30(); DocumentBuilder builder processor.newDocumentBuilder(); XdmNode input builder.build(new java.io.File(demo_intermediate.xml)); transformer.setErrorListener((e) - { throw new RuntimeException(e.getMessage()); }); transformer.applyTemplates(input, transformer.newSerializer(new java.io.File(output.cnc))); } }這段代碼里new Processor(true)表示啟用 schema-aware 模式如果你沒(méi)有使用 schema 可以傳false能節(jié)省少量加載時(shí)間。applyTemplates的輸出依賴于樣式表里xsl:output的聲明。運(yùn)行后查看output.cnc預(yù)期輸出類似N1 G01 X5.0000 Y10.0000 Z5.3000 N3 G01 X10.0000 Y20.0000 Z3.3000 N5 G01 X15.0000 Y30.0000 Z1.3000這里坐標(biāo)系順序已經(jīng)按照論文里的機(jī)器人接口做了轉(zhuǎn)置且 Z 值減了 14.7 的偏置。如果你希望生成標(biāo)準(zhǔn)三軸銑床代碼而不是機(jī)器人代碼在mill.xsl里把坐標(biāo)映射改成直接順序輸出即可。5.4 驗(yàn)證結(jié)果有效性的三個(gè)檢查點(diǎn)驗(yàn)證后置處理器輸出不能只看有沒(méi)有報(bào)錯(cuò)要從語(yǔ)法、幾何、語(yǔ)義三個(gè)層面檢查。第一層是語(yǔ)法檢查用目標(biāo)控制器的仿真軟件打開輸出文件或者至少用gcode-parser這類開源庫(kù)解析一遍確認(rèn)沒(méi)有非法指令。第二層是幾何檢查把輸出代碼驅(qū)動(dòng)到 DMU 仿真環(huán)境對(duì)比刀位點(diǎn)是否落在工件毛坯范圍內(nèi)。第三層是語(yǔ)義檢查確認(rèn)加工工步的順序、快進(jìn)與工進(jìn)切換、主軸啟停與 STEP-NC 原數(shù)據(jù)一致。對(duì)于機(jī)器人語(yǔ)言輸出驗(yàn)證時(shí)還要額外檢查姿態(tài)是否連續(xù)。XSLT 模板中如果只輸出坐標(biāo)不輸出姿態(tài)機(jī)器人會(huì)默認(rèn)保持上一姿態(tài)容易產(chǎn)生非預(yù)期軌跡。因此建議在刀軌第一個(gè)點(diǎn)處強(qiáng)制輸出O RY后續(xù)點(diǎn)只輸出坐標(biāo)。最后一個(gè)實(shí)用技巧在mill.xsl里加上xsl:strip-space elements*/可以去掉輸入 XML 中的空白文本節(jié)點(diǎn)既提升匹配性能也避免輸出 G 代碼時(shí)產(chǎn)生多余空行。這個(gè)聲明對(duì)大多數(shù) XSLT 處理機(jī)都有效是容易被忽略的優(yōu)化點(diǎn)。本文還有配套的精品資源點(diǎn)擊獲取