How Do I Map an Ignition Status Word to One State Integer?

James Nishida7 min read
HMI / SCADAOther ManufacturerTechnical Reference
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

A bit-coded PLC status word becomes a single state integer in Ignition through one expression tag. getBit() extracts each bit, and binEnum() returns the 1-based position of the first true argument, or 0 when none is true. Tag history on that expression tag records only state transitions. A gateway script that inserts rows into a database duplicates the historian and moves the work into Jython. Use one only if the checks below rule out the expression tag.

Check 1: whether anything needs past states

Prerequisite: know who consumes the state value. That could be a live display, a trend, a downtime report, or an alarm pipeline.

  1. Reading: does any trend, report, or query need historical state values?
  2. If no: put the expression in a property binding on the display component. No tag is created and nothing is stored. Go to Check 2 only to choose the expression itself.
  3. If yes: build an expression tag with history enabled. Continue to Check 2.

Storage load is lower than it looks. With history sample mode set to on-change and a discrete deadband, the historian writes one row each time the state integer changes. Scan rate does not drive the row count. A machine that changes state a few hundred times per shift produces a few hundred rows per shift. The status word's own history is not required. Historize the derived state and leave the raw word off unless bit-level forensics are needed.

Check 2: one-hot word or overlapping bits

Prerequisite: the PLC program or the device manual that defines each bit of the status word.

Reading: watch the word in the Tag Browser through several machine cycles and compare it against the bit definitions.

Observed word pattern Meaning Mapping to use
Exactly one bit set at any time One-hot state word binEnum() directly, bit 0 as first argument
Word equals 0 No state active (idle, or PLC not yet running) binEnum() returns 0; reserve 0 as "no state" in your state table
Two or more bits set together Overlapping status flags, or a transition overlap in the PLC scan Order binEnum() arguments by priority, or flag the condition as invalid

For a one-hot word, the state integer equals the bit number plus 1. For overlapping bits, binEnum() reports the first true argument in the list. List the highest-priority bit first. For example, put a fault bit ahead of a running bit. Keep a written map, because the result now reflects argument position rather than bit number.

Use a single-bit test to catch words that should be one-hot but are not. The expression x & (x - 1) is zero only when x has zero or one bit set. Any nonzero result means more than one bit is on. Use this test to return a sentinel such as -1, so a PLC logic error shows up in history instead of hiding behind the lowest-numbered bit.

Check 3: signed 16-bit word inside an Integer tag

Prerequisite: the PLC data type of the status word (signed INT or unsigned WORD) and the Ignition tag data type.

  1. Reading: view the tag value while bit 15 is set.
  2. If the value is negative (range -32768 to 32767): the word arrives signed. In a 32-bit Integer tag, sign extension sets bits 16 to 31 whenever bit 15 is on.
  3. If the value is 32768 or higher (range 0 to 65535): the word arrives unsigned and no correction is needed.

Sign extension does not affect getBit() on positions 0 to 15. It does break the single-bit test, because the extended upper bits make x & (x - 1) nonzero. Mask the word with & 65535 before any whole-word arithmetic or comparison.

Expression tag versus gateway script and database writes

Approach Execution History Failure mode under bursts
Expression tag, event-driven Expression engine, separate from the tag event script pool Built-in historian, on-change Re-evaluates on each source change
Tag Value Changed event script plus DB insert Jython, tag event script pool Custom table, custom pruning Per-tag event queue can overflow; check the missedEvents flag
Gateway Tag Change event script plus DB insert Jython, gateway event scripting Custom table, custom pruning Queued changes delay or collapse under load

Expression tags run in a different execution pool from tag event scripts. Evaluating an expression costs far less than running Jython. The scripted route also forces you to build the table schema, handle database outages, and manage pruning, all of which the historian already does. Do not call runScript() inside the expression. Doing so pulls Jython back into the expression path and brings the scripting load and failure modes with it.

Building the state expression tag

Prerequisites: the full status word tag path, a configured history provider, and the bit-to-state map from Check 2.

  1. Create an expression tag in the same folder as the status word. Set its data type to Integer. Confirm: the tag appears with Bad or Uncertain quality until the expression is entered.
  2. Set the execution mode to event-driven so the tag re-evaluates when the status word changes. Confirm: no tag group polling is assigned.
  3. Enter the expression using a relative reference to the source tag (one-hot form shown, with a -1 sentinel for overlapping bits). Confirm: quality goes Good and the value matches the current word.
    if(
      (({[.]StatusWord} & 65535) & (({[.]StatusWord} & 65535) - 1)) != 0,
      -1,
      binEnum(
        getBit({[.]StatusWord}, 0),
        getBit({[.]StatusWord}, 1),
        getBit({[.]StatusWord}, 2),
        getBit({[.]StatusWord}, 3)
        // continue through bit 15 as the word definition requires
      )
    )
    Replace StatusWord with your tag name. Remove the comment line before saving. For priority mapping, drop the if() wrapper and reorder the getBit() arguments, highest priority first.
  4. Enable history. Select the storage provider, set the deadband style to discrete, and set sample mode to on-change. Confirm: the history icon shows on the tag in the Tag Browser.
  5. Record the state table (0 = none, 1 = bit 0, ... 16 = bit 15, -1 = invalid) in the tag documentation field. Confirm: displays and reports decode from this table, not from raw bit numbers.

Exercising every bit and confirming stored transitions

Prerequisite: point the expression at a memory tag copy of the status word, not a live PLC address on a running machine.

Random integers are a weak test. A draw from 0 to 65535 (or -32768 to 32767 for a signed tag) almost always has several bits set, so it exercises only the -1 branch. Walk a single bit through each position, then add deliberate overlaps. From the Script Console:

import time
path = ["[provider]Folder/StatusWord"]   # replace with your memory tag path
for n in range(16):
    v = 1 << n
    if v > 32767:          # signed Int tag: bit 15 must be written as negative
        v = v - 65536
    system.tag.writeBlocking(path, [v])
    time.sleep(2)          # hold longer than the history storage interval
system.tag.writeBlocking(path, [3])    # bits 0 and 1 together
time.sleep(2)
system.tag.writeBlocking(path, [0])
  1. Watch the state tag during the walk. Confirm: it steps through 1 to 16 in order and stays positive on bit 15.
  2. Check the overlap write. Confirm: the state tag reads -1, or the priority-ordered position if you removed the sentinel.
  3. Check the final write of 0. Confirm: the state tag reads 0.
  4. Query the state tag in a trend or with a history query over the test window. Confirm: exactly 19 stored transitions (16 single bits, the overlap, 0, plus the starting value). There are no duplicate rows between writes. A missing bit-15 row means the mask or signed write is wrong.

FAQ

How do I convert a bit status word to a single state number in Ignition?

Create an event-driven expression tag using binEnum(getBit({[.]StatusWord},0), getBit({[.]StatusWord},1), ...). It returns bit number plus 1 for a one-hot word, and 0 when no bit is set.

How do I log state changes without a gateway script?

Enable history on the expression tag with a discrete deadband and on-change sample mode. The historian then stores one row per state change and handles storage and pruning.

How do I handle a status word where more than one bit can be on?

Order the binEnum() arguments by priority so the most important bit wins. Alternatively, test (x & 65535) & ((x & 65535) - 1) != 0 and return -1 to flag overlapping bits.

Why does my 16-bit PLC status word show negative in Ignition?

The PLC word is a signed INT, so bit 15 set produces -32768 to -1. In a 32-bit Integer tag this sign-extends the upper bits. Mask with & 65535 before whole-word comparisons; getBit() on bits 0 to 15 is unaffected.

How do I test every bit of a status word tag?

Write 1 << n for n = 0 to 15 to a memory tag copy with system.tag.writeBlocking(), writing bit 15 as -32768 on a signed tag. Then confirm the state tag steps 1 to 16 and history holds one row per step.

Back to blog