Skip to content

Commit f0e61f8

Browse files
committed
Improve imatch
1 parent 5148e55 commit f0e61f8

File tree

3 files changed

+102
-0
lines changed

3 files changed

+102
-0
lines changed

examples/custom_rule/README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Intro
2+
This folder shows two different ways a developer may implement a custom `Rule`.
3+
4+
## Custom Rule - Imatch
5+
The imatch Rule is a simple rule that triggers an alarm of MAJOR severity when the value of an NTScalar PV matches a specified value. Although this rule can apply for any type, as the name suggests it makes most sense in the case of an integer. We do not specify what should happen in the case of an NTScalarArray.
6+
7+
## Implementations
8+
### Public
9+
The NTScalar is a Normative Type and its fields are defined by the specification of that type. However, in practice tools are permissive and will understand a type which is a superset of a Normative Type, ignoring those fields they do not expect or understand. As such it is possible to add additional fields to an NTScalar.
10+
11+
In this case we add an `imatch` field which is a structure with a boolean `active` and an integer `imatch` field. If the `imatch` is active then the value of `imatch.imatch` is tested against the PV's value. If equal then a the severity is set to MAJOR otherwise it is cleared.
12+
13+
The advantage of this method is that the imatch field is publicly available and may be manipulated through the standard EPICS pvAccess tools. This includes through the standard interfaces that are a part of p4p and p4pillon.
14+
15+
## Hidden
16+
An alternative way to implement the rule without modifying the Normative Type. The value of the imatch variable is held in the handler / Rule instead of the p4p.Value. This has the advantage that the Normative Type is unmodified. It has the possible disadvantage that the imatch value cannot be easily manipuluated by external pvAccess tools (hence describing it as hidden). As the code illustrates, it also makes it harder to reliably trigger the rule in some circumstances.
17+
18+
## Notes
19+
This is a simplified example and does not consider some important cases. For example, what behaviour is correct in the case that the severity indicates invalidAlarm?
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
"""
2+
Demonstration of implementing a custom Rule
3+
"""
4+
5+
import logging
6+
7+
from p4p import Value
8+
from p4p.server import Server
9+
10+
from p4pillon.nt import NTScalar
11+
from p4pillon.rules import (
12+
AlarmRule,
13+
BaseRule,
14+
RulesFlow,
15+
TimestampRule,
16+
)
17+
from p4pillon.rules.rules import check_applicable_init
18+
from p4pillon.thread.sharednt import SharedNT
19+
20+
logger = logging.getLogger(__name__)
21+
22+
23+
class IMatchRule(BaseRule):
24+
name = "imatch"
25+
fields = ["alarm"]
26+
27+
def __init__(self, imatch: int | None = None):
28+
self._imatch = imatch
29+
30+
@property
31+
def imatch(self):
32+
return self._imatch
33+
34+
@imatch.setter
35+
def imatch(self, newval: int | None):
36+
self._imatch = newval
37+
38+
@check_applicable_init
39+
def init_rule(self, newpvstate: Value) -> RulesFlow:
40+
# Check if imatch alarms are present and active!
41+
if not self._imatch:
42+
logger.debug("imatch not active")
43+
return RulesFlow.CONTINUE
44+
45+
if self._imatch == newpvstate["value"]:
46+
newpvstate["alarm.severity"] = 2
47+
newpvstate["alarm.message"] = "IMATCH"
48+
else:
49+
newpvstate["alarm.severity"] = 0
50+
newpvstate["alarm.message"] = ""
51+
52+
return RulesFlow.CONTINUE
53+
54+
55+
def main():
56+
registered_handlers = [
57+
AlarmRule,
58+
IMatchRule, # <-- This is the new rule
59+
TimestampRule,
60+
]
61+
62+
pv1 = SharedNT(nt=NTScalar("i"), initial=5, imatch={"imatch": 5}, registered_handlers=registered_handlers)
63+
64+
pv2 = SharedNT(nt=NTScalar("i"), initial=5, imatch={"imatch": 3}, registered_handlers=registered_handlers)
65+
pv2.handler["imatch"].rule.imatch = 5
66+
67+
pv3 = SharedNT(nt=NTScalar("i"), initial=5, imatch={"imatch": 3}, registered_handlers=registered_handlers)
68+
pv3.handler["imatch"].rule.imatch = 5
69+
pv3.post(5)
70+
71+
Server.forever(
72+
providers=[
73+
{
74+
"demo:pv:name1": pv1,
75+
"demo:pv:name2": pv2,
76+
"demo:pv:name3": pv3,
77+
}
78+
]
79+
) # runs until KeyboardInterrupt
80+
81+
82+
if __name__ == "__main__":
83+
main()
File renamed without changes.

0 commit comments

Comments
 (0)