2016年4月29日 星期五

通訊協議個案研究

工控這個圈子大概是最熱衷發明新輪子的圈子,不但有圓的扁的,八角形,長刺的,定時爆炸跟不定時爆炸的。



其中,通訊協議(communication protocol)更是不斷誕生各種奇行種,就算是標準的 MODBUS RTU,也是有人硬要讓他位址長成橫的 = =

通訊協議的派系之分


字串派


這一派認為通訊協議應該用 ASCII 中的可見字元編碼,比方說 123 就編碼成 "7B",這有幾個優點:

  • 除錯容易,sniffer 下來的資料容易識別
  • 接收資料方便,因為只要拿個不可見字元來當結尾字元(sentinel),讀到結尾字元就代表讀到完整的封包。
  • 各種常用程式語言都內建相關 API,比方 C/C++ sprintf()。

MODBUS ASCII 就是一例,開頭字元用「:」,結束用 CR LF,中間編碼編成 Hex String:



二進位派:


這一派認為前面那一派浪費頻寬,而且很容易給人看光光沒安全感,應該所有的資料都最大限度使用二進位緊密表示,這樣才能充分運用頻寬,順便保密,一兼二顧,個人相信本派得到了台灣企業 cost down 的真傳

本派優點有:

  • 可運用 bit packing 技巧壓縮資料,比方說把年月時分秒放在一個 32bit 整數內。
  • 如果沒有公開協議,外人難以猜測內容,具有某種程度的保密效果。
  • 對於記憶體先天不足的小型裝置如微控制器,可以節省記憶體空間。

這一派也不是沒有缺點:
A + B + C + D = A + B + C  + D + E

簡單的 check sum 將無法發現這個錯誤,這也是為何 MODBUS RTU 要改用 CRC16 的原因。

整人混合派:


本派相信可以結合「字串派」跟「二進位」派的優點,我們以 V 廠牌 VS 系列控制器為例,這個廠牌公開了自己的協議,他的協議長像如下:



除了頭部的 10H 02H 與尾巴的 10H 03H CHKSUM(Hex String)外,在灰色部份內如果出現 10H,就需要轉成 10H 10H。這不是什麼新鮮的用法,例如 Visual Basic 字串常數裡要表示「"」,就要打成「""」(例 "A""B" => A"B)。

假如我們傳送的資料是完全隨機的,比方說灰色的部份是 4 bytes 好了,那麼出現 10H 的機率是多少呢?


看來我們需要去展開 10H 的機率真的很小,但是 PLC 可不是亂數產生器,我們必須作最壞的打算。不過就算真的要展開 10H,大不了準備另一塊 buffer 存放結果就好。

真正麻煩的是接收,請問要如何判斷結尾呢?事實上,你只能判斷結尾的 10H 前不是 10H(下圖中的 XX),才能放心判斷已經讀完一個完整封包了:

所以這個協議 header 裡,又放了後續資料的長度(不包括 10H 03H CHKSUM),但是要讀到這筆資料,還是得先解開 header,那如果 header 裡面有 10H 10H,就必須先折疊回 10H,才能讀到正確的資料...所以變成得一邊讀一邊解,要不解完先保留,總而言之都是 #@#$%^&...

其實,如果當初該位設計者能站在 user 角度想一想,改成下面這樣,大家日子就會好過多了:


這樣 user 只要讀到 10H  後面是 Hex String (因為 Hex String 不可能有 10H),就可以放心判斷已經讀取結束,可以進行下一步的解碼了。

或許,該位設計者用心良苦,如果您能克服這個協議帶來的挑戰,基本上也是個 power user,您大概也不需要他們的技術支援,我們也只能這樣正面思考了。

The End



沒有留言:

張貼留言