Code readingHow-to

Barcode scanner session property per user, not a shared tag

Per user, each barcode scanner result belongs in a session property, not a shared tag: the overwrite race in ms, the row insert, and the checks.

LumaSpec Vision9 min read Share on LinkedIn

On this page 8 sections
  1. 01Where should each operator's scanned barcode live?
  2. 02Why does the shared tag lose one of two simultaneous scans?
  3. 03How do you store the scan in a session property?
  4. 04How does each session write its own database row?
  5. 05How do you show one operator's scan on another screen?
  6. 06When is a shared tag the right answer?
  7. 07What checks prove the multi-user setup works?
  8. 08Questions

Multiple operators scan barcodes on their own phones or tablets, and every scan has to reach the database as that operator's row. The design question is where the decoded string lives between the scan event and the insert: a session property that belongs to one user, or a tag shared by every client. This guide gives the assignment line, the insert, the arithmetic behind the overwrite race, and the broadcast for other screens. The reader side can be the device camera in the browser or a fixed reader on Ethernet; the storage decision is the same either way. You will finish with a design that survives two people scanning in the same second.

Where should each operator's scanned barcode live?

In a session property that belongs to that operator's session, written with session.custom.Scanned_Barcode = data['text'] and inserted into the database from that same session rather than from a slot every client can write. A shared tag serves exactly one writer, so a multi-user scanning line gets the session property and a single-scanner line gets the tag.

A session is one browser tab or app instance per operator. The property is a string copy that no other session touches. A memory tag on the gateway is one global slot: whoever writes last wins, and the page that reads it next gets that value.

The scanner feeding the session does not change the answer. The operator's phone camera decoding in the browser works, as does a fixed reader pushing strings to the host over Ethernet — TCP client or listener: match your network barcode reader to your host covers that link. A fixed reader such as the LS-CRD-0028 decodes up to 60 codes/s and hands each string to the session as text.

Fixed code readers2560 x 2048 fixed code reader, 30 fps, 60 codes/s, 8 mm lens (LS-CRD-0028)LS-CRD-0028
Interface
Fast Ethernet
Resolution
5 MP
Sensor format
1/1.7"
Shutter
Global
$920.00View part

For capture on the device itself, start from scanning a barcode with a web camera into a database and webcam barcode scanning in browser-based vision systems; both stop at the decoded string, which is exactly where this design picks up.

Why does the shared tag lose one of two simultaneous scans?

Because the tag holds exactly one string for every client, the second write replaces the first before the first session's insert reads it; a 200 ms gap between two scans still destroys the earlier code when the insert runs 500 ms after the scan. Here is the worked case, with a page that writes the tag value to the database 500 ms after each scan:

Clock Event Tag value Database outcome
0.0 s Operator A scans ABC-1234 ABC-1234 nothing yet
0.2 s Operator B scans XYZ-9876 XYZ-9876 A's code overwritten
0.5 s A's insert reads the tag XYZ-9876 row: A, XYZ-9876
0.7 s B's insert reads the tag XYZ-9876 row: B, XYZ-9876

The database ends with two rows carrying XYZ-9876, and ABC-1234 is stored zero times: one lost code, one mislabeled row, and no error raised anywhere.

race window = insert time − tag write time lost scans = scans landing inside the window

The window here is 0.5 s − 0.0 s = 0.5 s, and B's scan landed 0.2 s inside it. Shorten the delay to 50 ms and the window becomes 50 ms; two scans 20 ms apart still collide. Faster polling narrows the failure window, it does not remove the failure mode.

How do you store the scan in a session property?

Add a String custom property at session level, assign the decoded text in the scan event with session.custom.Scanned_Barcode = data['text'], and bind the operator's display to that property — the storage is one line. The steps around it are what make the line safe.

  1. Add the property to the session, not to one view: name Scanned_Barcode, type String, initial value "". Session level is what keeps the value across navigation.
  2. In the decoder's scan event, assign the decoded text:
# Session custom property: Scanned_Barcode, type String, initial value ""
# data: dict from the decoder; example payload {"text": "ABC-1234"}
session.custom.Scanned_Barcode = data['text']
  1. Bind the operator's input field to session.custom.Scanned_Barcode, so the screen shows the exact string that will be inserted.
  2. After a confirmed insert, set the property back to "", so a page refresh cannot insert the same code twice.

The scan event is the same place a desktop HMI commits an entry field on Enter — barcode scanner to HMI IO field: commit tag data with Enter shows that pattern.

Two limits shape the design. Session properties live in the client session: they survive view navigation and die with logout or timeout, so a value that vanishes after a view change points at a property created on the view instead of the session — the causes are laid out in a session custom property that does not persist after navigation. And they are invisible to the gateway: tag change scripts run gateway-side and cannot read session properties, so a gateway reaction needs a memory tag written at scan time.

How does each session write its own database row?

Each session runs its own parameterized insert — one row per scan with a timestamp and the operator's id — so 20 operators scanning 12 codes a minute each is 4 inserts per second of independent rows, with nothing to coordinate. The insert belongs in the session that owns the scan:

-- one row per scan; :user_id and :barcode are bound from the session
INSERT INTO scan_log (scan_time, user_id, barcode)
VALUES (CURRENT_TIMESTAMP, :user_id, :barcode)
inserts per second = operators × scans per minute ÷ 60 20 × 12 ÷ 60 = 4 rows per second, one row per scan

