--- /dev/null
+#!/usr/bin/env python3
+
+"""
+(c) 2019 Philipp Klaus
+if called manually, this script was called like this:
+./epics_log.py /local.1/htdocs/mvdconfig/setup/PRESTO_2018_readout.xml --epics-ca-addr-list 192.168.10.46
+"""
+
+import argparse, sys, os
+# external dependencies
+import lxml.etree # (OpenSuse package: python3-lxml)
+import caproto # (No OpenSuse package / pip install caproto)
+#import epics # (No OpenSuse package / pip install pyepics)
+
+
+section_divider = '-' * 20
+
+def eprint(*args, **kwargs):
+ " print to stderr "
+ print(*args, **kwargs, file=sys.stderr)
+
+def success(last_words):
+ eprint(last_words)
+ print("SUCCESS")
+ sys.exit(0)
+
+def failure(last_words):
+ eprint(last_words)
+ print("FAILURE")
+ sys.exit(1)
+
+def caput(pv, value):
+ " synchroneously write (put) values to EPICS PVs "
+ # VARIANT pyepics
+ #epics.caput(pv, value, wait=True)
+ # VARIANT caproto (pure python package)
+ import caproto.sync.client
+ #caproto.sync.client.write(pv, value, notify=True)
+ # VARIANT with the caput CLI:
+ # os.system(f"caput {pv} {value}")
+
+def caput_many(pvlist, values):
+ """
+ An improved version of epics.caput_many()
+ with sensible overall connection timeout.
+ Needed because epics.caput_many() can take ages
+ if none of a larger number of PVs can be connected.
+ """
+
+ connection_timeout = 0.2
+ put_timeout = 0.2
+ context_timeout = 2
+
+ # VARIANT caproto threaded client Batch mode
+ import caproto.threading.client
+ import time, functools
+ ctx = caproto.threading.client.Context(timeout=context_timeout)
+ pvs = ctx.get_pvs(*pvlist)
+ start = time.time()
+ while (time.time() - start) < connection_timeout and sum(pv.connected for pv in pvs) < len(pvlist):
+ time.sleep(0.005)
+ connected_pvs = [pv for pv in pvs if pv.connected]
+ disconnected_pvs = [pv for pv in pvs if pv not in connected_pvs]
+ eprint(section_divider)
+ eprint('Starting to update PVs NOW')
+ eprint(f'PVs connected/disconnected/total: {len(connected_pvs)}/{len(disconnected_pvs)}/{len(pvs)}')
+ eprint(f'caproto Context() object: {ctx}')
+ results = {pv.name: False for pv in disconnected_pvs}
+ def convert_result_dict_to_list():
+ retlist = []
+ for pvname in pvlist:
+ if pvname in results:
+ retlist.append(1 if results[pvname] else -1)
+ else:
+ retlist.append(-1) # timeout
+ return retlist
+ if not connected_pvs: return convert_result_dict_to_list()
+ def stash_result(name, response):
+ results[name] = response.status.success == 1
+ with caproto.threading.client.Batch() as b:
+ for pv, value in zip(pvs, values):
+ if pv not in connected_pvs: continue
+ eprint(f"{pv.name} -> {value}")
+ b.write(pv, value, callback=functools.partial(stash_result, pv.name))
+ start = time.time()
+ while (time.time() - start) < put_timeout and len(results) < len(pvlist):
+ time.sleep(0.005)
+ eprint(section_divider)
+ return convert_result_dict_to_list()
+
+
+parser = argparse.ArgumentParser()
+parser.add_argument('setup_file')
+parser.add_argument('--pv-prefix', default='CBM:MVD:DAQ:PRESTO:')
+parser.add_argument('--epics-ca-addr-list', '-a', help="Set to an IP Address, if the IOC to write to cannot be reached via broadcasts.")
+args = parser.parse_args()
+
+eprint(section_divider)
+eprint('--- epics_log.py ---')
+
+if args.epics_ca_addr_list:
+ eprint(f'Setting EPICS_CA_ADDR_LIST to {args.epics_ca_addr_list}')
+ os.environ['EPICS_CA_ADDR_LIST'] = args.epics_ca_addr_list
+ os.environ['EPICS_CA_AUTO_ADDR_LIST'] = 'No'
+
+eprint("Setup File: %s" % args.setup_file)
+
+xml_doc = lxml.etree.parse(args.setup_file)
+
+system = xml_doc.findall("//system")[0]
+system_name = system.get('name', 'unknown') # eg. 'PRESTO_2018_readout_sys'
+if not 'PRESTO_2018_readout' in system_name:
+ failure('irrelevant system name:', system_name, 'quitting.')
+
+# tag name is 'description' (some XML peculiarity...)
+setup = xml_doc.findall(".//")[0]
+
+sensors = xml_doc.findall(".//sensor")
+
+updates = [] # all PV updates to be processed
+
+try:
+ eprint(section_divider)
+ for sensor in sensors:
+ name = sensor.get('name')
+ sensor_settings_xml = sensor.get('config')
+ assert name.startswith('sensor')
+ name = name[6:]
+
+ chain = sensor.getparent() # or .find('..')
+ board = chain.getparent()
+ full_id = board.get('address', '0000') + sensor.get('id', 'ffff')
+ eprint(f'Sensor: {name:3s} Full ID: {full_id}')
+ eprint(f'Sensor Settings XML File: {sensor_settings_xml}')
+
+ folder = os.path.join(os.path.split(args.setup_file)[0], '../config')
+ abs_path = os.path.abspath(os.path.join(folder, sensor_settings_xml))
+ sensor_xml_doc = lxml.etree.parse(abs_path)
+ fields = sensor_xml_doc.findall('.//field')
+ foi = 'IVDREF1A', 'IVDREF1B', 'IVDREF1C', 'IVDREF1D', 'IVDREF2' # fields of interest
+ for field in fields:
+ fname = field.get('name', '')
+ if fname in foi:
+ value = field.get('value')
+ value = int(value)
+ eprint(f"{fname} -> {value}")
+ # now send the new value to EPICS
+ #caput(f"{args.pv_prefix}SENSOR:{name}:{fname}", value)
+ pv = f"{args.pv_prefix}SENSOR:{name}:{fname}"
+ updates.append((pv, value))
+ eprint(section_divider)
+ pvs = [update[0] for update in updates]
+ values = [update[1] for update in updates]
+ results = caput_many(pvs, values)
+ nsuccess = sum(1 for r in results if r == 1)
+ ntotal = len(results)
+ eprint(f"Reached {nsuccess} out of {ntotal} PVs.")
+ if nsuccess != ntotal:
+ eprint("Couln't reach the IOCs for some PVs:")
+ for r, pv, value in zip(results, pvs, values):
+ if r == 1: continue
+ eprint(f" {r} {pv} {value}")
+ failure("Unsuccessful")
+except Exception as e:
+ failure(str(e))
+
+success('successfully updated EPICS with new setup/sensor settings')
print STDERR "Loading setup $name from file $setupFile\n" if $verbose;
+####################################
+## Logging to EPICS IOC and Archiver
+####################################
+
+# epics_log: Log important DAQ settings (like thresholds) to
+# an EPICS IOC and eventually to its Archiver DB.
+# Argument: Setup XML file
+# Result: "SUCCESS" or "FAILURE"
+sub epics_log {
+ my ($pv) = @_;
+ if (open(my $F, "-|", "python3 ./epics_log.py $setupFile -a 192.168.10.109 2>/tmp/epics_log.stderr")) {
+ $result = <$F>;
+ close $F;
+ chomp $result;
+ if ($verbose) {
+ binmode STDOUT, ':utf8';
+ if (open(my $fh, '<:encoding(UTF-8)', "/tmp/epics_log.stderr")) {
+ while (my $row = <$fh>) {
+ chomp $row;
+ print STDERR "$row\n";
+ }
+ } else {
+ warn "Could not stderr file of epics_log.py script";
+ }
+ }
+ return $result;
+ } else {
+ print STDERR "Cannot run 'epics_log.py'\n";
+ return "FAILURE";
+ }
+}
+# Do the logging
+my $log_result = epics_log($setupFile);
+print STDERR "EPICS LOG Result: $log_result\n";
+print STDERR 'timer: ', Time::HiRes::gettimeofday() - $start, " s\n";
+
+
+####################################
+##
+####################################
+
print STDERR "Stop CTS trigger source\n";
execute("trbcmd clearbit 0x$cts 0xa101 0x1");