The Word stays at 1 (0001) instead of 3 (0011) because the alarmAcked script throws on its first line, so the write never executes. Inside an Ignition alarm event script, tag.value is a qualified value (value + quality + timestamp), not an integer. OR-ing it with (1 << 1) raises a type error. The second line has a separate defect: tagPath is already the path object, and tagPath.value does not resolve to a writable path string.
writeBlocking and Other Fixes That Leave the Word at 1
These are the usual first attempts. Each one fails for the same reason: the script aborts before it reaches the write.
| Attempted fix | Why the Word stays at 1 |
|---|---|
Swap system.tag.writeAsync for system.tag.writeBlocking
|
The exception fires on the bitwise OR, one line before the write. Changing the write function changes nothing that runs. |
Change the mask (| 2, + 2) |
The left operand is still a qualified value object. Any arithmetic or bitwise operator on it fails the same way. |
| Move the test from a Memory tag to an OPC tag | Tag type is irrelevant. The Memory tag was a valid test bed, and the script logic is what is broken. |
Wrap the path as [tagPath.value]
|
The path object has no usable .value for this purpose. Even with the OR fixed, the write targets nothing valid. |
This is a logic fault. No timing, load, or comms quantity is involved. The deciding quantity is whether the script runs to completion, and tag diagnostics show that directly.
Qualified Value and TagPath Objects in the Alarm Event Scope
Alarm event scripts (alarmActive, alarmCleared, alarmAcked) execute in the Gateway scope. The tag argument exposes tag properties, and each property returns a qualified value. The integer lives one level deeper:
-
tag.valueis the qualified value object. -
tag.value.valueis the raw integer that can take a bitmask. -
tag.value.qualityis the quality code. Check it before trusting the number.
tagPath is a TagPath object that points at the source tag (the Word), not at the alarm. system.tag.writeAsync accepts it directly in a list ([tagPath]). Converting it with str(tagPath) gives an explicit string and makes the path readable in log output.
The corrected minimum script:
def alarmAcked(tag, tagPath, alarmName, alarmEvent, alarmPath, ackedBy, missedEvents):
val = tag.value.value | (1 << 1)
system.tag.writeAsync([tagPath], [val])
Reading the Failure in Tag Diagnostics and Gateway Logs
Confirm the exception before you change code, so the fix matches the fault.
- In the Designer Tag Browser, right-click the Word tag and open its diagnostics. Script errors on tag events are reported there with the exception text.
- Open the Gateway web page and go to the Status logs. Filter for script or tag event errors raised at the time of the acknowledgment.
- A type error that mentions unsupported operand types for
|confirms the qualified value problem. An error on the write call, or no write at all, points to the path argument. - Add a logger line before the OR if the diagnostics are not conclusive:
system.util.getLogger("AlarmAck").info("raw=%s path=%s" % (tag.value, str(tagPath))). The loggedrawshows the value, quality, and timestamp triple instead of a bare integer.
| Quantity | Expected | Where to read it |
|---|---|---|
| Word value before ack | 1 (bit 0 set) | Tag Browser value column |
| Word value after ack | 3 (bits 0 and 1 set) | Tag Browser value column, OPC client for PLC tags |
| Script execution result | No error entries | Tag diagnostics, Gateway Status logs |
| Source quality at event | Good |
tag.value.quality via logger |
| Acknowledging user | Operator username |
ackedBy argument |
Read-Modify-Write Procedure for a Shared PLC Status Word
The minimum fix works on a Memory tag. On a live PLC Word, another problem appears: the PLC and the HMI both write bits in the same register. tag.value.value is the value captured when the alarm event fired. If the PLC sets or clears another bit between that moment and the write, the script writes the stale Word back and overwrites the PLC's change. Shrink that window with a fresh read:
- Resolve the path once:
path = str(tagPath). - Read the current Word:
qv = system.tag.readBlocking([path])[0]. - Abort if
qv.qualityis not good. Writing a mask onto a bad-quality value writes garbage to the PLC. - Set only the ack bit:
newVal = int(qv.value) | (1 << 1). - Write it:
system.tag.writeBlocking([path], [newVal]). Check the returned quality so failures appear in the log.
def alarmAcked(tag, tagPath, alarmName, alarmEvent, alarmPath, ackedBy, missedEvents):
log = system.util.getLogger("AlarmAck")
path = str(tagPath)
qv = system.tag.readBlocking([path])[0]
if not qv.quality.isGood():
log.warn("Ack bit not written, bad quality on %s" % path)
return
newVal = int(qv.value) | (1 << 1)
result = system.tag.writeBlocking([path], [newVal])[0]
log.info("%s acked by %s, wrote %d, result %s" % (path, ackedBy, newVal, result))
A fresh read narrows the race window but does not remove it. Only the PLC side removes it completely. Either give the HMI its own acknowledge Word that the PLC copies into the status word, or write to a bit-level item if the device driver supports bit addressing on that register.
Alarm Mode, Ack Mode, and Bit Reset Pitfalls
- Alarm mode clears on ack. If the alarm is configured as Equal with setpoint 1, writing 3 makes the condition false and the alarm clears the instant it is acknowledged. Use Bit State mode with bit position 0 so the alarm tracks bit 0 only, whatever bit 1 does.
-
Ack Mode set to Auto. Auto-acknowledge fires
alarmAckedwhen the alarm clears, with no operator action. Bit 1 then gets set by the system instead of the user. Set Ack Mode to Manual for operator acknowledgment. -
Nobody resets bit 1. If neither side clears the ack bit, the next occurrence of the alarm starts with bit 1 already true. On the WinCC 7.2 side, the PLC normally cleared it when bit 0 dropped. Keep that logic in the PLC, or clear it with a mask such as
val & ~(1 << 1)inalarmActive. -
Multiple alarms on one Word. Each alarm's
alarmAckedfires independently. Simultaneous acks from shelved or grouped alarms produce overlapping read-modify-write cycles on the same register. This is the strongest argument for a separate ack register. -
Acks from the PLC side. If the PLC can acknowledge (bit 1 set by a panel pushbutton), Ignition does not ack the alarm journal entry by itself. Use a tag change script that calls
system.alarm.acknowledgeto keep both sides in step.
Confirming the Ack Bit Round Trip
- On the Memory tag, write
1. Confirm the alarm goes active in the alarm status table. - Acknowledge from the table. The Word must read
3within one scan of the tag group, and theAlarmAcklogger entry must show the operator inackedBy. - Confirm the alarm stays active and acknowledged, not cleared. If it clears, recheck the alarm mode.
- Write
0. The alarm clears and no new ack event fires, which confirms Ack Mode is not Auto. - Repeat against the PLC Word while the PLC toggles an unrelated bit, and confirm that bit survives the ack write.
FAQ
How do I get the integer value of a tag inside an Ignition alarm event script?
Use tag.value.value. tag.value returns a qualified value object, so bitwise or arithmetic operators on it raise a type error and the rest of the script never runs.
How do I pass tagPath to system.tag.writeAsync in alarmAcked?
Pass the object directly as [tagPath], or convert it with str(tagPath) for an explicit string. Do not use tagPath.value. The path already points at the source Word tag.
How do I stop my ack bit write from overwriting PLC bits in the same Word?
Read the Word fresh with system.tag.readBlocking immediately before the write, then OR only the ack bit. To remove the race entirely, move acknowledgment to a dedicated HMI ack register that the PLC merges. If the script runs cleanly, the Word updates on a Memory tag, and the PLC tag still refuses writes with a good driver connection, collect the Gateway logs and the device driver diagnostics and open a case with Inductive Automation support. Include the driver type and the exact item path.