Last active
August 3, 2017 07:05
-
-
Save mqu/85a3b60a64a963e771962b8d27e03da9 to your computer and use it in GitHub Desktop.
ruby API for IER-411french ANSP strip printer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/ruby | |
| # ier411.rb - ruby API to manage and write to IER-411 strip printer | |
| # | |
| # author : Marc Quinton / 2017.08 - DSNA-DTI/EEI. | |
| # link : https://gist.github.com/mqu/85a3b60a64a963e771962b8d27e03da9 | |
| # | |
| # keywords : ATC, ANSP, strip, printer, ruby, serial, xon-xoff. | |
| # | |
| # simple API to manage and write to IER-411 strip printer, later in use in French ANSP airports | |
| # | |
| # setup : | |
| # - direct connexion from PC/DB9 to printer/DB25 with null-modem cable (see below) | |
| # - printer settings : | |
| # - firmware : 98.084/09 - 06/05/93 | |
| # - tty : 9600, 7 bits, 1 stop bit, odd parity, no-flow control | |
| # - printer : dialog mode : "mot-d'etat' (p17) | |
| # - novram (nvram) settings : | |
| # - 0 1 2 3 4 5 6 7 8 9 A B C D E F | |
| # 00 - C 4 0 0 1 0 E 5 1 0 0 5 5 1 1 0 | |
| # 10 - 0 2 2 1 0 0 0 0 1 0 0 0 1 6 5 0 | |
| # 20 - 0 0 3 5 0 2 2 0 0 0 6 0 0 5 0 0 | |
| # 30 - 0 0 0 0 0 0 5 1 0 0 . . . . . . | |
| # | |
| # | |
| # printer documentation from IER manufacturer : | |
| # | |
| # - CENADOC : NT08-014 - https://ged.dsna-dti.aviation-civile.gouv.fr/doc/51520 | |
| # | |
| # IER-411 strip printer for ATC paper striping. | |
| # RS232 pinout : | |
| # | |
| # PC printer | |
| # DB9 DB25 | |
| # ----------------------- | |
| # 2 (RD) 2 (TD) | |
| # 3 (TD) 3 (RD) | |
| # 5 (GND) 7 (GND) | |
| # 6 (DSR) 20 (DTR) | |
| # 4,5,6 connected (RTS, CTS, DSR) | |
| # | |
| require "pp" | |
| require "timeout" | |
| # API : http://www.rubydoc.info/gems/serialport/SerialPort | |
| require 'serialport' # apt-get install ruby-serialport | |
| def debug txt | |
| $stdout.puts txt | |
| $stdout.flush | |
| end | |
| class ASCII | |
| NUL=0x00 | |
| SOH=0x01 | |
| STX=0x02 | |
| ETX=0x03 | |
| EOT=0x04 | |
| ENQ=0x05 | |
| ECK=0x06 | |
| BEL=0x07 | |
| BS=0x08 | |
| LF=0x0A | |
| FF=0x0C | |
| CR=0x0D | |
| DC1=0x11 # XON | |
| DC2=0x12 # | |
| DC3=0x13 # XOFF | |
| DC4=0x14 # | |
| NAK=0x15 # | |
| SYN=0x16 # | |
| CAN=0x18 # Cancel | |
| ESC=0x1B # | |
| SP=0x20 # | |
| end | |
| class TTY | |
| def initialize dev | |
| @dev=dev | |
| #params for serial port | |
| # IER411 : 9600, 7 bits, 1 bit de stop, parité impaire (odd), XON/XOFF | |
| baud = 9600 | |
| data_bits = 7 | |
| stop_bits = 1 | |
| parity = SerialPort::ODD | |
| @tty=SerialPort.new(dev, baud, data_bits, stop_bits, parity) | |
| @mutex=Mutex.new | |
| end | |
| def write txt | |
| @mutex.synchronize { | |
| if txt.is_a? Fixnum | |
| txt=[txt].pack('c') | |
| debug "# tty::write():#{txt}" | |
| return @tty.write txt | |
| else | |
| debug "# tty::write():#{txt}" | |
| return @tty.write txt | |
| end | |
| } | |
| end | |
| def getc | |
| @mutex.synchronize { | |
| @tty.getc | |
| } | |
| end | |
| # read as many characters as possible, before timeout (in ms) | |
| def read_timeout count=nil, delay=100 | |
| chars=[] | |
| begin | |
| Timeout.timeout(1) do | |
| while true | |
| puts 'reading from tty' | |
| c=getc | |
| puts "have read char : #{c}" | |
| chars << c | |
| end | |
| end | |
| rescue Timeout::Error | |
| puts 'read timeout' | |
| end | |
| return chars | |
| end | |
| def flush | |
| @tty.flush | |
| end | |
| end | |
| class IER411 < TTY | |
| # mot d'état : | |
| # bit 0 : defaut de réception | |
| # bit 1 : présence papier | |
| # bit 2 : mode local | |
| # bit 3 : reserve mini papier atteinte | |
| # bit 4 : impression en cours | |
| # bit 5 : defaut technique | |
| # bit 6 : débordement buffer (overflow) =~ 256 octets libres | |
| # bit 7 : parité impaire (?) | |
| STATUS_RECEPTION_DEFAULT = 1<<0 | |
| STATUS_PAPER_OK = 1<<1 | |
| STATUS_LOCAL_MODE = 1<<2 | |
| STATUS_MIN_PAPER = 1<<3 | |
| STATUS_PRINTING = 1<<4 | |
| STATUS_TECHNICAL_DEFAULT = 1<<5 | |
| STATUS_OVERFLOW = 1<<6 | |
| STATUS_ODD_PARITY = 1<<7 | |
| def initialize dev | |
| super | |
| # read timeout in ms | |
| # @tty.read_timeout=3000 | |
| # no flow control | |
| # the flow control flag, NONE, HARD, SOFT, or (HARD | SOFT) | |
| # see : | |
| # - https://github.com/hparra/ruby-serialport/blob/master/ext/native/serialport.c#L164 | |
| # - https://github.com/hparra/ruby-serialport/blob/master/ext/native/posix_serialport_impl.c#L497 | |
| @tty.flow_control=SerialPort::NONE | |
| @status={} | |
| end | |
| # p15 : demande d'annulation (cancel) | |
| # | |
| def cancel | |
| @tty.write ASCII::CAN | |
| end | |
| def ff | |
| # p25 : FormFeed (FF) / permet l'impression du message | |
| @tty.write '^,' | |
| end | |
| # impression d'un texte simple | |
| # taille maxi d'une ligne : 83 caractère | |
| # au dela : pas d'impression et pas de remontée d'erreur. | |
| def write txt | |
| # par précaution, on peut annuler toute commande en cours. | |
| # self.cancel | |
| # debut de message | |
| @tty.write '^A' | |
| # envoi du texte | |
| @tty.write txt | |
| # ejection du strip avec coupe (a faire en phase d'init de préférence) | |
| # write '^K0' | |
| # form-feed | |
| self.ff | |
| # fin de message | |
| @tty.write '^X' | |
| flush | |
| end | |
| def init | |
| @tty.write '^(' | |
| end | |
| # p16 : mot d'état : ASCII::ENQ ou ^% ou ASCII::CAN (cancel) | |
| # réponse : 1 octet avec le mot d'état, codé par bits. | |
| def get_state | |
| @tty.write ASCII::CAN | |
| # @tty.write '^A^%^Z' | |
| status=getc | |
| if status==nil | |
| puts "# printer read timeout" | |
| @status[:timeout=]=true | |
| return nil | |
| else | |
| status=status.unpack('c*')[0] | |
| @status[:timeout=]=false | |
| @status[:value]=status # mot d'état | |
| @status[:printing]=status&STATUS_PRINTING!=0 | |
| @status[:local_mode]=status&STATUS_LOCAL_MODE!=0 | |
| @status[:paper_ok]=status&STATUS_PAPER_OK!=0 | |
| @status[:reception_default]=status&STATUS_RECEPTION_DEFAULT!=0 | |
| @status[:overflow]=status&STATUS_OVERFLOW!=0 | |
| @status[:min_paper]=status&STATUS_MIN_PAPER!=0 | |
| @status[:technical_def]=status&STATUS_TECHNICAL_DEFAULT!=0 | |
| @status[:parity]=status&STATUS_ODD_PARITY!=0 | |
| return @status | |
| # puts sprintf("status=%d (%s)", status, status.to_s(2)) | |
| end | |
| end | |
| def status | |
| return @status | |
| end | |
| def printing? | |
| return @status[:printing]==true | |
| end | |
| def ready? | |
| return @status[:local_mode]==false && | |
| @status[:paper_ok]==true && | |
| @status[:technical_def]==false && | |
| @status[:printing]==false && | |
| @status[:overflow]==false && | |
| @status[:parity]==false && | |
| @status[:reception_default]==false | |
| end | |
| # return true when printer has finish printing and is not in printing mode | |
| # does not take care of "local_mode", "technical_def", ... | |
| def idle? | |
| return @status[:printing]==false | |
| end | |
| # continue when printer is idle | |
| def when_idle | |
| while true | |
| return true if idle? | |
| sleep 0.1 | |
| end | |
| end | |
| # continue when printer is idle | |
| def when_ready? | |
| while true | |
| return true if ready? | |
| sleep 0.1 | |
| end | |
| end | |
| end | |
| ier=IER411.new "/dev/ttyS0" | |
| ier.init | |
| # lecture permanent du mot d'état (polling) et mise a jour de l'état dans l'objet | |
| t=Thread.new { | |
| while true | |
| ier.get_state | |
| # pp ier.status | |
| sleep 0.2 | |
| end | |
| } | |
| test=:multi_line_test | |
| # test=:reverse_test | |
| test=:ready | |
| test=:strip | |
| test=:status | |
| case test | |
| when :ready | |
| while true | |
| sleep 0.5 | |
| pp ier.ready? | |
| end | |
| when :status | |
| while true | |
| pp ier.ready? | |
| sleep 1 | |
| end | |
| # OK | |
| when :multi_line_test | |
| ier.cancel | |
| # impression d'un message simple (sans formatage). | |
| msg='0123456789' | |
| msg=msg+msg+msg+msg+msg+msg+msg+msg | |
| msg=msg+(ASCII::LF).chr | |
| msg=msg+'0123456789'+(ASCII::LF).chr | |
| msg=msg+msg+msg | |
| ier.write msg | |
| sleep 2 | |
| exit 0 | |
| # KO | |
| when :font_size_test | |
| # impression d'un message simple (sans formatage). | |
| # font size ^Ellhh (ll=largeur, hh=hauteur) | |
| msg='^E0101' | |
| msg='0123456789' | |
| msg=msg+msg+msg+msg+msg+msg+msg+msg | |
| ier.write msg | |
| sleep 2 | |
| exit 0 | |
| # OK | |
| when :reverse_test | |
| msg='^R0123456789' | |
| msg=msg+msg+msg+msg+msg+msg+msg+msg | |
| ier.write msg | |
| sleep 2 | |
| exit 0 | |
| when :strip | |
| pp ier.idle? | |
| msg1='^E0101^E0301^J02^T004AEL2956^E0101^J02^T0145705^J06^T001B772^J06^T006487^J06^T010LIRF^J06^T016MUCU^J10^T016310^O0^J12^T0161101^E0101^J11^T023C1^J09^T023133.57^J01^T081L1 ^J01^T028VALKU^T034LMG^E0101^E0101^J03^T03010^T03622^E0101^O0^J03^T02811^T03411^E0101' | |
| msg2='^E0101^E0301^J02^T004CRX636^E0101^J02^T0146705^J06^T001SB20^J06^T006360^J06^T010LFSB^J06^T016LEMG^J10^T016250^O0^J12^T0161102^E0101^J11^T023T1^J09^T023^J01^T081G1 ^J01^T028MILPA^T034NINTU^T040MEBAK^T046LERGA^E0101^E0101^J03^T03015^T03617^T04223^T04836^E0101^O0^J03^T02811^T03411^T04011^T04611^E0101' | |
| ier.when_ready? | |
| ier.write msg1 | |
| ier.when_ready? | |
| ier.write msg2 | |
| ier.when_ready? | |
| ier.write msg1 | |
| end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment