System designHow-to

One barcode scan event: route data to different tags by context

Every barcode scan fires one generic event, so the wrong tag gets written. Learn to route scan data to different tags with a context value and a part map.

LumaSpec Vision10 min read Share on LinkedIn

On this page 11 sections
  1. 01Why every scan lands in the first tag
  2. 02What you need
  3. 03Route the scan in seven steps
  4. 04The whole handler, with the test that proves it
  5. 05Write on submit, not on scan
  6. 06Worked example: 24 SKUs, two contexts
  7. 07When the tag is right but the part is wrong
  8. 08Hardware for a fixed scan station
  9. 09Four tests before you call it done
  10. 10Go-live checklist
  11. 11Questions

You built an inventory screen with a scan button and a couple of barcodes, and every scan writes the first tag in your script. The cause is structural: the barcode-scanned event is generic, so every scan from every view calls the same handler, and a handler with a hard-coded tag path writes that tag for every code it receives. This guide routes that one event to many tags. You will attach a context value to each scan-barcode action, branch on it in a single handler, map scanned part numbers to tag paths, write from a submit action, and prove the wiring with a four-scan test. The same pattern runs a scan-to-login page and a cycle-count page on one screen.

Why every scan lands in the first tag

The barcode-scanned event your runtime hands you is generic by design: the scanner knows what it read, not why it was read. Every scan-barcode action on every view fires the same event, and the event arguments carry the decoded text plus one context object you control. A handler that ignores the context and writes [default]Inventory/Qty_BRG_6205 writes that tag for a bearing, a seal, or an operator badge alike. That is the whole bug, and it is why adding barcodes changed nothing.

Routing has two axes, and each needs its own key:

  • Which purpose fired the scan — answered by the context value you set on the action, such as login or cycleCount.
  • Which exact tag the value belongs in — answered by the decoded text through a part-number-to-tag-path map.

Branch on the context for the source, and on the value for the destination inside that branch. If your screen only ever shows one scanning view at a time, route barcode scan data by view with a session property covers that variant; this article covers several scan purposes sharing one event.

Scan purpose Context value Handler branch Where the write lands
Operator login login write the badge to the operator tag [default]Inventory/OperatorID
Cycle count cycleCount look up the part, open the count view [default]Inventory/Qty_<part>
Goods receipt receipt same lookup, sign chosen in the view [default]Inventory/Qty_<part>

receipt is a name you choose: contexts are your strings, not fixed vocabulary.

What you need

Route the scan in seven steps

  1. Name the contexts first. One word per purpose: login, cycleCount, receipt. The branch structure of your handler comes straight from this list, so write it before any script.
  2. Set the context on each scan-barcode action. On the login view the action carries login; on the inventory view it carries cycleCount. An action without a context is a scan you cannot route, so give every one a value.
  3. Branch before anything else runs. The first statement of the single barcode-scanned handler reads the context member and dispatches; no tag write sits ahead of that branch.
  4. Map part numbers to tag paths in one place. A dictionary lookup beats a chain of if-statements: 24 entries, one place to add SKU 25, one place to audit.
  5. Open the count view with the part number as a parameter. The scan event knows the code, not the quantity; the view collects the count from the operator and shows what was scanned.
  6. Write the tag from the submit button. Submit is the first moment that holds all three values — part, count, operator — so the write belongs there and nowhere else.
  7. Guard the edges. An unknown part pops a message and writes nothing; a repeat of the same code inside 1.5 s is swallowed as a duplicate.

The whole handler, with the test that proves it

# Illustrative handler. context is the event argument the runtime passes;
# "login" and "cycleCount" are the context strings set on the actions.
# Replace write_tag and open_view with your runtime's own calls.

PART_TO_TAG = {                                  # dict[str, str]: part number -> tag path
    "BRG-6205": "[default]Inventory/Qty_BRG_6205",
    "SEAL-14": "[default]Inventory/Qty_SEAL_14",
}

route = context["context"]                       # str: "login" or "cycleCount"
code = str(decoded_text)                         # str: the decoded text from the event

if route == "login":
    write_tag("[default]Inventory/OperatorID", code)
elif route == "cycleCount":
    target = PART_TO_TAG.get(code)               # str, or None when unknown
    if target is None:
        show_message("Unknown part: " + code)    # str shown to the operator
    else:
        open_view("countEntry", part=code, target=target)

