You have a barcode-driven dispatcher in TestStand 4.1.1. A Select step with one value per Case (Case 1, Case 2, Case 3, Case 4) routes every product correctly. Collapse the products that share a test into Case 1,2 and Case 3,4, and no Case fires for any product. The sequence keeps growing because every new barcode needs its own Case block.
Skip the fixes that waste your time
These are the moves engineers usually make first. None of them fixes the grouped Case.
- One Case per barcode, duplicated body. It works, but every shared test now lives in two or more places. The sequence doubles in size and the copies drift apart the first time someone edits only one of them.
-
Reformatting the value list. Adding spaces, wrapping values in braces, or quoting the list as
"1,2"does not turn the Case into a multi-value match. The quoted version becomes a single literal string that no scanned barcode will ever equal. - Nesting a second Select inside a Case. Adds depth and still needs one Case per value at the inner level.
- Cloning the whole test sequence per product. Same maintenance problem as duplicated Case bodies, only bigger.
Rule these out and move on. The problem is how the expression engine reads the comma.
Understand what the comma does inside a Case expression
A Case step does not take a value list. It takes one expression, evaluates it, and compares the result against the Select expression. In the TestStand expression language the comma is an operator, not a list separator: it evaluates each sub-expression left to right and returns the value of the last one. So 1,2 evaluates to 2, and 3,4 evaluates to 4.
That gives you at best a Case that matches only the last value in the list. If nothing matches at all, a second mismatch sits on top: the data type of the Select expression.
| What you see | Cause |
|---|---|
| Grouped Case fires only for the last value in the list | Comma operator returns the last sub-expression; earlier values are discarded |
| Grouped Case never fires; single-value Cases work | Select expression is a string from the scanner, Case evaluates to a number (or the reverse), so the comparison never returns True |
| Nothing fires, even single-value Cases | Scanner appends a terminator or padding, so the string never equals the expected code |
| Default Case always runs | Any of the above; the Default branch hides the mismatch |
Check the Select expression type before touching the Cases
Do this first. It takes two minutes and tells you which fix you need.
- Set a breakpoint on the
Selectstep and run one product through the barcode read. - Open the Watch View and add the variable feeding the Select. Read its type (Number or String) and its exact contents.
- Look for trailing characters. Many scanners append a carriage return or line feed, and some pad fixed-length fields. A string code with a hidden terminator will never equal a clean literal.
- Evaluate a single Case expression in the watch or expression browser and compare its type to the Select result. A String compared against a Number is a non-match every time.
If the codes are numeric, convert the scanned string once, right after the read, for example with Val() into a Number local. Do the conversion in one place, not in every Case.
Rebuild the dispatch so one branch handles several barcodes
Two layouts work reliably. Pick one and use it everywhere in the station.
Option A: If / ElseIf with OR conditions
This is the layout most TestStand developers recommend for this exact job. Each branch takes a Boolean expression, so a group of barcodes is just an OR chain.
- Replace the
Selectstep with anIfstep. - Write the first group as one Boolean expression.
- Add an
Else Iffor each additional group and a finalElsethat fails the UUT with an "unknown product" message.
If Locals.ProductCode == 1 || Locals.ProductCode == 2
Sequence Call -> Test_Family_A
Else If Locals.ProductCode == 3 || Locals.ProductCode == 4
Sequence Call -> Test_Family_B
Else
Fail: unknown product code
End
Locals.ProductCode is an example name; use your own variable. For string codes, compare against quoted literals: Locals.Barcode == "1001" || Locals.Barcode == "1002".
Option B: Select on True
If you want to keep the Select/Case visual layout, put a constant in the Select and the Booleans in the Cases.
- Set the
Selectexpression toTrue. - Set each
Caseexpression to a Boolean OR chain, for exampleLocals.ProductCode == 1 || Locals.ProductCode == 2. - Keep a Default Case that flags an unknown code instead of silently passing.
Each Case now evaluates to True or False, and the first one that equals the Select value of True runs. Keep the groups mutually exclusive; if a code appears in two Cases, only the first one ever fires.
Option C: Move the mapping into data
When the product list keeps growing, stop encoding it in step structure. Store a table of barcode-to-test-family pairs (a station global, a file loaded at startup, or a container array), look up the family name after the scan, and use a single Sequence Call that takes its sequence name from an expression. Adding a product becomes a data edit, not a sequence edit.
Prove every barcode lands in the right test
- Build a checklist of every barcode currently in production with its expected test family.
- Run each one, or feed the code manually into the variable at the breakpoint, and single-step through the dispatch. Confirm the expected branch executes.
- Feed one invalid code and confirm the Else/Default branch fails the UUT. A dispatcher that passes an unknown product is worse than one that crashes.
- Scan with the real scanner at least once per branch to catch terminator or padding characters that manual entry hides.
- Check the report: the executed sequence name should appear for every product. Keep the checklist and rerun it whenever you add a barcode.
Avoid the traps that bring the mismatch back
-
Single
=in a condition. In a TestStand expression,=assigns.Locals.ProductCode = 1overwrites the code and evaluates as the assigned value. Use==. -
Mixed types across branches. One branch compares against
1001, another against"1002". Convert once and compare one type everywhere. -
Leading zeros. Converting
"0012"to a number gives12. If the zeros are meaningful, compare as strings. - Overlapping groups. A code listed in two branches runs only the first. Keep each code in exactly one place, which Option C enforces by design.
- No Default branch. Without it, an unmatched code skips the whole dispatch and the UUT can report a pass with no tests run.
FAQ
What happens if I write Case 1,2 in a TestStand Select step?
The comma acts as an operator and the expression evaluates to the last value, 2. At best only that value matches; if the Select expression is a string, nothing matches.
What happens if the barcode is a string and my Case values are numbers?
The comparison never returns True, so the Case is skipped and the Default branch (if any) runs. Convert the scanned string once with Val() or compare against quoted string literals.
What happens if two Case expressions both evaluate to True with Select True?
Only the first matching Case runs; the later one is ignored. Keep each barcode in exactly one group.
How do I match several barcodes in one TestStand branch?
Use an If or Else If with an OR chain such as Locals.ProductCode == 1 || Locals.ProductCode == 2, or set the Select expression to True and put that OR chain in the Case.
What happens if the scanner adds a carriage return to the code?
String comparisons fail because the stored value contains the extra character. Check the variable in the Watch View, then strip the terminator in the scanner configuration or right after the read.
If a Select True or If/ElseIf dispatch still skips a branch after you have confirmed matching types and clean scanner data at a breakpoint, stop editing the sequence. Export a minimal sequence that reproduces the mismatch and take it to NI technical support with your exact TestStand version.