Cetak permadani Game of Thrones pada printer fiskal menggunakan Python

Suatu hari, di salah satu proyek, sebuah printer fiskal jatuh ke tangan saya. Setiap hari kita menemukan perangkat ini ketika kita melakukan pembayaran di toko, tetapi hanya sedikit orang yang menyadari apa sebenarnya mereka. Saya tidak akan merinci pekerjaan mereka, saya hanya akan mengatakan bahwa ini adalah hal-hal yang mencetak tanda terima dengan data pembelian pada kertas termal khusus (ya, hampir semua printer fiskal tidak memiliki tinta!).

Saya harus mencari cara untuk mendapatkan status fungsi printer fiskal dan pengaturan internalnya. Tugas telah lama selesai, dan printer fiskal ditinggalkan untuk waktu yang lama di sudut jauh ... Sampai saya datang dengan ide untuk membuat ulang sedikit: D

Printer semacam itu memungkinkan Anda untuk mencetak gambar monokrom. Ketika saya memiliki cukup banyak cetakan segel, lambang dan foto-foto rekan, saya memutuskan untuk melambai ke cetak permadani panjang berdasarkan seri di mana mereka terus-menerus membunuh seseorang dengan kata-kata "musim dingin sudah dekat" .

Outputnya seperti video:


Langkah-langkah terperinci untuk mencetak permadani dengan python di bawah kucing di bawah ini.

Dalam artikel ini, saya akan menjelaskan secara singkat prosedur dengan komentar rinci, dengan asumsi bahwa output akan menjadi artikel pelatihan dengan beberapa keterampilan praktis yang bermanfaat:

  • unduh video permadani dari youtube
  • buat gambar permadani monokrom panjang untuk dicetak pada printer fiskal
  • terhubung ke printer fiskal
  • cetak permadani pada printer fiskal
  • kami memasang klip video yang dihasilkan untuk publikasi di jejaring sosial

Unduh video permadani dari youtube


Ini dilakukan dengan sangat sederhana menggunakan pytube library, Anda hanya perlu menentukan indeks aliran video yang akan Anda unduh.

Fungsi untuk mengunduh video dari youtube:
import time, pytube #         def load_bmp_from_video(video_url, filename): t1 = time.clock() #     video = pytube.YouTube(video_url) #        streams = video.streams.all() for stream in streams: print(stream) #       ,  18: 360p mp4 video.streams.get_by_itag(18).download("./", filename = filename ) t2 = time.clock() #      print('download done', t2-t1) #       got.mp4   360x640 load_bmp_from_video(video_url = 'https://www.youtube.com/watch?v=aZV4PclhHeA&', filename = 'got') 

Ketika for stream in streams: print(stream) baris for stream in streams: print(stream) dijalankan, kita lihat di konsol daftar semua stream video yang terkandung dalam video:

 <Stream: itag = "22" mime_type = "video / mp4" res = "720p" fps = "30fps" vcodec = "avc1.64001F" acodec = "mp4a.40.2">
 <Stream: itag = "43" mime_type = "video / webm" res = "360p" fps = "30fps" vcodec = "vp8.0" acodec = "vorbis">
 <Stream: itag = "18" mime_type = "video / mp4" res = "360p" fps = "30fps" vcodec = "avc1.42001E" acodec = "mp4a.40.2">
 <Stream: itag = "137" mime_type = "video / mp4" res = "1080p" fps = "30fps" vcodec = "avc1.640028">
 <Stream: itag = "248" mime_type = "video / webm" res = "1080p" fps = "30fps" vcodec = "vp9">
 <Stream: itag = "136" mime_type = "video / mp4" res = "720p" fps = "30fps" vcodec = "avc1.4d401f">
 <Stream: itag = "247" mime_type = "video / webm" res = "720p" fps = "30fps" vcodec = "vp9">
 <Stream: itag = "135" mime_type = "video / mp4" res = "480p" fps = "30fps" vcodec = "avc1.4d401e">
 <Stream: itag = "244" mime_type = "video / webm" res = "480p" fps = "30fps" vcodec = "vp9">
 <Stream: itag = "134" mime_type = "video / mp4" res = "360p" fps = "30fps" vcodec = "avc1.4d401e">
 <Stream: itag = "243" mime_type = "video / webm" res = "360p" fps = "30fps" vcodec = "vp9">
 <Stream: itag = "133" mime_type = "video / mp4" res = "240p" fps = "30fps" vcodec = "avc1.4d4015">
 <Stream: itag = "242" mime_type = "video / webm" res = "240p" fps = "30fps" vcodec = "vp9">
 <Stream: itag = "160" mime_type = "video / mp4" res = "144p" fps = "30fps" vcodec = "avc1.4d400c">
 <Stream: itag = "278" mime_type = "video / webm" res = "144p" fps = "30fps" vcodec = "vp9">
 <Stream: itag = "140" mime_type = "audio / mp4" abr = "128kbps" acodec = "mp4a.40.2">
 <Stream: itag = "249" mime_type = "audio / webm" abr = "50kbps" acodec = "opus">
 <Stream: itag = "250" mime_type = "audio / webm" abr = "70kbps" acodec = "opus">
 <Stream: itag = "251" mime_type = "audio / webm" abr = "160kbps" acodec = "opus">

Saya memilih utas dengan id 18 karena ini adalah resolusi kecil - kami masih mencetaknya pada pita periksa, dan mengunduh lebih cepat))

Buat gambar permadani monokrom panjang untuk dicetak pada printer fiskal


Untuk pemrosesan video, kita memerlukan pustaka OpenCV yang terkenal dan Bantal (garpu modern PIL) (walaupun di sini bukannya OpenCV kita bisa menggunakan utilitas avconv dari alat libav , lebih lanjut tentang hal itu di bagian terakhir artikel ini). Banyak terima kasih kepada penulis untuk menulis python atau python-opencv , yang merupakan roda python, diinstal melalui PIP dan tidak memerlukan instalasi OpenCV sendiri ( hore! ).

Printer fiskal hanya dapat mencetak gambar khusus - file bmp monokrom dengan lebar tetap 528 piksel (tetapi panjangnya tidak terbatas, ho-ho-ho!). Selain itu, gambar permadani dalam klip video terus bergerak, jadi kita perlu memotong bingkai dengan hati-hati agar kita mendapatkan satu gambar panjang.

Semua ini dilakukan oleh fungsi berikut:
 import os, cv2, numpy as np from PIL import Image #            def save_frames_from_vide(filename): #          real_filename = filename.rsplit('.', 1)[0] #         for file in os.listdir('./'): if file.startswith('frame'): os.remove('./' + file) #         frames_list = [] vidcap = cv2.VideoCapture(filename) try: success, frame = vidcap.read() count = 1 while success: # and count < 500: #    #     1  100,     if count in [1, 100, 30945, 31000] or count % 370 == 0: #      (  ) mono_frame = frame if count == 370: mono_frame = mono_frame[0:mono_frame.shape[0], 172:mono_frame.shape[1]] if count == 30710: mono_frame = mono_frame[0:mono_frame.shape[0], 0:mono_frame.shape[1] - 200] mono_frame = mono_frame[20:-20, :] frames_list.append(mono_frame) print('read a new frame: ', success, count) success, frame = vidcap.read() count += 1 finally: vidcap.release() #     gobelin = np.concatenate((frames_list), axis = 1) #   -    cv2.imwrite('%s.png' % real_filename, gobelin) #        image = Image.open('%s.png' % real_filename) #  1        bmp fn = lambda x : 255 if x > 135 else 0 image = image.convert('L').point(fn, mode = '1') #       528  coef = 528. / image.height new_w = int(image.width * coef) new_h = int(image.height * coef) image = image.resize((new_w, new_h)) #    270         image = image.transpose(Image.ROTATE_270) image.save('%s_for_print.bmp' % real_filename) #       'got.png'  'got_for_print.bmp' save_frames_from_vide('got.mp4') 


Pada output, kita mendapatkan gambar panjang dengan gambar seluruh permadani, hanya sebuah fragmen yang ditunjukkan di bawah ini, gambar aslinya memiliki lebar 55.000 piksel dan tidak lulus sesuai dengan aturan publikasi:



Tetapi gambar seperti itu diperoleh setelah transformasi monokrom, hanya tanpa rotasi:



Kami mencetak permadani pada printer fiskal


