最近一個禮拜調研了下斑馬打印機怎樣實現網絡打印。
緣起:
之前實現打印方式是直接使用USB接口連接PC,使用串口通訊提供一套打印服務,在系統界面配置相關參數,即可調用打印服務;
後來業務需求變化,現場實施並沒有PC提供給打印機使用USB連接方式,因此,就開始做了這件事。
調研後方案:
硬件:一臺Zebra ZT210斑馬打印機、一個USR W610模塊、一根網線
方案一:
後端發送ZPL指令到打印機,封裝統一調用
方案二(捨棄):
使用本地斑馬打印機驅動調用打印機,普通打印Ctrl + P調起的界面(前端實現,此種方式有幾個難點:1.格式調整;2.二維碼或者條碼的生成,但這對於前端也不是什麼難點,有實現的,相比下後端實現簡單)
方案一實現過程:
打印流程:
有人的USR W610模塊實物(天線可以忽略,本次使用的網線,網口在電源線旁邊):
USR W610:充當一個TCP Server,上電後,插上網線,輸入模塊後面的ip(賬號密碼:admin/admin)就可以訪問它的管理界面。若之前設置過ip,找根筆戳一下重置按鈕。設置ip和端口,後端使用socket進行連接時會使用。
USR W610後臺界面:
· 後端核心代碼:
def post(self, request): """新增對象 Args: request (rest_framework.request.Request): HTTP request Returns: response(rest_framework.response.Response): HTTP response. error_response(rest_framework.response.error_response): error_response """ request_data = request.data template_name = request_data.get('template_name', '') if not template_name: return error_response(reason='模板名稱不能爲空', info='template_name is required', state=status.HTTP_400_BAD_REQUEST) function_name = request_data.get('function_name', '') if not function_name: return error_response(reason='功能模塊名稱不能爲空', info='function_name is required', state=status.HTTP_400_BAD_REQUEST) is_function_name_exist = PrinterConfig.objects.filter(function_name=function_name).exists() if not is_function_name_exist: return error_response(reason='功能模塊名稱不存在', info='function_name is not exist', state=status.HTTP_400_BAD_REQUEST) is_template_name_exist = PrinterConfig.objects.filter(template_name=template_name).exists() current_dir = os.path.join(settings.BASE_DIR, 'print_template') is_template_name_in = template_name in os.listdir(current_dir) if not any([is_template_name_in, is_template_name_exist]): return error_response(reason='模板名稱不存在', info='template_name is not exist', state=status.HTTP_400_BAD_REQUEST) file_path = os.path.join(current_dir, template_name) qr_code_file = open(file_path, 'r', encoding='utf-8') template_data = qr_code_file.read() template = Template(template_data) zpl = template.render(Context(request_data)) # 攜帶ZPL指令向打印機發送http請求 ip_port = PrinterConfig.objects.filter(template_name=template_name).values_list('printer_ip', 'printer_port') ip, port = ip_port[0][0], ip_port[0][1] if not all([ip, port]): return error_response(reason='打印配置的IP或端口未配置', info='printer ip and port must be configured', state=status.HTTP_400_BAD_REQUEST) # print_server = 'http://' + str(ip) + ':' + str(port) client = None try: # requests.post(print_server, data=zpl.encode('utf-8')) client = socket.socket(socket.AF_INET, type=socket.SOCK_STREAM) client.connect((ip, port)) client.send(zpl.encode('utf-8')) # 打印機未連接! except Exception as other_except: # pylint: disable=broad-except except_info = other_except.args[0] # except_info = other_except.args[0].args[0] # if except_info == 'Connection aborted.': if isinstance(except_info, tuple) and except_info == 'Connection aborted.': return Response({'result': '打印成功,請確認'}) else: logger.error(other_except) return error_response(reason='打印失敗,請檢查打印配置是否正確', info=str(other_except), state=status.HTTP_400_BAD_REQUEST) finally: client.close() return Response({'result': 'ok'})
特殊說明:
1、正如代碼所表現的,打印機不會給模塊響應,模塊也就不會給後端響應,打印成功會拋Connection aborted.異常,實際已經打印出來。但這裏爲什麼要請確認,是因爲在TCP連接正常情況下,即使把耗材取出,比如把標籤紙拿出去,也會打印成功,等換上紙後,打印任務隊列對接着打印,所以這個就需要現場人員確認了。
2、模板文件裏面的ZPL指令編寫,可以參考http://note.youdao.com/noteshare?id=05f00edb5f88cfe16543337f8c7f17aa&sub=77F69DD3BA7E4961A3435E9DFA7D15E5 也可以使用Zebra Designer工具進行設計生成.prn文件,文本打開即可看見ZPL指令。因爲自動生成的.prn文件中ZPL指令是經過GFA加密過的,不便於使用模板語法替換,生成的內容也相比自己寫的多很多。工具界面如下:
.prn生成的zpl指令示例:
自己參考ZPL指令手冊寫的指令示例:
^XA ^CI28 ^CW1,E:SIMSUN.TTF ^MD20 ~SD20 ^FO142,105 ^BQN,2,10 ^FD {{qr_code_print}} ^FS ^XZ
其他說明:zpl指令中有兩個值得注意的:SD 設置暗度:若打印字跡比較淡時設置 PR打印速率:若打印字跡比較稀時設置
參考資料:
https://www.cnblogs.com/chengeng/p/7676046.html
https://max.book118.com/html/2018/1006/8002046103001125.shtm
https://www.usr.cn/Down/USR-W610_instructions.pdf