2018年11月29日 星期四

HMI 回憶錄 (8)

軟體篇 PART3

忙了幾個月之後終於比較有空寫點東西了,這次來討論巨集(macro)

這一次來談談巨集。巨集之於 HMI 相當於 JavaScript 之於 Web Browser(嚴格來說,巨集頂多只能算 10 年前的 JavaScript)。

這樣講一些非工控出身的朋友恐怕還是不知道巨集是什麼,這邊先做個簡單的示範,跟之前一樣,一樣用台達 DOPSoft 隨便建個畫面,控制器選擇 Modbus RTU Master(HMI = Master, PLC = Slave):


然後在畫面上放個"設 On 按鈕":


然後我們希望接點 B1 = on 時執行巨集 "W40001 = W40002 + W40003":



什麼是接點 B1?還有 W40001, W40002, W40003 是什麼?
  • B1-B9999: Coil Address 1-9999, unit: bit
    • 通常映射到 PLC 外部的 Digital Output 或內部記憶體,例如:
      • Relay
      • SSR
      • 電晶體輸出
  • W40001-W40009: Holding Register Address 1-9999, unit: word
    • 映射到 PLC 內部某塊記憶體或暫存器,例如
      • Timer
      • Counter
      • PWM
      • RTC
      • ...
讀者看到這裡也別想太多,反正把 B1, W40001-40003 當成控制器內部記憶體中的 1 個 bit 跟 3 個 word 就好了,動作流程如下:

看到這裡,相信大部分的人已經有概念了。以現在來說,這樣的技術已經很普及了,除了 Web Browser,很多產品也都支援巨集的概念(遊戲尤其用的特別多),像是 Excel VBA,或是 Nginx、Wireshark 支援 Lua script,SPSS 支援 Python script 等等,如果你偏好 C 語言,還可以考慮 Q3VM

更別說自從有了 LLVM 之後,居然可以把 C/C++ 編譯成 wasm 在 web 上執行,真是嚇死寶寶了。

要知道在 1x  年前,工控界還為了巨集打官司。A 公司是 HMI 前輩,B 公司是後起之秀,A 公司認為 B 公司 HMI 巨集跟他的有 87% 像,非常不悅就把 B 公司告上法院,聽說還告贏了。但諷刺的是,B 公司後來越做越大,成了台灣知名企業,相反的 A 公司 HMI 卻越賣越差,聽說最近要停產了。(仔細一看,A 公司的巨集跟x菱 PLC 指令還真有點像,那三x是不是應該先告 A 公司才對!?哈哈哈~)

為何 A 公司堅持要告 B 公司?不妨回想一下,是不是三不五時媒體上就有人發表,台灣人沒創意、喜歡搞 me too、一窩蜂、蛋塔效應 !@#$%^^&...,那你覺得這些山寨廠商都是笨蛋嗎?不知道要創新?

重點來了,只有創業過的人才知道,推廣新技術或新產品最花錢的就是「教育訓練」(這就好比為何 Amazon 要補貼運費,他的目的就是要改變消費習慣),如果今天 B 廠商巨集用了新的語法,就算比 A 廠商好 10 倍,但因為 A 廠商佔有率夠高,就算語法爛又難用又怎樣?客戶已經被訓練習慣了,B 廠商要提供什麼誘因促使客戶學新的語法呢?就算是筆者這種賴家王老五都不一定有動力學,更別說其他只是混口飯吃的工程師,B 廠商此時剩下的選擇不多,要不是靠價格戰,要不然就是養一批人幫忙轉檔,怎麼算都不如直接山寨 A 廠商的語法來的快速有效。

巨集的重點

首先,巨集的重點不是語法,有些人認為 Lua, Python 速度不如 C/C++,所以堅持要用 C/C++,這種說法 10 年前還可以拿出來講,現在再提就沒意思了。先別提 10 年前的 CPU 與現在的 CPU 速度差多少,再來是這 10 年間編譯技術的進步,連 ARM 版本的 Lua JIT(Just-In-Time compiler)都有了,可以讓 script 在執行過程中轉成機械碼。

