R·ex / Zeng


音遊狗、安全狗、攻城獅、業餘設計師、段子手、苦學日語的少年。

盲除錯解決 Zebra 印表機吞紙問題的經歷

本來我以為,前端的工作內容只需要跟瀏覽器打交道,最多再做一做客戶端和後端;萬萬沒想到,如標題所示,我居然需要解決印表機的問題。

奇怪的需求

我們有一個畫面單的平臺,關於這個平臺,我之前已經寫過幾篇文章,這裡就不多提了。

一天,面單的產品突然把我拉到了一個群裡,因為我們的業務 PM(BPM)接到了一個緊急需求:在我們開始跟某個賣家合作時,他們提出:我們在列印面單的時候,必須支援透過一個叫 Zebra 的印表機來列印,否則就不跟我們合作。由於那是一個大賣家,因此這似乎是一個無法拒絕的需求。雖然我們完全沒聽說過這種印表機,還是硬著頭皮上了。

經過調查發現,Zebra 印表機與一般的印表機不太一樣,它無法透過系統的列印框來列印,甚至不是走的 COM 或 USB 介面,而是監聽了一個 TCP 埠來與電腦通訊。傳送給印表機的也不是一般的格式,而是一個叫 ZPL 的私有格式,看起來像是純文字(例子來自英文維基):

^XA
^LH30,60
^FO20,10
^ADN,90,50
^FDWikipedia^FS
^XZ

第一眼看上去並看不懂。但透過谷歌,我們搜到了 ZPL 的各類教程,以及一個簡易的預覽工具:Online ZPL Viewer

開始調研

在調研之初就遇到了一道選擇題:是直接生成 ZPL 格式,還是透過某些中間格式來轉換呢?這兩種方式似乎都可行:

  • 直接生成:面單平臺生成的面單原始格式是 JSON,這可以很方便的儲存和匯入匯出。我們寫了一個轉換器,當需要渲染面單時,可以呼叫轉換器將 JSON 轉換為 HTML,然後再呼叫 Headless Chrome 將 HTML 轉換為 PDF 返回給第三方。既然有這個轉換器,ZPL 又是純文字,語法還是確定的,稍微學一學 ZPL 再寫一個轉換器似乎是個最好的辦法。
  • 轉換生成:我們團隊沒人接觸過 ZPL,甚至我周圍都找不到 Zebra 印表機的影子。如何知道預覽工具與實際打印出來的差距有多大?如果有一個很成熟的庫可以幫忙轉換,無論是我們的支援速度,還是轉換效果,都會好很多。

經過與同事們的討論,如果直接生成的話,我們有學習成本,也有不可預期的相容問題,因此決定轉換生成。

技術方案與實現

搜遍了全網,唯一一個可用的庫是 kylemacfarlane/zplgrf,是一個 Python 庫。面單平臺本來已經基本從 Python 切換為 Golang 了,為了這個庫,還是得再起一個 Python 服務。瞭解了一下這個庫,它可以將 PDF 轉換為一個叫 GRF 的格式,再將其轉換為 ZPL,只需要很簡單的程式碼就可以實現:

from zplgrf import GRF
import sys

with open(sys.argv[1], 'rb') as pdf:
    data = pdf.read()
    # 203 dpi 就是 8 dpmm,一個比較低的精度
    pages = GRF.from_pdf(data, 'DEMO', dpi=203)
for grf in pages:
    print(grf.to_zpl())

我隨便從測試環境搞了一張面單,匯出成了 PDF 檔案,它長這樣:

原始 PDF

轉換後的 ZPL,雖然裡面看起來一堆 Base64,但預覽出來除了糊一點以外,似乎沒什麼問題:

轉換後的 ZPL,使用了 8 dpmm 的精度

ZPL 的解析度有很多選項,但高解析度意味著轉換速度慢。我們跟 BPM 合作,得知了目標使用者持有的印表機型號,多次對 UAT 環境的幾張真實面單採用 8 dpmm、12 dpmm、24 dpmm 的解析度做轉換並記錄轉換時間。經過權衡,我們決定還是採用 8 dpmm 的解析度。

經過一段時間與賣家中心團隊的聯調與測試,功能順利上了 UAT。

捲紙問題的修復

本來以為萬事大吉,結果業務報來了一個問題:對於某一個特定型號的印表機,在列印結束後,印表機會多吐一點點紙,然後把它捲回去。所有賣家的那個型號的印表機都能穩定復現這個問題,但他們列印其它平臺的 ZPL 面單就沒有問題。這可能會讓我們的使用者感到不滿,因此我們必須解決。

一個棘手的問題是,我們這邊完全沒有 Zebra 印表機,現買的話也太慢了。我只好硬著頭皮看生成的檔案,並且讓 BPM 提供了一個其它平臺的 ZPL,我要知道究竟是什麼東西導致的。

經過對比,我發現我們生成的 ZPL 檔案有一些奇怪的指令。參考了 ZPL 指令集文件,發現有個叫 ^MM 的指令可以控制列印完成時印表機的行為,如果把它設定為 R,可以阻止帶有捲紙控制元件的印表機的捲紙行為。我搜了一下那個型號的印表機(谷歌的結果排前幾名的居然是我們的競爭對手 Lazada 與 Tokopedia),確實有捲紙控制元件,這應該就是解決方法了。

在上面的程式碼中,GRF.from_pdf 函式可以接受一個引數 print_mode,將其修改為 R 之後,捲紙問題解決了,但業務反饋說出現了錯位問題……後來我找到了 Zebra 印表機的官方驅動文件,發現印表機的預設行為應當是 T,於是設定 print_mode='T',問題解決。

由於我們與業務有一些時差,我們所做的所有嘗試,業務都需要第二天才能驗證,因此解決的時間稍微長了一些。

文字模糊問題的迅速修復

終於把需求上線了。但剛上線沒多久,大量賣家就反饋文字不清晰,很多文字的部分是缺失的:

文字有一部分缺失

之前的經驗 立即告訴我,是純黑白與解析度導致的問題:這個字號的文字在 8 dpmm 的解析度下,一部分“筆畫”由於太細,會被直接轉換為白色。解決方法也很簡單:要麼提升解析度(花費更多時間且需要重新發版),要麼加粗一下面單的文字試一試。

我們先讓業務團隊幫忙修改面單,將文字加粗。問題居然就解決了,效果還不錯:

加粗了文字之後的效果

隨即,業務發來了感謝信,許多賣家感謝我們如此迅速的解決問題。

但這畢竟只是個臨時的解決方法,問題的根本原因還是解析度太低(印表機只支援純黑白的問題我們無法解決)。為了避免以後再出現類似的問題,我們需要提供一個高解析度的選項。經過估算,如果從 8 dpmm 提升到 12 dpmm,由於面積擴大到兩倍多,轉換時間應當也是兩倍多。我們的後端也會與賣家中心的後端進行進一步的測試,以評估這個修改的影響範圍。

從這個需求中學到的

  1. 理論上來說,問題都是可以解決的,需要有一定的搜尋技巧和探索精神。
  2. 學好英文很重要,我查的全部資料,都是沒有中文的。
  3. 在必要的情況下,可能需要深入閱讀官方的驅動文件或指令集文件。

總的來說,也是一次不錯的經歷,最後問題也解決了。之後再遇到類似的需求,我應該會比這次更加從容。

Disqus 載入中……如未能載入,請將 disqus.com 和 disquscdn.com 加入白名單。

這是我們共同度過的

第 3854 天