The two classic commissioning failure modes both show in one instrumented scan. Print route and target at the top of the handler, scan once, and read them back: comparing against the whole context object instead of its member sends every scan down the default path, and a lookup that quietly falls back to a hard-coded tag reproduces the original symptom.

# Test: scan "BRG-6205" with the action context set to "cycleCount".
# Pass: the count view opens showing part "BRG-6205"; entering 7 and
# submitting writes 7 to Qty_BRG_6205; OperatorID and every other
# Qty_ tag keep their previous values.

Write on submit, not on scan

At the instant the event fires you hold exactly two things: the decoded text and the context. The quantity arrives seconds later from a person, so a write inside the scan event either stores the wrong kind of value or stores nothing useful. The submit action is the first code that runs with part, count and operator in scope, which makes it the only honest place for the write — and it is where a fan-out to count, timestamp and operator belongs; setting several HMI tags from one button event walks through that pattern on the HMI side.

If the destination is a PLC tag rather than an HMI tag, the routing logic does not change, but the consumer does. A controller can read and clear your string within one scan cycle, so latch the barcode string on the pulse and handshake the reset with the program that consumes it.

Worked example: 24 SKUs, two contexts

Run the closet as built: 24 labelled bins, 24 entries in PART_TO_TAG, contexts login and cycleCount. A full count pass reads 24 codes in 96 s, which is 96 ÷ 24 = 4.0 s per scan at a steady desk pace. Set the duplicate guard to 1,500 ms: 1.5 s ÷ 4.0 s = 0.375, so the guard covers 37.5 percent of the average scan interval — enough to catch a doubled trigger from an inherited event, whose UI-side cause is covered in scan barcode action fires twice: fix inherited container events — while never swallowing the next genuine scan. One unknown code in the pass yields one popup and zero writes.

The decision, stated plainly: a 24-entry map in one dataset, the guard at 1,500 ms, one write per scan, and every routed scan logged with its context value and destination path.

When the tag is right but the part is wrong

Routing cannot fix a scan that decoded the wrong label. If two labels sit inside the field of view, the reader returns one string — whichever code it locked first — and the map faithfully routes bad data into a valid tag. Hold the image to one label before you blame the branch.

px_per_mm = image_width_px / field_of_view_mm narrow_bar_px = narrow_bar_mm × px_per_mm

Worked numbers for a presentation station: a 1,440-pixel-wide decode image over a 120 mm field of view gives 1,440 ÷ 120 = 12 px/mm. A 0.25 mm narrow bar, typical of warehouse-grade Code 128 labels, then spans 0.25 × 12 = 3 px — the working minimum for dependable decode once blur takes its share. Invert the formula for the limit: 1,440 × 0.25 ÷ 3 = 120 mm is the widest field of view that keeps a 0.25 mm bar at 3 px.

If the shelf can present two labels 80 mm apart, 120 mm sees both, so tighten to 100 mm — 1,440 ÷ 100 = 14.4 px/mm, bar at 3.6 px — or fence the presentation so one label faces the reader. The camera resolution calculator runs this arithmetic for your own reader, and when the station genuinely must read several codes at once, read multiple QR codes in one image and pick the right one extends the same routing idea to selection by position. Torn, tilted or glary labels are an image problem, not a routing one: 1D barcode preprocessing for rotation, blur and glare is the next stop.

Camera resolution for the smallest feature
Units
Pixels needed
–
Sensor size
– MP
Smallest sensor that fits
–
Pixel on the part
– mm
Pixels across the feature, that sensor
–

Pixels needed = field / feature × pixels per feature on each axis (field incl. margin). The lens must resolve the pixels too: Nyquist = 1000 / (2 × pixel µm) lp/mm.

Find cameras with enough pixels Open the full calculator

Hardware for a fixed scan station

When the closet grows into a station, swap the hand scanner for a reader that produces one event per presentation. For an overhead shelf pass, a smart camera with built-in illumination keeps the light moving with the lens:

Smart cameras & vision sensors1.6 MP smart camera, 60 fps, 16 mm lens, white light (polarized, transparent light) (LS-SMC-0063)LS-SMC-0063
Interface
GigE
Resolution
Up to 3 MP
$1,110.00View part

A compact vision sensor does the same job at a presentation point where the part faces the reader:

