System designTroubleshooting

Barcode scan event handling without recursive updates

Fix barcode scan event handling with empty-queue guards, serialized writes, explicit property replacement, and acknowledgment that preserves pending scans.

LumaSpec Vision10 min read Share on LinkedIn

On this page 9 sections
  1. 01Identify the event chain before changing the script
  2. 02Subscribe to completed data, not incidental activity
  3. 03Establish queue ownership and re-entry guards
  4. 04Write first, acknowledge second
  5. 05Preserve scans during failures and overlapping arrivals
  6. 06Calculate backlog and recovery requirements
  7. 07Choose reader hardware separately from handler behavior
  8. 08Verify the fix and prevent recurrence
  9. 09Questions

A barcode reaches the application, but copying it into a destination tag causes repeated events, an empty-array error, or missing scans. The usual problem is the handoff between the scan component and your script, not barcode decoding. This troubleshooting procedure separates scan completion, queue consumption, destination acknowledgment, and property updates. You can use it to diagnose an existing handler and design a replacement that preserves pending scans when writes fail or new scans arrive.

Identify the event chain before changing the script

An array property can change when a scan arrives and again when your handler removes that scan. If the handler assumes every change means new data, its own cleanup becomes another input event.

A typical failure sequence is:

  1. A completed scan makes the queue nonempty.
  2. The property-change handler reads the first entry and writes its value elsewhere.
  3. The handler replaces the queue with an empty list.
  4. That replacement generates another property-change notification.
  5. The handler reads the first entry again, although none exists.

An empty-array exception explains the last step. It does not prove the preceding write succeeded or that another scan was preserved.

Symptom Likely cause Test Corrective action
First-entry access fails Empty queue after cleanup or initialization Log queue length before indexing Return immediately when empty
Handler repeatedly invokes itself It changes the property it watches Log event origin and changed property Defer consumption and separate notification from processing
Several writes occur for one scan Multiple subscriptions or overlapping handlers Count handler starts and active writes Use one consumer and a re-entry guard
A scan disappears during cleanup An old snapshot replaces a newer queue Add a scan while the write is pending Remove only the acknowledged entry from current state
A failed write loses the scan Removal happens before confirmed success Simulate destination unavailability Retain the entry until acknowledgment

For problems before a complete value reaches your application, start with USB scanner integration and data capture. The remaining sections address what happens after that boundary.

Subscribe to completed data, not incidental activity

Prefer a documented scan-completed event when the component provides one. A keypress or generic action event may represent only part of the input, a terminator, or an unrelated interaction. Its suitability depends on the component's documented behavior.

If the supported interface exposes only a scans-array property, watch the documented property representing completed results. Establish whether notifications describe the whole array, an individual element, or a nested field. Also establish whether initialization and your own assignments generate notifications.

Temporarily log:

  • Event type, changed property, and documented origin value.
  • Queue length and handler entry time.
  • Scan identifier, write start, and write completion status.
  • Whether another write is already active.

Avoid logging full barcode contents when they contain sensitive information. A local transaction identifier often provides enough correlation.

Keep barcode values as strings throughout the handoff. For example, 000482 and 482 are different identifiers unless your application explicitly defines otherwise. Remove only documented framing characters; do not silently trim meaningful spaces or convert identifiers to numbers.

If decoding itself produces intermittent results, investigate barcode image preprocessing for blur and glare separately. Event guards cannot repair an incorrectly decoded value.

Establish queue ownership and re-entry guards

Check that the queue exists and contains an entry before accessing its first element. Then check whether a consumer already owns an active write. Both checks belong before any destination operation.

A busy flag prevents overlapping consumers only when checking and setting it cannot race. Run queue changes, pump requests, and completion handling through one serialized dispatcher, or use documented synchronization providing equivalent protection. A shared Boolean alone is insufficient across independent threads.

Use explicit property replacement when the framework requires assignments to publish changes. Mutating a local list wrapper may not update the component's observable property. Assign a newly constructed list through the documented property interface instead.

However, replacement is safe only with coordinated ownership. Reading a list, constructing a replacement, and assigning it later can overwrite an intervening append.

For reliable handling, give each completed scan an application record containing a unique identifier and its original string value. Preserve the identifier across retries. Identical barcode text can represent separate legitimate scans, so the text itself is not a suitable queue identifier.

