I am using the datalogger in KEPServerEx 6.16 to store "frames" of data to SQL. Things like recipes and test results, where I have a single set of data that I want inserted all at once.
I have a way set up that works pretty well. I stage the data to be sent in a buffer in the plc, set the datalog trigger to start and stop on a specific tag and to log all items on starts, and when the data is ready, I activate that trigger and read back the msg_count incrementing as an acknowledge it was sent to reset the trigger and stage the next data.
I found that every once in a while, I would get a null or double entry in my database doing this method. I did some experimenting, and what I believe is the cause is occasionally the trigger would be read before all the data had. The trigger is set to update every second while the log data is only every 100ms, so in theory that shouldn't happen.
I seemed to have fixed it for now by simply adding a timer delay between staging the data and activating the trigger, but I'm not sure that is robust. It doesn't actually prevent the data error from happening if the data updates extra slowly for some reason, and while it's not a concern right now, it artificially limits how quickly I can actually send the data out.
Because of this, I'm wondering if there is a better way to accomplish this, e.g. a comparison that all the data tags last updated timestamp is greater than the time the trigger tag became high, or causing an async read that has to finish before logging the data.
Has anyone dealt with situations like this or have any suggestions?
We do have the advanced tags addon, though it didn't seem to be able to do much with timestamps.
Thanks.
Solved! Go to Solution.
Hi @AK_11656070
Great question — and you've already done an excellent job architecting a frame-based data logging setup using KEPServerEX's DataLogger and a PLC-buffered trigger. You're describing a classic race condition — where the trigger fires before the rest of the data is guaranteed to be stable or updated.
If I am not wrong, your current setup:
Try the below Approach:
Since you have Advanced Tags, here's a more robust logic:
In the PLC:
Trigger_Ready
to True
.In Kepware:
Data_Ready_Valid = (Trigger_Ready == True) AND (Data_Timestamp > Last_Trigger_Timestamp)
Data_Ready_Valid
.On successful log:
msg_count
Feedback is your ACK to reset the trigger.Data_Timestamp
to Last_Trigger_Timestamp
.This ensures that data has been staged fully and is newer than the last trigger.
Use an Expression tag that evaluates when:
For example:
Trigger = Trigger_Ready AND Tag1 != 0 AND Tag2 != 0 AND Tag3 != 0
This can help prevent logging before the PLC has fully staged the data block.
If you want the data stable before the log, use "Log All Items with Timestamp of Last Logged Item" in the DataLogger settings. It helps by tying the timestamp to when the last tag is updated, not when the trigger is fired.
In parallel, you can also:
Hi @AK_11656070
Great question — and you've already done an excellent job architecting a frame-based data logging setup using KEPServerEX's DataLogger and a PLC-buffered trigger. You're describing a classic race condition — where the trigger fires before the rest of the data is guaranteed to be stable or updated.
If I am not wrong, your current setup:
Try the below Approach:
Since you have Advanced Tags, here's a more robust logic:
In the PLC:
Trigger_Ready
to True
.In Kepware:
Data_Ready_Valid = (Trigger_Ready == True) AND (Data_Timestamp > Last_Trigger_Timestamp)
Data_Ready_Valid
.On successful log:
msg_count
Feedback is your ACK to reset the trigger.Data_Timestamp
to Last_Trigger_Timestamp
.This ensures that data has been staged fully and is newer than the last trigger.
Use an Expression tag that evaluates when:
For example:
Trigger = Trigger_Ready AND Tag1 != 0 AND Tag2 != 0 AND Tag3 != 0
This can help prevent logging before the PLC has fully staged the data block.
If you want the data stable before the log, use "Log All Items with Timestamp of Last Logged Item" in the DataLogger settings. It helps by tying the timestamp to when the last tag is updated, not when the trigger is fired.
In parallel, you can also:
Thank you! I'm looking into those options, but not quite clear on some of them.
Method 1) I'm not quite clear how this prevents both Trigger_Ready and Data_TimeStamp being read from the PLC and updated before Data_EverythingElse is fully updated.
FWIW, the Data buffer is a UDT, although I never specified that earlier. If UDTs are reliably read as a block and the assumption of the data being in one, including the new timestamp, is what this method is relying on, great! I just haven't seen documentation indicating that the driver does read them as a block, however.
Method 2) I think this one will work, but I would need to add a data_reset process and handshake, instead of loading in the next piece of data as soon as I get the ACK. But overall this seems to be the most straightforward.
Method 3) I'm simply not finding either the "Log All Items with Timestamp of Last Logged Item" or the Trigger Evaluation Delay settings anywhere in the data logger config windows, help files, or manual. Is this possibly from a different version?
Hey @AK_11656070,
Please bear my longer texts:
You're right to question the reliability of this if the tags (including the timestamp) aren't guaranteed to be updated as a block. Kepware drivers do not guarantee atomic reads of UDTs — especially if individual elements update asynchronously or via different scan classes. Even if the UDT is treated as a structure, Kepware fetches each element individually unless the PLC supports block transfers and the driver is configured to use them.
So, if your timestamp tag is updated before the rest of the data, it’s entirely possible for Data_Ready_Valid
to go high prematurely.
The PLC must write all buffer elements and the timestamp in one scan cycle, ideally in a block move or at least in the same routine.
Kepware should read them under the same scan rate, preferably using a fast, consistent polling cycle (100ms or lower).
You’d also want to disable "Read After Write" on the timestamp tag in Kepware (if enabled), to prevent it from jumping ahead.
If you're unsure about atomicity, don't depend on this method alone — it's better when the timestamp is external to the PLC buffer and generated by Kepware after data validation.
Yes, this is the most deterministic approach and does not depend on read timing or tag atomicity.
Here’s a more concrete structure:
Load data into buffer.
Raise Trigger_Ready
.
Kepware logs when:
Trigger_Ready
is high AND
All Tags != 0/null/default
.
Once logged, Kepware (via feedback or PLC logic) sets Trigger_ACK
.
PLC clears buffer or resets Trigger_Ready
.
You can still stage the next frame during ACK, but only expose it to Kepware after ACK has been received — i.e., use an internal buffer and a visible “window” for logging.
This eliminates timing/race issues completely and gives you a handshake model that’s scalable.
You're absolutely right — these options aren’t directly exposed in the DataLogger GUI, and behavior varies based on Kepware version and DataLogger plugin limitations.
"Timestamp of Last Logged Item":
This behavior happens when DataLogger is configured with "All items on trigger" + "Use item timestamps", but there is no checkbox named exactly like that.
It's inferred behavior based on how items are timestamped and how the trigger is evaluated.
Trigger Evaluation Delay:
There is no GUI-exposed "delay" setting per se.
What you can control is:
Use a separate scan group for the trigger tag with a slower rate (e.g., 250ms).
Use a deadband or condition expression for the trigger logic (Advanced Tags).
Introduce a delay in the PLC logic between setting Trigger_Ready
and exposing the buffer.
If you're on 6.16, unfortunately, there's no internal evaluation delay setting unless you're customizing logging via the Configuration API or Advanced Tags workaround.
Use Method 2: A composite trigger expression (Advanced Tag) and handshake will be the most robust.
Avoid relying on timestamps inside the data buffer unless you can guarantee atomic block updates.
Use PLC logic to window the next frame exposure until ACK received.
Optionally introduce a soft timer in the PLC post-staging before Trigger_Ready
is exposed.
If advanced needs arise (batching logs, retry queue, etc.), consider external broker or MQTT-based buffering outside of Kepware.