跳轉到

工作流程:透過 HTTP API 進行重播驗收

專案建構以一次乾淨的重播收尾:整個任務播放完畢、每則訊息都依 id 讀過,數值上的交叉核對也都做完。本頁就是透過 HTTP API 驅動的那一次重播,給沒有畫布可看的腳本或代理程式 (agent) 使用:如何開始一次真正全新的執行、如何在它播放時盯著它並得知它已經結束,以及證明它切了該切之處的證據。

登入、回應封套,以及這裡每一條路由都遵循的其他約定,見透過 HTTP 驅動網頁服務(英文,未鏡像)。

flowchart LR
    Free["執行個體空閒?"]
    Reset["重置"]
    Start["開始<br>保留 runId"]
    Poll["輪詢狀態、觀察執行<br>同一個 runId"]
    Evidence["讀取證據"]
    Msgs["盤點訊息"]

    Free --> Reset --> Start --> Poll --> Evidence --> Msgs
    Msgs -->|修正後再重播| Free

1. 從空閒的執行個體與已重置的工作階段開始

服務替連上它的所有用戶端只持有一個專案與一次執行,所以重播一開始要先確認執行個體是空閒的,而且絕不以停止一次無法證明屬於自己的執行作結。

  1. GET /api/Execution/status。如果 executionStatus 是 Running 或 Paused,就表示有一次執行正在進行——不要開始、重置或停止它。重置也會停止正在進行的執行。
  2. POST /api/Execution/reset,每次開始之前都要做。開始並不會重置。一次執行完成之後,POST /api/Execution/start 會在那次執行留下的工作階段之上再播放一次任務——已加工的工件、它的步與它的訊息都還在。於是每一筆「首次讀取,否則寫入」記錄都看到已經產生的步,改為寫入而不是讀取,所以第 0 階段的記錄會以加工後的零件覆寫快取的毛胚;之後的每一次執行,不論有沒有重置,都從已經切過的毛胚開始,直到那個檔案被刪除為止。先前那次執行的步也會讓未接觸警告無從觸發,所以第二次執行裡沒有任何東西回報這項損害。

重置會清空步序列與工作階段的各份訊息清單,所以要先讀完前一次執行的證據(§4、§5),再為下一次執行重置。

2. 開始,並依執行 id 輪詢

  1. POST /api/Execution/start,接著檢查回應本體,而不只看狀態碼。一個已啟用、卻編譯不過的腳本命令,會以 HTTP 200、success: false 與 code: "script_compile_error" 拒絕開始;在有執行正在進行時開始,會回應 alreadyRunning: true,而且什麼也不會開始。保留真正開始時所傳回的 runId。
  2. 輪詢 GET /api/Execution/status,直到它回報那個 runId,且 executionStatus 為 Finished 或 Ready。重置之後,被停止、或以例外結束的執行,就是以 Ready 回報自己:只等 Finished 的輪詢程式,遇到失敗的執行永遠不會返回。少了 §1 的重置,這樣的執行會承接上一次執行的 Finished。Ready 背後的例外寫在服務日誌裡,GET /api/Project/logs 會傳回當天的日誌。runId 變大,表示在那之後另有呼叫端開始了一次執行。
  3. 在任何東西重置工作階段之前,先讀取下面的證據。

邊播放邊觀察

一趟長時間的播放,值得在它執行時就看著,而不只是在結束時評判。程式原點錯了、刀長或補正設錯了,或毛胚擺錯了位置,都不會讓執行停下:它會一連幾個小時不斷碰撞、空切或切得太深,而之後在那塊毛胚上播放的每一支程式都白費了。下面這些請求讀取進行中的執行、不改變任何東西,而且夠輕量,可以在輪詢程式等待時每隔幾分鐘重複一次:

