#!/bin/env python
#
# April 10, 2015
#
# This script is used to make automatic entries into the
# HDRUN e-log whenever a run is started. It is called by
# the run_go script which is run by CODA.
#
# This works by gathering information from various sources
# and writing it to a temporary HTML file that is then
# inserted using the /site/ace/certified/apps/bin/logentry
# utility. This utility is provided by the e-log maintainers.
# It is technically a violation of our policy to require
# files/programs from outside of the counting house, but
# this requires outside network connectivity anyway.
#
# Questions regarding this script may be directed to:
#
# David Lawrence
# davidl@jlab.org
# x5567
#
import os
import sys
import subprocess
import datetime
import glob
from epics import caget,caput
ELOGS = ['HDRUN']
ENTRYMAKERS = ['hdops']
RUN = '--unknown--'
if len(sys.argv) > 1 : RUN = sys.argv[1]
DAQ_HOME = os.getenv('DAQ_HOME', '/home/hdops/CDAQ/daq_dev_v0.31/daq')
#--------------------------------------------------------
# GetRealDir
#
# Get the environment variable and find the actual directory
# it refers to (resolving any symbolic links). Return a list
# with the environment variable name as the first element and
# the path as the second.
#--------------------------------------------------------
def GetRealDir(envar):
val = os.getenv(envar)
if val != None :
return [envar, val + ' ( → ' + os.path.realpath(val) + ' )']
else:
return [envar, '-- unknown --']
#--------------------------------------------------------
# MakeHEADER_HTML
#
# Return string with HTML for first part of log entry
#--------------------------------------------------------
def MakeHEADER_HTML():
global RUN
items =[];
items.append(['Time',datetime.datetime.now().strftime('%c')])
items.append(['/gluex/raid',os.path.realpath('/gluex/raid')])
items.append( GetRealDir('CODA' ) )
items.append( GetRealDir('DAQ_HOME' ) )
items.append( GetRealDir('COOL_HOME' ) )
items.append( GetRealDir('CODA_ROL' ) )
items.append( GetRealDir('LINUXVME_LIB') )
html = '
Automated log entry for run '+ RUN + '
\n'
html += 'This entry generated by the script: $Id$
\n'
html += '
\n'
html += '(n.b. some of the followng values come from environment variables in the hdops account. It is possible they do not reflect actual settings used!)\n'
html += '\n'
for (key,val) in items: html += ' '+key+' | : | '+val+' |
\n'
html += '
\n'
return html
#--------------------------------------------------------
# HTMLBox
#
# Wrap the given text in an HTML box such that the text is
# marked as the preformatted contents of the single cell of
# the table.
#--------------------------------------------------------
def HTMLBox(mylabel, mytext, bgcolor='#EEE'):
# Print label and open HTML table
html = ''
html += '' + mylabel + '\n'
html += '\n' # This just gives a border around outside of table
html += ' \n' # Actual table
# Print content
html += '\n' + mytext + ' | \n';
# Close HTML table
html += ' \n' # actual table
html += ' |
\n' # Border around table
return html
#--------------------------------------------------------
# MakeDAQ_HTML
#
# Get all DAQ values of interest and return them as
# a string formatted as an HTML table
#--------------------------------------------------------
def MakeDAQ_HTML():
global RUN, DAQ_HOME
# Open HTML table
html = '\n'
html += '
DAQ values
\n'
# Look for run comments file
run_comments_fname = '%s/../work/run_comments/run_%06d_info.txt' % (DAQ_HOME, int(RUN))
config_fname = '-- unknown --\n'
hosts_fname = '-- unknown --\n'
run_comments = '-- file not found --\n'
config_file = '-- file not found --\n'
coda_config = '-- unknown --\n'
hosts_file = '-- file not found --\n'
try:
# Read in run comments file
with open(run_comments_fname, "r") as myfile:
run_comments = myfile.read()
# Look for "CONFIG FILE::" line in run comments file
for item in run_comments.split('\n'):
if 'CONFIG FILE:: ' in item:
config_fname = item[len('CONFIG FILE:: '):].strip()
# Read in trigger/readout config file
try:
with open(config_fname, "r") as myfile2:
config_file = myfile2.read()
coda_config_dir = os.path.dirname(config_fname)
coda_config = os.path.basename(coda_config_dir)
hosts_fname = coda_config_dir + '/hosts'
# Read in hosts file
try:
with open(hosts_fname, "r") as myfile3:
hosts_file = myfile3.read()
except:
html += '(can\'t open %s)
\n' % hosts_fname
except:
html += '(can\'t open %s)
\n' % config_fname
except:
pass
html += HTMLBox(run_comments_fname + ' (at GO)', run_comments, '#EEF')
html += '
\n'
html += HTMLBox(hosts_fname , hosts_file, '#EFE')
html += '
\n'
html += HTMLBox(config_fname , config_file, '#DDD')
html += '\n'
return html
#--------------------------------------------------------
# MakeDAQ_CONFIG_HTML
#
# Copy all files generated by ROLs containing selected
# config. parameters into tables.
#--------------------------------------------------------
def MakeDAQ_CONFIG_HTML():
global RUN, DAQ_HOME
# Get list of files
config_dir = '%s/../work/DAQ_CONFIG_OUT' % DAQ_HOME
fnames = glob.glob('%s/daq_config_*.dat' % config_dir)
# Open HTML table
html = '\n'
html += 'Selected digitization module configuration parameters\n'
# html += '
\n'
bgcolor = '#EEE'
html += '\n' # This just gives a border around outside of table
html += ' \n' # Actual table
Nfiles = 0
for fname in fnames:
html += '\n'
if (Nfiles % 2) == 0 : html += '\n'
html += '\n'
#fname = fname_path[len(config_dir)len('/daq_config_:]
contents = ''
with open(fname, 'r') as f: contents = f.read()
# Print label and open HTML table
# Print content
# html += ' | ' + contents + ' | \n';
html += '' + contents + ' \n';
html += '\n'
if (Nfiles % 2) == 1 : html += '\n\n'
Nfiles += 1
if (Nfiles % 2) == 1 : html += ' | \n' # handle odd number of files
# Close HTML table
html += ' \n' # actual table
html += ' |
\n' # Border around table
# html += '
\n'
html += '\n'
return html
#--------------------------------------------------------
# MakeEPICS_TABLE
#
# This takes as input a list of epics variables for which
# to make an HTML table and a title for the table. It is
# called from MakeEPICS_HTML multiple times.
#--------------------------------------------------------
def MakeEPICS_TABLE(title, vars, color='black'):
# Open HTML table
html = '\n'
html += '' + title + '\n'
html += '
\n' # This just gives a border around outside of table
html += ' \n' # Actual table
# Get each EPICS value and write into table
for (var,units,description) in vars:
val = caget(pvname=var, timeout=float(0.5))
if val == None: val = '-- N/A --'
html += ' \n'
html += ' ' + var + ' | \n'
html += ' ' + str(val) + ' | \n'
html += ' ' + units + ' | \n'
html += ' ' + description + ' | \n'
html += ' \n'
# Close HTML table
html += ' \n' # actual table
html += ' |
\n' # Border around table
html += '\n'
return html
#--------------------------------------------------------
# MakeEPICS_HTML
#
# Get all EPICS values of interest and return them as
# a string formatted as an HTML table
#--------------------------------------------------------
def MakeEPICS_HTML():
# Make list of variables and their descriptions
# Each entry is a list containing:
#
# EPICSvar_name units description
beamlinevars = []
beamlinevars.append(['MMSHLDE' , 'MeV' ,'Beam energy from model' ])
beamlinevars.append(['HALLD:p' , 'MeV' ,'Observed energy in accelerator' ])
beamlinevars.append(['IBCAD00CRCUR6' , 'nA' ,'Beam Current' ])
beamlinevars.append(['IPM5C11.VAL' , 'nA' ,'Beam Current at 5C11' ])
beamlinevars.append(['IPM5C11A.VAL' , 'nA' ,'Beam Current at 5C11A' ])
beamlinevars.append(['IPM5C11B.VAL' , 'nA' ,'Beam Current at 5C11B' ])
beamlinevars.append(['IPMAD00C.VAL' , 'nA' ,'Beam Current at AD00' ])
beamlinevars.append(['HallD-PXI:Data:I_Shunt' , 'A' ,'Solenoid Current' ])
beamlinevars.append(['HD:GONI:RADIATOR_NAME' , ' ' ,'Diamond Radiator' ])
beamlinevars.append(['HD:GONI:RADIATOR_INDEX' , ' ' ,'Index of Radiator Slot' ])
beamlinevars.append(['HD:GONI:RADIATOR_ID' , ' ' ,'Diamond ID (0 = amorphous)' ])
beamlinevars.append(['HD:CBREM:PLANE' , ' ' ,'PARA = 1, PERP = 2' ])
beamlinevars.append(['HD:CBREM:REQ_EDGE' , ' ' ,'Coherent Peak Nominal Edge Energy'])
beamlinevars.append(['HD:GONI:X.RBV' , ' ' ,'Diamond Radiator X-value' ])
beamlinevars.append(['HD:GONI:Y.RBV' , ' ' ,'Diamond Radiator Y-value' ])
beamlinevars.append(['HD:GONI:ROLL.RBV' , ' ' ,'Diamond Radiator ROLL-value' ])
beamlinevars.append(['HD:GONI:PITCH.RBV' , ' ' ,'Diamond Radiator PITCH-value' ])
beamlinevars.append(['HD:GONI:YAW.RBV' , ' ' ,'Diamond Radiator YAW-value' ])
beamlinevars.append(['hd:radiator:motor.RBV' , ' ' ,'Tagger Amorphous radiator' ])
beamlinevars.append(['hd:polarimeter:motor.RBV' , ' ' ,'Polarimeter convertor' ])
beamlinevars.append(['US1-2-BOT:m2.RBV' , ' ' ,'PS convertor' ])
beamlinevars.append(['US1-2-BOT:m1.RBV' , ' ' ,'Collimator' ])
beamlinevars.append(['RESET:i:AmbUpStreamSol_Temp', 'F' ,'Temp. of Upstream Solenoid' ])
beamlinevars.append(['RESET:i:AmbDownstreamSol_Temp','F' ,'Temp. of Downstream Solenoid' ])
beamlinevars.append(['RESET:i:AmbUpstreamSol_Hum' , '%' ,'Humidity of Upstream Solenoid' ])
beamlinevars.append(['RESET:i:AmbDownStreamSol_Hum','%' ,'Humidity of Downstream Solenoid' ])
beamlinevars.append(['RESET:i:GasPanelBarPress1' , ' ' ,' ' ])
beamlinevars.append(['HD:coda:daq:availableRAID' , 'TB' ,'RAID disk space remaining' ])
#
# EPICSvar_name units description
beamhelivars = []
beamhelivars.append(['PWF1I04:spinCalc' , ' ' ,'Wien angle (Vertical)' ])
beamhelivars.append(['MFGANGLE' , ' ' ,'Wien angle (Solenoids)' ])
beamhelivars.append(['PWF1I06:spinCalc' , ' ' ,'Wien angle (Horizontal)' ])
beamhelivars.append(['IGL1I00DI24_24M' , ' ' ,'Insertable Half-wave plate' ])
beamhelivars.append(['psub_pl_pos' , ' ' ,'Rotating waveplate' ])
beamhelivars.append(['IGL1I00OD16_8' , ' ' ,'Pockels Cell ON/OFF' ])
beamhelivars.append(['SMRPOSA' , ' ' ,'Hall A Slit Position' ])
beamhelivars.append(['SMRPOSB' , ' ' ,'Hall B Slit Position' ])
beamhelivars.append(['SMRPOSC' , ' ' ,'Hall C Slit Position' ])
beamhelivars.append(['HELCLOCKd' , ' ' ,'Helicity Clock Read' ])
beamhelivars.append(['HELDELAYd' , ' ' ,'Helicity Delay Read' ])
beamhelivars.append(['HELTSETTLEd' , ' ' ,'Helicity Settle Read' ])
beamhelivars.append(['HELTSTABLEd' , ' ' ,'Helicity Stable Read' ])
beamhelivars.append(['HELPATTERNd' , ' ' ,'Helicity Pattern Read' ])
beamhelivars.append(['HELFREQ' , ' ' ,'Helicity Frequency' ])
# EPICSvar_name units description
bcalvars = []
bcalvars.append(['BCUS:i::MainProgram-BCAL_US_1_Temp', 'C' ,' ' ])
bcalvars.append(['BCUS:i::MainProgram-BCAL_US_2_Temp', 'C' ,' ' ])
bcalvars.append(['BCUS:i::MainProgram-BCAL_US_3_Temp', 'C' ,' ' ])
bcalvars.append(['BCUS:i::MainProgram-BCAL_US_4_Temp', 'C' ,' ' ])
bcalvars.append(['BCDS:i::MainProgram-BCAL_DS_1_Temp', 'C' ,' ' ])
bcalvars.append(['BCDS:i::MainProgram-BCAL_DS_2_Temp', 'C' ,' ' ])
bcalvars.append(['BCDS:i::MainProgram-BCAL_DS_3_Temp', 'C' ,' ' ])
bcalvars.append(['BCDS:i::MainProgram-BCAL_DS_4_Temp', 'C' ,' ' ])
bcalvars.append(['BCUS:i::MainProgram-BCAL_US_1_Humidity', '%' ,' ' ])
bcalvars.append(['BCUS:i::MainProgram-BCAL_US_2_Humidity', '%' ,' ' ])
bcalvars.append(['BCUS:i::MainProgram-BCAL_US_3_Humidity', '%' ,' ' ])
bcalvars.append(['BCUS:i::MainProgram-BCAL_US_4_Humidity', '%' ,' ' ])
bcalvars.append(['BCDS:i::MainProgram-BCAL_DS_1_Humidity', '%' ,' ' ])
bcalvars.append(['BCDS:i::MainProgram-BCAL_DS_2_Humidity', '%' ,' ' ])
bcalvars.append(['BCDS:i::MainProgram-BCAL_DS_3_Humidity', '%' ,' ' ])
bcalvars.append(['BCDS:i::MainProgram-BCAL_DS_4_Humidity', '%' ,' ' ])
bcalvars.append(['BCAL_DS:CHILL:AIN_C', ' ' ,' ' ])
bcalvars.append(['BCAL_US:CHILL:AIN_C', ' ' ,' ' ])
# EPICSvar_name units description
cdcvars = []
cdcvars.append(['GAS:i::FDC_Gas_Flow-MFC1_Flow', ' ' ,' ' ])
cdcvars.append(['GAS:i::FDC_Gas_Flow-MFC3_Flow', ' ' ,' ' ])
cdcvars.append(['GAS:i::FDC_Gas_Flow-MFC5_Flow', ' ' ,' ' ])
cdcvars.append(['GAS:i::FDC_CDC_Pressure-CDC_Pressure', ' ' ,' ' ])
cdcvars.append(['GAS:i::FDC_CDC_Pressure-CDC_Pressure_Inlet', ' ' ,' ' ])
cdcvars.append(['GAS:i::FDC_CDC_Pressure-CDC_Pressure_Outlet', ' ' ,' ' ])
cdcvars.append(['GAS:i::CDC_Temps-CDC_U1_Temp', 'C' ,' ' ])
cdcvars.append(['GAS:i::CDC_Temps-CDC_U2_Temp', 'C' ,' ' ])
cdcvars.append(['GAS:i::CDC_Temps-CDC_U3_Temp', 'C' ,' ' ])
cdcvars.append(['GAS:i::CDC_Temps-CDC_U4_Temp', 'C' ,' ' ])
cdcvars.append(['GAS:i::CDC_Temps-CDC_U5_Temp', 'C' ,' ' ])
cdcvars.append(['GAS:i::CDC_Temps-CDC_D1_Temp', 'C' ,' ' ])
cdcvars.append(['GAS:i::CDC_Temps-CDC_D2_Temp', 'C' ,' ' ])
cdcvars.append(['GAS:i::CDC_Temps-CDC_D3_Temp', 'C' ,' ' ])
cdcvars.append(['GAS:i::CDC_Temps-CDC_D4_Temp', 'C' ,' ' ])
cdcvars.append(['GAS:i::CDC_Temps-CDC_D5_Temp', 'C' ,' ' ])
cdcvars.append(['CDC:hv:A:1:v0set' , 'V' ,' ' ])
cdcvars.append(['CDC:hv:B:1:v0set' , 'V' ,' ' ])
cdcvars.append(['CDC:hv:A:1:vmon' , 'V' ,' ' ])
cdcvars.append(['CDC:hv:B:1:vmon' , 'V' ,' ' ])
# EPICSvar_name units description
fdcvars = []
fdcvars.append(['FDC:CHILL:TEMP', 'C' ,' ' ])
fdcvars.append(['FDC:CHILL:FLOW', ' ' ,' ' ])
fdcvars.append(['FDC:CHILL:PRESS', ' ' ,' ' ])
fdcvars.append(['FDC:CHILL:ALARM1', ' ' ,' ' ])
fdcvars.append(['GAS:i::FDC_Gas_Flow-MFC2_Flow', ' ' ,' ' ])
fdcvars.append(['GAS:i::FDC_Gas_Flow-MFC4_Flow', ' ' ,' ' ])
fdcvars.append(['GAS:i::FDC_Gas_Flow-MFC6_Flow', ' ' ,' ' ])
fdcvars.append(['GAS:i::FDC_Gas_Flow-MFC7_Flow', ' ' ,' ' ])
fdcvars.append(['GAS:i::FDC_Gas_Flow-MFC8_Flow', ' ' ,' ' ])
fdcvars.append(['GAS:i::FDC_Gas_Flow-MFC9_Flow', ' ' ,' ' ])
fdcvars.append(['GAS:i::FDC_CDC_Pressure-FDC_P1_Inlet_Press', ' ' ,' ' ])
fdcvars.append(['GAS:i::FDC_CDC_Pressure-FDC_P2_Inlet_Press', ' ' ,' ' ])
fdcvars.append(['GAS:i::FDC_CDC_Pressure-FDC_P3_Inlet_Press', ' ' ,' ' ])
fdcvars.append(['GAS:i::FDC_CDC_Pressure-FDC_P4_Inlet_Press', ' ' ,' ' ])
fdcvars.append(['GAS:i::FDC_CDC_Pressure-FDC_P1_Outlet_Press', ' ' ,' ' ])
fdcvars.append(['GAS:i::FDC_CDC_Pressure-FDC_P2_Outlet_Press', ' ' ,' ' ])
fdcvars.append(['GAS:i::FDC_CDC_Pressure-FDC_P3_Outlet_Press', ' ' ,' ' ])
fdcvars.append(['GAS:i::FDC_CDC_Pressure-FDC_P4_Outlet_Press', ' ' ,' ' ])
fdcvars.append(['GAS:i::FDC_CDC_Pressure-PT506', ' ' ,' ' ])
html = '\n'
html += '
EPICS values
\n'
html += MakeEPICS_TABLE('Beamline', beamlinevars, '#000')
html += MakeEPICS_TABLE('Beam Helicity', beamhelivars, '#000')
html += MakeEPICS_TABLE('BCAL', bcalvars, '#00F')
html += MakeEPICS_TABLE('CDC', cdcvars, '#0F0')
html += MakeEPICS_TABLE('FDC', fdcvars, '#F0F')
html += '\n'
return html
#======================================================
# Gather data
html = '\n'
html += MakeHEADER_HTML()
html += '
\n'
html += MakeEPICS_HTML()
html += '
\n'
html += MakeDAQ_HTML()
html += '
\n'
html += MakeDAQ_CONFIG_HTML()
html += '\n\n'
# Determine directory to hold tmp file
DAQ_HOME = os.getenv('DAQ_HOME')
if DAQ_HOME != None:
tmp_dir = '%s/../work' % DAQ_HOME
else:
tmp_dir = '/tmp'
# tmp filename
fname = '%s/tmp_elog.html' % tmp_dir
# Delete existing HTML file (if any)
if os.path.exists(fname) : os.unlink(fname)
# Write HTML to tmp file
f = open(fname, 'w')
f.write(html)
f.close()
# Title of log entry
title = '"Run %s automated entry"' % RUN
# write to e-Log
cmd = ['/site/ace/certified/apps/bin/logentry' ]
cmd.extend(['--title', title])
cmd.extend(['--html', '--body', fname])
cmd.extend(['--entrymaker','hdops'])
cmd.extend(['--tag','Autolog'])
for elog in ELOGS: cmd.extend(['--logbook', elog])
subprocess.call(cmd)