If the component appends independently and offers no atomic removal or coordinated assignment, move completed events into an application-owned queue using its documented interface. Do not assume an externally managed array supports a race-free read-modify-write operation. Without event identities or an atomic handoff, lossless consumption may require a different integration interface.

Write first, acknowledge second

The following language-neutral pseudocode describes a serialized consumer. It is not a script for a particular platform. Every producer append, queue replacement, and completion callback must execute through the same coordinator. A pump request schedules work for later rather than invoking the consumer recursively.

on_queue_notification():
    request_deferred_pump()

pump():
    if paused or busy:
        return
    current = read_owned_queue()
    if current is empty:
        return
    entry = current[0]
    busy = true
    active_id = entry.id
    begin_destination_write(entry.value, entry.id,
        completion = post_result_to_coordinator)

on_write_result(result):
    if result.entry_id != active_id:
        report_unexpected_completion()
        return
    if not result.confirmed_success:
        paused = true
        busy = false
        retain_queue_and_schedule_controlled_retry()
        return
    current = read_owned_queue()
    if current is empty or current[0].id != active_id:
        paused = true
        busy = false
        report_queue_ownership_error()
        return
    replace_owned_queue(copy_of(current[1:]))
    busy = false
    request_deferred_pump()

The write adapter must report synchronous submission failures as well as asynchronous completion failures. Define one completion per attempt and reject stale callbacks from earlier attempts. A retry resets the paused state through the coordinator before requesting another pump.

Inspect the actual result for the intended destination. A call returning without an exception is not universally equivalent to a successful tag write; some interfaces return per-destination status values.

The success path rereads the current queue before removing its head. Under the ownership contract, this preserves entries appended during the write. Replacing the queue with the saved snapshot's tail would discard those arrivals.

A cleanup notification may schedule another pump. That is harmless: the deferred pump either finds more work or returns because the queue is empty. Documented origin filtering can reduce unnecessary notifications, but it should not replace the empty check, ownership rules, or busy guard.

Preserve scans during failures and overlapping arrivals

Consider a queue initially containing records A and B. The consumer starts writing A. While that operation is pending, the producer adds C, so the current queue becomes A, B, C.

After confirmed success, the coordinator verifies that A remains the head and replaces the queue with B, C. If the write fails, the queue remains A, B, C. Neither outcome requires clearing the entire property.

Use a bounded retry policy with an alarm or operator-visible failure state. Do not let a persistent destination failure create an immediate retry loop. Preserve the original record identifier and value on every attempt.

A timeout creates an ambiguous case: the destination may have accepted the value even though acknowledgment never returned. Retrying therefore provides at-least-once delivery, not automatically exactly-once processing. Where duplicates matter, the receiving system must recognize the transaction identifier and handle repeated delivery idempotently.

Also distinguish a tag write from downstream consumption. A latest-value tag can be overwritten before another task reads it. If every scan must drive a separate action, use an acknowledged mailbox, message queue, or durable transaction record rather than treating a changing string tag as a complete delivery protocol.

The same distinction matters when connecting identification to motion. Barcode checks before multi-robot picking explain why an accepted code and permission to act should remain separate decisions.

Calculate backlog and recovery requirements

A correct handler still needs enough queue capacity for destination pauses. Estimate capacity from measured scan arrivals, the longest tolerated interruption, and any additional burst allowance.

minimum queue capacity = ceil(arrival rate × interruption time) + burst allowance ideal serial service rate = 1 / average write duration backlog recovery time ≈ backlog / (service rate − arrival rate)

Suppose the application receives 6 scans/s and must tolerate a 3 s destination interruption. Allow another 5 entries for a burst beyond that baseline:

minimum queue capacity = ceil(6 × 3) + 5 = 23 entries

If a serialized write averages 80 ms, its ideal service rate is:

service rate = 1 / 0.080 = 12.5 scans/s recovery time ≈ 18 / (12.5 − 6) = 2.8 s

That recovery estimate ignores scheduling overhead, retries, and latency variation. Measure sustained service rate and high-percentile write latency under representative load before setting operating limits.

If service rate is not greater than arrival rate, the queue cannot recover while arrivals continue. More capacity only postpones overflow. Define whether a full queue pauses acquisition through a documented mechanism, rejects new work with an alarm, or spills into durable storage. Silent dropping is not an acceptable default for traceability.