看什麼 請求 正常時
仍在執行、仍是你的 GET /api/Execution/status executionStatus 為 Running,且帶著你開始時傳回的那個 runId
進度 GET /api/execution/cl-strip/range 兩次查看之間 count 有增加
警報 GET /api/Execution/messages?minSeverity=Message&tail=0&idPrefix=<prefix>,對 Collision-- 與 Play-RapidCut 各查一次,Stroke 則改用 minSeverity=Error(排除開場一次性的設置警告 StrokeLimit--Unconfigured) 每份清單的 matched 都維持 0
新的警告 GET /api/Execution/messages?minSeverity=Warning&tail=1000 沒有你還沒交代過的 id(§5)
播放到哪裡 §4 的時序圖,帶 widthHint=0、比 count 早一段的 dispBegin、dispEnd=<count>,inspectingKey 為 FileNo、LineNo、ToolId、Cl.X、Cl.Y、Cl.Z,取其中最後一個不是 NaN 的值——count 也算進了還在計算中的步,它們回答 NaN 檔案與行號持續往前走;刀具是程式所呼叫的那一把,位在程式切削的地方
有沒有在切、切得多重 同一張時序圖,涵蓋最後幾千步,用 IsTouched、CuttingDepth_mm、MaxAbsForce_N、YieldingStressRatio 與 MaxSpindlePowerRatio——後三者是物理量:專案沒開物理、沒有物理授權,或顯示物理選項關著時,它們回答 NaN 或空的 items 在程式切削之處有接觸;切深不超過刀具的刃長——只有沒吃到壁面時才接近每層下刀量,壁面、轉角或側刃加工可能以整段刃長吃刀(§6);應力比與主軸功率比都低於 1

帶 tail=0 時,每份清單只回答它的 total 與符合篩選條件的 matched 計數,不傳回訊息本身,所以一次警報檢查,每個前綴只花一個小小的回應。播放期間不選取任何步時,應用程式的畫布會顯示刀具,以及到目前為止切出來的毛胚;幾何差異則要到播放結束才會建立。

只有在設置錯誤會毀掉之後的加工時,才停止播放——警報接連不斷、程式該切的地方沒有接觸、切深或負載遠遠超出該趟加工、刀具出現在程式從不去的地方。這時才停止它(POST /api/Execution/stop 會停止當下的任何一次執行,所以只停你確知是自己的那一次),修正設置,再從 §1 重播。不會毀掉之後加工的缺陷——一則已經弄清楚的警告、退刀時的一次碰觸——就記下來,讓播放跑完;它和其餘的證據一起在 §4 與 §5 評判。

輪詢程式放棄時,就讓它放棄:逾時不是呼叫 POST /api/Execution/stop 的理由。它不帶執行 id,會停止當下的任何一次執行——在共用的執行個體上,那未必是你的——而被停止的執行,不會跑到任務中它停下之處以下的各列:既沒有執行後作業的輸出,也沒有階段末記錄。

3. 先粗後細——並在兩者之間清掉毛胚快取

新專案最初的幾次播放檢查的是基準,而不是表面,所以它們以粗的初始解析度(PUT /api/Workpiece/init-resolution)與粗的加工解析度執行。為了驗收執行而改用精細解析度時,有一個陷阱。第一支程式上方的第 0 階段記錄,快取的是依當時的初始解析度網格化的毛胚,而之後的每一次執行都會把那個檔案讀回來——所以在變更初始解析度、毛胚幾何或它的預留量之後,下一次執行仍從舊的毛胚開始,而且沒有任何東西會說明這一點。

下一次開始之前,先刪除第 0 階段的檔案:DELETE /api/Mission/commands/{path}/recordmeshedgeom/file,其中 {path} 是該記錄項目的路徑(GET …/recordmeshedgeom/file-status 會回報檔案是否存在)。你打算從它接續的階段末檔案,同樣是以舊的寬度寫下的;刪掉它,或把它的階段再播放一次。變更加工解析度命令則不需要刪除任何東西——它管的是它下方的切削,而不是快取的毛胚。這些記錄所屬的配置見能接續的任務;在應用程式中,記錄自己的重設會刪除它的檔案。

4. 完成的執行不等於通過的執行

Finished 說的是任務走到了終點,並沒有說切到了任何東西。一支從未裝上刀具的程式——執行器解析不了的刀具字、空的刀具名稱表——會播放每一行,完全不產生任何加工步,卻仍以 Finished 結束。播放結束時的警告 Play-Touch--None 只在有步、而其中沒有一步碰到毛胚時才會提出,所以一趟零步的播放永遠走不到它;NC 訊息也往往完全看不出沒裝上刀具這件事——不論主軸上有沒有刀具,G43 H1 都照表補正,而不帶 H 的 G43 則沿用模態的 H(程式寫出任何 H 之前為 H0),一聲不吭,一如控制器的做法。

