Symbol Hand-Scanner ansteuern und Daten auslesen

  • Hallo zusammen,

    Ich versuche gerade ein Programm zu schreiben, welches mir die Daten von einem Symbol Handscanner ausließt und die EANs die im Scannerspeicher vorhanden sind in eine Datenbank schreibt...

    Verbunden wird der Scanner per USB Kabel über einen Prolific USB to Serial Comm Port

    Bisher haben wir die Daten per Python Script ausgelesen, welches ich damals im Netz gefunden hatte:

    Spoiler anzeigen

    #!/usr/bin/env python
    # This code is hereby placed in the public domain
    import sys, time, datetime, serial, struct, pprint

    if sys.platform == 'darwin':
    serial_port = '/dev/cu.usbserial'
    elif sys.platform == 'linux2':
    serial_port = '/dev/ttyUSB0'
    elif sys.platform == 'win32':
    # this port varies from PC to PC
    serial_port = 'COM5'
    else:
    serial_port = 0

    version = '$Id: win_cs1504.py,v 1.5 2009/11/07 03:40:47 majid Exp majid $'
    # Revision history:
    # $Log: win_cs1504.py,v $
    # Revision 1.5 2009/11/07 03:40:47 majid
    # fixes for signed/unsigned warnings in 2.6
    # improvements to Excel formatting
    #
    # Revision 1.4 2007/01/29 05:49:51 majid
    # per Tom Eastmond's recommendation, set serial port defaults for Linux
    #
    # Revision 1.3 2007/01/20 08:37:17 majid
    # added public domain license comment
    #
    # Revision 1.2 2006/10/19 17:49:14 majid
    # AssertionError, not AssertError
    #
    # Revision 1.1 2006/10/19 17:45:44 majid
    # Initial revision
    #
    # Revision 1.1 2006/10/01 13:37:38 majid
    # Windows enhancements
    #
    # Revision 1.1 2006/04/14 19:21:10 majid
    # check in March and April articles
    #
    # Revision 1.2 2006/04/05 21:08:20 majid
    # fix for Windows, suggested by Joseph C. Brill
    #

    ########################################################################
    # bar code conventions

    def format_isbn(isbn):
    """Produce an ISBN check digit"""
    # calculate check digit
    isbn = isbn.replace('-', '')
    assert len(isbn) >= 9 and len(isbn) <= 10
    check = 0
    for i in range(9):
    check += (10 - i) * (ord(isbn) - ord('0'))
    check = -check % 11
    if check == 10:
    check = 'X'
    else:
    check = str(check)
    if len(isbn) > 9:
    assert isbn[-1] == check
    else:
    isbn = isbn + check
    # see http://www.isbn-international.org/en/userman/chapter4.html
    # XXX I need to implement a complete ISBN digit grouper
    return isbn

    def expand(symbology, code):
    """Expand certain types of common book codes"""
    # 10-digit ISBNs are encoded as EAN-13 with the charming fictitious country
    # code 978, a.k.a. "bookland"
    # see http://www.adams1.com/pub/russadam/isbn.html
    if symbology.startswith('EAN-13') and code.startswith('978'):
    symbology = 'ISBN'
    code = format_isbn(code[3:12])
    return symbology, code

    ########################################################################
    # Symbol CS 1504 protocol

    symbologies = {
    0x16: 'Bookland',
    0x0E: 'MSI',
    0x02: 'Codabar',
    0x11: 'PDF-417',
    0x0c: 'Code 11',
    0x26: 'Postbar (Canada)',
    0x20: 'Code 32',
    0x1e: 'Postnet (US)',
    0x03: 'Code 128',
    0x23: 'Postal (Australia)',
    0x01: 'Code 39',
    0x22: 'Postal (Japan)',
    0x13: 'Code 39 Full ASCII',
    0x27: 'Postal (UK)',
    0x07: 'Code 93',
    0x1c: 'QR code',
    0x1d: 'Composite',
    0x31: 'RSS limited',
    0x17: 'Coupon',
    0x30: 'RSS-14',
    0x04: 'D25',
    0x32: 'RSS Expanded',
    0x1b: 'Data Matrix',
    0x24: 'Signature',
    0x0f: 'EAN-128',
    0x15: 'Trioptic Code 39',
    0x0b: 'EAN-13',
    0x08: 'UPCA',
    0x4b: 'EAN-13+2',
    0x48: 'UPCA+2',
    0x8b: 'EAN-13+5',
    0x88: 'UPCA+5',
    0x0a: 'EAN-8',
    0x09: 'UPCE',
    0x4a: 'EAN-8+2',
    0x49: 'UPCE+2',
    0x8a: 'EAN-8+5',
    0x89: 'UPCE+5',
    0x05: 'IATA',
    0x10: 'UPCE1',
    0x19: 'ISBT-128',
    0x50: 'UPCE1+2',
    0x21: 'ISBT-128 concatenated',
    0x90: 'UPCE1+5',
    0x06: 'ITF',
    0x28: 'Macro PDF'
    }
    MAX_RESP = 6144

    class CS1504:

    def __init__(self, port='/dev/cu.usbserial'):
    self.ser = serial.Serial(port,
    baudrate=9600,
    bytesize=8,
    parity=serial.PARITY_ODD,
    stopbits=serial.STOPBITS_ONE,
    timeout=2)
    self.delta = datetime.timedelta(0)
    self.serial = None
    self.sw_ver = None
    self.last_barcodes = []

    def interrogate(self):
    """Initiate communications with the scanner"""
    print >> sys.stderr, 'Benutze COM PORT:', self.ser.portstr + '... ',
    count = 0
    while count < 50:
    self.send('\x01\x02\x00')
    try:
    data = self.recv(23)
    except AssertionError:
    time.sleep(1.0)
    data = None
    if not data:
    count += 1
    time.sleep(0.2)
    continue
    print >> sys.stderr, 'Verbunden'
    break
    if not data:
    raise IOError
    version, status = map(ord, data[2:4])
    assert status in [0, 22]
    if status == 22:
    print >> sys.stderr, 'WARUNG: Batterie fast leer'
    self.serial = data[4:12]
    self.sw_ver = data[12:20]
    assert data[20] == '\0'
    print >> sys.stderr, 'serial#', self.serial.encode('hex')
    print >> sys.stderr, 'SW version', self.sw_ver

    def get_time(self):
    """Get the time set in the scanner and calculate drift"""
    print >> sys.stderr, 'Uhrzeit ermitteln'
    self.send('\x0a\x02\x00')
    self.time_response(True)

    def set_time(self):
    """Reset the time in the scanner"""
    print >> sys.stderr, 'Zuruecksetzten der Scanner Uhr...',
    now = list(datetime.datetime.now().timetuple()[0:6])
    now[0] -= 2000
    now.reverse()
    self.send('\x09\x02\x06' + ''.join(map(chr, now)) + '\0')
    self.time_response()
    print >> sys.stderr, 'fertig'

    def time_response(self, calculate_drift=False):
    now = datetime.datetime.now()
    data = self.recv(12)
    assert data[2] == '\x06'
    s, mi, h, d, m, y = map(ord, data[3:9])
    y += 2000
    if s == 0xff:
    print >> sys.stderr, 'WARNUNG: scanner berichtet geringes batterie level'
    s = 0
    ts = datetime.datetime(y, m, d, h, mi, s)
    # determine the clock drift so we can correct timestamps
    if calculate_drift:
    self.delta = now - ts
    print >> sys.stderr, 'clock drift', self.delta
    if abs(self.delta).seconds > 60:
    print >> sys.stderr, 'WARNING: big gap between host & scanner clocks',
    print >> sys.stderr, self.delta

    def get_barcodes(self):
    """Retrieve the bar codes and timestamps from the scanner's memory, and
    correct for clock drift
    """
    print >> sys.stderr, 'Lese Artikelnummern...',
    count = 0
    # retry up to 5 times
    while count < 5:
    try:
    self.send('\x07\x02\x00')
    data = self.recv()
    assert data[2:10] == self.serial, data[2:10].encode('hex')
    break
    except AssertionError:
    count += 1
    time.sleep(0.2)
    self.last_barcodes = []
    data = data[10:-3]
    while data:
    length = ord(data[0])
    first, data = data[1:length+1], data[length+1:]
    symbology = symbologies.get(ord(first[0]), 'UNKNOWN')
    code = first[1:-4]
    t = struct.unpack('>I', first[-4:])[0]
    y = 2000 + int(t & 0x3f)
    t >>= 6
    m = int(t & 0x0f)
    t >>= 4
    d = int(t & 0x1f)
    t >>= 5
    h = int(t & 0x1f)
    t >>= 5
    mi = int(t & 0x3f)
    t >>= 6
    s = int(t & 0x3f)
    if s == 0x3f:
    print >> sys.stderr, 'WARNING: bar code timestamp error due to low battery condition'
    s = 0
    ts = datetime.datetime(y, m, d, h, mi, s) + self.delta
    symbology, code = expand(symbology, code)
    self.last_barcodes.append((symbology, code, ts))
    print >> sys.stderr, 'fertig (%d gelesen)' % len(self.last_barcodes)
    return self.last_barcodes

    def clear_barcodes(self):
    """Clear the bar codes in the scanner's memory"""
    print >> sys.stderr, 'Entfehrne Artikelnummern...',
    self.send('\x02\x02\x00')
    data = self.recv(5)
    print >> sys.stderr, 'fertig'

    def power_down(self):
    """Shut the scanner down to conserve battery life"""
    print >> sys.stderr, 'Herunterfahren...',
    self.send('\x05\x02\x00')
    data = self.recv(5)
    print >> sys.stderr, 'fertig'

    def send(self, cmd):
    """Send a command to the scanner"""
    self.ser.write(cmd)
    self.ser.write(crc16(cmd))

    def recv(self, length=MAX_RESP):
    """Receive a response. For fixed-size responses, specifying it will take
    less time as we won't need to wait for the timeout to return data
    """
    data = self.ser.read(length)
    if data:
    assert data.startswith('\x06\x02'), data.encode('hex')
    assert data[-2:] == crc16(data[:-2])
    assert data[-3] == '\0'
    return data

    def close(self):
    self.ser.close()

    def __del__(self):
    self.close()
    del self.ser

    ########################################################################
    # Modified from:
    # http://news.hping.org/comp.lang.python.archive/18112.html
    # to use the algorithm as specified by Symbol
    # original crc16.py by Bryan G. Olson, 2005
    # This module is free software and may be used and
    # distributed under the same terms as Python itself.
    import array
    def crc16(string, value=0):
    """CRC function using Symbol's specified algorithm
    """
    value = 0xffff
    for ch in string:
    value = table[ord(ch) ^ (value & 0xff)] ^ (value >> 8)
    #return value
    return struct.pack('>H', ~value & 0xffff)

    # CRC-16 poly: p(x) = x**16 + x**15 + x**2 + 1
    # top bit implicit, reflected
    poly = 0xa001
    table = array.array('H')
    for byte in range(256):
    crc = 0
    for bit in range(8):
    if (byte ^ crc) & 1:
    crc = (crc >> 1) ^ poly
    else:
    crc >>= 1
    byte >>= 1
    table.append(crc)

    assert crc16('\x01\x02\x00') == '\x9f\xde', \
    map(hex, map(ord, crc16('\x01\x02\x00')))

    if __name__ == '__main__':
    scanner = CS1504(serial_port)
    scanner.interrogate()
    scanner.get_time()
    scanner.set_time()
    barcodes = scanner.get_barcodes()
    for symbology, code, timestamp in barcodes:
    print '%s,%s,%s' % (symbology, code, str(timestamp).split('.')[0])
    if barcodes and sys.platform == 'win32':
    for symbology, code, timestamp in barcodes:
    print '%s,%s,%s' % (symbology, code, str(timestamp).split('.')[0])
    ################################################################
    # on Windows, copy the barcodes to the clipboard as text, one per line
    print >> sys.stderr, 'Kopiere Artikelnummern in den Speicher...',
    import win32con, win32clipboard
    win32clipboard.OpenClipboard()
    win32clipboard.SetClipboardData(win32con.CF_TEXT,
    '\r\n'.join([code[1] for code in barcodes]))
    print >> sys.stderr, 'fertig'
    win32clipboard.CloseClipboard()
    ################################################################
    # on Windows, open Excel and insert the data there
    import win32com.client, os
    excel = win32com.client.Dispatch('Excel.Application')
    # XXX we are not checking the case where the active worksheet is a Chart
    # XXX rather than regular rows and columns of cells
    sheet = excel.ActiveSheet
    # sanity check - no workbook might be open, if so, create a new one
    if not sheet:
    workbook = excel.Workbooks.Add()
    sheet = workbook.Worksheets[0]
    # barcodes are numeric but should be treated as strings
    # Excel will complain unless you force error checking off
    excel.ErrorCheckingOptions.NumberAsText = False
    print >> sys.stderr, 'Schreiben nach Excel...',
    row = 1
    # Find some empty space to put our data in
    while sheet.Cells(row, 1).Value != None \
    or sheet.Cells(row, 2).Value != None \
    or sheet.Cells(row, 3).Value != None:
    row += 1
    # insert all the barcodes we retrieved from the scanner
    for symbology, code, timestamp in barcodes:
    sheet.Cells(row, 1).Value = symbology
    # prefixing with a single-quote forces Excel to interpret the bar code
    # as text, not a number
    new_code = code.replace(' ', '')
    sheet.Cells(row, 2).Value = new_code
    sheet.Cells(row, 3).Value = timestamp
    row += 1
    # Make the columns autofit
    sheet.Columns("A:C").AutoFit()
    print >> sys.stderr, 'fertig..'
    # close the objects
    sheet = None
    workbook = None
    excel = None
    # end of Excel support
    ################################################################
    if barcodes:
    scanner.clear_barcodes()
    scanner.power_down()
    if sys.platform == 'win32':
    # wait 10 seconds so the user can read console output before it closes
    time.sleep(15)

    Was ich bishher versucht habe ist folgendes:

    Im Englischen Autoit Forum habe ich die _CommMG.au3 mit commmg.dll gefunden und auch ausprobiert. Ich kann auch auf den Scanner connecten (man sieht das die LED am Scanner leuchtet wenn ich darauf zugreife)

    Jedoch weiß ich nicht wie ich auf den Internen Speicher zugreifen kann...

    Den _CommGetString() oder _CommGetLine liefern keine Ergebnisse. Ich habe versucht das Python Script zu verstehen, jedoch weiß ich nicht so genau was diese tun... Evtl kann jemand von euch Helfen?

    Vielen Dank im Vorraus, Gruß Tobi

  • Ok danke dir,

    Leider bekomme ich es überhaupt nicht hin... Komme einfach an überhaupt keine Daten.

    Ich habs jetzt einfach anders gelöst indem ich die Excel Datei nach dem Importieren auslese... Wenn es aber doch einem in den Finger jucken sollte, darf man gerne noch Vorschläge einbringen.