第二,巨集大部分用於簡單的資料運算與資料搬移(如下圖所示),筆者在這個行業打滾了 8 年,還沒碰過比加減乘除與查表更複雜的運算。這樣說好了,前面提到的 A 廠商巨集語法類似組合語言,你會想用組合語言寫個排序嗎?


第三,巨集速度的瓶頸是通訊,就算通過網路讀寫 PLC 只要花 1ms,相較於讀寫 HMI 內部記憶體仍然慢了 n 倍,除非是最近幾年很熱們的 EtherCAT,但市場上還有一狗票 PLC 仍然依靠 RS232/422/485 這三個老界面進行通訊。像台灣很多人用的三菱 FX 系列,這幾年 FX3U CPU Port baudrate 才開始提供 115200bps,而且還當個寶鎖起來,要通過層層加密才能啟動。

所以與其檢討這些 script language performance,不如先檢討自己的系統架構設計是否合理,有沒有盲點。就像 ChamberPlus 老大常耳提面命要有系統觀念,這一點真的不容易,筆者搞了很多年才有一點感覺。

那巨集的難點在哪呢?或者應該說,HMI 設計的難點在哪?

Synchronize

HMI 內有很多功能都需要與 PLC 通訊,例如 Alarm, Data Logger, GUI, Macro...假設每個功能是一個 task,那總不能每個 task  都直接跟 PLC 通訊:


  • 如果是 RS232/422/485,一次只有一個 task 能夠搶到 serial port,其他 task 只能在外面乾等
  • TCP/IP: 大部分 PLC 支援的 TCP 連線數很有限,從個位數到 10 多個而已。UDP 除了 packet lost issue 外,數個 task  到數十個 task 同時發出 request 會如何也是未知數。
  • 各個 task 想要讀取的 PLC 暫存器、資料記憶體、接點會重複,重複讀取造成頻寬浪費。
  • HMI 功能會成長,客戶也會要求加功能,畫面也會越做越複雜,但是頻寬不會同時跟著成長。
所以唯一合理的設計是在編譯時把所有 task 欲讀取的 PLC 位址合併排序(這也是編譯的過程之一),統一讓一個 task (通常稱為 PLC driver) 去讀取,筆者在另一篇深入淺出 RS485 通訊 PART 1也提過類似的概念:


這樣就結束了嗎?難的地方才剛剛開始,假如 Macro Task 把 D0 改寫成 456,那我們知道網路通訊是有延遲(latency)的,以 RS232/422/485 為例,9600bps n81 傳送一個 byte 就要 1ms 左右(1/9600*10 = 1.04 ms),假設 request 需要用到 5byte = 5ms,reply 也是 5byte,那總共就需要 10ms。那你覺得這 10ms 內 Alarm, Data Logger, GUI 應該看到 123 還是 456???


不妨這麼想。首先,假設寫入動作就如同 A 從 ATM 匯款給 B,在匯款尚未成功前,B 刷存摺金額還是不變,第二點是寫入動作應該越快完成越好。第二點比較好解釋,但要怎麼讓寫入動作越快完成越好?

所以說這時候有沒有 RTOS 就有差了,如果 PLC driver task priority 最高,Macro 透過IPC(Inter-Process Communication) 通知 PLC driver task 有東西要寫進去,PLC driver task 就可以馬上接手處理。以下圖來說,(1) 當 PLC driver 在讀第 n 次時有 task 透過 IPC 要求 PLC driver 寫入 D0,(2) 原本 n+1 次 Read 就可以替換成 Write,於是寫入動作可以即時(Real-Time)完成。

另外,還記得筆者之前提過工控通訊需要精準的 sleep 嗎?這時候你就知道為什麼了,PLC driver task polling PLC 就是依靠這個精準的 sleep 作為時間間隔,整個系統才會是可預測的(deterministic),你的寫入動作也才能如期完成,如果 sleep 飄來飄去的,就算客人願意修改 PLC 程式配合你,他搞不好也改不出來,或是你的產品表現時好時壞。


