News:

  • May 29, 2026, 12:12:31 PM

Login with username, password and session length

Author Topic: Ring buffer for fault logging  (Read 5835 times)

Bobby Tables

  • Newbie
  • *
  • Posts: 4
Ring buffer for fault logging
« on: March 26, 2026, 01:05:52 PM »
I've been lurking for a while but this is my first post.

Background for the issue:
I want to record the machine state as a file that is able to be loaded into DmDesigner on the simulator so I can troubleshoot the machine state remotely. The BACKUP instruction takes multiple scans to complete so machine states may change before it is done.

I would also like to log important state history leading up to the fault in a ring buffer to be logged in the event of a fault. So as to not bog down the system and the log with writing non important events.

I want to log fault bits being turned on at rungs that trigger them 0->1. The machine itself has a fault reset switch that will turn off the faults when the cause has been resolved. But I want to use FILELOG or DMLOGGER as an event log so I know what rung caused the fault, important machine state bits, etc. so errors can be reproduced.

To solve the multi scan BACKUP issue I would like to similarly buffer the machine state of the last few scans before the fault occurred.

The issue is FILELOG and DMLOGGER do not have a ring buffer to allow the order and time to be preserved. And I have had trouble finding example of implementing a ring buffer for an event log on here.


Question for the forum:

  • Does anyone have references on how to implement an event log ring buffer in DmDesigner?
  • And has anyone been able to buffer the BACKUP instruction?


The implementation I want is
  • A fault bit 0->1
  • The event is logged in a ring buffer
  • The log is only sent to FILELOG or DMLOGGER upon the machine stop occurring and it waits until the loging is complete before restarting
  • The ring buffer is emptied
  • The machine enters the restarting state
Event logging rough idea:

---|state_variable1 rising contact|---(write to buffer)

---|write_complete_bit|----(MATH: buffer_length=buffer_length+1)

---|state_variable1 falling contact|---(write to buffer)

---|write_complete_bit|----(MATH: buffer_length(V###)=buffer_length+1)

---|fault_on_bit|----(read_from_buffer_trigger_bit)

[fault handling logic follows and the machine enters a stopped state]

----|machine_is_stopped_state_bit|------[logic to read from buffer in order using for loop and/or buffer read instruction for FIFO into FILELOG or DMLOGGER]
 
Or creating an image reading from a different larger buffer with the whole machine state in the last few scans
 
---|read_1_buffer_entry_complete_bit|----(MATH:  buffer_length=buffer_length-1)

---|EQU buffer_length=0|-----(buffer_length_is_full_bit)

The FIFO buffer by default doesn't seem to necessarily be a ring buffer implementation. Is there a way to modify it to work like one?
for reference
https://en.wikipedia.org/wiki/Circular_buffertcplclib_tc2_utilities/35413643.html&id=

BobO

  • Host Moderator
  • Hero Member
  • *****
  • Posts: 6157
  • Yes Pinky, Do-more will control the world!
Re: Ring buffer for fault logging
« Reply #1 on: March 26, 2026, 01:51:07 PM »
A ring buffer is just a simple fast implementation of a FIFO that allows addition of records without copying/shifting the data. A FIFO isn't always implemented as a ring buffer, but a ring is generally considered to be a FIFO.

If you are intending the FIFO to store the last N records, losing the oldest as you add a new record, just call FIFOUNLOAD before FIFOLOAD when MyFifo.Full is true. A ring buffer implementation would need to bump the tail to accomplish the same thing.

If you would prefer to implement this as ring buffer, it's really quite simple. Make your buffer size a power of 2. Use one V location as the head pointer and another as the tail pointer. Create a bit mask of the buffer size - 1. If the buffer is 256 elements, the mask is 0x00FF. For 1024, it would be 0x03FF.

Empty = Head == Tail
Full = (Head+1)&Mask == Tail

Add a record (if !Full):
Buffer[Head] = NewVal
Head = (Head+1)&Mask

Remove a record (if !Empty):
OldVal = Buffer[Tail]
Tail = (Tail+1)&Mask

But honestly, just use the FIFO.


"It has recently come to our attention that users spend 95% of their time using 5% of the available features. That might be relevant." -BobO