diff --git a/bin/lsdcGui_amx b/bin/lsdcGui_amx index c684d14a..b39909a2 100755 --- a/bin/lsdcGui_amx +++ b/bin/lsdcGui_amx @@ -1,6 +1,6 @@ export PROJDIR=/nsls2/software/mx/daq/ export CONFIGDIR=${PROJDIR}bnlpx_config/ -export LSDCHOME=${PROJDIR}lsdc_amx_prod +export LSDCHOME=${PROJDIR}lsdc_amx export PATH=/usr/local/bin:/usr/bin:/bin export PYTHONPATH=".:${CONFIGDIR}:/opt/dectris/albula/4.0/python:${LSDCHOME}" diff --git a/bin/lsdcGui_fmx b/bin/lsdcGui_fmx index 159fdb4c..e028aee7 100755 --- a/bin/lsdcGui_fmx +++ b/bin/lsdcGui_fmx @@ -1,6 +1,6 @@ export PROJDIR=/nsls2/software/mx/daq/ export CONFIGDIR=${PROJDIR}bnlpx_config/ -export LSDCHOME=${PROJDIR}lsdc_fmx_prod +export LSDCHOME=${PROJDIR}lsdc_fmx export PATH=/usr/local/bin:/usr/bin:/bin export PYTHONPATH=".:${CONFIGDIR}:/opt/dectris/albula/4.0/python:${LSDCHOME}" diff --git a/bin/lsdcGui_nyx b/bin/lsdcGui_nyx index a0aa62ac..55d2122d 100755 --- a/bin/lsdcGui_nyx +++ b/bin/lsdcGui_nyx @@ -1,6 +1,6 @@ export PROJDIR=/nsls2/software/mx/daq/ export CONFIGDIR=${PROJDIR}bnlpx_config/ -export LSDCHOME=${PROJDIR}lsdc_nyx_prod +export LSDCHOME=${PROJDIR}lsdc_nyx export EPICS_CA_AUTO_ADDR_LIST=NO export EPICS_CA_ADDR_LIST=10.67.147.255 diff --git a/bin/lsdcRemote_amx.cmd b/bin/lsdcRemote_amx.cmd index ee8f0626..13f770b7 100755 --- a/bin/lsdcRemote_amx.cmd +++ b/bin/lsdcRemote_amx.cmd @@ -1,7 +1,7 @@ #!/bin/bash -l export PROJDIR=/nsls2/software/mx/daq/ export CONFIGDIR=${PROJDIR}bnlpx_config/ -export LSDCHOME=${PROJDIR}lsdc_amx_prod +export LSDCHOME=${PROJDIR}lsdc_amx export PYTHONPATH=".:${CONFIGDIR}:/usr/lib64/edna-mx/mxv1/src:/usr/lib64/edna-mx/kernel/src:${LSDCHOME}:${PROJDIR}/RobotControlLib" export PATH=/usr/local/bin:/usr/bin:/bin:${PROJDIR}/software/bin:/opt/ccp4/bin source ${CONFIGDIR}daq_env_amx.txt diff --git a/bin/lsdcRemote_fmx.cmd b/bin/lsdcRemote_fmx.cmd index a892611b..052d8241 100755 --- a/bin/lsdcRemote_fmx.cmd +++ b/bin/lsdcRemote_fmx.cmd @@ -1,7 +1,7 @@ #!/bin/bash -l export PROJDIR=/nsls2/software/mx/daq/ export CONFIGDIR=${PROJDIR}bnlpx_config/ -export LSDCHOME=${PROJDIR}lsdc_fmx_prod +export LSDCHOME=${PROJDIR}lsdc_fmx export PYTHONPATH=".:${CONFIGDIR}:/usr/lib64/edna-mx/mxv1/src:/usr/lib64/edna-mx/kernel/src:${LSDCHOME}:${PROJDIR}/RobotControlLib" export PATH=/usr/local/bin:/usr/bin:/bin:${PROJDIR}/software/bin:/opt/ccp4/bin source ${CONFIGDIR}daq_env.txt diff --git a/bin/lsdcServer_amx.cmd b/bin/lsdcServer_amx.cmd index 24fc70e6..64e6d4ed 100755 --- a/bin/lsdcServer_amx.cmd +++ b/bin/lsdcServer_amx.cmd @@ -1,7 +1,7 @@ #!/bin/bash -l export PROJDIR=/nsls2/software/mx/daq/ export CONFIGDIR=${PROJDIR}bnlpx_config/ -export LSDCHOME=${PROJDIR}lsdc_amx_prod +export LSDCHOME=${PROJDIR}lsdc_amx export PYTHONPATH=".:${CONFIGDIR}:/usr/lib64/edna-mx/mxv1/src:/usr/lib64/edna-mx/kernel/src:${LSDCHOME}:${PROJDIR}/RobotControlLib" export PATH=/usr/local/bin:/usr/bin:/bin:${PROJDIR}/software/bin:/opt/ccp4/bin source ${CONFIGDIR}daq_env_amx.txt diff --git a/bin/lsdcServer_fmx.cmd b/bin/lsdcServer_fmx.cmd index 096bdb41..59e63cfb 100755 --- a/bin/lsdcServer_fmx.cmd +++ b/bin/lsdcServer_fmx.cmd @@ -1,7 +1,7 @@ #!/bin/bash -l export PROJDIR=/nsls2/software/mx/daq/ export CONFIGDIR=${PROJDIR}bnlpx_config/ -export LSDCHOME=${PROJDIR}lsdc_fmx_prod +export LSDCHOME=${PROJDIR}lsdc_fmx export PYTHONPATH=".:${CONFIGDIR}:/usr/lib64/edna-mx/mxv1/src:/usr/lib64/edna-mx/kernel/src:${LSDCHOME}:${PROJDIR}/RobotControlLib" export PATH=/usr/local/bin:/usr/bin:/bin:${PROJDIR}/software/bin:/opt/ccp4/bin source ${CONFIGDIR}daq_env.txt diff --git a/bin/lsdcServer_nyx.cmd b/bin/lsdcServer_nyx.cmd index e46707bf..b06b291b 100755 --- a/bin/lsdcServer_nyx.cmd +++ b/bin/lsdcServer_nyx.cmd @@ -1,7 +1,7 @@ #!/bin/bash -l export PROJDIR=/nsls2/software/mx/daq/ export CONFIGDIR=${PROJDIR}bnlpx_config/ -export LSDCHOME=${PROJDIR}lsdc_nyx_prod +export LSDCHOME=${PROJDIR}lsdc_nyx export EPICS_CA_AUTO_ADDR_LIST=NO export PYTHONPATH=".:${CONFIGDIR}:/usr/lib64/edna-mx/mxv1/src:/usr/lib64/edna-mx/kernel/src:${LSDCHOME}:${PROJDIR}/RobotControlLib" export PATH=/usr/local/bin:/usr/bin:/bin:${PROJDIR}/software/bin:/opt/ccp4/bin diff --git a/config_params.py b/config_params.py index 09cd0f5d..5a984c22 100644 --- a/config_params.py +++ b/config_params.py @@ -54,6 +54,7 @@ class RasterStatus(Enum): HUTCH_TIMER_DELAY = 500 SAMPLE_TIMER_DELAY = 0 +SERVER_CHECK_DELAY = 2000 ROBOT_MIN_DISTANCE = 200.0 ROBOT_DISTANCE_TOLERANCE = 0.050 diff --git a/daq_macros.py b/daq_macros.py index 42887266..58be0967 100644 --- a/daq_macros.py +++ b/daq_macros.py @@ -3207,7 +3207,7 @@ def loop_center_xrec(): pic_prefix = "findloop" output_file = 'xrec_result.txt' clean_up_files(pic_prefix, output_file) - zebraCamDaq(0,360,40,.4,pic_prefix,os.getcwd(),0) + zebraCamDaq(0,360,40,.4,pic_prefix,getBlConfig("visitDirectory"),0) comm_s = f'xrec {os.environ["CONFIGDIR"]}/xrec_360_40Fast.txt {output_file}' logger.info(comm_s) try: diff --git a/daq_main2.py b/daq_main2.py index beaaced1..51812ce1 100755 --- a/daq_main2.py +++ b/daq_main2.py @@ -5,8 +5,7 @@ import sys import os from daq_main_common import pybass_init, run_server -from config_params import LSDC_SERVICE_USERS - +from utils.healthcheck import perform_server_checks #TODO understand why imports are required here - GUI requires imports in daq_main_common from daq_macros import * from daq_lib import * @@ -36,13 +35,8 @@ logger.addHandler(handler1) logger.addHandler(handler2) -if not getpass.getuser() in LSDC_SERVICE_USERS: - message = "LSDC server not being started by a LSDC service user account, aborting!" - print(message) - logger.error(message) - sys.exit(1) -else: - print(f"continuing as we are using a service user: {getpass.getuser()}") +perform_server_checks() +setBlConfig("visitDirectory", os.getcwd()) sitefilename = "" global command_list,immediate_command_list,z command_list = [] diff --git a/daq_utils.py b/daq_utils.py index 0f4a10d0..169b4f26 100644 --- a/daq_utils.py +++ b/daq_utils.py @@ -209,7 +209,7 @@ def createDefaultRequest(sample_id,createVisit=True): setProposalID(propNum,createVisit) screenDist, screenEnergy, screenExptime, screenPhiend, screenPhist, screenReso, screenTransmissionPercent, screenWidth, screenbeamHeight, screenbeamWidth = getScreenDefaultParams() sampleName = str(db_lib.getSampleNamebyID(sample_id)) - basePath = os.getcwd() + basePath = getBlConfig("visitDirectory") runNum = db_lib.getSampleRequestCount(sample_id) (puckPosition,samplePositionInContainer,containerID) = db_lib.getCoordsfromSampleID(beamline,sample_id) request = {"sample": sample_id} @@ -284,7 +284,7 @@ def create_filename(prefix,number): else: tmp_filename = "%s_%05d.cbf" % (prefix,int(number)) if (prefix[0] != "/"): - cwd = os.getcwd() + cwd = getBlConfig("visitDirectory") filename = "%s/%s" % (cwd,tmp_filename) else: filename = tmp_filename @@ -364,27 +364,19 @@ def readPVDesc(): counter_inf = line.split() counter_dict[counter_inf[1]] = beamline_designation + counter_inf[0] -def createVisitNameRaw(proposalName, maxNumber=None): - if maxNumber: - number = maxNumber + 1 - else: - number = 1 - return f'mx{proposalName}-{number}', number - -def createVisitName(proposalName, maxNumber=None): - return createVisitNameRaw(proposalName, maxNumber) - -def setProposalID(proposalID,createVisit=True): # TODO JA proposalID implies a database ID, which it is not (just proposal number). Misleading - if (getProposalID() != proposalID): #proposalID changed - create a new visit. - logger.info("you changed proposals! " + str(proposalID)) - logger.info('ignoring createVisit for now - ISPyB required to properly account for visit numbers') - try: - visitName, visitNum = createVisitName(proposalID) - db_lib.setBeamlineConfigParam(beamline,"proposal",proposalID) - except Exception as e: - visitName = "999999-1234" - logger.error("error in set proposal. Error: %s" % e) - setVisitName(visitName) +def setProposalID(proposalID,createVisit=True): + if (getProposalID() != proposalID): #proposalID changed - create a new visit. + logger.info("you changed proposals! " + str(proposalID)) + try: + if (createVisit): + visitName = ispybLib.createVisit(proposalID) + db_lib.setBeamlineConfigParam(beamline,"proposal",proposalID) + else: + visitName, visitNum = ispybLib.createVisitName(proposalID) + except Exception as e: + visitName = "999999-1234" + logger.error("ispyb error in set proposal. Error: %s" % e) + setVisitName(visitName) def getProposalID(): return getBlConfig("proposal") diff --git a/embl_robot.py b/embl_robot.py index 5982241c..622106ec 100644 --- a/embl_robot.py +++ b/embl_robot.py @@ -207,7 +207,7 @@ def preMount(self, gov_robot, puckPos, pinPos, sampID, **kwargs): prefix90 = sampName + "_" + str(puckPos) + "_" + str(pinPos) + "_" + str(reqCount) + "_PA_90" kwargs['prefix1'] = prefix1 kwargs['prefix90'] = prefix90 - top_view.topViewSnap(prefix1,os.getcwd()+"/pinAlign",1,acquire=0) + top_view.topViewSnap(prefix1,getBlConfig("visitDirectory")+"/pinAlign",1,acquire=0) except Exception as e: e_s = str(e) message = "TopView check ERROR, will continue: " + e_s @@ -262,9 +262,6 @@ def mount(self, gov_robot, puckPos,pinPos,sampID,**kwargs): else: robotStatus = beamline_support.get_any_epics_pv("SW:RobotState","VAL") if (robotStatus != "Ready"): - if (daq_utils.beamline == "fmx"): - daq_macros.homePins() - time.sleep(3.0) gov_status = gov_lib.setGovRobot(gov_robot, 'SE') if not gov_status.success: return MOUNT_FAILURE @@ -361,6 +358,8 @@ def preUnmount(self, gov_robot, puckPos,pinPos,sampID): #will somehow know where detDist = beamline_lib.motorPosFromDescriptor("detectorDist") if (detDist bool: + if not getpass.getuser().startswith("lsdc-"): + return False + print(f"continuing as we are using a service user: {getpass.getuser()}") + return True + +def has_write_permission(directory): + try: + testfile = tempfile.TemporaryFile(dir = directory) + testfile.close() + except OSError as e: + if e.errno == 13: # Permission denied + return False + raise + return True + +@healthcheck(name="server working directory", remediation="", fatal=True) +def check_curr_visit_dir() -> bool: + import daq_utils + # Check if current visit dir is valid + visit_dir_env_var = "CURRENT_VISIT_DIR" + if visit_dir_env_var not in os.environ: + check_curr_visit_dir.remediation = ( + f"{visit_dir_env_var} environment variable not found" + ) + return False + if os.environ[visit_dir_env_var] == "": + check_curr_visit_dir.remediation = f"{visit_dir_env_var} is empty" + return False + current_visit_dir = Path(os.environ[visit_dir_env_var]) + if not current_visit_dir.exists(): + check_curr_visit_dir.remediation = ( + f"{visit_dir_env_var} = {current_visit_dir} does not exist" + ) + return False + + # Check if current visit dir is one of the dirs defined in BASE_DATA_DIRS + base_dir_env_var = f"BASE_DATA_DIRS_{daq_utils.beamline.upper()}" + if base_dir_env_var not in os.environ: + check_curr_visit_dir.remediation = f"{base_dir_env_var} evironment variable not found" + return False + # Splitting on : if there are multiple base dirs + base_dirs = [Path(p) for p in os.environ[base_dir_env_var].split(":")] + pass_dir_found = False + for i, part in enumerate(reversed(current_visit_dir.parts)): + if 'pass-' in part: + pass_dir_found = True + if current_visit_dir.parents[i] not in base_dirs: + check_curr_visit_dir.remediation = f"{current_visit_dir.parents[i]} not found in {base_dirs}" + return False + else: + break + if not pass_dir_found: + check_curr_visit_dir.remediation = f"Pass folder not found in {current_visit_dir}" + return False + + # Check if current visit dir is writable + if not has_write_permission(current_visit_dir): + check_curr_visit_dir.remediation = f"Server does not have write permission to {current_visit_dir}" + return False + + return True + + +@healthcheck(name="existence of environment file", remediation="", fatal=True) +def check_env_file() -> bool: + import daq_utils + + env_path = Path(f"/nsls2/software/mx/current_visit_lsdc_{daq_utils.beamline}") + if not env_path.exists(): + check_env_file.remidiation = f"Environment file not found at {env_path}" + return False + return True + def perform_checks(): - """Call this function to contruct a DAG where each node is evaluated using - breadth first search. DAGs allow certain tests to be run only after passing + """Call this function to contruct a DAG where each node is evaluated using + breadth first search. DAGs allow certain tests to be run only after passing specific tests. For example """ + check_functions = [check_daq_utils, check_working_directory, check_db] + run_checks(check_functions) + + +def perform_server_checks(): + check_functions = [check_service_user, check_env_file, check_curr_visit_dir] + run_checks(check_functions) + + +def run_checks(check_functions): checks = DiGraph() - for c in [check_daq_utils, check_working_directory, check_db]: + for c in check_functions: for d in c.depends: checks.add_edge(d, c) - print(u'\u2500' * 20) + print("\u2500" * 20) print("Begin checks") for check in bfs_tree(checks, start): try: if all([parent.passed for parent in checks.predecessors(check)]): - print(f'Checking {check.name}...', end='\t') + print(f"Checking {check.name}...", end="\t") if check(): - print(f'{bcolors.OKGREEN}Success{bcolors.ENDC}') + print(f"{bcolors.OKGREEN}Success{bcolors.ENDC}") else: handle_fail(check) except Exception as e: - print(f'Exception: {e}') - logger.error(f'Exception during checks {e}') + print(f"Exception: {e}") + logger.error(f"Exception during checks {e}") handle_fail(check) print("End checks") - print(u'\u2500' * 20) - \ No newline at end of file + print("\u2500" * 20)