Fix Ignition Designer Freezes from Runtime History

Patricia Callen8 min read
HMI / SCADAOther ManufacturerTroubleshooting
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

Move the runtime calculation out of the display binding and into a continuously maintained counter. The freeze occurs because each template instance launches a tag-history query from system.date.getDate(2026, 0, 1) to the current time every 1000 ms. When queries finish more slowly than they are requested, work accumulates, the historian database stays busy, and the Designer becomes unresponsive.

How do you prove that history queries are backing up?

Measure one execution before changing the display or counter. Record a timestamp immediately before system.tag.queryTagHistory, record another immediately after it returns, and log the elapsed time with the tag path. Test the same path and date range used by the template.

Compare that elapsed time with the 1000 ms execution interval in:

runScript(
    "maintenance.getRuntimeHours",
    1000,
    toString({Run Time Display.tagPath})
)

If one query takes at least one second, a single template instance can request another query before the prior work has cleared. If it takes less than one second, count every visible template instance: each instance has its own binding and therefore adds another recurring query. Query time can also rise as the historical range grows, so a design that ran acceptably for several weeks can degrade without a new circular reference.

If removing the tag binding restores normal Designer performance, the binding execution path is the controlling branch. A circular reference is not required to create this symptom.

Which reading identifies the failing part of the signal chain?

Look at the trend first. Compare the live Boolean state, the historian record, the runtime result, and the displayed value before adjusting code or timing.

Signal Source or operation Wrong-value or performance symptom
Equipment running state Historized Boolean tag A bad or stale input produces incorrect runtime regardless of query or display changes.
History path Run Time Display.tagPath, converted with toString A null, blank, or incorrect path returns 0.0 or generates a warning.
Runtime aggregation aggregationMode="DurationOn" over the requested start and end dates A large range makes every refresh expensive even though returnSize=1 requests one result row.
Refresh request runScript with 1000 ms Queries repeat every second and can accumulate faster than they complete.
Numeric value float(secondsOn) / 3600.0 A valid result is hours; a null result or caught exception is displayed as zero.
Diagnostic warning RuntimeCalc logger Repeated failures can produce a warning on every invocation because the shown code contains no actual rate limiter.

The final element—the numeric display—does not create runtime. It merely presents the result of the entire upstream chain. Tuning the display cannot correct a bad Boolean signal, an invalid path, or an overloaded historian query.

What does each query outcome mean?

Inspect the input and result in a fixed order so that a zero is not mistaken for valid stopped time.

  1. Read the custom tagPath property. If it is null or blank, correct the template parameter assignment; the function intentionally returns 0.0.
  2. Confirm that the path addresses the intended historized Boolean. If the live tag changes but its history does not, correct history collection before calculating runtime.
  3. Run one history query using the same start and end dates. If it is slow, remove it from the one-second display binding and proceed to accumulation.
  4. If result is null or rowCount is zero, investigate path, date range, and historian availability. The current function converts all of those cases to 0.0.
  5. If column 1 is null, treat the value as missing data during diagnosis. The current function also converts this condition to 0.0.
  6. If an exception occurs, inspect the RuntimeCalc warning. Do not accept the displayed zero as a confirmed runtime because the exception handler uses the same value for failure and genuine zero hours.

returnSize=1 limits the returned aggregate, not necessarily the historical work required to calculate it. DurationOn must evaluate the requested interval before it can return the number of seconds that the Boolean was on.

Where should the runtime be accumulated?

Put the primary counter in the PLC when the PLC owns the running signal. The controller already evaluates that signal cyclically and can maintain runtime without a database round trip. Provide separate counters when the application needs both a lifetime total and a resettable maintenance total; the maintenance counter can then drive service reminders while the lifetime counter remains intact.

The signal chain becomes direct: the PLC measures the running state, adds elapsed run time to the counters, and publishes the totals to Ignition. The display reads the current counter value. Historize the counter only when trends or reports need its past values.

If PLC modification is unavailable and approximate SCADA runtime is acceptable, maintain an accumulator tag in Ignition. A documented expression approach uses:

if({[.]running tag}, getSecond(now()) % 2, -1)

