Skip to content

Commit 07dbcd2

Browse files
committed
Add first example of new custom Rule
1 parent 76420e6 commit 07dbcd2

File tree

1 file changed

+105
-0
lines changed

1 file changed

+105
-0
lines changed
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
"""
2+
Demonstration of implementing a custom Rule
3+
"""
4+
5+
import logging
6+
7+
import p4p.nt.common as nt_common
8+
from p4p import Type, Value
9+
from p4p.nt.scalar import _metaHelper
10+
from p4p.server import Server
11+
12+
from p4pillon.nt import NTScalar
13+
from p4pillon.rules import (
14+
AlarmRule,
15+
BaseRule,
16+
ControlRule,
17+
RulesFlow,
18+
TimestampRule,
19+
ValueAlarmRule,
20+
)
21+
from p4pillon.rules.rules import check_applicable_init
22+
from p4pillon.thread.sharednt import SharedNT
23+
24+
logger = logging.getLogger(__name__)
25+
26+
27+
class NTScalarMatch(NTScalar):
28+
"""
29+
Extend NTScalar with imatch fields
30+
"""
31+
32+
@staticmethod
33+
def buildType(valtype, extra=[], *args, **kws):
34+
"""Build a Type
35+
36+
:param str valtype: A type code to be used with the 'value' field. See :ref:`valuecodes`
37+
:param list extra: A list of tuples describing additional non-standard fields
38+
:param bool display: Include optional fields for display meta-data
39+
:param bool control: Include optional fields for control meta-data
40+
:param bool valueAlarm: Include optional fields for alarm level meta-data
41+
:param bool form: Include ``display.form`` instead of the deprecated ``display.format``.
42+
:returns: A :py:class:`Type`
43+
"""
44+
isarray = valtype[:1] == "a"
45+
fields = [
46+
("value", valtype),
47+
("alarm", nt_common.alarm),
48+
("timeStamp", nt_common.timeStamp),
49+
("imatch", Type(id="imatch_t", spec=[("active", "?"), ("imatch", "i")])),
50+
]
51+
_metaHelper(fields, valtype, *args, **kws)
52+
fields.extend(extra)
53+
return Type(id="epics:nt/NTScalarArray:1.0" if isarray else "epics:nt/NTScalar:1.0", spec=fields)
54+
55+
def __init__(self, valtype="d", **kws):
56+
self.type = self.buildType(valtype, **kws)
57+
58+
59+
class IMatchRule(BaseRule):
60+
name = "imatch"
61+
fields = ["alarm", "imatch"]
62+
63+
@check_applicable_init
64+
def init_rule(self, newpvstate: Value) -> RulesFlow:
65+
# Check if imatch alarms are present and active!
66+
if not newpvstate["imatch.active"]:
67+
# TODO: This is wrong! If valueAlarm was active and then made inactive
68+
# the alarm will not be cleared
69+
logger.debug("imatch not active")
70+
return RulesFlow.CONTINUE
71+
72+
if newpvstate["imatch.imatch"] == newpvstate["value"]:
73+
newpvstate["alarm.severity"] = 2
74+
newpvstate["alarm.message"] = "IMATCH"
75+
else:
76+
newpvstate["alarm.severity"] = 0
77+
newpvstate["alarm.message"] = ""
78+
79+
return RulesFlow.CONTINUE
80+
81+
82+
# Register the new rule
83+
SharedNT.registered_handlers = [
84+
AlarmRule,
85+
ControlRule,
86+
ValueAlarmRule,
87+
IMatchRule, # <-- This is the new rule
88+
TimestampRule,
89+
]
90+
91+
92+
def main():
93+
pv = SharedNT(nt=NTScalarMatch("i"), initial={"value": 5, "imatch.active": True, "imatch.imatch": 5})
94+
95+
Server.forever(
96+
providers=[
97+
{
98+
"demo:pv:name": pv, # PV name only appears here
99+
}
100+
]
101+
) # runs until KeyboardInterrupt
102+
103+
104+
if __name__ == "__main__":
105+
main()

0 commit comments

Comments
 (0)