extface 0.7.1 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/models/extface/driver/datecs/dp_x5_new.rb +359 -0
- data/app/views/extface/driver/datecs/dp_x5_new/_control.html.erb +12 -0
- data/app/views/extface/driver/datecs/dp_x5_new/_settings.html.erb +0 -0
- data/lib/extface/version.rb +1 -1
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/log/test.log +17 -0
- data/test/fixtures/extface/devices.yml +6 -0
- data/test/fixtures/extface/drivers.yml +3 -0
- data/test/models/extface/driver/datecs/dp_x5_new_test.rb +63 -0
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8efd853272c69b139418f39bb6da6668ff26421b3306bb19362b297766b8dd1c
|
4
|
+
data.tar.gz: 5e1b4d8944463cad340a6a9cf595abb9809cd83c4a7921b597046a54228bbda0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: abc4767ce19343e70426c08ad4f05ea6b0bfd752864d62f3ee7ac27ec649edbc53f5925f2b92549cec95ac2843b2259473962f4136ba27f0cd8889289e99f606
|
7
|
+
data.tar.gz: 87e86cb335fcd01d503e638334943bd7d27653f84ec0a96a8f7435f8d615e388282edaf9f51671c32e94319c6b0ea5435fb27c13d46a9f8c6343a2c38cb0b2a7
|
@@ -0,0 +1,359 @@
|
|
1
|
+
### http://www.datecs.bg/en/downloads/pdf?id=PM_XXXXXX_-BUL-FPprotocol_v2_04.pdf
|
2
|
+
module Extface
|
3
|
+
class Driver::Datecs::DpX5New < Extface::Driver::Base::Fiscal
|
4
|
+
NAME = 'Datecs DPX5 New'.freeze
|
5
|
+
|
6
|
+
RESPONSE_TIMEOUT = 3 #seconds
|
7
|
+
INVALID_FRAME_RETRIES = 6 #count (bad length, bad checksum)
|
8
|
+
ACKS_MAX_WAIT = 60 #count / nothing is forever
|
9
|
+
NAKS_MAX_COUNT = 3 #count
|
10
|
+
BAD_SEQ_MAX_COUNT = 3
|
11
|
+
NO_RESP_MAX_COUNT = 3
|
12
|
+
|
13
|
+
TAX_GROUPS_MAP = {
|
14
|
+
1 => "\xc0",
|
15
|
+
2 => "\xc1",
|
16
|
+
3 => "\xc2",
|
17
|
+
4 => "\xc3",
|
18
|
+
5 => "\xc4",
|
19
|
+
6 => "\xc5",
|
20
|
+
7 => "\xc6",
|
21
|
+
8 => "\xc7"
|
22
|
+
}
|
23
|
+
|
24
|
+
PAYMENT_TYPE_MAP = {
|
25
|
+
1 => "P",
|
26
|
+
2 => "N",
|
27
|
+
3 => "C",
|
28
|
+
4 => "D",
|
29
|
+
5 => "B"
|
30
|
+
}
|
31
|
+
|
32
|
+
include Extface::Driver::Datecs::CommandsV1
|
33
|
+
|
34
|
+
def handle(buffer)
|
35
|
+
#if i = buffer.index(/[\x03\x16\x15]/) # find position of frame possible delimiter
|
36
|
+
if i = buffer.index("\x03") || buffer.index("\x16") || buffer.index("\x15")
|
37
|
+
rpush buffer[0..i] # this will make data available for #pull(timeout) method
|
38
|
+
return i+1 # return number of bytes processed
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
#tests
|
43
|
+
def non_fiscal_test
|
44
|
+
device.session("Non Fiscal Text") do |s|
|
45
|
+
s.notify "Printing Non Fiscal Text"
|
46
|
+
s.open_non_fiscal_doc
|
47
|
+
s.print "********************************"
|
48
|
+
s.print "Extface Print Test".center(32)
|
49
|
+
s.print "********************************"
|
50
|
+
s.fsend Printer::PAPER_MOVE, "1"
|
51
|
+
s.print "Driver: " + "#{self.class::NAME}".truncate(24)
|
52
|
+
s.close_non_fiscal_doc
|
53
|
+
s.notify "Printing finished"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def fiscal_test
|
58
|
+
sale_and_pay_items_session([
|
59
|
+
SaleItem.new( price: 0.01, text1: "Extface Test" )
|
60
|
+
])
|
61
|
+
end
|
62
|
+
|
63
|
+
#reports
|
64
|
+
def z_report_session
|
65
|
+
device.session("Z Report") do |s|
|
66
|
+
s.notify "Z Report Start"
|
67
|
+
s.fsend Closure::DAY_FIN_REPORT, "0"
|
68
|
+
s.notify "Z Report End"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def x_report_session
|
73
|
+
device.session("X Report") do |s|
|
74
|
+
s.notify "X Report Start"
|
75
|
+
s.fsend Closure::DAY_FIN_REPORT, "2"
|
76
|
+
s.notify "X Report End"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def period_report_session(from, to, detailed = true)
|
81
|
+
device.session("Period Report #{ '(detailed)' if detailed }") do |s|
|
82
|
+
s.notify "Period Report Start #{ '(detailed)' if detailed }"
|
83
|
+
s.fsend detailed ? Reports::REPORT_FP_BY_DATE : Reports::COMPACT_REPORT_FP_BY_DATE, "#{from.strftime('%d%m%y')},#{to.strftime('%d%m%y')}"
|
84
|
+
s.notify "Period Report End"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
#print
|
89
|
+
def open_non_fiscal_doc
|
90
|
+
fsend Sales::START_NON_FISCAL_DOC
|
91
|
+
@print_session = true
|
92
|
+
end
|
93
|
+
|
94
|
+
def print(text) #up to 38 sybols, TODO check
|
95
|
+
raise "Not in print session" unless @print_session
|
96
|
+
fsend Sales::PRINT_NON_FISCAL_TEXT, text
|
97
|
+
end
|
98
|
+
|
99
|
+
def close_non_fiscal_doc
|
100
|
+
fsend Sales::END_NON_FISCAL_DOC
|
101
|
+
@print_session = false
|
102
|
+
end
|
103
|
+
|
104
|
+
def check_status
|
105
|
+
flush #clear receive buffer
|
106
|
+
fsend(Info::GET_STATUS, 'X') # get 6 bytes status
|
107
|
+
errors.empty?
|
108
|
+
end
|
109
|
+
|
110
|
+
#fiscal
|
111
|
+
def open_fiscal_doc(operator = "1", password = "1")
|
112
|
+
fsend Sales::START_FISCAL_DOC, "#{operator.presence || "1"},#{password.presence || "1"},,1"
|
113
|
+
@fiscal_session = true
|
114
|
+
end
|
115
|
+
|
116
|
+
def close_fiscal_doc
|
117
|
+
raise "Not in fiscal session" unless @fiscal_session
|
118
|
+
fsend Sales::END_FISCAL_DOC
|
119
|
+
@fiscal_session = false
|
120
|
+
end
|
121
|
+
|
122
|
+
def add_sale(sale_item)
|
123
|
+
raise "Not in fiscal session" unless @fiscal_session
|
124
|
+
fsend Sales::SALE_AND_SHOW, build_sale_data(sale_item)
|
125
|
+
end
|
126
|
+
|
127
|
+
def add_comment(text)
|
128
|
+
raise "Not in fiscal session" unless @fiscal_session
|
129
|
+
end
|
130
|
+
|
131
|
+
def add_payment(value = nil, type_num = nil)
|
132
|
+
raise "Not in fiscal session" unless @fiscal_session
|
133
|
+
payment_data = "".tap() do |data|
|
134
|
+
data << "\t"
|
135
|
+
data << PAYMENT_TYPE_MAP[type_num || 1]
|
136
|
+
data << ("%.2f" % value) unless value.blank?
|
137
|
+
end
|
138
|
+
fsend(Sales::TOTAL, payment_data)
|
139
|
+
end
|
140
|
+
|
141
|
+
def total_payment
|
142
|
+
raise "Not in fiscal session" unless @fiscal_session
|
143
|
+
fsend(Sales::TOTAL, "\t")
|
144
|
+
end
|
145
|
+
|
146
|
+
#basket
|
147
|
+
def sale_and_pay_items_session(items = [], operator = "1", password = "1")
|
148
|
+
device.session("Fiscal Doc") do |s|
|
149
|
+
s.notify "Fiscal Doc Start"
|
150
|
+
s.open_fiscal_doc
|
151
|
+
s.notify "Register Sale"
|
152
|
+
items.each do |item|
|
153
|
+
s.add_sale(item)
|
154
|
+
end
|
155
|
+
s.notify "Register Payment"
|
156
|
+
s.total_payment
|
157
|
+
s.notify "Close Fiscal Receipt"
|
158
|
+
s.close_fiscal_doc
|
159
|
+
s.notify "Fiscal Doc End"
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def cancel_doc_session
|
164
|
+
device.session("Doc cancel") do |s|
|
165
|
+
s.notify "Doc Cancel Start"
|
166
|
+
s.fsend Sales::CANCEL_FISCAL_DOC
|
167
|
+
s.paper_cut
|
168
|
+
s.notify "Doc Cancel End"
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
#common
|
173
|
+
def fsend(cmd, data = "") #return data or nil
|
174
|
+
packet_data = build_packet(cmd, data) #store packet to be able to re-transmit it with the same sequence number
|
175
|
+
result = false
|
176
|
+
invalid_frames = 0 #counter for bad responses
|
177
|
+
nak_messages = 0 #counter for rejected packets (should re-transmit the packet)
|
178
|
+
no_resp = 0
|
179
|
+
flush #prevent double packet response issue like daisy driver
|
180
|
+
push packet_data #send packet
|
181
|
+
ACKS_MAX_WAIT.times do |retries|
|
182
|
+
errors.clear
|
183
|
+
if resp = frecv(RESPONSE_TIMEOUT)
|
184
|
+
if resp.valid?
|
185
|
+
human_status_errors(resp.status)
|
186
|
+
if errors.empty?
|
187
|
+
result = resp.data
|
188
|
+
break
|
189
|
+
else
|
190
|
+
raise errors.full_messages.join(',')
|
191
|
+
end
|
192
|
+
else #ack, nak or bad
|
193
|
+
if resp.nak?
|
194
|
+
nak_messages += 1
|
195
|
+
if nak_messages > NAKS_MAX_COUNT
|
196
|
+
errors.add :base, "#{NAKS_MAX_COUNT} NAKs Received. Abort!"
|
197
|
+
break
|
198
|
+
end
|
199
|
+
elsif !resp.ack?
|
200
|
+
invalid_frames += 1
|
201
|
+
if invalid_frames > INVALID_FRAME_RETRIES
|
202
|
+
errors.add :base, "#{INVALID_FRAME_RETRIES} Broken Packets Received. Abort!"
|
203
|
+
break
|
204
|
+
end
|
205
|
+
end
|
206
|
+
push packet_data unless resp.ack?
|
207
|
+
end
|
208
|
+
else
|
209
|
+
no_resp += 1
|
210
|
+
if no_resp > NO_RESP_MAX_COUNT
|
211
|
+
p "No reply in #{NO_RESP_MAX_COUNT * RESPONSE_TIMEOUT} seconds. Abort!"
|
212
|
+
errors.add :base, "No reply in #{NO_RESP_MAX_COUNT * RESPONSE_TIMEOUT} seconds. Abort!"
|
213
|
+
return result
|
214
|
+
end
|
215
|
+
end
|
216
|
+
errors.add :base, "#{ACKS_MAX_WAIT} ACKs Received. Abort!"
|
217
|
+
end
|
218
|
+
return result
|
219
|
+
end
|
220
|
+
|
221
|
+
def frecv(timeout) # return Frame or nil
|
222
|
+
rframe = nil
|
223
|
+
BAD_SEQ_MAX_COUNT.times do
|
224
|
+
errors.clear
|
225
|
+
if frame_bytes = pull(timeout)
|
226
|
+
rframe = Frame.new(frame_bytes.b)
|
227
|
+
if rframe.seq.nil? || rframe.seq.ord == sequence_number(false) #accept only current sequence number as reply
|
228
|
+
break
|
229
|
+
else
|
230
|
+
errors.add :base, "Sequence mismatch"
|
231
|
+
p "Invalid sequence (expected: #{sequence_number(false).to_s(16)}, got: #{rframe.seq.ord.to_s(16)})"
|
232
|
+
rframe = nil #invalidate mismatch sequence frame for the last retry
|
233
|
+
end
|
234
|
+
else
|
235
|
+
errors.add :base, "No data received from device"
|
236
|
+
break
|
237
|
+
end
|
238
|
+
end
|
239
|
+
return rframe
|
240
|
+
end
|
241
|
+
|
242
|
+
def build_packet(cmd, data = "")
|
243
|
+
"".b.tap() do |packet|
|
244
|
+
packet << STX #Preamble. 1 byte long. Value: 01H.
|
245
|
+
packet << 0x20 + 4 + data.b.length #Number of bytes from <01> preamble (excluded) to <05> (included) plus the fixed offset of 20H
|
246
|
+
packet << sequence_number #Sequence number of the frame. Length : 1 byte. Value: 20H – FFH.
|
247
|
+
packet << cmd #Length: 1 byte. Value: 20H - 7FH.
|
248
|
+
packet << data.b #Length: 0 - 218 bytes for Host to printer
|
249
|
+
packet << PA1 #Post-amble. Length: 1 byte. Value: 05H.
|
250
|
+
packet << Frame.bcc(packet[1..-1])#Control sum (0000H-FFFFH). Length: 4 bytes. Value of each byte: 30H-3FH
|
251
|
+
packet << ETX #Terminator. Length: 1 byte. Value: 03H.
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
def paper_cut
|
256
|
+
device.session('Paper Cut') do |s|
|
257
|
+
s.push build_packet(Printer::PAPER_CUT)
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
def human_status_errors(status) #6 bytes status
|
262
|
+
status_0 = status[0].ord
|
263
|
+
errors.add :base, "Fiscal Device General Error" unless (status_0 & 0x20).zero?
|
264
|
+
errors.add :base, "Invalid Command" unless (status_0 & 0x02).zero?
|
265
|
+
errors.add :base, "Date & Time Not Set" unless (status_0 & 0x04).zero?
|
266
|
+
errors.add :base, "Syntax Error" unless (status_0 & 0x01).zero?
|
267
|
+
status_1 = status[1].ord
|
268
|
+
errors.add :base, "Unpermitted Command In This Mode" unless (status_1 & 0x02).zero?
|
269
|
+
errors.add :base, "Field Overflow" unless (status_1 & 0x01).zero?
|
270
|
+
end
|
271
|
+
|
272
|
+
private
|
273
|
+
def build_sale_data(item)
|
274
|
+
encoded_text1 = device.encoding.present? ? item.text1.encode(device.encoding).b : item.text1
|
275
|
+
encoded_text1 = encoded_text1.mb_chars.slice!(0..19).to_s.b + '...' if encoded_text1 && encoded_text1.b.length > 22
|
276
|
+
|
277
|
+
encoded_text2 = device.encoding.present? ? item.text2.encode(device.encoding).b : item.text2
|
278
|
+
encoded_text2 = encoded_text2.mb_chars.slice!(0..19).to_s.b + '...' if encoded_text2 && encoded_text2.b.length > 22
|
279
|
+
|
280
|
+
"".b.tap() do |data|
|
281
|
+
data << encoded_text1 unless encoded_text1.blank?
|
282
|
+
data << "\x0a#{encoded_text2}" unless encoded_text2.blank?
|
283
|
+
data << "\t"
|
284
|
+
data << TAX_GROUPS_MAP[item.tax_group || 2].b
|
285
|
+
data << ("%.2f" % item.price)
|
286
|
+
data << "*#{'%.3f' % item.qty}" unless item.qty.blank?
|
287
|
+
data << ",#{item.percent}" unless item.percent.blank?
|
288
|
+
data << ",;#{'%.2f' % item.neto}" unless item.neto.blank?
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
def sequence_number(increment = true)
|
293
|
+
@seq ||= 0x1f
|
294
|
+
@seq += 1 if increment
|
295
|
+
@seq = 0x1f if @seq == 0x7f
|
296
|
+
@seq
|
297
|
+
end
|
298
|
+
|
299
|
+
class Frame
|
300
|
+
include ActiveModel::Validations
|
301
|
+
attr_reader :frame, :len, :seq, :cmd, :data, :status, :bcc
|
302
|
+
|
303
|
+
validates_presence_of :frame#, unless: :unpacked?
|
304
|
+
validate :bcc_validation
|
305
|
+
validate :len_validation
|
306
|
+
|
307
|
+
def initialize(buffer)
|
308
|
+
if match = buffer.match(/\x01(.{1})(.{1})(.{1})(.*)\x04(.{6})\x05(.{4})\x03/nm)
|
309
|
+
@frame = match.to_a.first
|
310
|
+
@len, @seq, @cmd, @data, @status, @bcc = match.captures
|
311
|
+
else
|
312
|
+
if buffer[/^\x16+$/] # only ACKs
|
313
|
+
@ack = true
|
314
|
+
elsif buffer.index("\x15")
|
315
|
+
@nak = true
|
316
|
+
end
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
def ack?; !!@ack; end #should wait, response is yet to come
|
321
|
+
|
322
|
+
def nak?; !!@nak; end #should retry command with same seq
|
323
|
+
|
324
|
+
private
|
325
|
+
|
326
|
+
def unpacked? # is it packed or unpacked message?
|
327
|
+
@ack || @nak
|
328
|
+
end
|
329
|
+
|
330
|
+
def bcc_validation
|
331
|
+
unless unpacked? || frame.blank?
|
332
|
+
calc_bcc = self.class.bcc frame[1..-6]
|
333
|
+
errors.add(:bcc, I18n.t('errors.messages.invalid')) if bcc != calc_bcc
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
def len_validation
|
338
|
+
unless unpacked? || frame.blank?
|
339
|
+
errors.add(:len, I18n.t('errors.messages.invalid')) if frame.nil? || len.ord != (frame[1..-6].length + 0x20)
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
class << self
|
344
|
+
def bcc(buffer)
|
345
|
+
sum = 0
|
346
|
+
buffer.each_byte do |byte|
|
347
|
+
sum += byte
|
348
|
+
end
|
349
|
+
"".tap() do |bcc|
|
350
|
+
4.times do |halfbyte|
|
351
|
+
bcc.insert 0, (0x30 + ((sum >> (halfbyte*4)) & 0x0f)).chr
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
358
|
+
end
|
359
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
|
2
|
+
<%= button_to 'Paper Cut', fiscal_device_path(@device), remote: true, name: :paper_cut, value: true, class: 'btn btn-warning' %>
|
3
|
+
|
4
|
+
<%= button_to 'Non Fiscal Test', fiscal_device_path(@device), remote: true, name: :non_fiscal_test, value: true, class: 'btn btn-warning' %>
|
5
|
+
|
6
|
+
<%= button_to 'Fiscal Test', fiscal_device_path(@device), remote: true, name: :fiscal_test, value: true, class: 'btn btn-warning' %>
|
7
|
+
|
8
|
+
<%= button_to 'X Report', fiscal_device_path(@device), remote: true, name: :x_report, value: true, class: 'btn btn-warning' %>
|
9
|
+
<%= button_to 'Z Report', fiscal_device_path(@device), remote: true, name: :z_report, value: true, class: 'btn btn-warning' %>
|
10
|
+
|
11
|
+
<%= button_to 'Cancel Fiscal Doc', fiscal_device_path(@device), remote: true, name: :cancel_fiscal_doc, value: true, class: 'btn btn-danger' %>
|
12
|
+
|
File without changes
|
data/lib/extface/version.rb
CHANGED
data/test/dummy/db/test.sqlite3
CHANGED
Binary file
|
data/test/dummy/log/test.log
CHANGED
@@ -18746,3 +18746,20 @@ Completed 200 OK in 10004ms (ActiveRecord: 3.2ms)
|
|
18746
18746
|
[1m[36mActiveRecord::InternalMetadata Load (0.2ms)[0m [1m[34mSELECT "ar_internal_metadata".* FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = ? LIMIT ?[0m [["key", "environment"], ["LIMIT", 1]]
|
18747
18747
|
[1m[35m (0.1ms)[0m [1m[36mbegin transaction[0m
|
18748
18748
|
[1m[35m (0.0ms)[0m [1m[36mcommit transaction[0m
|
18749
|
+
[1m[35m (0.2ms)[0m [1m[34mSELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC[0m
|
18750
|
+
[1m[35m (87.7ms)[0m [1m[35mDROP TABLE IF EXISTS "extface_devices"[0m
|
18751
|
+
[1m[35m (0.3ms)[0m [1m[34mSELECT sqlite_version(*)[0m
|
18752
|
+
[1m[35m (105.1ms)[0m [1m[35mCREATE TABLE "extface_devices" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "uuid" varchar, "name" varchar, "extfaceable_id" integer, "extfaceable_type" varchar, "driver_id" integer, "created_at" datetime, "updated_at" datetime, "encoding" varchar)[0m
|
18753
|
+
[1m[35m (114.7ms)[0m [1m[35mDROP TABLE IF EXISTS "extface_drivers"[0m
|
18754
|
+
[1m[35m (115.2ms)[0m [1m[35mCREATE TABLE "extface_drivers" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "type" varchar, "settings" text, "created_at" datetime, "updated_at" datetime)[0m
|
18755
|
+
[1m[35m (105.2ms)[0m [1m[35mDROP TABLE IF EXISTS "extface_jobs"[0m
|
18756
|
+
[1m[35m (94.8ms)[0m [1m[35mCREATE TABLE "extface_jobs" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "device_id" integer, "created_at" datetime, "updated_at" datetime, "description" varchar, "error" varchar, "failed_at" datetime, "completed_at" datetime, "connected_at" datetime, "started_at" datetime)[0m
|
18757
|
+
[1m[35m (199.7ms)[0m [1m[35mCREATE INDEX "index_extface_jobs_on_device_id" ON "extface_jobs" ("device_id")[0m
|
18758
|
+
[1m[35m (123.0ms)[0m [1m[35mDROP TABLE IF EXISTS "extface_serial_configs"[0m
|
18759
|
+
[1m[35m (122.5ms)[0m [1m[35mCREATE TABLE "extface_serial_configs" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "driver_id" integer, "serial_boud_rate" integer, "serial_data_length" integer, "serial_parity_check" integer, "serial_stop_bits" integer, "serial_handshake" integer, "created_at" datetime, "updated_at" datetime)[0m
|
18760
|
+
[1m[35m (97.9ms)[0m [1m[35mDROP TABLE IF EXISTS "shops"[0m
|
18761
|
+
[1m[35m (114.9ms)[0m [1m[35mCREATE TABLE "shops" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "created_at" datetime, "updated_at" datetime)[0m
|
18762
|
+
[1m[35m (0.3ms)[0m [1m[34mSELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC[0m
|
18763
|
+
[1m[36mActiveRecord::InternalMetadata Load (0.2ms)[0m [1m[34mSELECT "ar_internal_metadata".* FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = ? LIMIT ?[0m [["key", "environment"], ["LIMIT", 1]]
|
18764
|
+
[1m[35m (0.1ms)[0m [1m[36mbegin transaction[0m
|
18765
|
+
[1m[35m (0.1ms)[0m [1m[36mcommit transaction[0m
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Extface
|
4
|
+
class Driver::Datecs::Dp25Test < ActiveSupport::TestCase
|
5
|
+
setup do
|
6
|
+
@driver = extface_drivers(:datecs_dpX5_new)
|
7
|
+
@driver.flush # clear receive buffer
|
8
|
+
end
|
9
|
+
|
10
|
+
test "handle" do
|
11
|
+
assert_equal nil, @driver.handle('bad packet')
|
12
|
+
assert_equal 6, @driver.handle("\x01data\x03data"), "Frame not match"
|
13
|
+
assert_equal 9, @driver.handle("pre\x01data\x03data"), "Frame with preamble not match"
|
14
|
+
assert_equal 8, @driver.handle("\x16\x16\x01data\x03data"), "Frame with ACK preamble not match"
|
15
|
+
assert_equal 4, @driver.handle("pre\x15"), "NAK not match"
|
16
|
+
end
|
17
|
+
|
18
|
+
xtest "build packet" do
|
19
|
+
assert_equal "\x01\x24\x20\x4a\x05\x30\x30\x39\x33\x03".b, @driver.build_packet(0x4a), "packet without data"
|
20
|
+
assert_equal "\x01\x25\x21\x4a\x58\x05\x30\x30\x3e\x3d\x03".b, @driver.build_packet(0x4a, 'X'), "packet with data"
|
21
|
+
end
|
22
|
+
|
23
|
+
xtest "response frame" do
|
24
|
+
frame_class = @driver.class::Frame
|
25
|
+
assert frame_class.new("\x15").nak?, "NAK message failed"
|
26
|
+
assert frame_class.new("\x16\x16").ack?, "ACK message failed"
|
27
|
+
assert_nothing_raised do
|
28
|
+
assert_equal false, frame_class.new("bad data\x01\x25\x21\x4asome broken packet\x58\x05\x30\x30\x3e\x3d\x03".b).valid?
|
29
|
+
end
|
30
|
+
frame = frame_class.new("\x16\x01\x2C\x2F\x2D\x50\x04\x88\x80\xC0\x80\x80\xB0\x05\x30\x34\x35\x39\x03".b)
|
31
|
+
assert frame.valid?, "Vailid frame not recognized"
|
32
|
+
assert_equal "\x01\x2C\x2F\x2D\x50\x04\x88\x80\xC0\x80\x80\xB0\x05\x30\x34\x35\x39\x03".b, frame.frame
|
33
|
+
assert_equal "\x2c".b, frame.len
|
34
|
+
assert_equal "\x2f".b, frame.seq
|
35
|
+
assert_equal "\x2d".b, frame.cmd
|
36
|
+
assert_equal "\x50".b, frame.data
|
37
|
+
assert_equal "\x88\x80\xC0\x80\x80\xB0".b, frame.status
|
38
|
+
assert_equal "\x30\x34\x35\x39".b, frame.bcc
|
39
|
+
#bad check sum
|
40
|
+
frame = frame_class.new("\x01\x2C\x2F\x2D\x50\x04\x88\x80\xC0\x80\x80\xB0\x05\x30\x34\x35\x38\x03".b)
|
41
|
+
assert_equal false, frame.valid?
|
42
|
+
assert frame.errors.messages[:bcc]
|
43
|
+
#bad length
|
44
|
+
frame = frame_class.new("\x01\x2b\x2F\x2D\x50\x04\x88\x80\xC0\x80\x80\xB0\x05\x30\x34\x35\x38\x03".b)
|
45
|
+
assert_equal false, frame.valid?
|
46
|
+
assert frame.errors.messages[:len]
|
47
|
+
end
|
48
|
+
|
49
|
+
xtest "fsend" do
|
50
|
+
job = extface_jobs(:one)
|
51
|
+
job_thread = Thread.new do
|
52
|
+
@driver.set_job(job)
|
53
|
+
result = @driver.fsend(0x2C) # paper move command
|
54
|
+
end
|
55
|
+
simulate_device_pull(job)
|
56
|
+
@driver.handle("\x01\x31\x20\x4A\x88\x80\xC0\x80\x80\xB8\x04\x88\x80\xC0\x80\x80\xB8\x05\x30\x37\x3A\x34\x03".b)
|
57
|
+
simulate_device_pull(job)
|
58
|
+
@driver.handle("\x01\x38\x21\x30\x30\x30\x30\x30\x35\x30\x2C\x30\x30\x30\x30\x34\x39\x04\x80\x80\x88\x80\x80\xB8\x05\x30\x36\x35\x30\x03".b)
|
59
|
+
job_thread.join
|
60
|
+
assert @driver.errors.empty?
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: extface
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.
|
4
|
+
version: 0.7.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alex Vangelov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-02-
|
11
|
+
date: 2019-02-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: redis
|
@@ -106,6 +106,7 @@ files:
|
|
106
106
|
- app/models/extface/driver/datecs/commands_v1.rb
|
107
107
|
- app/models/extface/driver/datecs/dp25.rb
|
108
108
|
- app/models/extface/driver/datecs/dp35.rb
|
109
|
+
- app/models/extface/driver/datecs/dp_x5_new.rb
|
109
110
|
- app/models/extface/driver/datecs/fp550.rb
|
110
111
|
- app/models/extface/driver/eltrade/commands_fp4.rb
|
111
112
|
- app/models/extface/driver/eltrade_tm_u220.rb
|
@@ -132,6 +133,8 @@ files:
|
|
132
133
|
- app/views/extface/driver/datecs/dp25/_settings.html.erb
|
133
134
|
- app/views/extface/driver/datecs/dp35/_control.html.erb
|
134
135
|
- app/views/extface/driver/datecs/dp35/_settings.html.erb
|
136
|
+
- app/views/extface/driver/datecs/dp_x5_new/_control.html.erb
|
137
|
+
- app/views/extface/driver/datecs/dp_x5_new/_settings.html.erb
|
135
138
|
- app/views/extface/driver/datecs/fp550/_control.html.erb
|
136
139
|
- app/views/extface/driver/datecs/fp550/_settings.html.erb
|
137
140
|
- app/views/extface/driver/eltrade_tm_u220/_control_.html.erb
|
@@ -263,6 +266,7 @@ files:
|
|
263
266
|
- test/models/extface/driver/daisy_fx1200_test.rb
|
264
267
|
- test/models/extface/driver/datecs/dp25_test.rb
|
265
268
|
- test/models/extface/driver/datecs/dp35_test.rb
|
269
|
+
- test/models/extface/driver/datecs/dp_x5_new_test.rb
|
266
270
|
- test/models/extface/driver/datecs/fp550_test.rb
|
267
271
|
- test/models/extface/driver/generic_pos_test.rb
|
268
272
|
- test/models/extface/driver/posiflex/aura80_test.rb
|
@@ -391,6 +395,7 @@ test_files:
|
|
391
395
|
- test/models/extface/driver/posiflex/aura80_test.rb
|
392
396
|
- test/models/extface/driver/daisy_fx1200_test.rb
|
393
397
|
- test/models/extface/driver/star_tsp200_test.rb
|
398
|
+
- test/models/extface/driver/datecs/dp_x5_new_test.rb
|
394
399
|
- test/models/extface/driver/datecs/dp35_test.rb
|
395
400
|
- test/models/extface/driver/datecs/fp550_test.rb
|
396
401
|
- test/models/extface/driver/datecs/dp25_test.rb
|