Smart cameras & vision sensorsHikrobot MV-SCB007EC-12S-WBN SCB vision sensor, 1.6 MP, Color, 12 mm lens, Manual focusMV-SCB007EC-12S-WBN
Interface
Fast Ethernet
Resolution
Up to 3 MP
Sensor format
1/2.9"
Shutter
Global
$1,989.00 · quotedView and quote

Decode happens on the device for both, over GigE or Fast Ethernet, so the station pushes one finished string per scan into your handler — exactly one event per presentation.

Add an LED illuminator to a fixed reader and drive it from one of the light controllers rather than a bench supply, so the light fires only during the exposure window; camera strobe output timing: exposure pulse and image timestamp sets that timing. A compact driver for that job:

Light controllers4-Channel Digital LED light controller, 16-24V, AC100-240V 50/60 Hz, 8 A total output (LS-CTL-0322)LS-CTL-0322
Output
Constant voltage 24 V
Channels
4 channels
Control
Digital
$539.00View partBuild a system with it

Treat any strobed illuminator as a bright source: mount it off the operator's line of sight, and let the low duty cycle keep it cool — a strobe that fires only during exposures is easier on eyes and wiring than a lamp left burning.

At a station a cell controller starts, trigger the decode from a PLC tag instead of a UI button keeps the event source singular, which is what the context branch expects. For power and trigger wiring at the station:

The drag-chain lead is an M8 8-pin power and I/O cable, 8-core, 7 m; the second is a 17-pin M12 power cable with open leads, 7 m. Browse the vision sensor finder by interface and mount, or build the station in the configurator.

Four tests before you call it done

  1. Login scan. From the login view, scan badge OP-42. Pass: OperatorID reads OP-42, and every Qty_ tag is unchanged.
  2. Known part. From the count view, scan BRG-6205, enter 7, submit. Pass: Qty_BRG_6205 reads 7 and OperatorID is unchanged.
  3. Unknown part. Scan UNLISTED-0000, a code absent from PART_TO_TAG. Pass: the popup appears and no tag changes anywhere.
  4. Double scan. Scan SEAL-14 twice inside 1.5 s. Pass: one count view opens and one write lands; the guard eats the second read.

Go-live checklist

  • Exactly one barcode-scanned handler, and the context branch is its first statement.
  • Every scan-barcode action carries a context — no orphan actions anywhere in the project.
  • PART_TO_TAG lives in one dataset; adding a SKU adds one entry and touches no script.
  • Unknown codes write nothing and tell the operator why.
  • Writes happen on submit; PLC-consumed strings are latched on a pulse.
  • Duplicate guard at 1,500 ms; every routed scan logged with context value, decoded code, destination path and timestamp — barcode scan timestamps to track part cycle time per workstation turns that log into throughput data.
  • Field of view sized to one label using the resolution arithmetic above.

Questions

Do I need a separate barcode-scanned event for each view?

No. The event is generic by design: one handler receives every scan. Configure a context string on each scan-barcode action — login, cycle count — and branch on it as the first statement of that single handler. Add a context when you add a scan purpose, not a new handler. A second handler earns its place only when a different physical device feeds the same screen, because the decoded text then arrives through different event arguments and merging them costs more than one branch.

Should I branch on the barcode value or on the context?

Both, in that order. The context answers why the scan happened, so the same printed code lands in an operator tag at login and in a quantity tag at the shelf. The value then picks the exact tag through your part-number-to-tag-path map. Branching on value alone fails the day an operator badge number collides with a part number, or the same carton is scanned for two purposes in one shift.

How many tags can one handler route to?

More than your inventory will ever hold. The map is a dictionary lookup, so 20 entries and 200 entries cost the same per scan. The practical limit is the tag provider, not the routing: keep one scan to one write, and push any fan-out — count, timestamp, operator — onto the submit action where the values already exist. A few hundred SKUs on one handler is routine.

Why does the first tag still receive every scan after I add context?

Two usual causes. First, the comparison reads the whole context object instead of its member, so context["context"] == "login" matches while context == "login" never does. Second, the branch runs but every path writes the same tag because the mapping was never consulted — a default write sits after the if-chain instead of inside an else. Print the context value and the resolved tag path for one scan and both faults show immediately.

Can the same routing write PLC tags instead of HMI tags?

Yes. The routing does not change — context picks the branch, the map picks the path — but a PLC consumer can read and clear your string within one scan cycle. Latch the written value on a rising edge and handshake the reset with the consuming program. Write the count as a number, keep the part number as its own string tag, and never pack both into one string you must parse apart downstream.