Saya memiliki printer fiskal spesifik dari model Atol fprint-22 yang saya miliki, tetapi aturan umum berlaku untuk model printer fiskal lainnya. Selain itu, buku fiskal saya sangat kuno dan belum mendukung persyaratan FZ-54 yang ketinggalan jaman baru (saya ingat bahwa setelah berlakunya undang-undang ini, semua petugas fiskal diwajibkan untuk mengirim data melalui OFD ke kantor pajak, yang menimbulkan rasa sakit dan penderitaan - kilasan setiap perangkat).

Penyimpangan kecil tentang printer fiskal. Mereka berhubungan dengan perangkat POS - ini semua jenis periferal untuk kebutuhan perdagangan, yang terhubung ke PC dan diintegrasikan ke dalam sistem akuntansi dan pembayaran tunggal. Dari perangkat-perangkat terkenal ini, Anda pasti melihat pemindai barcode dan terminal pembayaran kartu kredit. Untuk semua perangkat ini, protokol interaksi UnifiedPOS terpadu diciptakan.

Singkatnya, ini adalah topik yang terpisah dan lingkaran spesialis yang sangat sempit yang terlibat dalam perangkat POS. Situasinya diperumit oleh kenyataan bahwa sebagian besar perangkat ini dirancang untuk beroperasi secara eksklusif di bawah Windows melalui objek COM - file dll dengan deskripsi dokumenter fungsionalitas yang sangat buruk. Meskipun saya mendengar tentang sistem kas yang berjalan di bawah FreeBSD, saya belum pernah melihat hal seperti itu ketika bekerja dengan perangkat POS, untungnya, saya tidak memerlukan perendaman terperinci ke dalam dunia proses bisnis POS Ritel ...

Oleh karena itu, prosedur untuk bekerja dengan sebagian besar perangkat ini adalah sebagai berikut:

  • driver diinstal dari pabriknya
  • koneksi ke perangkat melalui utilitas pabrikan dikonfigurasikan
  • mencari kunci registri yang diinginkan dengan perangkat yang diinginkan
  • mencari pengaturan yang diperlukan untuk menghubungkannya
    (sebagian besar bekerja pada antarmuka perangkat lunak serial RS-232)
  • terhubung ke perangkat melalui objek COM dari driver pabrikan
  • bekerja dengan perangkat melalui objek API COM
  • Obyek fisik dan port fisik perangkat COM dilepaskan
    ( poin penting )

Karena saya memiliki buku fiskal kuno yang saya miliki, driver untuk itu digunakan khusus untuk versi ke-8. Sekarang pabrikan telah menambahkan driver versi 10, yang sangat menyederhanakan bekerja dengan printer fiskal melalui modul python wrapper yang terpisah, yang merupakan kabar baik.

Kode berikut menunjukkan fungsi yang menghubungkan ke printer fiskal menggunakan algoritma yang dijelaskan di atas, menghasilkan bunyi bip, dan mencetak gambar permadani monokrom yang diperoleh sebelumnya. Kita perlu menginstal pywin32 .

Kode itu ternyata cukup panjang dan membosankan, jadi saya letakkan di bawah spoiler:
 import win32com.client from _winreg import HKEY_CURRENT_USER, OpenKey, EnumValue #         class FiscallError(Exception) #     COM  , #  COM     def fiscal_print(filename): driver = None try: #      #         8.16. try: hKey = OpenKey(HKEY_CURRENT_USER, r"Software\Atol\Drivers\8.0\KKM\Devices") except Exception as err: raise FiscallError('      ' + '  ' + '   FPrint22-') #       , #       com  try: device_name,device_connect_params,device_connect_dt=EnumValue(hKey,0) except Exception as err: raise FiscallError('     ' + '     ' + '  FPrint22-') #       try: connect_dir = dict([tup.split(u'=') for tup in device_connect_params]) except Exception as err: raise FiscallError('     ' + '    FPrint22-') #    COM  try: driver = win32com.client.Dispatch("AddIn.FPrnM8") except Exception as err: raise FiscallError(' COM  AddIn.FPrnM8   ' + ' FPrint22-    , ' + '    ') #         add_code = driver.AddDevice() if driver.ResultCode != 0: raise FiscallError('     FPrint22-' + ' [ %s] - %s'% (driver.ResultCode, driver.ResultDescription)) #       COM  driver.Model = connect_dir['Model'] driver.PortNumber = connect_dir['PortNumber'] driver.UseAccessPassword = connect_dir['UseAccessPassword'] driver.DefaultPassword = connect_dir['UseAccessPassword'] driver.PortNumber = connect_dir['PortNumber'] driver.BaudRate = connect_dir['BaudRate'] #         #       COM  driver.DeviceEnabled = 1 #         GetStatus, #       . 61    v8.0 res = driver.GetStatus() if driver.ResultCode != 0: raise FiscallError('     FPrint22- ' + '[ %s] - %s' % (driver.ResultCode, driver.ResultDescription)) ###   #  ,       #   ,       driver.Beep() #  ,      (528) print('driver.PixelLineLength:', driver.PixelLineLength) # !!!       # (    COM ) driver.Alignment = 0 driver.LeftMargin = 0 driver.PrintPurpose = 1 driver.AutoSize = False driver.Scale = 100 #      driver.FileName = filename #    bmp  driver.PrintBitmapFromFile #        10  for i in range(10): driver.PrintString() #      driver.FullCut() # ! except FiscallError as err: raise err except Exception as err: raise FiscallError('    ' + '   FPrint22- - %s' % str(err)) finally: if driver: driver.DeviceEnabled = 0 fiscal_print('got_for_print.bmp') 


