1. 程式人生 > >python實現斑馬印表機網路列印

python實現斑馬印表機網路列印

  最近一個禮拜調研了下斑馬印表機怎樣實現網路列印。

  緣起:

    之前實現列印方式是直接使用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