Skip to content

Instantly share code, notes, and snippets.

@pklaus
Last active August 28, 2017 08:58
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pklaus/d86d8a00a57017888699f2a4e2e32e28 to your computer and use it in GitHub Desktop.
Save pklaus/d86d8a00a57017888699f2a4e2e32e28 to your computer and use it in GitHub Desktop.

LUFFT_OPUS20 - EPICS IOC

Installation / Requirements

For this to work, you need some requirements as mentioned here:

  • Python 2.6+ including 3.x
  • Python headers (package name "python-dev" or similar)
  • SWIG 1.3.29+ (package name "swig")
  • readline library (package name "libreadline-dev")

Once, the prerequisits are met, run:

pip3 install -r requirements.txt --upgrade --user

Starting

$ python3 ./lufft_opus20_ioc.py -h
usage: lufft_opus20_ioc.py [-h] --sys SYS --sub SUB --esys ESYS
                           opus20_hostname

positional arguments:
  opus20_hostname  The hostname of IP address of the OPUS20 logger.

optional arguments:
  -h, --help       show this help message and exit
  --sys SYS        The detector system
  --sub SUB        The detector sub-system
  --esys ESYS      The environmental sub system

For example:

./lufft_opus20_ioc.py --sys CBM --sub MVD --esys CLEANROOM 192.168.1.12

Will serve the following PVs:

  • CBM:MVD:ENVIRON:CLEANROOM:Temperature
  • CBM:MVD:ENVIRON:CLEANROOM:RelativeHumidity
  • CBM:MVD:ENVIRON:CLEANROOM:AbsoluteHumidity
  • CBM:MVD:ENVIRON:CLEANROOM:Dewpoint
  • CBM:MVD:ENVIRON:CLEANROOM:BatteryVoltage
# readonly.as
# Access security rules
# documentation: http://www.aps.anl.gov/epics/base/R3-16/1-docs/AppDevGuide/AccessSecurity.html
# presentation: EPICS Channel Access Gateway and Access Security
# https://indico.gsi.de/event/4324/contribution/1/material/slides/0.pdf
# examples: https://github.com/ISISComputingGroup/EPICS-AccessSecurity/blob/master/default.acf
ASG(readonly) {
RULE(1, READ)
}
from pcaspy import Driver, Alarm, Severity
from pcaspy.driver import manager
from opus20 import Opus20, OPUS20_CHANNEL_SPEC, Opus20ConnectionException
import time, threading
class Opus20Driver(Driver):
def __init__(self, hostname, opus20_port=None, opus20_timeout=0.1, scan_period=5.0):
self.hostname = hostname
self.opus20_port = opus20_port
self.opus20_timeout = opus20_timeout
self.scan_period = scan_period
self.connect_opus20()
super(Opus20Driver, self).__init__()
def connect_opus20(self):
kwargs = {}
if self.opus20_port: kwargs['port'] = self.opus20_port
if self.opus20_timeout: kwargs['timeout'] = self.opus20_timeout
self.o20 = Opus20(self.hostname, **kwargs)
if self.scan_period > 0:
self.tid = threading.Thread(target=self.scan_all)
self.tid.setDaemon(True)
self.tid.start()
def scan_all(self):
last_time = time.time()
while True:
mapping = [
('Temperature', 0x0064),
('RelativeHumidity', 0x00c8),
('AbsoluteHumidity', 0x00cd),
('Dewpoint', 0x006e),
('BatteryVoltage', 0x2724),
]
pv_names = [tup[0] for tup in mapping]
o20_ids = [tup[1] for tup in mapping]
try:
values = self.o20.multi_channel_value(o20_ids)
except:
values = [None] * len(o20_ids)
for i, reason in enumerate(pv_names):
self.pvDB[reason].mask = 0
if values[i] is not None:
value = values[i]
self.setParamStatus(reason, Alarm.NO_ALARM, Severity.NO_ALARM)
self.setParam(reason, value)
else:
self.setParamStatus(reason, Alarm.COMM_ALARM, Severity.MINOR_ALARM)
manager.pvs[self.port][reason].updateValue(self.pvDB[reason])
# if the process was suspended, reset last_time:
if time.time() - last_time > self.scan_period:
last_time = time.time()
time.sleep(max(0.0, self.scan_period - (time.time() - last_time)))
last_time += self.scan_period
#!/usr/bin/env python
# external dependencies: PCASpy and opus20
import pcaspy
import opus20
# local modules
from lufft_opus20_pvdb import pvdb
from lufft_opus20_driver import Opus20Driver
prefix = '{sys}:{sub}:ENVIRON:{esys}:'
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('opus20_hostname', help='The hostname of IP address of the OPUS20 logger.')
parser.add_argument('--sys', required=True, help='The detector system')
parser.add_argument('--sub', required=True, help='The detector sub-system')
parser.add_argument('--esys', required=True, help='The environmental sub system')
args = parser.parse_args()
prefix = prefix.format(sys=args.sys, sub=args.sub, esys=args.esys)
server = pcaspy.SimpleServer()
server.initAccessSecurityFile('lufft_opus20_access.as', P=prefix)
server.createPV(prefix, pvdb)
driver = Opus20Driver(args.opus20_hostname)
print("Starting the IOC with the following prefix:")
print(prefix)
while True:
server.process(0.1)
pvdb = {
'Temperature' : {
'prec' : 3,
'unit' : 'deg C',
'lolo' : 17,
'low' : 20,
'high' : 28,
'hihi' : 30,
'mdel' : .05,
'adel' : .20,
'asg' : 'readonly',
},
'RelativeHumidity' : {
'prec' : 3,
'unit' : '%',
'lolo' : 30,
'low' : 40,
'high' : 60,
'hihi' : 70,
'mdel' : .15,
'adel' : .60,
'asg' : 'readonly',
},
'AbsoluteHumidity' : {
'prec' : 3,
'unit' : 'g/m3',
'low' : 5,
'high' : 20,
'mdel' : .03,
'adel' : .12,
'asg' : 'readonly',
},
'Dewpoint' : {
'prec' : 3,
'unit' : 'deg C',
'lolo' : -30,
'low' : -10,
'high' : 16,
'hihi' : 20,
'mdel' : .03,
'adel' : .12,
'asg' : 'readonly',
},
'BatteryVoltage' : {
'prec' : 3,
'unit' : 'V',
'lolo' : 5.0,
'low' : 5.3,
'high' : 6.2,
'hihi' : 6.5,
'mdel' : .003,
'adel' : .012,
'asg' : 'readonly',
},
}
#PCASpy
https://github.com/paulscherrerinstitute/pcaspy/archive/master.zip
opus20
@xiaoqiangwang
Copy link

I notice the following codes are using the internals of Driver. https://gist.github.com/pklaus/d86d8a00a57017888699f2a4e2e32e28#file-lufft_opus20_driver-py-L47-L55

It might be re-factored into

            for i, reason in enumerate(pv_names):
                if values[i] is not None:
                    self.setParam(reason, values[i])
                    self.setParamStatus(reason, Alarm.NO_ALARM, Severity.NO_ALARM)
                else:
                    self.setParamStatus(reason, Alarm.COMM_ALARM, Severity.MINOR_ALARM)
            self.updatePVs()

In addition to the removal of access to internal variable/methods, it reordered setParamStatus/setParam because setParamStatus overwrites whatever the changes setParam may have.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment