diff --git a/sbndprmdaq/analysis/analysis_estimate.py b/sbndprmdaq/analysis/analysis_estimate.py index 14af624..ccc93f4 100644 --- a/sbndprmdaq/analysis/analysis_estimate.py +++ b/sbndprmdaq/analysis/analysis_estimate.py @@ -324,20 +324,20 @@ def _sanity_check(self): Checks values are sensible ''' if np.abs((self._max_a - self._baseline_a) / (self._baseline_rms_a)) < 5: - print('no_anode') - return 'no_anode' + print('no_anode, anode too small, ignoring') + # return 'no_anode' - if self._max_a < 0: + if self._max_a - self._baseline_a < 0: print('no_anode') - return 'no_anode' + return 'anode is negative' - if np.abs((self._max_c - self._baseline_c) / (self._baseline_rms_a)) < 5: - print('no_cathode') + if np.abs((self._max_c - self._baseline_c) / (self._baseline_rms_c)) < 5: + print('no_cathode, cathode too small') return 'no_cathode' - if self._max_c > 0: + if self._max_c - self._baseline_c > 0: print('no_cathode') - return 'no_cathode' + return 'cathode is positive' if self._deltat_c > 100: print('cathode_deltat_large') diff --git a/sbndprmdaq/data_storage.py b/sbndprmdaq/data_storage.py index ba0bc75..6772312 100644 --- a/sbndprmdaq/data_storage.py +++ b/sbndprmdaq/data_storage.py @@ -159,7 +159,7 @@ def update_dataframe(self, measurement, prm_id): ''' #pylint: disable=invalid-name - self._logger.warning('Updating dataframe.') + self._logger.info('Updating dataframe.') if measurement is None: self._logger.warning('No measurement available to save to dataframe.') @@ -188,7 +188,7 @@ def update_dataframe(self, measurement, prm_id): df = df.append(meas, ignore_index=True) - df.to_csv(dataframe_file_name) + df.to_csv(dataframe_file_name, index=False) def get_dataframe_path(self): ''' diff --git a/sbndprmdaq/eclapi.py b/sbndprmdaq/eclapi.py index efc6c71..fcd46d4 100644 --- a/sbndprmdaq/eclapi.py +++ b/sbndprmdaq/eclapi.py @@ -29,7 +29,7 @@ def __init__(self, url, user, password): self._password = password self._user = user - self._to = 10 # timeout in seconds + self._to = 60 # timeout in seconds def generate_salt(self): diff --git a/sbndprmdaq/externals.py b/sbndprmdaq/externals.py index 2a4197b..50d509b 100644 --- a/sbndprmdaq/externals.py +++ b/sbndprmdaq/externals.py @@ -33,6 +33,7 @@ def pmt_hv_on(): voltages = [] for pv in pvs: + # print(pv, epics.caget(pv)) voltages.append(epics.caget(pv)) voltages = np.array(voltages) @@ -177,3 +178,67 @@ def prm_covered(self, prm_id): if float(formatted[0][1]) > 20: return True return False + + def plc_on(self, prm_id): + ''' + Returns True if the PrM PLC is ON (PrMs can operate) + + Args: + prm_id (int): the PrM ID + ''' + + if self._connection is None: + self.connect() + + if self._connection.status != psycopg2.extensions.STATUS_READY: + self.connect() + + if prm_id == 3: + # Inline can always operate + return True + else: + pv = 'prm' + + + current_time = datetime.datetime.now() + this_month = current_time.month + month_2digit = str(this_month).zfill(2) + + query = """SELECT d.tagid, COALESCE((d.intvalue::numeric)::text, (trunc(d.floatvalue::numeric,3))::text), d.t_stamp +FROM cryo_prd.sqlt_data_1_2024_{} d, cryo_prd.sqlth_te s +WHERE d.tagid=s.id +AND s.tagpath LIKE '%sbnd%' +AND s.tagpath LIKE '%{}%' +AND s.tagpath LIKE '%{}%' +ORDER BY d.t_stamp DESC +LIMIT {}""".format(month_2digit, '', pv, 1) + + cursor = self._connection.cursor() + + cursor.execute(query) + + dbrows = cursor.fetchall() + + cursor.close() + + formatted = [] + for row in dbrows: + try: + time = datetime.datetime.fromtimestamp(row[2]/1000) # ms since epoch + time = time.strftime("%Y-%m-%d %H:%M:%S") + except: + time = row[2] + formatted.append((row[0], row[1], row[2], time)) + + + return int(formatted[0][1]) + + + +if __name__ == "__main__": + + api = IgnitionAPI() + + print(api.plc_on(prm_id=1)) + + diff --git a/sbndprmdaq/mainwindow.py b/sbndprmdaq/mainwindow.py index 5d34d09..dedd404 100644 --- a/sbndprmdaq/mainwindow.py +++ b/sbndprmdaq/mainwindow.py @@ -774,6 +774,14 @@ def _check_external_status(self): self._prm_controls[2]._liquid_level_label.setDisabled(True) self._prm_controls[2]._digitizer_image.setDisabled(True) + if self._config['check_plc']: + for prm_id in [1, 2, 3]: + if not self._ignition_api.plc_on(prm_id=prm_id): + self.inhibit_run(True, [prm_id]) + else: + self.inhibit_run(False, [prm_id]) + + def inhibit_run(self, do_inhibit=True, prm_ids=(1, 2)): ''' diff --git a/sbndprmdaq/summary_plot.py b/sbndprmdaq/summary_plot.py index dc16092..bec5497 100644 --- a/sbndprmdaq/summary_plot.py +++ b/sbndprmdaq/summary_plot.py @@ -6,10 +6,15 @@ import time import datetime import xml.etree.ElementTree as ET +import requests -import pandas as pd from PyQt5.QtCore import QTimer + +import pandas as pd +import matplotlib as mpl import matplotlib.pyplot as plt +from labellines import labelLine, labelLines +import numpy as np from sbndprmdaq.eclapi import ECL, ECLEntry @@ -105,9 +110,20 @@ def make_summary_plots(self): # Read the dataframe df = pd.read_csv(self._dataframe_filename) + # Add Qa/Qc ratio + df['qaqc'] = df['qa']/df['qc'] + # Convert the date string to datetime object df['date'] = pd.to_datetime(df['date']) + # Add a datetime index + df['datetime'] = pd.to_datetime(df['date']) + df = df.set_index('datetime') + + # Drop old column + df = df.drop(['Unnamed: 0'], axis=1) + + self._make_summary_plot(df) self._number_of_runs(df, prm_ids=[1, 2, 3]) @@ -148,7 +164,7 @@ def _make_summary_plot(self, df): mask = df['date'] > first_day df_s = df[mask] - dfs[prm_id] = df_s.query(f'prm_id == {prm_id}') + dfs[prm_id] = df_s.query(f'prm_id == {prm_id} and qaqc > 0 and qaqc < 1') timestr = time.strftime("%Y%m%d-%H%M%S") @@ -156,6 +172,7 @@ def _make_summary_plot(self, df): events = { datetime.datetime(2024, 3, 25, 15, 00): 'Lowered HV on PrM 2', + datetime.datetime(2024, 5, 26, 8, 50): 'Increased HV on PrM 2', } # @@ -178,7 +195,7 @@ def _make_summary_plot(self, df): ax.set_title('Preliminary', loc='right', color='gray') ax.legend(fontsize=12, loc=2) - ax.set_ylim([0.050, 3.0]) + ax.set_ylim([0.050, 8.0]) plt.setp(ax.get_xticklabels(), rotation=45, ha="right", rotation_mode="anchor") @@ -229,6 +246,108 @@ def _make_summary_plot(self, df): self._logger.info(f'Plot saved to {filename}.') self._current_plots[f'prm{prm_id}_qcqa'] = filename + # + # Qa/Qc Plot + # + + for prm_id in self._config['prms']: + + df_daily_mean = dfs[prm_id].resample('D').mean() + df_daily_std = dfs[prm_id].resample('D').std() + + df_daily_mean['date'] = df_daily_mean.index + df_daily_std['date'] = df_daily_std.index + + print(df_daily_mean) + + _, ax = plt.subplots(ncols=1, nrows=1, figsize=(10, 8)) + + def qaqc(t, tau): + return np.exp(-t/tau) + + date = np.array(dfs[prm_id]['date']) + qa = np.array(dfs[prm_id]['qa']) + qc = np.array(dfs[prm_id]['qc']) + + mask = (qa != -1) & (qc != -1) + + ax.plot(date[mask], qa[mask]/qc[mask], label=labels[prm_id], + linestyle='None', marker="o", markersize=5, color='green', alpha=0.5, zorder=1) + + ax.errorbar(df_daily_mean['date'], df_daily_mean['qaqc'], yerr=df_daily_std['qaqc'], + label=labels[prm_id] + ' Daily Average', linestyle='None', marker="o", markersize=5, color='black', alpha=1, zorder=2) + + + ax.set_ylabel(r'$Q_A/Q_C$',fontsize=16) + ax.set_xlabel('Time',fontsize=16) + ax.tick_params(labelsize=12) + ax.grid(True) + ax.set_title('Preliminary', loc='right', color='gray') + + today = datetime.date.today() + ax.axvline(today, color='red', label='Today', linestyle='solid') + + if prm_id == 1: + d = datetime.datetime(2024, 6, 28, 17, 00) + num = mpl.dates.date2num(d) + xmin, xmax = ax.get_xlim() + frac = (num - xmin) / (xmax - xmin) + + for i, tau in enumerate([0.50, 1, 3, 6, 9]): + ax.axhline(qaqc(1.1, tau), + xmin=0, + xmax=frac, + color='black', label=f'{tau:.2f} ms', linestyle=linestyles[i]) + + ax.axhline(qaqc(1.6, tau), + xmin=frac, + xmax=1, + color='black', linestyle=linestyles[i]) + else: + d = datetime.datetime(2024, 3, 25, 15, 00) + num = mpl.dates.date2num(d) + xmin, xmax = ax.get_xlim() + frac = (num - xmin) / (xmax - xmin) + + d = datetime.datetime(2024, 5, 26, 8, 50) + num = mpl.dates.date2num(d) + xmin, xmax = ax.get_xlim() + frac2 = (num - xmin) / (xmax - xmin) + + for i, tau in enumerate([0.10, 1, 3, 6, 9]): + ax.axhline(qaqc(0.25, tau), + xmin=0, + xmax=frac, + color='black', label=f'{tau:.2f} ms', linestyle=linestyles[i]) + + ax.axhline(qaqc(0.39, tau), + xmin=frac, + xmax=frac2, + color='black', linestyle=linestyles[i]) + + ax.axhline(qaqc(0.25, tau), + xmin=frac2, + xmax=1, + color='black', linestyle=linestyles[i]) + + ax.set_title('Preliminary', loc='right', color='gray') + + # labelLines(ax.get_lines())# , zorder=2.5)#, xvals=xvals) + ax.legend(fontsize=12, loc=2) + # for l in ax.get_lines(): + # print('LINE:', l) + ax.set_ylim([0, 1.1]) + + plt.xticks(rotation=45, ha="right", rotation_mode="anchor") + + plt.tight_layout() + if self._plot_savedir is not None: + filename = self._plot_savedir + f'prm{prm_id}_qa_over_qc_{timestr}.png' + plt.savefig(filename) + self._logger.info(f'Plot saved to {filename}.') + self._current_plots[f'prm{prm_id}_qa_over_qc'] = filename + + # plt.show() @@ -236,6 +355,7 @@ def _number_of_runs(self, df, prm_ids=(1, 2, 3)): password = self._read_ecl_password() + # https://dbweb0.fnal.gov/ECL/sbnd ecl = ECL(url='https://dbweb9.fnal.gov:8443/ECL/sbnd/E', user='sbndprm', password=password) # Retrieve the last 20 entries @@ -251,7 +371,11 @@ def _number_of_runs(self, df, prm_ids=(1, 2, 3)): # Loop over entries (they are in decreasing order in time) for entry in entries: - text = entry.find('./text').text + + text = entry.find('./text') + if text is None: + continue + text = text.text if 'Purity Monitors Automated Plots' in text: @@ -308,7 +432,14 @@ def _send_to_ecl(self): print(entry.show()) if self._config['post_to_ecl']: - ecl.post(entry, do_post=True) + + try: + ecl.post(entry, do_post=True) + except: + self._logger.info('Post timeout. Trying one more time in 5 secs') + time.sleep(5) + ecl.post(entry, do_post=True) + def _read_ecl_password(self): diff --git a/settings.yaml b/settings.yaml index d676c47..8d4a880 100644 --- a/settings.yaml +++ b/settings.yaml @@ -34,7 +34,7 @@ save_as_txt: true arduino_address: '/dev/arduino' arduino_pin: 7 -disable_arduino: False +disable_arduino: True prm_id_to_mpod_ip: 1: "10.226.35.154" @@ -53,19 +53,22 @@ mpod_prm3_anodegrid_ch: 301 # Default HV values prm_hv_default: 1: - cathode: -120 - anodegrid: 4432 - anode: 4360 + # cathode: -120 + # anodegrid: 4432 + # anode: 4360 # cathode: -500 # anodegrid: 4000 # anode: 4100 + cathode: -80 + anodegrid: 2954 + anode: 2902 2: - # cathode: -250 - # anodegrid: 3010 - # anode: 3170 - cathode: -150 - anodegrid: 1807 - anode: 1900 + cathode: -250 + anodegrid: 3010 + anode: 3170 + # cathode: -150 + # anodegrid: 1807 + # anode: 1900 3: # cathode: -120 # anodegrid: 4431 @@ -104,6 +107,9 @@ check_pmt_hv: True check_lar_level: True enforce_level: False +# Checks if the PLC is ON +check_plc: True + # Data storage data_storage: True data_storage_host: "sbndgpvm01.fnal.gov" @@ -129,11 +135,11 @@ analysis_config: 'ana_type': 'estimate' # 'estimate', 'fit', 'fit_difference' 'deltat_start_c': 310 'deltat_start_a': 310 - 'trigger_sample': 300 - 'signal_range_c': [320, 500] - 'signal_range_a': [2300, 2500] - 'baseline_range_c': [0,250] - 'baseline_range_a': [2000,2200] + 'trigger_sample': 480 + 'signal_range_c': [480, 800] + 'signal_range_a': [3500, 3900] + 'baseline_range_c': [0, 460] + 'baseline_range_a': [3000, 3900] 'title': 'SBND PrM 1 - Internal - Long' 'plot_range': [0, 1000] 'lowpass_cutoff_freq': 100000 # only for fit ana types @@ -143,11 +149,11 @@ analysis_config: 'ana_type': 'estimate' 'deltat_start_c': 310 'deltat_start_a': 310 - 'trigger_sample': 300 - 'signal_range_c': [320, 500] - 'signal_range_a': [1000, 1500] - 'baseline_range_c': [0,250] - 'baseline_range_a': [310,900] + 'trigger_sample': 480 + 'signal_range_c': [480, 800] + 'signal_range_a': [900, 1100] + 'baseline_range_c': [0, 460] + 'baseline_range_a': [600,900] 'title': 'SBND PrM 2 - Internal - Short' 'plot_range': [0, 1000] 'lowpass_cutoff_freq': 100000