first tests
This commit is contained in:
31
alarms-dicts.txt
Normal file
31
alarms-dicts.txt
Normal file
@ -0,0 +1,31 @@
|
||||
status = {
|
||||
0: "NO ALARM",
|
||||
1: "READ",
|
||||
2: "WRITE",
|
||||
3: "HIHI",
|
||||
4: "HIGH",
|
||||
5: "LOLO",
|
||||
6: "LOW",
|
||||
7: "STATE",
|
||||
8: "COS",
|
||||
9: "COMM",
|
||||
10: "TIMEOUT",
|
||||
11: "HW LIMIT",
|
||||
12: "CALC",
|
||||
13: "SCAN",
|
||||
14: "LINK",
|
||||
15: "SOFT",
|
||||
16: "BAD SUB",
|
||||
17: "UDF",
|
||||
18: "DISABLE",
|
||||
19: "SIMM",
|
||||
20: "READ ACCESS",
|
||||
21: "WRITE ACCESS"
|
||||
}
|
||||
|
||||
severity = {
|
||||
0: "NO ALARM",
|
||||
1: "MINOR",
|
||||
2: "MAJOR",
|
||||
3: "INVALID"
|
||||
}
|
44
alarms.py
Normal file
44
alarms.py
Normal file
@ -0,0 +1,44 @@
|
||||
|
||||
STATUS = {
|
||||
0: "no alarm",
|
||||
1: "read",
|
||||
2: "write",
|
||||
3: "hihi",
|
||||
4: "high",
|
||||
5: "lolo",
|
||||
6: "low",
|
||||
7: "state",
|
||||
8: "cos",
|
||||
9: "comm",
|
||||
10: "timeout",
|
||||
11: "hw limit",
|
||||
12: "calc",
|
||||
13: "scan",
|
||||
14: "link",
|
||||
15: "soft",
|
||||
16: "bad sub",
|
||||
17: "udf",
|
||||
18: "disable",
|
||||
19: "simm",
|
||||
20: "read access",
|
||||
21: "write access"
|
||||
}
|
||||
|
||||
SEVERITY = {
|
||||
0: "no alarm",
|
||||
1: "minor",
|
||||
2: "major",
|
||||
3: "invalid"
|
||||
}
|
||||
|
||||
|
||||
|
||||
def message(status_code, severity_code):
|
||||
status = STATUS.get(status_code, "unknown")
|
||||
if status_code == severity_code == 0:
|
||||
return status
|
||||
severity = SEVERITY.get(severity_code, "unknown")
|
||||
return f"{status} ({severity})"
|
||||
|
||||
|
||||
|
34
alarms.txt
Normal file
34
alarms.txt
Normal file
@ -0,0 +1,34 @@
|
||||
/usr/local/epics/base/include/alarm.h
|
||||
|
||||
Severity ist "wie schlimm ist es?"
|
||||
0 = NO_ALARM
|
||||
1 = MINOR
|
||||
2 = MAJOR
|
||||
3 = INVALID (das heisst dem Wert darf nicht geglaubt werden, besonders nicht für Feedbacks.)
|
||||
|
||||
Status ist "Was für ein Alarm ist das?"
|
||||
0 = NO_ALARM
|
||||
1 = READ (Lesefehler, oft mit INVALID Severity)
|
||||
2 = WRITE (Schreibfehler, oft mit INVALID Severity)
|
||||
3 = HIHI (Wert ist sehr hoch, oft mit MAJOR Severity)
|
||||
4 = HIGH (Wert ist hoch, oft mit MINOR Severity)
|
||||
5 = LOLO (Wert ist sehr tief, oft mit MAJOR Severity)
|
||||
6 = LOW (Wert ist tief, oft mit MINOR Severity)
|
||||
7 = STATE (Wert ist ein Alarmzustand (z.B. OFF))
|
||||
8 = COS (=Change of State, Zustand hat sich geändert = Signalflanke)
|
||||
9 = COMM (Communikationsfehler, oft mit INVALID Severity)
|
||||
10 = TIMEOUT
|
||||
11 = HW_LIMIT
|
||||
12 = CALC (Calculation gibt NaN oder Inf oder so)
|
||||
13 = SCAN (Record konnte 5 mal nicht gescannt werden weil busy)
|
||||
14 = LINK (Record hat Alarm geerbt, weil der Link ein MS hat, oder Link ist ungültig)
|
||||
15 = SOFT
|
||||
16 = BAD_SUB (Subroutine Record hat keine Subroutine)
|
||||
17 = UDF (Undefined, Record hat keinen Wert, typischerweise noch nie prozessiert seit dem Start, normalerweise mit Severity INVALID)
|
||||
18 = DISABLE (Record wurde disabled)
|
||||
19 = SIMM (Record is in Simulationsmodus)
|
||||
20 = READ_ACCESS
|
||||
21 = WRITE_ACCESS
|
||||
|
||||
|
||||
|
18
chans.txt
Normal file
18
chans.txt
Normal file
@ -0,0 +1,18 @@
|
||||
SIN-TIMAST-TMA:Bunch-2-Appl-Freq-RB # rep rate
|
||||
|
||||
# vacuum gauges
|
||||
SATOP21-VMFR165-A010:PRESSURE
|
||||
SATOP21-VMCC165-A010:PRESSURE
|
||||
|
||||
|
||||
# slits
|
||||
SATOP21-OAPU161:MOTOR_W.RBV
|
||||
|
||||
|
||||
CRAP:CRAP0
|
||||
CRAP:CRAP1
|
||||
CRAP:CRAP2
|
||||
CRAP:CRAP3
|
||||
|
||||
SAROP21-OKB:MODE
|
||||
SATOP11-VVPG092-A010:PLC_OPEN
|
19
config.py
Normal file
19
config.py
Normal file
@ -0,0 +1,19 @@
|
||||
|
||||
def load(fname):
|
||||
chans = set()
|
||||
for line in read_lines(fname):
|
||||
line = remove_comments(line).strip()
|
||||
if not line:
|
||||
continue
|
||||
chans.add(line)
|
||||
return sorted(chans)
|
||||
|
||||
def read_lines(fname):
|
||||
with open(fname, "r") as f:
|
||||
yield from f
|
||||
|
||||
def remove_comments(line, comment_char="#"):
|
||||
return line.split(comment_char)[0]
|
||||
|
||||
|
||||
|
16
execute.py
Normal file
16
execute.py
Normal file
@ -0,0 +1,16 @@
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
|
||||
|
||||
def parallel(func, targets, names):
|
||||
with ThreadPoolExecutor() as executor:
|
||||
results = executor.map(func, targets)
|
||||
# return list(results)
|
||||
return dict(zip(names, results))
|
||||
|
||||
|
||||
def serial(func, targets):
|
||||
return [func(t) for t in targets]
|
||||
# return {t: func(t) for t in targets}
|
||||
|
||||
|
||||
|
26
logger.py
Normal file
26
logger.py
Normal file
@ -0,0 +1,26 @@
|
||||
import logging as log
|
||||
|
||||
|
||||
LEVELS = (
|
||||
# log.CRITICAL,
|
||||
log.ERROR,
|
||||
log.WARNING,
|
||||
log.INFO,
|
||||
log.DEBUG,
|
||||
log.NOTSET
|
||||
)
|
||||
|
||||
|
||||
def set_log_level(verbosity):
|
||||
ntotal = len(LEVELS) - 1
|
||||
index = min(verbosity, ntotal)
|
||||
level = LEVELS[index]
|
||||
log.basicConfig(level=level, format="%(asctime)s %(levelname)s %(message)s")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#from logger import log, set_log_level
|
||||
|
||||
#set_log_level(clargs.verbose)
|
57
pvcollection.py
Normal file
57
pvcollection.py
Normal file
@ -0,0 +1,57 @@
|
||||
import epics
|
||||
|
||||
from config import load
|
||||
from execute import parallel
|
||||
#from execute import serial as parallel
|
||||
|
||||
|
||||
class PVCollection:
|
||||
|
||||
def __init__(self, chans):
|
||||
self.chans = chans
|
||||
self.pvs = [PV(ch) for ch in chans]
|
||||
|
||||
@classmethod
|
||||
def from_file(cls, fname):
|
||||
chans = load(fname)
|
||||
return cls(chans)
|
||||
|
||||
def connected(self):
|
||||
return self._run_all(lambda pv: pv.wait_for_connection(0.01))
|
||||
|
||||
def status(self):
|
||||
return self._run_all(lambda pv: pv.status)
|
||||
|
||||
def severity(self):
|
||||
return self._run_all(lambda pv: pv.severity)
|
||||
|
||||
def value(self):
|
||||
return self._run_all(lambda pv: pv.value)
|
||||
|
||||
def _run_all(self, func):
|
||||
return parallel(func, self.pvs)
|
||||
|
||||
|
||||
|
||||
class PV(epics.PV):
|
||||
|
||||
@property
|
||||
def status(self):
|
||||
if not self.connected:
|
||||
return -1
|
||||
return super().status
|
||||
|
||||
@property
|
||||
def severity(self):
|
||||
if not self.connected:
|
||||
return -1
|
||||
return super().severity
|
||||
|
||||
@property
|
||||
def value(self):
|
||||
if not self.connected:
|
||||
return None
|
||||
return super().value
|
||||
|
||||
|
||||
|
241
sani.py
Executable file
241
sani.py
Executable file
@ -0,0 +1,241 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||
|
||||
parser.add_argument("filename")
|
||||
|
||||
#parser.add_argument("-v", "--verbose", action="count", default=0)
|
||||
parser.add_argument("-s", "--show", action="store_true")
|
||||
parser.add_argument("-t", "--timeout", type=float, default=2)
|
||||
|
||||
clargs = parser.parse_args()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
import enum
|
||||
|
||||
import epics
|
||||
import pandas as pd
|
||||
|
||||
from pvcollection import PVCollection
|
||||
from alarms import message
|
||||
from config import load
|
||||
from execute import parallel
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
MSG_NOT_CONNECTED = "did not connect"
|
||||
MSG_SUCCESS = "OK"
|
||||
|
||||
|
||||
|
||||
def v1a():
|
||||
cs = load(clargs.filename)
|
||||
pvs = [epics.PV(c) for c in cs]
|
||||
for pv in pvs:
|
||||
c = pv.pvname
|
||||
connected = pv.wait_for_connection(clargs.timeout)
|
||||
if not connected:
|
||||
msg = MSG_NOT_CONNECTED
|
||||
elif pv.status != 0 or pv.severity != 0:
|
||||
msg = message(pv.status, pv.severity)
|
||||
else:
|
||||
if not clargs.show:
|
||||
continue
|
||||
msg = MSG_SUCCESS
|
||||
# print(c, msg)
|
||||
|
||||
|
||||
def v1b():
|
||||
chans = load(clargs.filename)
|
||||
pvs = [epics.PV(ch) for ch in chans]
|
||||
connected = parallel(lambda pv: pv.wait_for_connection(clargs.timeout), pvs)
|
||||
|
||||
for ch, pv, con in zip(chans, pvs, connected):
|
||||
if not con:
|
||||
msg = MSG_NOT_CONNECTED
|
||||
elif pv.status != 0 or pv.severity != 0:
|
||||
msg = message(pv.status, pv.severity)
|
||||
else:
|
||||
if not clargs.show:
|
||||
continue
|
||||
msg = MSG_SUCCESS
|
||||
# print(ch, msg)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def v1c():
|
||||
chans = load(clargs.filename)
|
||||
length = maxstrlen(chans)
|
||||
pvs = [epics.PV(ch) for ch in chans]
|
||||
connected = parallel(lambda pv: pv.wait_for_connection(clargs.timeout), pvs)
|
||||
|
||||
data = {}
|
||||
for ch, pv, con in zip(chans, pvs, connected):
|
||||
if not con:
|
||||
status = severity = -1
|
||||
value = None
|
||||
msg = MSG_NOT_CONNECTED
|
||||
else:
|
||||
status = pv.status
|
||||
severity = pv.severity
|
||||
if status == 0 and severity == 0:
|
||||
if not clargs.show:
|
||||
continue
|
||||
msg = MSG_SUCCESS
|
||||
else:
|
||||
msg = message(status, severity)
|
||||
value = pv.value
|
||||
|
||||
# print(ch.ljust(length), msg)
|
||||
data[ch] = (con, status, severity, value)
|
||||
|
||||
columns = ("connected", "status", "severity", "value")
|
||||
df = pd.DataFrame.from_dict(data, columns=columns, orient="index")
|
||||
# print()
|
||||
# print(df)
|
||||
# print()
|
||||
# print(df.dtypes)
|
||||
|
||||
|
||||
|
||||
def maxstrlen(seq):
|
||||
return max(strlen(i) for i in seq)
|
||||
|
||||
def strlen(val):
|
||||
return len(str(val))
|
||||
|
||||
|
||||
|
||||
#cs = load(clargs.filename)
|
||||
#pvs = [epics.PV(c) for c in cs]
|
||||
#data = []
|
||||
#for pv in pvs:
|
||||
# c = pv.pvname
|
||||
# connected = pv.wait_for_connection(clargs.timeout)
|
||||
|
||||
# if not connected:
|
||||
# print(c, "didn't connect")
|
||||
# data.append((connected, -1, -1, None))
|
||||
# continue
|
||||
# if pv.status != 0 or pv.severity != 0:
|
||||
# print(c, message(pv.status, pv.severity))
|
||||
|
||||
# data.append((connected, pv.status, pv.severity, pv.value))
|
||||
|
||||
#df = pd.DataFrame(data, index=cs, columns=["connected", "status", "severity", "value"])
|
||||
|
||||
#print(df)
|
||||
#print(df.dtypes)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def v2a():
|
||||
pvc = PVCollection.from_file(clargs.filename)
|
||||
|
||||
data = {
|
||||
"connected": pvc.connected(),
|
||||
"status": pvc.status(),
|
||||
"severity": pvc.severity(),
|
||||
"value": pvc.value()
|
||||
}
|
||||
|
||||
df = pd.DataFrame(data, index=pvc.chans)
|
||||
|
||||
# print(df)
|
||||
# print(df.dtypes)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def get_data(pv):
|
||||
connected = pv.wait_for_connection(clargs.timeout)
|
||||
|
||||
if not connected:
|
||||
value = None
|
||||
status = severity = -1
|
||||
msg = MSG_NOT_CONNECTED
|
||||
else:
|
||||
value = pv.value #TODO: not needed if shot not ok
|
||||
status = pv.status
|
||||
severity = pv.severity
|
||||
if status == 0 and severity == 0:
|
||||
msg = MSG_SUCCESS
|
||||
else:
|
||||
msg = message(status, severity)
|
||||
|
||||
data = {
|
||||
"connected": connected,
|
||||
"value": value,
|
||||
"status": status,
|
||||
"severity": severity
|
||||
}
|
||||
|
||||
if clargs.show:
|
||||
print(pv.pvname, msg)
|
||||
return data#, msg
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def v3():
|
||||
chans = load(clargs.filename)
|
||||
pvs = (epics.PV(ch) for ch in chans) # putting PV constructors into threads has weird effects
|
||||
data = parallel(get_data, pvs, chans)
|
||||
df = pd.DataFrame(data).T
|
||||
return df
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
res = v3()
|
||||
print(res)
|
||||
#for m in res:
|
||||
# print(m)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user