Keluarannya berupa sebuah naskah, sebuah pertunjukan tentang "A Song of Ice and Fire":



Printer pada akhirnya benar-benar melolong, dicetak perlahan dan redup, dan kemudian sepenuhnya selesai hanya mencetak adegan terakhir - tidak ada printer fiskal yang pernah melihat beban seperti itu: D

Masih menyiapkan video dan memposting di jejaring sosial. Sebagai seri audio , saya menemukan komposisi amatir di jaringan 8bit - tema utama dari seri ini . Idenya adalah untuk menempatkan satu sama lain tanpa menggunakan editor video sama sekali, saya akan menulis tentang ini nanti di bagian akhir artikel.

Kami memasang klip video yang dihasilkan untuk publikasi di jejaring sosial


Untuk keperluan ini, ada alat konsol yang sangat berguna dan kuat yang menggantikan seluruh editor video - libav . Ini termasuk utilitas avconf dan ffmpeg untuk bekerja dengan file video dan audio. Sejujurnya, bagi saya alat ini adalah penemuan nyata, saya merekomendasikannya kepada semua orang!

Ide dasar instalasi:

  • potong dari pengambilan video pada bagian smartphone di bagian awal dan bagian di bagian akhir
    (agar sesuai file mp3 dengan musik 8bit untuk 3 drama)
  • tulis 3 kehilangan file audio ke satu file
  • overlay file video dan file audio ke file video baru
  • konversi file video dari format mov ke format mp4
    (Ponsel cerdas saya merekam video dengan ekstensi ekstensi)

Untuk keperluan ini, saya menulis sebuah skrip untuk dijalankan pada baris perintah, yang dapat dieksekusi baik bash di linux dan bat in win (perbedaannya ditunjukkan dalam komentar skrip):
 #     avconv -ss 00:00:10 -i got_print.mov -t 00:06:00 -c:v copy got_print_tmp.mov #      ( win) (echo file 'got_8bit.mp3' & echo file 'got_8bit.mp3' & echo file 'got_8bit.mp3') > list.txt #      ( linux) # cat list.txt # file 'got_8bit.mp3' # file 'got_8bit.mp3' # file 'got_8bit.mp3' #     3  ffmpeg -f concat -i list.txt -acodec copy got_8bit_3.mp3 #         avconv -i got_print_tmp.mov -i got_8bit_3.mp3 -c copy got_print_final.mov #       mp4 avconv -i got_print_final.mov -c:v libx264 got_print_final.mp4 #    (windows) del got_8bit_3.mp3 del got_print_tmp.mov #    (linux) # rm got_8bit_3.mp3 # rm got_print_tmp.mov 


Itu saja, video dibuat:


PS: artikel pertama saya tentang Habré, berencana untuk menulis artikel pendek sebagai permulaan, memotong sebanyak yang saya bisa) Saya berharap bahwa bacaan itu menyenangkan, dan hasil pekerjaan saya - menarik)

Source: https://habr.com/ru/post/id465485/


All Articles