Class: Driver::Datecs::Fp550

Inherits:
Extface::Driver::Base::Fiscal
  • Object
show all
Includes:
Extface::Driver::Datecs::CommandsV1
Defined in:
app/models/extface/driver/datecs/fp550.rb

Defined Under Namespace

Classes: Frame

Constant Summary collapse

NAME =
'Datecs FP550 (Serial)'.freeze
RESPONSE_TIMEOUT =

seconds

3
INVALID_FRAME_RETRIES =

count (bad length, bad checksum)

6
ACKS_MAX_WAIT =

count / nothing is forever

60
NAKS_MAX_COUNT =

count

3
TAX_GROUPS_MAP =
{
  1 => "\xc0",
  2 => "\xc1",
  3 => "\xc2",
  4 => "\xc3",
  5 => "\xc4",
  6 => "\xc5",
  7 => "\xc6",
  8 => "\xc7"
}
PAYMENT_TYPE_MAP =
{
  1 => "P",
  2 => "N",
  3 => "C",
  4 => "D",
  5 => "B"
}

Instance Method Summary collapse

Instance Method Details

#add_comment(text) ⇒ Object



124
125
126
# File 'app/models/extface/driver/datecs/fp550.rb', line 124

def add_comment(text)
  raise "Not in fiscal session" unless @fiscal_session
end

#add_payment(value = nil, type_num = nil) ⇒ Object



128
129
130
131
132
133
134
135
136
# File 'app/models/extface/driver/datecs/fp550.rb', line 128

def add_payment(value = nil, type_num = nil)
  raise "Not in fiscal session" unless @fiscal_session
  payment_data = "".tap() do |data|
                        data << "\t"
                        data << PAYMENT_TYPE_MAP[type_num || 1]
                        data << ("%.2f" % value) unless value.blank?
                      end
  fsend(Sales::TOTAL, payment_data)
end

#add_sale(sale_item) ⇒ Object



119
120
121
122
# File 'app/models/extface/driver/datecs/fp550.rb', line 119

def add_sale(sale_item)
  raise "Not in fiscal session" unless @fiscal_session
  fsend Sales::SALE_AND_SHOW, build_sale_data(sale_item)
end

#build_packet(cmd, data = "") ⇒ Object



218
219
220
221
222
223
224
225
226
227
228
229
# File 'app/models/extface/driver/datecs/fp550.rb', line 218

def build_packet(cmd, data = "")
  "".b.tap() do |packet|
    packet << STX                     #Preamble. 1 byte long. Value: 01H.
    packet << 0x20 + 4 + data.length  #Number of bytes from <01> preamble (excluded) to <05> (included) plus the fixed offset of 20H
    packet << sequence_number         #Sequence number of the frame. Length : 1 byte. Value: 20H – FFH.
    packet << cmd                     #Length: 1 byte. Value: 20H - 7FH.
    packet << data                    #Length: 0 - 218 bytes for Host to printer
    packet << PA1                     #Post-amble. Length: 1 byte. Value: 05H.
    packet << Frame.bcc(packet[1..-1])#Control sum (0000H-FFFFH). Length: 4 bytes. Value of each byte: 30H-3FH
    packet << ETX                     #Terminator. Length: 1 byte. Value: 03H.
  end
end

#cancel_doc_session ⇒ Object



160
161
162
163
164
165
166
167
# File 'app/models/extface/driver/datecs/fp550.rb', line 160

def cancel_doc_session
  device.session("Doc cancel") do |s|
    s.notify "Doc Cancel Start"
    s.fsend Sales::CANCEL_FISCAL_DOC
    s.paper_cut
    s.notify "Doc Cancel End"
  end
end

#check_status ⇒ Object



101
102
103
104
105
# File 'app/models/extface/driver/datecs/fp550.rb', line 101

def check_status
  flush #clear receive buffer
  fsend(Info::GET_STATUS, 'X') # get 6 bytes status
  errors.empty?
end

#close_fiscal_doc ⇒ Object



