How to validate user input versus a named set

There are several places in our process phases where a user is prompted for string input, and that input needs to match an entry in a named set. In one case, there are over 40 possible legitimate responses, so a drop-down is not practical. So far, we've been doing essentially:

'^/NAMED_SET.CVS' := '^/USER_RESPONSE_STRING.CV';

and then verify the CVI isn't zero. While this does weed out any string input that isn't in the list, we recently discovered that entering an integer is silently accepted! Looks like, despite assigning to the .CVS component of the named set, a string made up of integer characters (like "44") is silently converted to an integer data type and assigned, even if that integer doesn't have an entry in the named set.

What is a good way of weeding out integer input (which will always be invalid, as named sets can't contain integer-character names)?

  • In reply to Matt Stoner:

    I managed to do this entirely in the phase, without having to have something on the graphics do the checking.
    1. Set the named set variable (let's call it NAMED_SET) to 0 (NONE). Prompt the user for the named set entry. Copy the prompt string to a phase variable (let's call it RESPONSE).
    2. Have the transitions from that step be the "good" result (RESPONSE = "NONE" and NAMED_SET != NONE and NAMED_SET != UNKNOWN), and the "bad" result (NAMED_SET = UNKNOWN). (NONE=0, UNKNOWN=255), with the "bad" result causing the step to repeat.
    3. On the failure monitor, have an action block that copies the phase variable to the named set, and also copies it to a temporary string, appending an extra character ('^/TEMPSTRING.CV := "x" + '^/RESPONSE.CV';)
    4. Have a condition block checking for BLOCK_ERR on the first action block - if there is one, it's because the user entered something that doesn't match the named set. This triggers an action block to set NAMED_SET := 255 (UNKNOWN) and RESPONSE := "NONE". (Which triggers the prompt step to repeat.)
    5. Have another action block:
    IF (RESPONSE != "NONE") and (NAMED_SET != "NONE") THEN
    IF (TEMPSTRING != ("x" + NAMED_SET.CVS)) THEN /* Assigned the named set value, but its string doesn't match what the operator typed, so it must have been an integer. Reject it. */
    NAMED_SET := 255 (UNKNOWN);
    ENDIF;
    RESPONSE := "NONE";
    ENDIF;

    Note: These **MUST** be separate actions; it looks like there's some kind of optimizing compiler that effectively negates the check if they're combined. (Ex. If you do NAMED_SET := RESPONSE; TEMPSTRING := NAMED_SET.CVS, then enter a string that isn't in the named set, you can end up with TEMPSTRING being the string that was entered even though it isn't valid in the named set!)

    Thus:
    1. If they entered a string that isn't in the named set, the first action block has an error, so the condition block's action block causes the prompt step to repeat.
    2. If they entered an integer, the third action block will detect that what they typed isn't the same as the named set's string, so it causes the prompt step to repeat.
    3. If they entered a valid string, the named set will stay as a known value and the response will be set to NONE, allowing the phase to proceed.

    I've thoroughly tested this myself, and had someone else try what they could think of - it seems to work perfectly. If you can spot any holes in it, please let me know.