Is there an efficient way to do this that doesn't involve explicitly calling every *.InAlarm bit to activate annunciation?
The ".InAlarm" bit is an object of some UDT or AOI. Because those are scattered tags, there's no "easy" way to do it. But there are a few ways to do it "efficiently".
METHOD A
If this is a UDT under your control, you could add in a reference a bit, and in your logic, one-shot a latch to that bit on the .InAlarm.
Your horn logic would then detect that bit, trigger the horn, and unlatch the bit. The horn silence button would kill the horn, but another new alarm (or a true-to-false-to-true transition of the previous alarm) will re-trigger the horn.
Adding a tag to 500 AOIs is marginally easier than adding 500 rungs of logic that do the same. You also have the flexibility to have different bits for horns in different areas and so forth.
METHOD 2
Create an array of your UDT/AOI with 500 (or more, to allow for spares), and change your specific tags the refer to the unique tagname to an alias that points to a single element in your array, making sure that each instance points to a different element.
Example: If you have an AOI with tags TT_123 and TT_456 of datatype p_Analog, create a tag "All_Analogs" of datatype p_Analog[500], with TT_123 as an alias to All_Analogs[123].
Your HMI links will not be affected by this, since the comm driver will still by asking for TT_123.InAlarm, which is still a legitimate tag.
Now that all your alarm-containing tags are in a single object, a file instruction or a For-Next loop can find an instance where All_Analogs[Control.POS].InAlarm = 1, and then set a bit in a ActiveAlarm DINT[20] array
Set bit ActiveAlarm[(Control.POS AND NOT 31) / 32].[Control.POS AND 31]. Bit 123 is ActiveAlarm[3].27, which corresponds to All_Analogs[123] which is aliased by TT_123.
If all the elements in ActiveAlarms <> 0, you have an alarm. You can do bit twiddling using the XOR command to create a similar array which indicates only a NEW alarm (one that wasn't on last scan) to trigger your horn.
METHOD III
Write 500 rungs of contact-to-coil logic to do the above, using either a one-shot OTL to a single bit, or an OTE to an alarm array. Which is what you're trying to avoid.
But any method is still going to involve making 500 edits to existing logic, either adding something to an AOI; making 500 tags aliases to another tag, or making 500 contact-to-coil rungs. (which, you could code in Excel and then just paste in, so this might actually be the fasted method).
--------
Wouldn't it be nice if our customers would tell us requirements like these up front, BEFORE we started coding and not when we were almost done?