Ignition UDT Log: OPC Read Trigger Into a Custom DB Table

Claire Rousseau6 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 ControlLogix UDT updates every member in one PLC scan, yet an Ignition 8.1 Table fed by Tag History shows the same snapshot spread across many near-duplicate rows. The Tag Historian is not built for record logging. It stores each member tag independently, on its own change and deadband evaluation, with its own timestamp. A wide-format history query then builds one row for every timestamp at which any member was stored, and it fills the other columns with the previous or interpolated values. Ten members changing a few milliseconds apart as their subscription updates arrive therefore produce up to ten rows for one PLC event.

To log a snapshot as a record, use a trigger that reads every member at one instant and inserts one row into a custom database table. The SQL Bridge module does this with a triggered transaction group set to OPC Read mode. Without SQL Bridge, a gateway tag change event script does the same job using system.opc.readValues(). Do not push these values to the historian. Write them as rows in your own table and point the Table component at that table.

PLC Snapshot and Trigger Handshake

Before anything else, confirm the PLC writes the trigger after it writes the snapshot data. The trigger is the only tag Ignition subscribes to for this purpose. Every other member is read on demand after the trigger fires.

  1. Keep the snapshot UDT instance in the controller, and copy all members into it in one rung or routine.
  2. Add a trigger tag outside the snapshot. Use an integer counter that the PLC increments after the copy. A counter produces a distinct change on every event. A BOOL that toggles quickly can be missed between subscription updates.
  3. Add an acknowledge tag that Ignition writes back with the counter value it logged. The PLC can then detect a missed or unlogged event by comparing the counter with the acknowledge value.
  4. If the process can supply it, add a PLC-side timestamp member to the snapshot. A controller timestamp records when the event happened. A gateway timestamp records when the script ran.

Check: Create an Ignition tag for the trigger only, and watch it in the Designer Tag Browser while the process runs. Do not move on until it increments exactly once per PLC event and shows Good quality.

Custom Log Table and Database Connection

Create one table with one column per UDT member, plus a timestamp and the trigger value. Adapt the data types to your database.

CREATE TABLE udt_log (
  id         INTEGER PRIMARY KEY,   -- use your DB's auto-increment syntax
  t_stamp    DATETIME NOT NULL,
  trig_count INTEGER,
  batch_id   VARCHAR(40),
  temp       FLOAT,
  pressure   FLOAT,
  weight     FLOAT
);
CREATE INDEX ix_udt_log_tstamp ON udt_log (t_stamp);

The table and column names are placeholders. Map them to your actual UDT members. Index the timestamp column, because every Table query will filter on it.

Check: In the Gateway web page, open Config > Databases > Connections and confirm the connection reports Valid. Run a manual INSERT and SELECT against the table from the Designer's Database Query Browser before you write any script.

Gateway Tag Change Event Script

The Tag History approach and a naive tag change script both fail for the same reason. Ignition's tag values come from subscriptions, and the trigger update can reach the gateway before the updated values of the other members. Reading the members from the tag cache inside the trigger event can therefore capture a mix of old and new data. An explicit OPC read issued after the trigger fires goes to the device and returns the current snapshot. The SQL Bridge OPC Read mode does the same thing.

Create the script under Project > Gateway Events > Tag Change, and set the trigger tag path. Gateway scope requires the full provider-qualified path.


  1. Copy each OPC Item Path from an existing tag's editor instead of typing it by hand. The device name and the path syntax must match the Logix driver exactly.
  2. Keep the initialChange guard. Without it, every gateway restart or project save inserts a phantom row.
  3. Use prepared statements (? placeholders). String members such as batch IDs break concatenated SQL.
  4. If you add a PLC timestamp member, read it with the other members and insert it in place of system.date.now().

Check: Increment the trigger once from the PLC. Confirm that exactly one row appears in udt_log, that the acknowledge tag matches the trigger, and that the gateway logs (Status > Diagnostics > Logs, filtered on UDTLog) show no warnings.

Historian Settings on the UDT Members

The members no longer need history for this log. If you leave history enabled, the historian keeps writing the fragmented data in parallel and uses storage for no benefit.

  1. Open the UDT definition, not an instance, so the change applies to every instance.
  2. On each member used only for the snapshot log, set History Enabled to false.
  3. Keep history on any member that also needs trending. Trends need per-tag change storage, and the historian is the right tool for that job.

Check: Confirm on one instance that the members no longer show the history icon. Confirm that no member overrides the definition setting.

Table Component Query Binding

Bind the Table's data property to a Named Query against udt_log instead of a Tag History binding.

SELECT t_stamp, batch_id, temp, pressure, weight
FROM udt_log
WHERE t_stamp BETWEEN :startDate AND :endDate
ORDER BY t_stamp DESC

Pass the start and end dates from date pickers or a range selector. Set a polling rate only if operators need to see live rows appear. For after-the-fact review, an on-demand refresh is enough.

Check: The Table shows one row per trigger increment, and each row's values match what the PLC held at that event.

End-to-End Verification

Symptom during test Likely cause Check
Mixed old and new values in one row Members read from the tag cache instead of by OPC read, or the PLC sets the trigger before the copy completes Review the script read method and the PLC rung order
Row missing for an event BOOL trigger toggled faster than the subscription rate Compare trig_count gaps with the PLC counter
Extra row after restart initialChange not handled Restart the gateway and count the rows
No rows, trigger changing Wrong OPC item path or DB connection name Gateway logs; OPC read results quality
Still duplicate-looking rows Table still bound to Tag History Inspect the data binding type
  1. Run the process through at least ten PLC events, including two in quick succession.
  2. Confirm that trig_count in the table has no gaps and no repeats.
  3. Compare one logged row member by member against the PLC snapshot, using the controller's monitor view.
  4. Restart the gateway and confirm no row is added until the next real trigger.
  5. Confirm that the PLC acknowledge comparison shows zero unlogged events.

FAQ

What happens if I read UDT member tag values instead of doing an OPC read in the trigger script?

Tag values come from subscriptions, so the trigger can arrive before the updated members. You can log a row that mixes the previous snapshot with the new one. system.opc.readValues() forces a fresh read after the trigger.

What happens if I leave Tag History enabled on the UDT members?

The historian keeps storing each member separately, and history queries keep returning fragmented rows. Disable history in the UDT definition for members used only in the snapshot log. Keep it on members that also feed trends.

What happens if the trigger changes faster than Ignition can log it?

A BOOL trigger can toggle and return between updates, and the event is lost. Use an incrementing counter with an acknowledge tag, so gaps show up in trig_count and the PLC can detect unlogged events.

Do I need the SQL Bridge module to log a UDT snapshot to a database?

No. SQL Bridge provides a triggered transaction group with OPC Read mode, but a gateway tag change event script that uses system.opc.readValues() and a prepared INSERT produces the same one-row-per-event result.

Back to blog