有人會問上圖 network latency 會不會飄來飄去?造成我的產品表現不好?這個你大可放心,以 PLC CPU Port 來說,原本就是用於監控 PLC 程序,所以 (2) 的時間都很固定(筆者甚至用示波器去量過數家知名品牌 PLC)。如果是 PLC 通訊模組,PLC 廠商也老早考慮進去了,以三菱來說,他會在 protocol 內要你填入願意等 PLC 多久等等之類的參數,不過如果是台廠,那就不好說了,哈哈哈。

現在假設 PLC driver 寫入成功,也就是下圖 (1) 箭頭指向的地方,假設這時候 checksum、PLC 回傳碼都正確無誤,下一步要做的就是更新 D0,有些人會認為既然 D0 = 456,那來個先斬後奏,先始就把 Cache 內的 D0 改成 456 再寫入 PLC 不就好了,這樣就其他 task 可以繼續運作豈不美哉。


這樣做是錯的!原因如下:
  • PLC 裡面有程式在運作,你寫 456 進去,不代表後面讀出來也會是 456。
  • 你去 ATM 匯款,還沒有匯款完成 ATM 就在螢幕上顯示匯款成功,你覺得對嗎?
  • 計算機結構裡的 write back, write through cache 概念不能套用進來。
這是工控裡面一個很重要的觀念,台灣不少公司認為 RD 找剛畢業的就好,如果這番操作是可行的,那為何筆者以前還要花很大的力氣跟一些剛畢業沒多久、甚至已經有工作經驗、學歷比筆者好的同事解釋這個觀念呢?就像 ChamberPlus 老大在一篇文章中講的"...我國的電腦教育多成功啊,連中小學生都會寫程式啊,那為什麼全世界有名的軟體公司都沒有在我國呢?!那我們搞的系統東西為何還那麼不精緻呢?.."

那這時候馬上再讀一次 D0 就好?只對了一半,因為此時 Cache 內的資料已經不可信了,所以動作應該是將 D0 設定為失效,如果用 ARM I Cache, D cache 類比,就是 cache line 中的 valid bit 清為 0。

因為此時已經將 Cache 中的 D0 設為失效,所有參考 D0 的 task 就會要求 PLC driver 更新 D0 。這邊把 PLC driver 寫入 D0 的步驟表列如下:
  1. 將 456 寫入 D0
  2. 將 Cache 中的 D0 設定為失效(或直接刪除也行,看你高興)
一旦將 D0 設為失效,下一次所有想要讀取 D0 的 task 就會要求 PLC driver 刷新 D0,這會比立刻讀回 D0 更有效運用頻寬。筆者當初在實作這個部份時,因為已經受限於前人定下的架構,只能用立即讀回這種作法,甚至想要把 D0 設為失效都沒辦法。

當然啦,筆者人微言輕,以上說法想必一堆人半信半疑,各位可以參考對岸某位高手寫的文章:「缓存更新的套路」,這篇文章提到了各種 Cache 更新 pattern 還有出處。筆者寫下這篇文章主要作為留念跟回憶,至於有沒有人要信已是其次。

以上講的東西,在 Web SCADA、IoT ...的挑戰會更加嚴峻,當網路上有成千上萬個節點共享資料時,如何保證資料的一致性跟正確性?這也是為何有人提出區塊鏈可以用在工控網路上,不過在筆者離開那個圈子前,感受到台廠還是偏硬體為主,這方面的論述幾乎為 0,還記得當初筆者老東家說要做 IoT Gateway 時,居然是先花錢找人做工業設計!至於軟體要做成什麼樣子反到沒什麼人在意。要知道 Raspberry Pi 連衣服都沒有,但是已經有人拿來接案,也搶走 IPC 廠的生意,筆者上週逛書店時發現一本書「失敗學:那些殭屍企業教我的事」,裡面有句話讓筆者印象深刻,就是「把自己的品味投射到市場的品味」。

其實這篇文章還缺了一些東西,不過實在寫的太長,我猜已經很多人不想看了,就留到下篇吧!

沒有留言:

張貼留言