]> jspc29.x-matter.uni-frankfurt.de Git - mvdsensorcontrol.git/commitdiff
init_setup: add threshold logging to EPICS
authorMaps <maps@ikf>
Wed, 21 Aug 2019 08:52:24 +0000 (10:52 +0200)
committerMaps <maps@ikf>
Wed, 21 Aug 2019 08:52:24 +0000 (10:52 +0200)
tools/epics_log.py [new file with mode: 0755]
tools/init_setup.pl

diff --git a/tools/epics_log.py b/tools/epics_log.py
new file mode 100755 (executable)
index 0000000..f529bdb
--- /dev/null
@@ -0,0 +1,167 @@
+#!/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')
index d9aa6a89c07a39215be4c1d7fa86326aa93f0a06..0d6e1e6c4b8f1e2fb0a50994a4fa3ebb728a80de 100755 (executable)
@@ -83,6 +83,47 @@ my $cts = $dbsys->getDocumentElement->findnodes('cts')->[0]->getAttribute('addre
 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");