零步的執行也會躲在接續配置的背後。沒有產生任何步時,程式下方的每一筆「首次讀取,否則寫入」記錄仍然會讀取,所以先前一次成功的執行所留下的階段末檔案會被載入回來——而畫布與幾何差異會把那個先前的結果,顯示得像是這次執行做出來的一樣。

所以要憑證據驗收一次重播:

檢查 請求 通過條件
有步 GET /api/execution/cl-strip/range count > 0
有東西被切到 GET /api/execution/strip-chart?aspect=Individual&inspectingKey=IsTouched&xValueCategory=IndexByStep&widthHint=1&dispBegin=0&dispEnd=<count> items[0].max 中的最大值為 1
每支程式都執行過 GET /api/NcProgram/files 每個檔案——以及列在其呼叫者的 children 之下的每支副程式——都至少有一趟 (invocation) 的 executedLineCount 不為零
差異已經建立 GET /api/Workpiece/diff-settings hasDiffOverlay 為 true(而且 detectionRadius 不再是 null)
訊息 GET /api/Execution/messages 每個 id 都有交代(§5)

GET /api/Workpiece/diff-settings 回應 { hasWorkpiece, diffVisualRadius, detectionRadius, hasDiffOverlay };沒有載入專案時則回應 { hasWorkpiece: false }。hasDiffOverlay 說的是此刻有一份覆蓋層掛在即時的網格樹上——既不代表找到了偏差,也不代表有要求過比對——而 detectionRadius 在覆蓋層存在之前為 null,所以 null 的半徑與 false 的旗標是同一個答案。重置會把兩者都清掉。

要明確傳入 dispBegin 與 dispEnd:少了它們,時序圖只涵蓋目前的顯示範圍,而不是整次執行。widthHint=1 最多傳回兩個點:第一個涵蓋去掉最後一步的範圍,第二個只涵蓋最後一步。範圍的峰值取 items[0].max 中的最大值(谷值則取 items[0].min 中的最小值);widthHint=0 則改為每一步傳回一個點。

對某一行的效果有疑問時,GET /api/NcProgram/syntax-piece?fileIndex=<i>&lineIndex=<n> 會傳回執行器剖析出來的那個語句,連同它產生的步——要看出某個位址字究竟附在哪個命令上,這是最直接的方法。

5. 盤點訊息

GET /api/Execution/messages 每份清單傳回一個區段——shell、nc、step 與 ncManip——並以同樣的方式篩選它們全部:

  • minSeverity=Warning&head=50 傳回最前面的符合項,一連串連鎖訊息的起因就在那裡;預設則是從尾端取。
  • tail 的上限是 1000,而 tail=0 只傳回計數——這是估量每份清單有多大的快速方法。
  • idPrefix 把範圍縮小到同一族的 id;kinds 指名要讀哪幾份清單。
  • sinceIndex 一次只替一份清單分頁,所以這時 kinds 必須恰好指名一份。

什麼時候可以停——只剩下已宣告的資訊性訊息,再加上維持交付原樣之寫法的預期訊息——見專案建構 §5。

6. 交叉核對用的數值

§4 的時序圖請求,可以傳回任何步屬性在任何步範圍內的峰值——也就是它傳回的各點中,items[0].max 的最大值。鍵從 GET /api/execution/step-properties 取得——切深檢查用 CuttingDepth_mm,力的峰值用 MaxAbsForce_N。一組粗加工的切深峰值應等於它的每層下刀量;精加工程式的切深峰值卻不是它的預留量,因為壁面或轉角可能以整段刃長吃刀——精加工工作的 Z 基準,改用由參照網格建立工件 §1 的距離測試來檢查。以比預留量還粗的加工解析度執行,能告訴你程式有沒有碰到毛胚、碰在哪裡;力與切深則只能取自解析度比預留量更細的執行。要把範圍縮小到一個檔案,就從 GET /api/NcProgram/files 取得該趟的 firstSentenceIndex 與 lastSentenceIndex,再以 GET /api/NcProgram/steps-of-sentence?sentenceIndex=<n> 把兩者各自換成步。

第 i 步結束時的仿真時間,就是它的結束時間碼:xValueCategory=IndexByTime&widthHint=0&dispBegin=<i>&dispEnd=<i+1> 會在 xs 中以秒為單位傳回它。一個檔案的步範圍兩端之差,就是要拿來與後處理器估計值比較的時間,而這項比較的限度見專案建構 §6。

延伸閱讀