-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathVerifone250.py
410 lines (289 loc) Β· 11.7 KB
/
Verifone250.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
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
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
from __future__ import print_function
import serial,time
ESCAPE=27
red="red"
black="black"
class Verifone250(object):
# from Verifone250 import Verifone250
# v = Verifone250(port="/dev/cu.wchusbserial110")
# v = Verifone250(port="/dev/cu.wchusbserial110",DEBUG_Remote=True,
# speed=9600, bytesize=Serial.SEVENBITS, parity=serial.PARITY_EVEN,
# autoReset = True)
# Do we reset formatting after every print command?
# Or let it carry over, per printer's idiosyncratic logic:
autoReset = True
# Seven-bit mode is required for double wide printing.
# Eight-bit mode allows for more characters.
# Set per DIP switches inside ribbon area
bytesize=serial.SEVENBITS
# Do we spew a lot of debug via print()?
DEBUG_Remote = False
# Is the printer currently in doubleWide or doubleTall mode?
doubleWide = False
doubleTall = False
# Is the printer in "Printer 200 Emulation Mode" or "Native Mode"?
# Native mode is required for all escape sequences.
nativeMode = False
# Set per DIP switches inside ribbon area
parity=serial.PARITY_EVEN
# Serial port to which the Verifone250 is attached
port = ""
# Is the printer currently in black or red mode?
isRed = False
# Any extra junk to pass to pyserial?
serialParameters = {}
# Set per DIP switches inside ribbon area
speed = 9600
# Printer supports hardware handshakes instead of just blindly waiting,
# but none of my other equipment supports this :P
# so we accumulate delay at an estimated rate and wait for it when printing.
timePending = 0.0
# Serial timeouts. Really only matters for reads
timeout=5
write_timeout=None
def __init__(self, *args, **kwargs):
# Flags
if type (args) is not None:
for arg in args:
setattr(self,key,True)
# key=value parameters
if type (kwargs) is not None:
for key, value in kwargs.items():
if type(value) is dict:
if getattr(self,key):
tempdict = getattr(self,key).copy()
tempdict.update(value)
value = tempdict
setattr(self,key,value)
self.ser = serial.Serial(self.port, self.speed, bytesize=self.bytesize, parity=self.parity, timeout=self.timeout, write_timeout=self.write_timeout, **self.serialParameters)
if self.DEBUG_Remote:
print(str(self.ser.get_settings()))
self.softReset()
def bufferChars(self,stuff):
# DIY
self.ser.write(stuff)
def eject(self, lines=1):
# Yack out some paper! Max 10 lines
self.enterNativeMode()
self.remoteWrite(bytearray([ESCAPE, ord('b'), 48 + (lines % 10), ord(';')]))
time.sleep(0.4 * int(lines % 10))
def enterDoubleWideMode(self):
# Double wide mode can be mixed and matched in a single line.
if self.doubleWide:
self.debug("ENTER DOUBLEWIDE already in")
return
self.debug("ENTER DOUBLEWIDE")
self.enterNativeMode()
self.doubleWide = True
self.remoteWrite(bytearray([30]))
def exitDoubleWideMode(self):
if self.doubleWide:
self.debug("EXIT DOUBLEWIDE")
self.doubleWide = False
self.remoteWrite(bytearray([31]))
else:
self.debug("EXIT DOUBLEWIDE already out")
def enterDoubleTallMode(self):
# Double tall mode is line at a time.
# If you send a line in multiple chunks, it goes by
# what's set at newline time
if self.doubleTall:
self.debug("ENTER DOUBLETALL already in")
return
self.debug("ENTER DOUBLETALL")
self.enterNativeMode()
self.doubleTall = True
self.remoteWrite(bytearray([ESCAPE,ord('f'),49,49,ord(';')]))
def exitDoubleTallMode(self):
if self.doubleTall:
self.debug("EXIT DOUBLETALL")
self.doubleTall = False
self.remoteWrite(bytearray([ESCAPE,ord('f'),48,48,ord(';')]))
else:
self.debug("EXIT DOUBLETALL already out")
def enterNativeMode(self):
# Entering native mode flushes the buffer, so we try to avoid it:
if self.nativeMode:
return
self.debug("ENTER NATIVE")
self.remoteWrite(bytearray([0x1C]))
self.nativeMode = True
self.__clearFormatting()
def __clearFormatting(self):
# Internal helper. Doesn't write anything to printer
self.debug("CLEAR FORMATTING")
self.doubleTall = False
self.doubleWide = False
self.isRed = False
def debug(self, stuff):
if self.DEBUG_Remote:
print(stuff)
def exitNativeMode(self):
# This is the same as "Enter Printer 200 Emulation Mode"
# Exiting native mode flushes the buffer, so we try to avoid it:
if self.nativeMode:
self.debug("EXIT NATIVE")
self.remoteWrite(bytearray([0x1D]))
self.nativeMode = False
return
self.__clearFormatting()
def flushBuffer(self):
# Newline
self.remoteWrite(chr(10))
def __formatRemoteValues(self,values):
# A pretty printer for debug output
returnValue=""
for a in values:
if (type(a) is int):
if (a > 31 and a < 127):
returnValue = returnValue + "'" + str(a) + "' (" + str(a) + "), "
else:
returnValue = returnValue + "(" + str(a) + "), "
elif (type(a) is str):
if ((ord(a) > 31 ) and ( ord(a) < 127)):
returnValue = returnValue + "'" + str(a) + "' (" + str(ord(a)) + "), "
else:
returnValue = returnValue + "(" + str(ord(a)) + "), "
return returnValue
def getBytes(self,number):
# Only a few functions require this
return self.ser.read(number)
def getPrinterStatus(self):
# The commands retrieving data from the printer all fail on mine.
# I'm not sure if this is a problem in the code or the hardware.
# Let me know if you get this to work.
# https://github.com/combs/Python-Verifone250/issues/1
self.remoteWrite(bytearray([ESCAPE,0x64]))
rawVal = self.getBytes(1)
if len(rawVal) > 0:
rawVal = rawVal[0]
else:
if self.DEBUG_Remote:
print("Timed out waiting for getPrinterStatus.")
return {"timeout": True}
returnVals = {}
returnVals["raw"] = rawVal
returnVals["paperLow"] = rawVal & 1
returnVals["alwaysHigh"] = rawVal & 32
returnVals["mechanismFailure"] = rawVal & 64
returnVals["timeout"] = False
return returnVals
def identifyPrinter(self):
# The commands retrieving data from the printer all fail on mine.
# I'm not sure if this is a problem in the code or the hardware.
# Let me know if you get this to work.
# https://github.com/combs/Python-Verifone250/issues/1
self.remoteWrite(bytearray([ESCAPE, ord('i')]))
rawVal = self.getBytes(1)
if len(rawVal) > 0:
rawVal = rawVal[0]
return rawVal
else:
if self.DEBUG_Remote:
print("Timed out waiting for identifyPrinter.")
return
def printChars(self,stuff,**kwargs):
# Queue up some characters in the buffer.
# v.printChars("text",color="red",doubleWide=False,doubleTall=True,autoReset=True)
autoReset = kwargs.get("autoReset", self.autoReset)
self.debug(kwargs)
if autoReset==True:
self.exitDoubleTallMode()
self.exitDoubleWideMode()
self.setBlack()
if kwargs.get("doubleTall", False)==True:
self.enterDoubleTallMode()
# else:
# self.exitDoubleTallMode()
if kwargs.get("doubleWide", False)==True:
self.enterDoubleWideMode()
# else:
# self.exitDoubleWideMode()
if kwargs.get("color", "black") == "red":
self.setRed()
# else:
# self.setBlack()
self.remoteWrite(*stuff)
def printLine(self,stuff,**kwargs):
# Recommended way to tee stuff up unless you need to mix and match
# colors/widths in one line.
# v.printLine("text",color="red",doubleWide=False,doubleTall=True,autoReset=True)
self.printChars(stuff,**kwargs)
self.flushBuffer()
def remoteWrite(self,*values):
# Dump some raw stuff to the printer.
# v.remoteWrite('c',chr(50),chr(0x38), etc)
theBytes = bytearray()
for val in values:
if type(val)==str:
try:
theBytes += val.encode('ascii')
except UnicodeEncodeError:
theBytes += val.encode('utf-8')
else:
theBytes += val
if self.DEBUG_Remote:
print("-> " + self.__formatRemoteValues(theBytes))
for theByte in theBytes:
self.ser.write([theByte])
time.sleep( 1 / self.speed * 2 )
# if self.DEBUG_Remote:
# print("waiting for flush", end="")
# realistically a usb serial has its own weird magical buffer, so
# the kernel's idea of whether this serial buffer is empty is a bit
# hooey. But hey, let's honor it if possible
while (self.ser.out_waiting):
time.sleep(0.0001)
if self.DEBUG_Remote:
print('.', end="")
if self.DEBUG_Remote:
print(" ")
if theByte==10:
# Newline, triggers printer to print its buffer.
# The printer resets to black every line :P
self.isRed = False
self.timePending += 0.2
if self.doubleTall:
self.timePending *= 2
if self.DEBUG_Remote:
print("Pausing for",self.timePending,"seconds")
time.sleep(self.timePending)
self.timePending = 0.0
else:
# Let's guess 0.03 seconds per character
self.timePending += 0.03
if self.doubleWide:
self.timePending += 0.03
# do it double
def retrievePrinterInformation(self):
# The commands retrieving data from the printer all fail on mine.
# I'm not sure if this is a problem in the code or the hardware.
# Let me know if you get this to work.
# https://github.com/combs/Python-Verifone250/issues/1
self.remoteWrite(bytearray([ESCAPE, 0x72, ord('0')]))
# TODO: retrieve the bytes. How are they delimited?
def softReset(self):
self.nativeMode = False
self.enterNativeMode()
self.remoteWrite(bytearray([ESCAPE,ord('c')]))
self.nativeMode = False
self.__clearFormatting()
def swapColor(self):
# Instead of just having a "red" or "black" command, the printer has a
# "toggle" escape sequence. So we hang on to a local idea of what it is
# set to, and then update the printer as needed.
# BTW, the printer resets this every line, just for lullz
self.isRed = not (self.isRed)
self.remoteWrite(chr(18))
def setColor(self,red=False):
# Just convenience stuff
if red is self.isRed:
return
else:
self.swapColor()
def setRed(self):
# Just convenience stuff
self.setColor(red=True)
def setBlack(self):
# Just convenience stuff
self.setColor(red=False)