Barcode Count Example¶
This example counts items with a USB barcode scanner. Each item carries a 2D barcode, and every scan becomes one test result, so Lente counts the items, by product and lot.
The example is in public/prism/scripts/example/barcode_count_v0,
count_0.scr - the script
tst00xx.py - the test program
make_barcodes.py - makes a PDF sheet of QR codes to print
barcodes_P001.pdf - a sample sheet
The driver is public/prism/drivers/scanners.
Barcode Format¶
The barcode holds P###-S#####, where P### is the product model number and ##### is the product serial number, for example P001-S00123.
The test checks each scan against the regex in the script, TST010_scan regex, ^(?P<product>Pd{3})-S(?P<sn>d{5})$. The regex groups product and sn pick the fields from the barcode, so a different barcode format only needs a new regex,
product is saved as the result info product, P001
sn is saved as key0, sn:00123
the whole barcode is saved as the measurement TST010_scan.barcode
A scan that does not match the format is recorded as a FAIL.
Scanning the same barcode twice makes two results. To count each item once, select Unique units, last result in the Lente Throughput & Yield chart, which counts each product and key0 once, see Lente screenshots.
Make a sheet of barcodes to print with,
cd public/prism/scripts/example/barcode_count_v0
python make_barcodes.py --product P001 --start 123 --count 24
This writes barcodes_P001.pdf, 12 codes per Letter page.
Lot¶
The Lot is entered when the script is loaded, in the format YYYYMMDD-##, a date and a two digit number, for example 20261025-01 for the first lot of the day. It is saved as the result info lot, and Lente can show yield and counts by lot.
Scanner¶
The scanner must be in USB keyboard mode (HID), the usual factory setting. The scanner “types” the barcode followed by Enter, into the Prism text box.
The Prism Test Portal page must have the keyboard focus when scanning
The driver finds the scanner by its USB manufacturer, see SCANNER_MANUFACTURERS in hwdrv_scanners.py. Datalogic is supported, add your scanner’s manufacturer as required. python public/prism/drivers/scanners/hwdrv_scanners.py lists the scanners found.
If no scanner is found, the script does not load
A scanner in keyboard mode cannot be told apart from another scanner, or from the keyboard, so there is only one channel, even with more scanners attached.
Running¶
After the script is loaded and started, the operator just keeps scanning. The scanner driver restarts the script after each scan, there is no button to press.
A scan made before Prism shows the next text box is lost, so Prism beeps when it is ready for the next scan. The driver checks every 0.2 seconds (play_poll_sec, see Drivers), so the next text box is ready about a second after a scan.
The beep plays on the Prism computer, using aplay. Set AUDIO_DEVICE in Scanners.py if the default sound output is not the right one, aplay -L lists them.
Set “ready_beep”: false in the TST010_scan item to turn the beep off.
The scan item has a 5 minute timeout (“timeout”: 300 in the script). If nothing is scanned in that time, the item is recorded as TIMEOUT and the script stops restarting, so an idle station sends one TIMEOUT result, not one every 5 minutes. To resume, press the channel number (Start) button on the Test Portal, and the next good scan turns the automatic restarts back on.
Change the timeout to suit, see Timeouts and Aborts.
Command Line Testing¶
The script runs with prism_dev.py (see Development), the barcode is scanned into the terminal prompt,
python prism_dev.py --script public/prism/scripts/example/barcode_count_v0/count_0.scr
prism_dev.py runs the script once, and the result file is written to the current folder.
Script¶
// Example: Count barcoded items with a USB barcode scanner (keyboard mode)
// Barcode string looks like: P###-S#####, P### product, ##### serial number
// The regex groups "product" and "sn" pick the fields from the barcode,
// info:product is filled in from the barcode, dB key0 is the serial number
// The script restarts after each scan, if no scan within the item timeout,
// the item records TIMEOUT and the restarts stop, press channel Start to resume.
// Make barcodes with make_barcodes.py, see sample barcodes_P001.pdf
// See the count in Lente, Results Analytics, Throughput & Yield: select Lot,
// and "Unique units, last result" so a barcode scanned twice counts once.
{
"subs": {
"Lot": {
"title": "Lot (format YYYYMMDD-##)",
"type" : "str", "widget": "textinput",
"regex": "^20\\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\\d|3[01])-\\d{2}$",
"default": "20261025-01"
},
"Loc": {
"title": "Location",
"type" : "str", "widget": "select", "choices": ["canada/ontario/milton",
"us/newyork/buffalo"]
}
},
"info": {
"product": "N/A",
"bom": "N/A",
"lot": "%%Lot",
"location": "%%Loc"
},
"config": {
"fail_fast": true,
"drivers": ["public.prism.drivers.scanners.hwdrv_scanners"]
},
"tests": [
{
"module": "public.prism.scripts.example.barcode_count_v0.tst00xx",
"options": { "fail_fast": false },
"items": [
{"id": "TST010_scan", "enable": true, "timeout": 300, "ready_beep": true,
"regex": "^(?P<product>P\\d{3})-S(?P<sn>\\d{5})$" },
{"id": "TST990_TEARDOWN", "enable": true }
]
}
]
}
Program¶
#! /usr/bin/env python
# -*- coding: utf-8 -*-
"""
Sistemi Corporation, copyright, all rights reserved, 2026
Martin Guthrie
"""
import re
import logging
from core.test_item import TestItem
from public.prism.api import ResultAPI
from public.prism.drivers.scanners.Scanners import DRIVER_TYPE as DRIVER_TYPE_SCANNER
# file and class name must match
class tst00xx(TestItem):
# The item timeout ends the textbox wait, so this only has to be longer than any item timeout
TEXTBOX_TIMEOUT_SEC = 24 * 60 * 60
def __init__(self, controller, chan, shared_state):
super().__init__(controller, chan, shared_state)
self.logger = logging.getLogger("tst00xx.{}".format(self.chan))
def _scanner(self):
""" The scanner driver for this channel, or None """
drivers = self.shared_state.get_drivers(self.chan, type=DRIVER_TYPE_SCANNER)
if not drivers:
return None
return drivers[0]["obj"]["hwdrv"]
def TST990_TEARDOWN(self):
""" Always called at the end of testing
- process any cleanup, closing, etc
{"id": "TST990_TEARDOWN", "enable": true },
"""
ctx = self.item_start() # always first line of test
self.item_end() # always last line of test
def TST010_scan(self):
""" Scan barcode
- regex is the barcode format, P###-S#####, its groups "product" and "sn"
pick the fields, product goes to info product, sn is the serial number key
- ready_beep, beep when ready for the scan, default true
- if nothing is scanned before the item timeout, Prism records TIMEOUT,
and the scanner stops restarting the script, until the next good scan
{"id": "TST010_scan", "enable": true, "timeout": 300, "ready_beep": true,
"regex": "^(?P<product>P\\d{3})-S(?P<sn>\\d{5})$" },
"""
ctx = self.item_start() # always first line of test
scanner = self._scanner()
self.log_bullet("Scan barcode")
if scanner and ctx.item.get("ready_beep", True):
scanner.ready_beep()
user_text = self.input_textbox("Scan:", "barcode", timeout=self.TEXTBOX_TIMEOUT_SEC)
if not user_text["success"]:
# timed out or aborted, stop restarting the script, Prism records the TIMEOUT
if scanner:
scanner.stop_player()
self.log_bullet("No scan: {}".format(user_text.get("err")))
self.log_bullet("Press channel Start to resume")
self.item_end(ResultAPI.RECORD_RESULT_FAIL)
return
barcode = user_text["textbox"].strip()
match = re.match(ctx.item.regex, barcode)
if not match:
_, _result, _bullet = ctx.record.measurement("barcode", barcode, ResultAPI.UNIT_STRING,
force_fail=True)
self.log_bullet("Bad format: {}".format(barcode))
self.item_end(_result)
return
ctx.record.record_info_set({"product": match.group("product")})
ctx.record.add_key("sn", match.group("sn"), slot=0)
# Note: ResultAPI.UNIT_STRING is used to format the measurement correctly in JSON
_, _result, _bullet = ctx.record.measurement("barcode", barcode, ResultAPI.UNIT_STRING)
self.log_bullet(_bullet)
# a good scan (re)starts the automatic restarts, after a timeout and channel Start
if scanner:
scanner.start_player()
self.item_end(_result) # always last line of test