113
114
115
116
117
# File 'app/models/extface/driver/datecs/fp550.rb', line 113

def close_fiscal_doc
  raise "Not in fiscal session" unless @fiscal_session
  fsend Sales::END_FISCAL_DOC
  @fiscal_session = false
end

#close_non_fiscal_doc ⇒ Object



96
97
98
99
# File 'app/models/extface/driver/datecs/fp550.rb', line 96

def close_non_fiscal_doc
  fsend Sales::END_NON_FISCAL_DOC
  @print_session = false
end

#fiscal_test ⇒ Object



54
55
56
57
58
# File 'app/models/extface/driver/datecs/fp550.rb', line 54

def fiscal_test
  sale_and_pay_items_session([
    SaleItem.new( price: 0.01, text1: "Extface Test" )
  ])
end

#frecv(timeout) ⇒ Object

return RespFrame or nil



209
210
211
212
213
214
215
216
# File 'app/models/extface/driver/datecs/fp550.rb', line 209

def frecv(timeout) # return RespFrame or nil
  if frame_bytes = pull(timeout)
    return Frame.new(frame_bytes.b)
  else
    errors.add :base, "No data received from device"
    return nil
  end
end

#fsend(cmd, data = "") ⇒ Object

common



170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'app/models/extface/driver/datecs/fp550.rb', line 170

def fsend(cmd, data = "") #return data or nil
  packet_data = build_packet(cmd, data) #store packet to be able to re-transmit it with the same sequence number
  result = false
  invalid_frames = 0 #counter for bad responses
  nak_messages = 0 #counter for rejected packets (should re-transmit the packet)
  push packet_data #send packet
  ACKS_MAX_WAIT.times do |retries|
    errors.clear
    if resp = frecv(RESPONSE_TIMEOUT)
      if resp.valid?
        human_status_errors(resp.status)
        if errors.empty?
          result = resp.data
          break
        else
          raise errors.full_messages.join(',')
        end
      else #ack, nak or bad
        if resp.nak?
          nak_messages += 1
          if nak_messages > NAKS_MAX_COUNT
            errors.add :base, "#{NAKS_MAX_COUNT} NAKs Received. Abort!"
            break
          end
        elsif !resp.ack?
          invalid_frames += 1
          if invalid_frames > INVALID_FRAME_RETRIES
            errors.add :base, "#{INVALID_FRAME_RETRIES} Broken Packets Received. Abort!"
            break
          end
        end
        push packet_data unless resp.ack?
      end
    end
    errors.add :base, "#{ACKS_MAX_WAIT} ACKs Received. Abort!"
  end
  return result
end

#handle(buffer) ⇒ Object



31
32
33
34
35
36
37
# File 'app/models/extface/driver/datecs/fp550.rb', line 31

def handle(buffer)
  #if i = buffer.index(/[\x03\x16\x15]/)   # find position of frame possible delimiter
  if i = buffer.index("\x03") || buffer.index("\x16") || buffer.index("\x15")
    rpush buffer[0..i]                    # this will make data available for #pull(timeout) method
    return i+1                            # return number of bytes processed
  end
end

#human_status_errors(status) ⇒ Object

6 bytes status



237
238
239
240
241
242
243
244
245
246
# File 'app/models/extface/driver/datecs/fp550.rb', line 237

def human_status_errors(status) #6 bytes status
  status_0 = status[0].ord
  errors.add :base, "Fiscal Device General Error" unless (status_0 & 0x20).zero?
  errors.add :base, "Invalid Command" unless (status_0 & 0x02).zero?
  errors.add :base, "Date & Time Not Set" unless (status_0 & 0x04).zero?
  errors.add :base, "Syntax Error" unless (status_0 & 0x01).zero?
  status_1 = status[1].ord
  errors.add :base, "Unpermitted Command In This Mode" unless (status_1 & 0x02).zero?
  errors.add :base, "Field Overflow" unless (status_1 & 0x01).zero?
end

#non_fiscal_test ⇒ Object

tests



