Or, can I just have it exit the loop on LogEvent and resume the program on the next scan? This way it is guaranteed to only have to process one event. It has the added bonus of logging the event as quickly as possible without finishing the loop.
I honestly think you need to eliminate all LOOPs and use the natural scan of the PLC as your "loop", but using events to
1. Increment the processing index
2. Log the event
Make all of this event driven. I would stick this all in a stage program code block. Since this is probably something that you want to do all the time, I would only have $Main kick it off "RUN MonitorAlarms" and MonitorAlarms intializes itself in its first SG (say SG0, hence you never JMP back to SG0), and it auto-resets the index whenever it reaches the end
SG S0 Initialize (set index to 0, etc.)
SG S1 See if alarm state @index has changed (you will need a duplicate array of alarm bits to save previous alarm state); if it has, jmp to S2, otherwise jmp to S3
SG S2 Log Alarm Event, once that is done, jmp to S3
SG S3 Put current alarm state (Alarm[index] into PrevAlarm[index]
Increment index; if index exceeds length, reset index to 0, unconditionally JMP to S1 (NOT S0)
This will minimize scan time.
What are you doing to "Log Alarm Event"? Sending an EMAIL? That can take seconds per alarm. If you have no alarms, this will take N scans where N is the number of alarms. If N is 100, with a 1ms scan time, that's 100ms "calendar time". But if "Log Alarm Event" takes 3 seconds, and all alarms turn ON, that will take 300 seconds (5 minutes) of "calendar time" to process all 100 alarms.