An in-memory property does not provide restart persistence. If scans must survive application termination or power loss, persist acceptance and acknowledgment state in a suitable durable queue.

Choose reader hardware separately from handler behavior

The event model belongs to the host application and its integration interface. A different reader does not inherently fix recursive property updates or unsafe acknowledgment logic.

Use handheld code readers for operator-presented scans and fixed code readers when the installation requires mounted acquisition. Compare connection requirements through the camera and code-reader finder, then verify the documented host interface before ordering.

The LS-HCR-0001 is a wired USB handheld reader. Confirm the intended host input mode and completion behavior for your application:

Handheld code readersHandheld industrial barcode reader, 1.0 MP, USB 2.0 (LS-HCR-0001)LS-HCR-0001
Resolution
Up to 3 MP
Shutter
Global
Interface
USB
Connection
Wired
$539.00View part

The LS-CRD-0047 is a USB fixed code reader. Check mounting, code presentation, and host integration independently of the queue design:

Fixed code readersCompact LS-CRD-S02 series fixed industrial barcode reader, 1.2 MP, USB 2.0, focal length Short 60~120 (LS-CRD-0047)LS-CRD-0047
Resolution
Up to 3 MP
Shutter
Global
Interface
USB
$559.00View part

For installations aggregating acquisition and processing, vision controllers may form part of the architecture. Their role does not remove the need for documented event delivery, synchronization, and destination acknowledgment.

Verify the fix and prevent recurrence

Run these tests with a controlled destination before returning the handler to production:

  • Empty startup: initialize with no scans. No indexing error or destination write should occur.
  • Single scan: confirm one accepted record, one successful write, and removal of that record only.
  • Cleanup notification: verify that your own property replacement does not initiate another destination write for the removed record.
  • Arrival during writing: delay completion and add another scan. The new record must remain pending.
  • Destination failure: reject a write. The original head must remain available for retry.
  • Repeated barcode text: scan the same value twice. Distinct records must remain distinguishable.
  • Ambiguous timeout: test the receiving system's duplicate-handling policy rather than assuming the first attempt failed.
  • Restart and overload: verify the declared persistence and queue-full behavior, including operator-visible diagnostics.

Maintain counters for accepted records, confirmed acknowledgments, current depth, and explicit terminal failures. With no drops or removals outside the consumer, accepted records should equal acknowledged records plus pending records over the same accounting interval.

Document event subscriptions, queue ownership, retry limits, and the meaning of success beside the implementation. Check for duplicate subscriptions after view reopening or reconnection. If notifications can be coalesced or missed, use the framework's documented reconciliation mechanism to recheck authoritative queue state.

When the source exposes only unframed keystrokes, solve scan assembly first. When downstream processing requires its own acknowledgment, extend the protocol rather than clearing a latest-value tag faster.

Before selecting parts or revising the integration, bring the event trace, destination status results, peak arrival rate, and restart requirements to application support. Those details determine whether a guarded property handler is sufficient or a durable queue is needed.

Questions

Why does reading the first scan produce an empty-array error?

Clearing the property can generate another notification, so the handler runs again with no entries available. Initialization can produce the same condition. Test for an empty queue before indexing, and keep notification handling separate from the serialized consumer that performs destination writes.

Should the application use a scan-completed event or a property-change event?

Use the documented event that represents a complete scan, rather than assuming a generic action or keypress is sufficient. A property-change event can work when you handle empty values, initialization, self-generated notifications, and concurrent arrivals. Confirm the component's event semantics before choosing the subscription.

Can assigning a new empty list safely acknowledge a scan?

Explicit replacement may be required for an observable property, but assigning an empty list can discard other pending scans. After confirmed success, remove only the matching head from current queue state through a coordinated writer. If the component manages concurrent appends independently, use its documented acknowledgment interface instead.

How should failed or timed-out destination writes be handled?

Keep the pending record until success is confirmed. A timeout may mean the destination accepted the write but its acknowledgment was lost. Retry with the same transaction identifier and use an idempotent receiver when duplicate processing would be harmful. Add durable storage if pending scans must survive restart.

Does a successful tag write mean every scan has been processed?

Not necessarily. A successful tag write confirms only the behavior documented by that write interface. Another application task may miss intermediate values if later scans overwrite the tag. Use a receiving acknowledgment or transactional queue when each scan must be processed individually.