40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'app/models/extface/driver/datecs/fp550.rb', line 40

def non_fiscal_test
  device.session("Non Fiscal Text") do |s|
    s.notify "Printing Non Fiscal Text"
    s.open_non_fiscal_doc
    s.print "********************************"
    s.print "Extface Print Test".center(32)
    s.print "********************************"
    s.fsend Printer::PAPER_MOVE, "1"
    s.print "Driver: " + "#{self.class::NAME}".truncate(24)
    s.close_non_fiscal_doc
    s.notify "Printing finished"
  end
end

#open_fiscal_doc(operator = "1", password = "000000") ⇒ Object

fiscal



108
109
110
111
# File 'app/models/extface/driver/datecs/fp550.rb', line 108

def open_fiscal_doc(operator = "1", password = "000000")
  fsend Sales::START_FISCAL_DOC, "#{operator.presence || "1"},#{password.presence || "000000"},00001"
  @fiscal_session = true
end

#open_non_fiscal_doc ⇒ Object

print



86
87
88
89
# File 'app/models/extface/driver/datecs/fp550.rb', line 86

def open_non_fiscal_doc
  fsend Sales::START_NON_FISCAL_DOC
  @print_session = true
end

#paper_cut ⇒ Object



231
232
233
234
235
# File 'app/models/extface/driver/datecs/fp550.rb', line 231

def paper_cut
  device.session('Paper Cut') do |s|
    s.push build_packet(Printer::PAPER_CUT)
  end
end

#period_report_session(from, to, detailed = true) ⇒ Object



77
78
79
80
81
82
83
# File 'app/models/extface/driver/datecs/fp550.rb', line 77

def period_report_session(from, to, detailed = true)
  device.session("Period Report #{ '(detailed)' if detailed }") do |s|
    s.notify "Period Report Start #{ '(detailed)' if detailed }"
    s.fsend detailed ? Reports::REPORT_FP_BY_DATE : Reports::COMPACT_REPORT_FP_BY_DATE, "#{from.strftime('%d%m%y')},#{to.strftime('%d%m%y')}"
    s.notify "Period Report End"
  end
end

up to 38 sybols, TODO check



91
92
93
94
# File 'app/models/extface/driver/datecs/fp550.rb', line 91

def print(text) #up to 38 sybols, TODO check
  raise "Not in print session" unless @print_session
  fsend Sales::PRINT_NON_FISCAL_TEXT, text
end

#sale_and_pay_items_session(items = [], operator = "1", password = "1") ⇒ Object

basket



144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'app/models/extface/driver/datecs/fp550.rb', line 144

def sale_and_pay_items_session(items = [], operator = "1", password = "1")
  device.session("Fiscal Doc") do |s|
    s.notify "Fiscal Doc Start"
    s.open_fiscal_doc
    s.notify "Register Sale"
    items.each do |item|
      s.add_sale(item)
    end
    s.notify "Register Payment"
    s.total_payment
    s.notify "Close Fiscal Receipt"
    s.close_fiscal_doc
    s.notify "Fiscal Doc End"
  end
end

#total_payment ⇒ Object



138
139
140
141
# File 'app/models/extface/driver/datecs/fp550.rb', line 138

def total_payment
  raise "Not in fiscal session" unless @fiscal_session
  fsend(Sales::TOTAL, "\t")
end

#x_report_session ⇒ Object



69
70
71
72
73
74
75
# File 'app/models/extface/driver/datecs/fp550.rb', line 69

def x_report_session
  device.session("X Report") do |s|
    s.notify "X Report Start"
    s.fsend Closure::DAY_FIN_REPORT, "2"
    s.notify "X Report End"
  end
end

#z_report_session ⇒ Object

reports



61
62
63
64
65
66
67
# File 'app/models/extface/driver/datecs/fp550.rb', line 61

def z_report_session
  device.session("Z Report") do |s|
    s.notify "Z Report Start"
    s.fsend Closure::DAY_FIN_REPORT, "0"
    s.notify "Z Report End"
  end
end