Skip to content

Instantly share code, notes, and snippets.

@mqu
Last active August 3, 2017 07:05
Show Gist options
  • Select an option

  • Save mqu/85a3b60a64a963e771962b8d27e03da9 to your computer and use it in GitHub Desktop.

Select an option

Save mqu/85a3b60a64a963e771962b8d27e03da9 to your computer and use it in GitHub Desktop.
ruby API for IER-411french ANSP strip printer
#!/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