While the running tag is true, this value changes every second and can trigger a value-change script. When the triggered value is at least zero, add one second to a memory accumulator tag. Calls that may block are generally poor choices inside tag-event handlers; direct memory-tag access is the narrow exception described for this approach. Execution delays, gateway downtime, and missed events affect SCADA-side accuracy, which is why the PLC remains the preferred location.

How do you replace the recurring history query?

  1. Validate the live Boolean tag against the actual device state. Tuning does not fix wiring or an inverted status signal.
  2. Create the runtime counter in the PLC where possible. Add both lifetime and maintenance-reset counters if both operating history and service scheduling are required.
  3. If the counter must reside in Ignition, create a memory accumulator and update it only while the running state is active. Define how the value survives a gateway restart before relying on it for maintenance decisions.
  4. Expose the accumulated total to the project as a tag. Choose one unit for storage and convert only at the presentation boundary.
  5. Bind the numeric display directly to that accumulator. If the stored value is seconds, display hours by dividing by 3600.0.
  6. Remove the one-second runScript history binding from every template instance.
  7. Historize the accumulator if trend or reporting requirements call for it. Use DurationOn only for deliberate reports, reconciliation, or bounded diagnostic queries—not as the live display engine.

If a report still uses getRuntimeHours, trigger it on demand or at a cadence chosen from measured query duration and database capacity. Prevent overlapping executions. As the requested interval grows, repeat the timing measurement rather than assuming the earlier cadence remains safe.

Which script corrections matter after the load is removed?

Place imports at script-library scope rather than inside a frequently called function. In a project library, the system namespace is normally available without import system, so importing it on each invocation adds unnecessary work and can create scoping complications.

Legacy scoping is a separate case. In 8.1, a script entered directly in some Gateway Event script windows may not expose system in the same way. Keep the event script to a one-line call into a project library, where the operational code and imports have consistent scope. Verify the target release behavior before applying that 8.1 detail to 8.3.

Correct the exception path as well. The comment claiming “throttled logging” does not match the shown implementation: logger.warn executes on every caught exception. Apply a real rate limit or log only on a transition into fault, and expose calculation health separately from the numeric runtime. A failed calculation should not be indistinguishable from a valid 0.0.

How do you verify the resolved design?

  1. Open the screen with one template instance and confirm that Designer interaction remains responsive.
  2. Add the expected number of template instances and repeat the check. The number of history queries should no longer rise with the number of displays.
  3. Command or observe a known run-stop sequence. Compare the live Boolean trend with the accumulator: it must increase only while the signal is on and remain unchanged while it is off.
  4. Check the displayed conversion. For an accumulator stored in seconds, the numeric display must use seconds divided by 3600.0.
  5. Test the maintenance reset without altering the lifetime total.
  6. Restart the relevant runtime environment during a controlled test and confirm that the chosen counter retains or restores its value according to the design.
  7. Measure historian query time and database activity after removing the binding. Recurring queries from each template should be absent; any remaining report query should complete before another is allowed to start.
  8. Force an invalid path in a test instance and confirm that operators can distinguish a calculation fault from genuine zero runtime.

FAQ

How do I stop Ignition Designer freezing when I bind the runtime tag?

Remove the runScript binding that calls history every 1000 ms. Bind the display to a PLC or Ignition accumulator tag instead.

How do I calculate runtime without querying tag history every second?

Accumulate elapsed run time while the Boolean running signal is true. Store the total in the PLC when possible; otherwise update a persistent SCADA accumulator and convert seconds to hours with seconds / 3600.0.

How do I keep both lifetime and maintenance runtime?

Maintain two counters from the same running signal. Never reset the lifetime counter; reset only the maintenance counter after the service action.

How do I tell whether a zero runtime is valid?

Check the live Boolean, tag path, history rows, aggregate value, and RuntimeCalc warnings. The shown function returns 0.0 for blank paths, empty results, null values, and exceptions, so the display alone cannot distinguish those states.

When should I stop troubleshooting and contact official support?

Stop changing bindings when a single bounded query still hangs, gateway diagnostics show work that does not clear, or Designer responsiveness remains poor after all recurring history calls are removed. Capture the query duration, requested dates, template-instance count, logs, product version, and a minimal reproduction, then escalate to official Ignition support. Do not continue increasing timeouts or refresh rates while the gateway or historian database is saturated.

Back to blog