Insert, never update: the log is append-only, and the user id column is what keeps rows distinguishable. Do not build cross-session write synchronization — the rows are independent, so there is nothing to synchronize. If the operator's screen lists their own recent scans, that view is a query bound to the user id; refreshing a query binding on a session custom property covers its refresh behavior. When the code must match a stored part number before insert, gate it with an HMI string comparison — string tag comparison for barcode scanner setup walks that configuration.

Keep the data paths straight while wiring this into the plant: barcode reader module confusion: vision vs MES vs web scanner paths separates the reader, the vision system and the MES links.

How do you show one operator's scan on another screen?

Broadcast it with system.util.sendMessage(project="<projectName>", messageHandler="BARCODESCANNED", payload={'text': data.text}, scope='S') and handle BARCODESCANNED in each receiving session; the message informs other screens while the session property stays the storage. The scope 'S' addresses sessions, and the handler name must match the call:

# session message handler, name: BARCODESCANNED
# payload: dict; example {'text': 'XYZ-9876', 'user': 'cell-3'}
session.custom.Last_Broadcast_Scan = payload['text']

One boundary to respect: a broadcast copies text to other screens, it cannot make another device scan. Each operator's device reads its own code — if three stations need the same code, each scans it, or one scans and the others acknowledge the broadcast.

When is a shared tag the right answer?

When exactly one scanner writes the value and exactly one consumer reads it — a fixed reader on a conveyor feeding one PLC tag — the shared tag is correct, simpler, and readable by gateway scripts that session properties are not. One writer, one reader, no race.

That covers the single-scanner line, the MES consuming a code from a tag, and gateway-side logging of every scan: write the string and a timestamp to a memory tag once per scan and let the gateway script watch it. For the reader side of that line, start from high speed conveyor barcode reading: choose the reader and setup.

For the single-writer case, fixed code readers such as the LS-CRD-0047 (1.2 MP, USB 2.0, 60-120 mm reading distance) or the MV-ID3013PM-12M-WBN (1.3 MP, 12 mm focal length, Fast Ethernet) write one string to one tag all shift.

Fixed code readersCompact fixed industrial barcode reader, 1.2 MP, USB 2.0, 60-120 mm reading distance (LS-CRD-0047)LS-CRD-0047
Resolution
Up to 3 MP
Shutter
Global
$559.00View part
Fixed code readersHikrobot MV-ID3013PM-12M-WBN 12mm focal length 1.3 MP Compact Industrial Code ReaderMV-ID3013PM-12M-WBN
Interface
Fast Ethernet
Resolution
Up to 3 MP
$1,979.00View part

The decision per case:

Scanning case Where the string lives Who writes the database
Several operators on their own devices A session property in each session Each session inserts one row per scan
One scan shown on other screens Session property plus a broadcast message Only the scanning session
One fixed reader feeding a PLC or MES One shared memory tag The gateway or PLC consumer
Gateway-side logging of every scan A memory tag written at scan time A gateway script, one row per write

The one combination to refuse is a tag handed between two human sessions: that is the mislabeled-row case in the race table above.

What checks prove the multi-user setup works?

Scan from two devices within one second of each other, then confirm two rows in the database with different user ids and both codes intact — that single pass catches the overwrite, the wrong-scope property, and the double insert together. Run it before go-live, not after the first complaint.

  • Two devices, two scans inside one second: two rows, both barcodes correct, neither mislabeled.
  • SELECT user_id, barcode FROM scan_log ORDER BY scan_time DESC LIMIT 2; returns both rows after the simultaneous test.
  • Navigate to another view and back: the property still holds the scan until the insert clears it.
  • Refresh the page after an insert: no duplicate row, because the property was cleared.
  • Log out and back in: the property starts empty, with no stale barcode from the previous user.
  • Drop the wireless link mid-scan: the operator sees the failure, no partial row exists, and a retry inserts once.

If codes sit behind film or shrink wrap, fix glare before touching software: polarizing covers and light boards from code reader accessories remove reflections at the source. And when the code read and a presence check belong on one device, smart cameras run both on the sensor and still hand the string to the same session.

For the hardware side, filter the camera and reader finder by interface and resolution, or send the scanning layout to us through the contact page and we will review the session design with you.

Questions

Can two operators scan into the same session property at the same time?

They each scan into their own copy: every operator's device runs a separate session, so the two writes never meet. Operator A's insert reads A's copy and B's insert reads B's, which makes the timing between the two scans irrelevant. The only shared resource is the database table, and two appended rows with different user ids do not conflict.

Does a session property survive navigation, logout and gateway scripts?

It survives navigation between views for the life of the session, and it is cleared at logout or session timeout. It does not reach the gateway: tag change scripts run on the gateway and cannot read client-session properties. When the gateway must react to a scan, write a memory tag at the moment of the scan and let the gateway script watch that tag instead.

Should the scanned barcode also go to a tag for the MES?

Only when a gateway-side consumer needs the value. Write the string and a timestamp to a memory tag once per scan and treat that tag as an event record for the MES or a logging script — never as the storage for per-user screens. The session property stays the source the operator sees, and the database row is written from the session.

What type and size should the scanned barcode property be?

A String, initialized empty, sized for your longest payload. Plain product codes usually stay under 20 characters, while GS1 strings carrying several application identifiers can pass 50, so set the property and the database column to the longest symbol you actually print. Clear the property back to empty after a confirmed insert so a page refresh cannot add a second row.

How many operators can scan this way before it breaks?

The sessions never touch each other, so the working limit is the database insert rate and the wireless network, not the property. At 20 operators scanning 12 codes a minute each — 4 inserts per second — a standard SQL database is close to idle. Size the database for your scan rate, keep one insert per scan, and the number of properties simply follows headcount.