def setUpClass(cls): if driver.get_backend_status() in driver.BACKEND_RUNNING: microscope = model.getMicroscope() if microscope.role != "meteor": logging.info( "There is already running backend. It will be turned off, and the backend of METEOR will be turned on." ) test.stop_backend() test.start_backend(METEOR_CONFIG) else: logging.info( "There is METEOR backend already running. It will be used." ) else: try: logging.info("METEOR backend will be turned on.") test.start_backend(METEOR_CONFIG) except Exception: raise # get the stage components cls.stage = model.getComponent(role="stage-bare") # get the metadata stage_md = cls.stage.getMetadata() cls.stage_grid_centers = stage_md[model.MD_SAMPLE_CENTERS] cls.stage_loading = stage_md[model.MD_FAV_POS_DEACTIVE]
def setUpClass(cls): if driver.get_backend_status() == driver.BACKEND_RUNNING: logging.info("A running backend is already found, skipping tests") cls.backend_was_running = True return # run the backend as a daemon # we cannot run it normally as the child would also think he's in a unittest cmd = ODEMISD_CMD + ODEMISD_ARG + [SPARC_CONFIG] ret = subprocess.call(cmd) if ret != 0: logging.error("Failed starting backend with '%s'", cmd) time.sleep(1) # time to start # Find CCD & SEM components cls.microscope = model.getMicroscope() for comp in model.getComponents(): if comp.role == "ccd": cls.ccd = comp elif comp.role == "spectrometer": cls.spec = comp elif comp.role == "e-beam": cls.ebeam = comp elif comp.role == "se-detector": cls.sed = comp elif comp.role == "light": cls.light = comp elif comp.role == "filter": cls.light_filter = comp
def setUpClass(cls): if driver.get_backend_status() == driver.BACKEND_RUNNING: logging.info("A running backend is already found, skipping tests") cls.backend_was_running = True return # run the backend as a daemon # we cannot run it normally as the child would also think he's in a unittest cmd = ODEMISD_CMD + ODEMISD_ARG + [SPARC_CONFIG] ret = subprocess.call(cmd) if ret != 0: logging.error("Failed starting backend with '%s'", cmd) time.sleep(1) # time to start # Find CCD & SEM components cls.microscope = model.getMicroscope() for comp in model.getComponents(): if comp.role == "ccd": cls.ccd = comp elif comp.role == "spectrometer": cls.spec = comp elif comp.role == "e-beam": cls.ebeam = comp elif comp.role == "se-detector": cls.sed = comp
def stop_backend(): """ Stop the backend and wait for it to be fully stopped """ cmd = ODEMISD_CMD + ["--kill"] ret = subprocess.call(cmd) if ret != 0: raise IOError("Failed stopping backend with '%s' (returned %d)" % (cmd, ret)) # wait for the backend to be fully stopped time.sleep(1) # time to stop end = time.time() + 15 # s timeout while time.time() < end: status = driver.get_backend_status() if status in (driver.BACKEND_RUNNING, driver.BACKEND_STARTING): logging.info("Backend is stopping...") time.sleep(1) else: break else: raise IOError("Backend still stopping after 15 s") model._core._microscope = None # force reset of the microscope for next connection if status != driver.BACKEND_STOPPED: raise IOError("Backend failed to stop, now %s" % status)
def setUpClass(cls): if driver.get_backend_status() == driver.BACKEND_RUNNING: logging.info("A running backend is already found, skipping tests") cls.backend_was_running = True return # run the backend as a daemon # we cannot run it normally as the child would also think he's in a unittest cmd = ODEMISD_CMD + ODEMISD_ARG + [SECOM_LENS_CONFIG] ret = subprocess.call(cmd) if ret != 0: logging.error("Failed starting backend with '%s'", cmd) time.sleep(1) # time to start # find components by their role cls.ebeam = model.getComponent(role="e-beam") cls.sed = model.getComponent(role="bs-detector") cls.ccd = model.getComponent(role="ccd") cls.sem_stage = model.getComponent(role="sem-stage") cls.opt_stage = model.getComponent(role="align") cls.ebeam_focus = model.getComponent(role="ebeam-focus") cls.focus = model.getComponent(role="focus") cls.light = model.getComponent(role="light") cls.light_filter = model.getComponent(role="filter") cls.combined_stage = model.getComponent(role="stage")
def start_backend(config): """ Start the backend by checking the currently running backend. Basically 2 cases/scenarios: 1. If no running backend => run the requested one. 2. If there is running backend => a. If the running backend is same as requested one => do nothing b. If the running backend is different from the requested one => stop the running, and run the requested one. In case the backend fails to start a IOError will be raised. config (str): path to the microscope config file. """ # check if a backend is running if driver.get_backend_status() in (driver.BACKEND_RUNNING, driver.BACKEND_STARTING): current_model = model.getMicroscope().model try: req_model = modelgen.Instantiator(open(config)).ast except Exception as exp: raise ValueError(exp) if current_model == req_model: logging.info("Backend for %s already running", config) return else: logging.info( "There is a backend running already, it will be turned off, and the backend \ %s will be run instead.", config) stop_backend() run_backend(config) # check if no backend is running else: run_backend(config)
def setUpClass(cls): try: import fastem_calibrations except ImportError as err: raise unittest.SkipTest( f"Skipping the fastem tests, correct libraries " f"to perform the tests are not available.\n" f"Got the error: {err}") if TEST_NOHW: test.start_backend(FASTEM_CONFIG) elif driver.get_backend_status() != driver.BACKEND_RUNNING: raise IOError( "Backend controlling a real hardware should be started before running this test case" ) # get the hardware components cls.scanner = model.getComponent(role='e-beam') cls.asm = model.getComponent(role="asm") cls.mppc = model.getComponent(role="mppc") cls.multibeam = model.getComponent(role="multibeam") cls.descanner = model.getComponent(role="descanner") cls.stage = model.getComponent( role="stage" ) # TODO replace with stage-scan when ROA conversion method available cls.ccd = model.getComponent(role="diagnostic-ccd") cls.beamshift = model.getComponent(role="ebeam-shift") cls.det_rotator = model.getComponent(role="det-rotator")
def main(args): """ Handles the command line arguments args is the list of arguments passed return (int): value to return to the OS as program exit code """ # arguments handling parser = argparse.ArgumentParser() parser.add_argument("--role", dest="role", metavar="<component>", help="display and update an image on the screen") parser.add_argument("--gridsize", dest="gridsize", nargs=2, metavar="<gridsize>", type=int, default=None, help="size of the grid of spots in x y, default 14 14") parser.add_argument("--log-level", dest="loglev", metavar="<level>", type=int, choices=[0, 1, 2], default=0, help="set verbosity level (0-2, default = 0)") options = parser.parse_args(args[1:]) # Set up logging before everything else loglev_names = [logging.WARNING, logging.INFO, logging.DEBUG] loglev = loglev_names[min(len(loglev_names) - 1, options.loglev)] # change the log format to be more descriptive handler = logging.StreamHandler() logging.getLogger().setLevel(loglev) handler.setFormatter(logging.Formatter('%(asctime)s (%(module)s) %(levelname)s: %(message)s')) logging.getLogger().addHandler(handler) if options.role: if get_backend_status() != BACKEND_RUNNING: raise ValueError("Backend is not running while role command is specified.") ccd = model.getComponent(role=options.role) live_display(ccd, ccd.data, kill_ccd=False, gridsize=options.gridsize) else: ccd = ueye.Camera("camera", "ccd", device=None) ccd.SetFrameRate(2) live_display(ccd, ccd.data, gridsize=options.gridsize) return 0
def main(args): """ Handles the command line arguments. Parameters ---------- args: The list of arguments passed. Returns ------- (int) value to return to the OS as program exit code. """ parser = argparse.ArgumentParser() parser.add_argument('--debug', action="store_true", default=False) parser.add_argument("--role", dest="role", default="diagnostic-ccd", metavar="<component>", help="Role of the camera to connect to via the Odemis " "back-end. Ex: 'ccd'.") parser.add_argument("--channel", dest="channel", default=1, metavar="<component>", help="Number of the AWG output channel.") parser.add_argument("--auto", dest="auto", default=True, metavar="<component>", help="If True automatically align the ebeam scan to" "multiprobe. To do this a server must be running " "on the microscope PC.") options = parser.parse_args(args[1:]) if options.debug: logging.getLogger().setLevel(logging.DEBUG) else: logging.getLogger().setLevel(logging.WARNING) if get_backend_status() != BACKEND_RUNNING: raise ValueError("Backend is not running.") ccd = model.getComponent(role=options.role) try: sem_rotation = get_sem_rotation(ccd, auto=options.auto, channel=options.channel) if options.auto: scanner = model.getComponent(role="ebeam-control") val = scanner.rotation.value scanner.rotation.value = val + sem_rotation print("Added {:.2f}° to the scan rotation using SEM PC." "Rotation is now set to: {:.2f}°".format( math.degrees(sem_rotation), math.degrees(scanner.rotation.value))) else: print("Add {:.2f}° to the scan rotation using SEM PC. If negative" "subtract this value from the scan rotation.".format( math.degrees(sem_rotation))) except Exception as exp: logging.error("%s", exp, exc_info=True) return 128 return 0
def test_no_running_backend(self): # check if there is no running backend backend_status = driver.get_backend_status() self.assertIn(backend_status, [driver.BACKEND_STOPPED, driver.BACKEND_DEAD]) # run enzel test.start_backend(ENZEL_CONFIG) # now check if the role is enzel role = model.getMicroscope().role self.assertEqual(role, "enzel")
def setUpClass(cls): if TEST_NOHW: test.start_backend(FASTEM_CONFIG) elif driver.get_backend_status() != driver.BACKEND_RUNNING: raise IOError("Backend controlling a real hardware should be started before running this test case") cls.ebeam = model.getComponent(role="e-beam") cls.efocuser = model.getComponent(role="ebeam-focus") cls.sed = model.getComponent(role="se-detector") cls.stage = model.getComponent(role="stage") cls.stage.reference({"x", "y"}).result()
def setUpClass(cls): if driver.get_backend_status() == driver.BACKEND_RUNNING: logging.info("A running backend is already found, skipping tests") cls.backend_was_running = True return # run the backend as a daemon # we cannot run it normally as the child would also think he's in a unittest cmd = ODEMISD_CMD + ODEMISD_ARG + [SECOM_CONFIG] ret = subprocess.call(cmd) if ret != 0: logging.error("Failed starting backend with '%s'", cmd) time.sleep(1) # time to start
def run_backend(config): """ Run the backend based on the passed config yaml file. config (str): path to the microscope config file raises: LookupError: if a backend is already running IOError: if backend failed to start """ if driver.get_backend_status() in (driver.BACKEND_RUNNING, driver.BACKEND_STARTING): raise LookupError("A running backend is already found") logging.info("Starting backend with config file '%s'", config) # run the backend as a daemon # we cannot run it normally as the child would also think he's in a unittest cmd = ODEMISD_CMD + ODEMISD_ARG + [config] ret = subprocess.call(cmd, preexec_fn=setlimits) if ret != 0: raise IOError("Failed starting backend with '%s' (returned %d)" % (cmd, ret)) time.sleep(3) # give some time to the backend to start a little bit timeout = 30 # s timeout end = time.time() + timeout while time.time() < end: status = driver.get_backend_status() if status == driver.BACKEND_STARTING: logging.info("Backend is starting...") time.sleep(1) else: break else: raise IOError("Backend still starting after %d s" % (timeout, )) if status != driver.BACKEND_RUNNING: raise IOError("Backend failed to start, now %s" % status)
def setUp(self): # reset the logging (because otherwise it accumulates) if logging.root: del logging.root.handlers[:] if driver.get_backend_status() == driver.BACKEND_RUNNING: self.skipTest("Running backend found") # run the backend as a daemon # we cannot run it normally as the child would also think he's in a unittest cmd = ODEMISD_CMD + ODEMISD_ARG + [SECOM_CONFIG] ret = subprocess.call(cmd) self.assertEqual(ret, 0, "trying to run '%s'" % cmd) time.sleep(1) # time to start
def main(args): """ Handles the command line arguments args is the list of arguments passed return (int): value to return to the OS as program exit code """ # arguments handling parser = argparse.ArgumentParser() parser.add_argument("--role", dest="role", metavar="<component>", help="display and update an image on the screen") parser.add_argument("--gridsize", dest="gridsize", nargs=2, metavar="<gridsize>", type=int, default=None, help="size of the grid of spots in x y, default 14 14") parser.add_argument("--log-level", dest="loglev", metavar="<level>", type=int, choices=[0, 1, 2], default=0, help="set verbosity level (0-2, default = 0)") options = parser.parse_args(args[1:]) # Set up logging before everything else loglev_names = [logging.WARNING, logging.INFO, logging.DEBUG] loglev = loglev_names[min(len(loglev_names) - 1, options.loglev)] # change the log format to be more descriptive handler = logging.StreamHandler() logging.getLogger().setLevel(loglev) handler.setFormatter( logging.Formatter( '%(asctime)s (%(module)s) %(levelname)s: %(message)s')) logging.getLogger().addHandler(handler) if options.role: if get_backend_status() != BACKEND_RUNNING: raise ValueError( "Backend is not running while role command is specified.") ccd = model.getComponent(role=options.role) live_display(ccd, ccd.data, kill_ccd=False, gridsize=options.gridsize) else: ccd = ueye.Camera("camera", "ccd", device=None) ccd.SetFrameRate(2) live_display(ccd, ccd.data, gridsize=options.gridsize) return 0
def setUpClass(cls): if TEST_NOHW: test.start_backend(FASTEM_CONFIG_ASM) elif driver.get_backend_status() != driver.BACKEND_RUNNING: raise IOError("Backend controlling a real hardware should be started before running this test case") # get the hardware components cls.asm = model.getComponent(role="asm") cls.mppc = model.getComponent(role="mppc") cls.multibeam = model.getComponent(role="multibeam") cls.descanner = model.getComponent(role="descanner") cls.stage = model.getComponent( role="stage") # TODO replace with stage-scan when ROA conversion method available cls.stage.reference({"x", "y"}).result()
def start_backend(config): """ Start the backend config (str): path to the microscope config file raises: LookupError: if a backend is already running IOError: if backend failed to start """ if driver.get_backend_status() in (driver.BACKEND_RUNNING, driver.BACKEND_STARTING): raise LookupError("A running backend is already found") logging.info("Starting backend with config file '%s'", config) # run the backend as a daemon # we cannot run it normally as the child would also think he's in a unittest cmd = ODEMISD_CMD + ODEMISD_ARG + [config] ret = subprocess.call(cmd, preexec_fn=setlimits) if ret != 0: raise IOError("Failed starting backend with '%s' (returned %d)" % (cmd, ret)) time.sleep(3) # give some time to the backend to start a little bit timeout = 30 # s timeout end = time.time() + timeout while time.time() < end: status = driver.get_backend_status() if status == driver.BACKEND_STARTING: logging.info("Backend is starting...") time.sleep(1) else: break else: raise IOError("Backend still starting after %d s" % (timeout,)) if status != driver.BACKEND_RUNNING: raise IOError("Backend failed to start, now %s" % status)
def test_speedUpPyroConnect(self): need_stop = False if driver.get_backend_status() != driver.BACKEND_RUNNING: need_stop = True cmd = ODEMISD_CMD + ODEMISD_ARG + [SECOM_CONFIG] ret = subprocess.call(cmd) if ret != 0: logging.error("Failed starting backend with '%s'", cmd) time.sleep(1) # time to start else: model._components._microscope = None # force reset of the microscope for next connection speedUpPyroConnect(model.getMicroscope()) if need_stop: cmd = ODEMISD_CMD + ["--kill"] subprocess.call(cmd)
def setUpClass(cls): if TEST_NOHW: test.start_backend(FASTEM_CONFIG_ASM) elif driver.get_backend_status() != driver.BACKEND_RUNNING: raise IOError("Backend controlling a real hardware should be started before running this test case") # Get the hardware components from the simulators or hardware cls.scanner = model.getComponent(role='e-beam') cls.asm = model.getComponent(role="asm") cls.mppc = model.getComponent(role="mppc") cls.multibeam = model.getComponent(role="multibeam") cls.descanner = model.getComponent(role="descanner") cls.stage = model.getComponent( role="stage") # TODO replace with stage-scan when ROA conversion method available cls.ccd = model.getComponent(role="diagnostic-ccd") cls.beamshift = model.getComponent(role="ebeam-shift") cls.lens = model.getComponent(role="lens") cls.beamshift.shift.value = (0, 0) cls.stage.reference({"x", "y"}).result()
def setUpClass(cls): if driver.get_backend_status() == driver.BACKEND_RUNNING: logging.info("A running backend is already found, skipping tests") cls.backend_was_running = True return # run the backend as a daemon # we cannot run it normally as the child would also think he's in a unittest cmd = ODEMISD_CMD + ODEMISD_ARG + [SECOM_LENS_CONFIG] ret = subprocess.call(cmd) if ret != 0: logging.error("Failed starting backend with '%s'", cmd) time.sleep(1) # time to start # find components by their role cls.ebeam = model.getComponent(role="e-beam") cls.sed = model.getComponent(role="se-detector") cls.ccd = model.getComponent(role="ccd") cls.light = model.getComponent(role="light") cls.light_filter = model.getComponent(role="filter")
def setUpClass(cls): if driver.get_backend_status() == driver.BACKEND_RUNNING: logging.info("A running backend is already found, skipping tests") cls.backend_was_running = True return # run the backend as a daemon # we cannot run it normally as the child would also think he's in a unittest cmd = ODEMISD_CMD + ODEMISD_ARG + [SECOM_CONFIG] ret = subprocess.call(cmd) if ret != 0: logging.error("Failed starting backend with '%s'", cmd) time.sleep(1) # time to start # create some streams connected to the backend cls.microscope = model.getMicroscope() for comp in model.getComponents(): if comp.role == "ccd": cls.ccd = comp elif comp.role == "spectrometer": cls.spec = comp elif comp.role == "e-beam": cls.ebeam = comp elif comp.role == "se-detector": cls.sed = comp elif comp.role == "light": cls.light = comp elif comp.role == "filter": cls.light_filter = comp s1 = stream.FluoStream("fluo1", cls.ccd, cls.ccd.data, cls.light, cls.light_filter) s2 = stream.FluoStream("fluo2", cls.ccd, cls.ccd.data, cls.light, cls.light_filter) s2.excitation.value = s2.excitation.range[1] s3 = stream.BrightfieldStream("bf", cls.ccd, cls.ccd.data, cls.light) cls.streams = [s1, s2, s3]
def setUpClass(cls): if TEST_NOHW: test.start_backend(FASTEM_CONFIG_ASM) elif driver.get_backend_status() != driver.BACKEND_RUNNING: raise IOError("Backend controlling a real hardware should be started before running this test case") # get the hardware components cls.scanner = model.getComponent(role='e-beam') cls.asm = model.getComponent(role="asm") cls.mppc = model.getComponent(role="mppc") cls.multibeam = model.getComponent(role="multibeam") cls.descanner = model.getComponent(role="descanner") cls.stage = model.getComponent( role="stage") # TODO replace with stage-scan when ROA conversion method available cls.ccd = model.getComponent(role="diagnostic-ccd") cls.beamshift = model.getComponent(role="ebeam-shift") cls.lens = model.getComponent(role="lens") # Normally the beamshift MD_CALIB is set when running the calibrations. # Set it here explicitly because we do not run the calibrations in these test cases. cls.beamshift.updateMetadata({model.MD_CALIB: cls.scanner.beamShiftTransformationMatrix.value}) cls.beamshift.shift.value = (0, 0) cls.stage.reference({"x", "y"}).result()
def setUpClass(cls): if driver.get_backend_status() == driver.BACKEND_RUNNING: logging.info("A running backend is already found, skipping tests") cls.backend_was_running = True return # run the backend as a daemon # we cannot run it normally as the child would also think he's in a unittest cmd = ODEMISD_CMD + ODEMISD_ARG + [SECOM_LENS_CONFIG] # FIXME: give an informative warning when the comedi module has not been loaded ret = subprocess.call(cmd) if ret != 0: logging.error("Failed starting backend with '%s'", cmd) time.sleep(1) # time to start # find components by their role cls.stage = model.getComponent(role="stage") cls.sem_stage = model.getComponent(role="sem-stage") cls.align = model.getComponent(role="align") cls.tmcm = model.getComponent(name="TMCM") # low level actuator
def setUpClass(cls): # make sure initially no backend is running. if driver.get_backend_status() == driver.BACKEND_RUNNING: test.stop_backend()
def main(args): """ Handles the command line arguments args is the list of arguments passed return (int): value to return to the OS as program exit code """ # arguments handling parser = argparse.ArgumentParser(prog="odemis-cli", description=odemis.__fullname__) parser.add_argument('--version', dest="version", action='store_true', help="show program's version number and exit") opt_grp = parser.add_argument_group('Options') opt_grp.add_argument("--log-level", dest="loglev", metavar="<level>", type=int, default=0, help="set verbosity level (0-2, default = 0)") opt_grp.add_argument( "--machine", dest="machine", action="store_true", default=False, help="display in a machine-friendly way (i.e., no pretty printing)") dm_grp = parser.add_argument_group('Microscope management') dm_grpe = dm_grp.add_mutually_exclusive_group() dm_grpe.add_argument("--kill", "-k", dest="kill", action="store_true", default=False, help="kill the running back-end") dm_grpe.add_argument( "--check", dest="check", action="store_true", default=False, help="check for a running back-end (only returns exit code)") dm_grpe.add_argument( "--scan", dest="scan", action="store_true", default=False, help= "scan for possible devices to connect (the back-end must be stopped)") dm_grpe.add_argument("--list", "-l", dest="list", action="store_true", default=False, help="list the components of the microscope") dm_grpe.add_argument("--list-prop", "-L", dest="listprop", metavar="<component>", help="list the properties of a component") dm_grpe.add_argument( "--set-attr", "-s", dest="setattr", nargs=3, action='append', metavar=("<component>", "<attribute>", "<value>"), help="set the attribute of a component (lists are delimited by commas," " dictionary keys are delimited by colon)") dm_grpe.add_argument("--move", "-m", dest="move", nargs=3, action='append', metavar=("<component>", "<axis>", "<distance>"), help=u"move the axis by the amount of µm.") dm_grpe.add_argument("--position", "-p", dest="position", nargs=3, action='append', metavar=("<component>", "<axis>", "<position>"), help=u"move the axis to the given position in µm.") dm_grpe.add_argument( "--stop", "-S", dest="stop", action="store_true", default=False, help="immediately stop all the actuators in all directions.") dm_grpe.add_argument( "--acquire", "-a", dest="acquire", nargs="+", metavar=("<component>", "data-flow"), help="acquire an image (default data-flow is \"data\")") dm_grp.add_argument( "--output", "-o", dest="output", help= "name of the file where the image should be saved after acquisition. The file format is derived from the extension (TIFF and HDF5 are supported)." ) dm_grpe.add_argument( "--live", dest="live", nargs="+", metavar=("<component>", "data-flow"), help= "display and update an image on the screen (default data-flow is \"data\")" ) options = parser.parse_args(args[1:]) # To allow printing unicode even with pipes ensure_output_encoding() # Cannot use the internal feature, because it doesn't support multiline if options.version: print(odemis.__fullname__ + " " + odemis.__version__ + "\n" + odemis.__copyright__ + "\n" + "Licensed under the " + odemis.__license__) return 0 # Set up logging before everything else if options.loglev < 0: parser.error("log-level must be positive.") # TODO: allow to put logging level so low that nothing is ever output loglev_names = [logging.WARNING, logging.INFO, logging.DEBUG] loglev = loglev_names[min(len(loglev_names) - 1, options.loglev)] # change the log format to be more descriptive handler = logging.StreamHandler() logging.getLogger().setLevel(loglev) handler.setFormatter( logging.Formatter( '%(asctime)s (%(module)s) %(levelname)s: %(message)s')) logging.getLogger().addHandler(handler) # anything to do? if (not options.check and not options.kill and not options.scan and not options.list and not options.stop and options.move is None and options.position is None and options.listprop is None and options.setattr is None and options.acquire is None and options.live is None): logging.error("no action specified.") return 127 if options.acquire is not None and options.output is None: logging.error("name of the output file must be specified.") return 127 logging.debug("Trying to find the backend") status = get_backend_status() if options.check: logging.info("Status of back-end is %s", status) return status_to_xtcode[status] # scan needs to have the backend stopped if options.scan: if status == BACKEND_RUNNING: logging.error("Back-end running while trying to scan for devices") return 127 try: return scan() except Exception: logging.exception("Unexpected error while performing scan.") return 127 # check if there is already a backend running if status == BACKEND_STOPPED: logging.error("No running back-end") return 127 elif status == BACKEND_DEAD: logging.error("Back-end appears to be non-responsive.") return 127 try: if options.kill: return kill_backend() logging.debug("Executing the actions") if options.list: return list_components(pretty=not options.machine) elif options.listprop is not None: # Speed up is only worthy if many VAs are accessed odemis.util.driver.speedUpPyroConnect(model.getMicroscope()) return list_properties(options.listprop, pretty=not options.machine) elif options.setattr is not None: for c, a, v in options.setattr: ret = set_attr(c, a, v) if ret != 0: return ret elif options.position is not None: for c, a, d in options.position: ret = move_abs(c, a, d) # TODO warn if same axis multiple times if ret != 0: return ret time.sleep(0.5) elif options.move is not None: for c, a, d in options.move: ret = move(c, a, d) # TODO move commands to the same actuator should be agglomerated if ret != 0: return ret time.sleep(0.5) # wait a bit for the futures to close nicely elif options.stop: return stop_move() elif options.acquire is not None: component = options.acquire[0] if len(options.acquire) == 1: dataflows = ["data"] else: dataflows = options.acquire[1:] filename = options.output.decode(sys.getfilesystemencoding()) return acquire(component, dataflows, filename) elif options.live is not None: component = options.live[0] if len(options.live) == 1: dataflow = "data" elif len(options.live) == 2: dataflow = options.acquire[2] else: logging.error("live command accepts only one data-flow") return 127 return live_display(component, dataflow) except: logging.exception("Unexpected error while performing action.") return 127 return 0
def main(args): """ Contains the console handling code for the daemon args is the list of arguments passed return (int): value to return to the OS as program exit code """ #print args # arguments handling parser = argparse.ArgumentParser(description=odemis.__fullname__) parser.add_argument('--version', dest="version", action='store_true', help="show program's version number and exit") dm_grp = parser.add_argument_group('Daemon management') dm_grpe = dm_grp.add_mutually_exclusive_group() dm_grpe.add_argument("--kill", "-k", dest="kill", action="store_true", default=False, help="Kill the running back-end") dm_grpe.add_argument( "--check", dest="check", action="store_true", default=False, help="Check for a running back-end (only returns exit code)") dm_grpe.add_argument("--daemonize", "-D", action="store_true", dest="daemon", default=False, help="Daemonize the back-end") opt_grp = parser.add_argument_group('Options') opt_grp.add_argument( '--validate', dest="validate", action="store_true", default=False, help="Validate the microscope description file and exit") dm_grpe.add_argument( "--debug", action="store_true", dest="debug", default=False, help="Activate debug mode, where everything runs in one process") opt_grp.add_argument("--log-level", dest="loglev", metavar="LEVEL", type=int, default=0, help="Set verbosity level (0-2, default = 0)") opt_grp.add_argument( "--log-target", dest="logtarget", metavar="{auto,stderr,filename}", default="auto", help="Specify the log target (auto, stderr, filename)") # The settings file is opened here because root privileges are dropped at some point after # the initialization. opt_grp.add_argument( "--settings", dest='settings', type=argparse.FileType('a+'), help="Path to the settings file " "(stores values of persistent properties and metadata). " "Default is %s, if writable." % DEFAULT_SETTINGS_FILE) parser.add_argument( "model", metavar="file.odm.yaml", nargs='?', type=open, help="Microscope model instantiation file (*.odm.yaml)") options = parser.parse_args(args[1:]) # Cannot use the internal feature, because it doesn't support multiline if options.version: print(odemis.__fullname__ + " " + odemis.__version__ + "\n" + odemis.__copyright__ + "\n" + "Licensed under the " + odemis.__license__) return 0 # Set up logging before everything else if options.loglev < 0: parser.error("log-level must be positive.") loglev_names = [logging.WARNING, logging.INFO, logging.DEBUG] loglev = loglev_names[min(len(loglev_names) - 1, options.loglev)] # auto = {odemis.log if daemon, stderr otherwise} if options.logtarget == "auto": # default to SysLogHandler ? if options.daemon: options.logtarget = "odemis.log" else: options.logtarget = "stderr" if options.logtarget == "stderr": handler = logging.StreamHandler() else: if sys.platform.startswith('linux'): # On Linux, we use logrotate, so nothing much to do handler = WatchedFileHandler(options.logtarget) else: # Rotate the log, with max 5*50Mb used. # Note: we used to rely on RotatingFileHandler, but due to multi- # processes, it would be rotated multiple times every time it reached the # limit. So now, just do it at startup, and hope it doesn't reach huge # size in one run. rotateLog(options.logtarget, maxBytes=50 * (2**20), backupCount=5) handler = FileHandler(options.logtarget) logging.getLogger().setLevel(loglev) handler.setFormatter( logging.Formatter( "%(asctime)s\t%(levelname)s\t%(module)s:%(lineno)d:\t%(message)s")) logging.getLogger().addHandler(handler) if loglev <= logging.DEBUG: # Activate also Pyro logging # TODO: options.logtarget pyrolog = logging.getLogger("Pyro4") pyrolog.setLevel(min(pyrolog.getEffectiveLevel(), logging.INFO)) # Useful to debug cases of multiple conflicting installations logging.info("Starting Odemis back-end v%s (from %s) using Python %d.%d", odemis.__version__, __file__, sys.version_info[0], sys.version_info[1]) if options.validate and (options.kill or options.check or options.daemon): logging.error( "Impossible to validate a model and manage the daemon simultaneously" ) return 1 # Daemon management # python-daemon is a fancy library but seems to do too many things for us. # We just need to contact the backend and see what happens status = get_backend_status() if options.check: logging.info("Status of back-end is %s", status) return status_to_xtcode[status] try: if options.kill: if status != BACKEND_RUNNING: raise IOError("No running back-end to kill") backend = model.getContainer(model.BACKEND_NAME) backend.terminate() return 0 # check if there is already a backend running if status == BACKEND_RUNNING: raise IOError("Back-end already running, cannot start a new one") if options.model is None: raise ValueError("No microscope model instantiation file provided") if options.settings is None: try: options.settings = open(DEFAULT_SETTINGS_FILE, "r+") except IOError as ex: logging.warning("%s. Will not be able to use persistent data", ex) if options.debug: cont_pol = BackendRunner.CONTAINER_ALL_IN_ONE else: cont_pol = BackendRunner.CONTAINER_SEPARATED # let's become the back-end for real runner = BackendRunner(options.model, options.settings, options.daemon, dry_run=options.validate, containement=cont_pol) runner.run() except ValueError as exp: logging.error("%s", exp) return 127 except IOError as exp: logging.error("%s", exp) return 129 except Exception: logging.exception("Unexpected error while performing action.") return 130 return 0
def main(args): """ Contains the console handling code for the daemon args is the list of arguments passed return (int): value to return to the OS as program exit code """ #print args # arguments handling parser = argparse.ArgumentParser(description=odemis.__fullname__) parser.add_argument('--version', dest="version", action='store_true', help="show program's version number and exit") dm_grp = parser.add_argument_group('Daemon management') dm_grpe = dm_grp.add_mutually_exclusive_group() dm_grpe.add_argument("--kill", "-k", dest="kill", action="store_true", default=False, help="Kill the running back-end") dm_grpe.add_argument("--check", dest="check", action="store_true", default=False, help="Check for a running back-end (only returns exit code)") dm_grpe.add_argument("--daemonize", "-D", action="store_true", dest="daemon", default=False, help="Daemonize the back-end") opt_grp = parser.add_argument_group('Options') opt_grp.add_argument('--validate', dest="validate", action="store_true", default=False, help="Validate the microscope description file and exit") dm_grpe.add_argument("--debug", action="store_true", dest="debug", default=False, help="Activate debug mode, where everything runs in one process") opt_grp.add_argument("--log-level", dest="loglev", metavar="LEVEL", type=int, default=0, help="Set verbosity level (0-2, default = 0)") opt_grp.add_argument("--log-target", dest="logtarget", metavar="{auto,stderr,filename}", default="auto", help="Specify the log target (auto, stderr, filename)") # The settings file is opened here because root privileges are dropped at some point after # the initialization. opt_grp.add_argument("--settings", dest='settings', type=argparse.FileType('a+'), help="Path to the settings file " "(stores values of persistent properties and metadata). " "Default is %s, if writable." % DEFAULT_SETTINGS_FILE) parser.add_argument("model", metavar="file.odm.yaml", nargs='?', type=open, help="Microscope model instantiation file (*.odm.yaml)") options = parser.parse_args(args[1:]) # Cannot use the internal feature, because it doesn't support multiline if options.version: print(odemis.__fullname__ + " " + odemis.__version__ + "\n" + odemis.__copyright__ + "\n" + "Licensed under the " + odemis.__license__) return 0 # Set up logging before everything else if options.loglev < 0: parser.error("log-level must be positive.") loglev_names = [logging.WARNING, logging.INFO, logging.DEBUG] loglev = loglev_names[min(len(loglev_names) - 1, options.loglev)] # auto = {odemis.log if daemon, stderr otherwise} if options.logtarget == "auto": # default to SysLogHandler ? if options.daemon: options.logtarget = "odemis.log" else: options.logtarget = "stderr" if options.logtarget == "stderr": handler = logging.StreamHandler() else: if os.sys.platform.startswith('linux'): # On Linux, we use logrotate, so nothing much to do handler = WatchedFileHandler(options.logtarget) else: # Rotate the log, with max 5*50Mb used. # Note: we used to rely on RotatingFileHandler, but due to multi- # processes, it would be rotated multiple times every time it reached the # limit. So now, just do it at startup, and hope it doesn't reach huge # size in one run. rotateLog(options.logtarget, maxBytes=50 * (2 ** 20), backupCount=5) handler = FileHandler(options.logtarget) logging.getLogger().setLevel(loglev) handler.setFormatter(logging.Formatter('%(asctime)s (%(module)s) %(levelname)s: %(message)s')) logging.getLogger().addHandler(handler) if loglev <= logging.DEBUG: # Activate also Pyro logging # TODO: options.logtarget pyrolog = logging.getLogger("Pyro4") pyrolog.setLevel(min(pyrolog.getEffectiveLevel(), logging.INFO)) # Useful to debug cases of multiple conflicting installations logging.info("Starting Odemis back-end v%s (from %s)", odemis.__version__, __file__) if options.validate and (options.kill or options.check or options.daemon): logging.error("Impossible to validate a model and manage the daemon simultaneously") return 1 # Daemon management # python-daemon is a fancy library but seems to do too many things for us. # We just need to contact the backend and see what happens status = get_backend_status() if options.check: logging.info("Status of back-end is %s", status) return status_to_xtcode[status] try: if options.kill: if status != BACKEND_RUNNING: raise IOError("No running back-end to kill") backend = model.getContainer(model.BACKEND_NAME) backend.terminate() return 0 # check if there is already a backend running if status == BACKEND_RUNNING: raise IOError("Back-end already running, cannot start a new one") if options.model is None: raise ValueError("No microscope model instantiation file provided") if options.settings is None: try: options.settings = open(DEFAULT_SETTINGS_FILE, "a+") except IOError as ex: logging.warning("%s. Will not be able to use persistent data", ex) if options.debug: cont_pol = BackendRunner.CONTAINER_ALL_IN_ONE else: cont_pol = BackendRunner.CONTAINER_SEPARATED # let's become the back-end for real runner = BackendRunner(options.model, options.settings, options.daemon, dry_run=options.validate, containement=cont_pol) runner.run() except ValueError as exp: logging.error("%s", exp) return 127 except IOError as exp: logging.error("%s", exp) return 129 except Exception: logging.exception("Unexpected error while performing action.") return 130 return 0
def main(args): """ Contains the console handling code for the daemon args is the list of arguments passed return (int): value to return to the OS as program exit code """ #print args # arguments handling parser = argparse.ArgumentParser(description=odemis.__fullname__) parser.add_argument('--version', dest="version", action='store_true', help="show program's version number and exit") dm_grp = parser.add_argument_group('Daemon management') dm_grpe = dm_grp.add_mutually_exclusive_group() dm_grpe.add_argument("--kill", "-k", dest="kill", action="store_true", default=False, help="Kill the running back-end") dm_grpe.add_argument("--check", dest="check", action="store_true", default=False, help="Check for a running back-end (only returns exit code)") dm_grpe.add_argument("--daemonize", "-D", action="store_true", dest="daemon", default=False, help="Daemonize the back-end after startup") opt_grp = parser.add_argument_group('Options') opt_grp.add_argument('--validate', dest="validate", action="store_true", default=False, help="Validate the microscope description file and exit") dm_grpe.add_argument("--debug", action="store_true", dest="debug", default=False, help="Activate debug mode, where everything runs in one process") opt_grp.add_argument("--log-level", dest="loglev", metavar="LEVEL", type=int, default=0, help="Set verbosity level (0-2, default = 0)") opt_grp.add_argument("--log-target", dest="logtarget", metavar="{auto,stderr,filename}", default="auto", help="Specify the log target (auto, stderr, filename)") parser.add_argument("model", metavar="file.odm.yaml", nargs='?', type=open, help="Microscope model instantiation file (*.odm.yaml)") options = parser.parse_args(args[1:]) # Cannot use the internal feature, because it doesn't support multiline if options.version: print (odemis.__fullname__ + " " + odemis.__version__ + "\n" + odemis.__copyright__ + "\n" + "Licensed under the " + odemis.__license__) return 0 # Set up logging before everything else if options.loglev < 0: parser.error("log-level must be positive.") loglev_names = [logging.WARNING, logging.INFO, logging.DEBUG] loglev = loglev_names[min(len(loglev_names) - 1, options.loglev)] # auto = {odemis.log if daemon, stderr otherwise} if options.logtarget == "auto": # default to SysLogHandler ? if options.daemon: # Rotate the log, with max 500Mb used handler = RotatingFileHandler("odemis.log", maxBytes=100 * (2 ** 20), backupCount=5) else: handler = logging.StreamHandler() elif options.logtarget == "stderr": handler = logging.StreamHandler() else: handler = RotatingFileHandler(options.logtarget, maxBytes=100 * (2 ** 20), backupCount=5) logging.getLogger().setLevel(loglev) handler.setFormatter(logging.Formatter('%(asctime)s (%(module)s) %(levelname)s: %(message)s')) logging.getLogger().addHandler(handler) # Useful to debug cases of multiple conflicting installations logging.debug("Starting Odemis back-end (from %s)", __file__) if options.validate and (options.kill or options.check or options.daemon): logging.error("Impossible to validate a model and manage the daemon simultaneously") return 127 # Daemon management # python-daemon is a fancy library but seems to do too many things for us. # We just need to contact the backend and see what happens status = get_backend_status() if options.kill: if status != BACKEND_RUNNING: logging.error("No running back-end to kill") return 127 try: backend = model.getContainer(model.BACKEND_NAME) backend.terminate() except: logging.error("Failed to stop the back-end") return 127 return 0 elif options.check: logging.info("Status of back-end is %s", status) return status_to_xtcode[status] # check if there is already a backend running if status == BACKEND_RUNNING: logging.error("Back-end already running, cannot start a new one") if options.model is None: logging.error("No microscope model instantiation file provided") return 127 if options.debug: #cont_pol = BackendRunner.CONTAINER_DISABLE cont_pol = BackendRunner.CONTAINER_ALL_IN_ONE else: cont_pol = BackendRunner.CONTAINER_SEPARATED # let's become the back-end for real runner = BackendRunner(options.model, options.daemon, options.validate, cont_pol) return runner.run()
def main(args): """ Parse the command line arguments and launch the Odemis daemon Args: args ([str]): The arguments passed to the executable Returns: int: Value to return to the OS as program exit code """ # arguments handling parser = argparse.ArgumentParser(prog="odemis-start", description="Odemis starter program") parser.add_argument('model_conf', nargs='?', help="Model definition (yaml) file to use") parser.add_argument( '-s', '--selector', dest='model_sel', help="Model selector command (instead of using a model definition)") parser.add_argument( '-n', '--nogui', dest='nogui', action='store_true', help="Don't launch the GUI after the back end has started") parser.add_argument('-l', '--log-target', dest='logtarget', help="Location of the back end log file") options = parser.parse_args(args[1:]) if options.model_sel and options.model_conf: raise ValueError( "Cannot have both a model selector and model definition") # Use the log level for ourselves first try: loglevel = int(odemis_config["LOGLEVEL"]) if options.logtarget: odemis_config['LOGFILE'] = options.logtarget except ValueError: loglevel = 1 odemis_config["LOGLEVEL"] = "%d" % loglevel logging.getLogger().setLevel(loglevel) # pyrolog = logging.getLogger("Pyro4") # pyrolog.setLevel(min(pyrolog.getEffectiveLevel(), logging.DEBUG)) try: # TODO: just bring the focus? # Kill GUI if an instance is already there # TODO: use psutil.process_iter() for this gui_killed = subprocess.call( ["/usr/bin/pkill", "-f", odemis_config["GUI"]]) if gui_killed == 0: logging.info("Found the GUI still running, killing it first...") status = driver.get_backend_status() if status != driver.BACKEND_RUNNING: starter = BackendStarter(odemis_config, options.nogui) # TODO: if backend running but with a different model, also restart it if status == driver.BACKEND_DEAD: logging.warning( "Back-end is not responding, will restart it...") # subprocess.call(["/usr/bin/pkill", "-f", config["BACKEND"]]) subprocess.call(["sudo", "/usr/bin/odemis-stop"]) time.sleep(3) try: if status in (driver.BACKEND_DEAD, driver.BACKEND_STOPPED): starter.show_popup("Starting Odemis back-end") # Get the model file in this order: # 1. the command line (either model selector or model file) # 2. the model file # 3. the model selector if options.model_conf: modelfile = options.model_conf elif options.model_sel: modelfile = run_model_selector(options.model_sel) elif "MODEL" in odemis_config: modelfile = odemis_config["MODEL"] if "MODEL_SELECTOR" in odemis_config: logging.warning( "Both MODEL and MODEL_SELECTOR defined in odemis.conf, using MODEL" ) elif "MODEL_SELECTOR" in odemis_config: modelfile = run_model_selector( odemis_config["MODEL_SELECTOR"]) else: raise ValueError("No microscope model specified") starter.start_backend(modelfile) if status in (driver.BACKEND_DEAD, driver.BACKEND_STOPPED, driver.BACKEND_STARTING): starter.wait_backend_is_ready() except ValueError: raise # Typically cancelled by user except IOError: starter.display_backend_log() raise except Exception as ex: # Something went very wrong (ie, back-end is blocked half-way # starting) => stop backend proc = subprocess.Popen(["sudo", "/usr/bin/odemis-stop"]) show_error_box( "Error starting Odemis back-end", "Unexpected error while starting Odemis back-end:\n" "%s\n\n" "Try starting Odemis again, and use the \"Report a " "problem\" function if the issue continues." % ex) proc.wait() raise else: logging.debug("Back-end already started, so not starting again") if not options.nogui: # Return when the GUI is done logging.info("Starting the GUI...") subprocess.check_call( ["odemis-gui", "--log-level", odemis_config["LOGLEVEL"]]) except ValueError as exp: logging.error("%s", exp) return 127 except IOError as exp: logging.error("%s", exp) return 129 except Exception: logging.exception("Unexpected error while performing action.") return 130 return 0
def main(args): """ Handles the command line arguments args is the list of arguments passed return (int): value to return to the OS as program exit code """ # arguments handling parser = argparse.ArgumentParser(prog="odemis-cli", description=odemis.__fullname__) parser.add_argument('--version', dest="version", action='store_true', help="show program's version number and exit") opt_grp = parser.add_argument_group('Options') opt_grp.add_argument("--log-level", dest="loglev", metavar="<level>", type=int, default=0, help="set verbosity level (0-2, default = 0)") opt_grp.add_argument("--machine", dest="machine", action="store_true", default=False, help="display in a machine-friendly way (i.e., no pretty printing)") dm_grp = parser.add_argument_group('Microscope management') dm_grpe = dm_grp.add_mutually_exclusive_group() dm_grpe.add_argument("--kill", "-k", dest="kill", action="store_true", default=False, help="kill the running back-end") dm_grpe.add_argument("--check", dest="check", action="store_true", default=False, help="check for a running back-end (only returns exit code)") dm_grpe.add_argument("--scan", dest="scan", action="store_true", default=False, help="scan for possible devices to connect (the back-end must be stopped)") dm_grpe.add_argument("--list", "-l", dest="list", action="store_true", default=False, help="list the components of the microscope") dm_grpe.add_argument("--list-prop", "-L", dest="listprop", metavar="<component>", help="list the properties of a component") dm_grpe.add_argument("--set-attr", "-s", dest="setattr", nargs=3, action='append', metavar=("<component>", "<attribute>", "<value>"), help="set the attribute of a component (lists are delimited by commas," " dictionary keys are delimited by colon)") dm_grpe.add_argument("--move", "-m", dest="move", nargs=3, action='append', metavar=("<component>", "<axis>", "<distance>"), help=u"move the axis by the amount of µm.") dm_grpe.add_argument("--position", "-p", dest="position", nargs=3, action='append', metavar=("<component>", "<axis>", "<position>"), help=u"move the axis to the given position in µm.") dm_grpe.add_argument("--stop", "-S", dest="stop", action="store_true", default=False, help="immediately stop all the actuators in all directions.") dm_grpe.add_argument("--acquire", "-a", dest="acquire", nargs="+", metavar=("<component>", "data-flow"), help="acquire an image (default data-flow is \"data\")") dm_grp.add_argument("--output", "-o", dest="output", help="name of the file where the image should be saved after acquisition. The file format is derived from the extension (TIFF and HDF5 are supported).") dm_grpe.add_argument("--live", dest="live", nargs="+", metavar=("<component>", "data-flow"), help="display and update an image on the screen (default data-flow is \"data\")") options = parser.parse_args(args[1:]) # To allow printing unicode even with pipes ensure_output_encoding() # Cannot use the internal feature, because it doesn't support multiline if options.version: print (odemis.__fullname__ + " " + odemis.__version__ + "\n" + odemis.__copyright__ + "\n" + "Licensed under the " + odemis.__license__) return 0 # Set up logging before everything else if options.loglev < 0: parser.error("log-level must be positive.") # TODO: allow to put logging level so low that nothing is ever output loglev_names = [logging.WARNING, logging.INFO, logging.DEBUG] loglev = loglev_names[min(len(loglev_names) - 1, options.loglev)] # change the log format to be more descriptive handler = logging.StreamHandler() logging.getLogger().setLevel(loglev) handler.setFormatter(logging.Formatter('%(asctime)s (%(module)s) %(levelname)s: %(message)s')) logging.getLogger().addHandler(handler) # anything to do? if (not options.check and not options.kill and not options.scan and not options.list and not options.stop and options.move is None and options.position is None and options.listprop is None and options.setattr is None and options.acquire is None and options.live is None): logging.error("no action specified.") return 127 if options.acquire is not None and options.output is None: logging.error("name of the output file must be specified.") return 127 logging.debug("Trying to find the backend") status = get_backend_status() if options.check: logging.info("Status of back-end is %s", status) return status_to_xtcode[status] # scan needs to have the backend stopped if options.scan: if status == BACKEND_RUNNING: logging.error("Back-end running while trying to scan for devices") return 127 try: return scan() except Exception: logging.exception("Unexpected error while performing scan.") return 127 # check if there is already a backend running if status == BACKEND_STOPPED: logging.error("No running back-end") return 127 elif status == BACKEND_DEAD: logging.error("Back-end appears to be non-responsive.") return 127 try: if options.kill: return kill_backend() logging.debug("Executing the actions") if options.list: return list_components(pretty=not options.machine) elif options.listprop is not None: # Speed up is only worthy if many VAs are accessed odemis.util.driver.speedUpPyroConnect(model.getMicroscope()) return list_properties(options.listprop, pretty=not options.machine) elif options.setattr is not None: for c, a, v in options.setattr: ret = set_attr(c, a, v) if ret != 0: return ret elif options.position is not None: for c, a, d in options.position: ret = move_abs(c, a, d) # TODO warn if same axis multiple times if ret != 0: return ret time.sleep(0.5) elif options.move is not None: for c, a, d in options.move: ret = move(c, a, d) # TODO move commands to the same actuator should be agglomerated if ret != 0: return ret time.sleep(0.5) # wait a bit for the futures to close nicely elif options.stop: return stop_move() elif options.acquire is not None: component = options.acquire[0] if len(options.acquire) == 1: dataflows = ["data"] else: dataflows = options.acquire[1:] filename = options.output.decode(sys.getfilesystemencoding()) return acquire(component, dataflows, filename) elif options.live is not None: component = options.live[0] if len(options.live) == 1: dataflow = "data" elif len(options.live) == 2: dataflow = options.acquire[2] else: logging.error("live command accepts only one data-flow") return 127 return live_display(component, dataflow) except: logging.exception("Unexpected error while performing action.") return 127 return 0
def main(args): """ Handles the command line arguments args is the list of arguments passed return (int): value to return to the OS as program exit code """ # arguments handling parser = argparse.ArgumentParser() group = parser.add_mutually_exclusive_group() group.add_argument("--role", dest="role", metavar="<component>", help="display and update an image on the screen") group.add_argument("--file", metavar="<filename>", dest="filename", help="display and update an image on the screen") parser.add_argument("--gridsize", dest="gridsize", nargs=2, metavar="<gridsize>", type=int, default=None, help="size of the grid of spots in x y, default 8 8") parser.add_argument("--magnification", dest="magnification", type=float, help="magnification (typically 40 or 50)") parser.add_argument("--log-level", dest="loglev", metavar="<level>", type=int, choices=[0, 1, 2], default=0, help="set verbosity level (0-2, default = 0)") options = parser.parse_args(args[1:]) # Set up logging before everything else loglev_names = [logging.WARNING, logging.INFO, logging.DEBUG] loglev = loglev_names[min(len(loglev_names) - 1, options.loglev)] # change the log format to be more descriptive handler = logging.StreamHandler() logging.getLogger().setLevel(loglev) handler.setFormatter( logging.Formatter( '%(asctime)s (%(module)s) %(levelname)s: %(message)s')) logging.getLogger().addHandler(handler) # Magnification: use cli input value. If none is specified, try to read out lens magnification. try: lens = model.getComponent(role="lens") lens_mag = lens.magnification.value except Exception as ex: logging.debug("Failed to read magnification from lens, ex: %s", ex) lens_mag = None if options.magnification: magnification = options.magnification if lens_mag and lens_mag != magnification: logging.warning( "Requested magnification %s differs from lens magnification %s.", magnification, lens_mag) elif lens_mag: magnification = lens_mag logging.debug( "No magnification specified, using lens magnification %s.", lens_mag) else: magnification = DEFAULT_MAGNIFICATION logging.warning("No magnification specified, falling back to %s.", magnification) pxsize = PIXEL_SIZE_SAMPLE_PLANE / magnification if options.filename: logging.info("Will process image file %s" % options.filename) converter = dataio.find_fittest_converter(options.filename, default=None, mode=os.O_RDONLY) data = converter.read_data(options.filename)[0] fakeccd = StaticCCD(options.filename, "fakeccd", data) live_display(fakeccd, fakeccd.data, pxsize, gridsize=options.gridsize) elif options.role: if get_backend_status() != BACKEND_RUNNING: raise ValueError( "Backend is not running while role command is specified.") ccd = model.getComponent(role=options.role) live_display(ccd, ccd.data, pxsize, kill_ccd=False, gridsize=options.gridsize) else: ccd = ueye.Camera("camera", "ccd", device=None) ccd.SetFrameRate(2) live_display(ccd, ccd.data, pxsize, gridsize=options.gridsize) return 0
def main(args): """ Handles the command line arguments args is the list of arguments passed return (int): value to return to the OS as program exit code """ # arguments handling parser = argparse.ArgumentParser(prog="odemis-cli", description=odemis.__fullname__) parser.add_argument('--version', dest="version", action='store_true', help="show program's version number and exit") opt_grp = parser.add_argument_group('Options') opt_grp.add_argument("--log-level", dest="loglev", metavar="<level>", type=int, default=0, help="set verbosity level (0-2, default = 0)") opt_grp.add_argument("--machine", dest="machine", action="store_true", default=False, help="display in a machine-friendly way (i.e., no pretty printing)") dm_grp = parser.add_argument_group('Microscope management') dm_grpe = dm_grp.add_mutually_exclusive_group() dm_grpe.add_argument("--kill", "-k", dest="kill", action="store_true", default=False, help="kill the running back-end") dm_grpe.add_argument("--check", dest="check", action="store_true", default=False, help="check for a running back-end (only returns exit code)") dm_grpe.add_argument("--scan", dest="scan", const=True, default=False, nargs="?", metavar="class", help="scan for possible devices to connect (the " "back-end must be stopped). Optionally class name of " "a specific hardware to scan can be specified.") dm_grpe.add_argument("--list", "-l", dest="list", action="store_true", default=False, help="list the components of the microscope") dm_grpe.add_argument("--list-prop", "-L", dest="listprop", metavar="<component>", help="list the properties of a component. Use '*' to list all the components.") dm_grpe.add_argument("--set-attr", "-s", dest="setattr", nargs="+", action='append', metavar=("<component>", "<attribute>"), help="set the attribute of a component. First the component name, " "then a series of attribute/value to be set. " "(Lists are delimited by commas, dictionary keys are delimited by colon)") dm_grpe.add_argument("--update-metadata", "-u", dest="upmd", nargs="+", action='append', metavar=("<component>", "<key>"), help="update the metadata entry of a component. First the component name, " "then a series of key/value to be set. " "(Lists are delimited by commas)") dm_grpe.add_argument("--move", "-m", dest="move", nargs=3, action='append', metavar=("<component>", "<axis>", "<distance>"), help=u"move the axis by the given amount (µm for distances).") dm_grpe.add_argument("--position", "-p", dest="position", nargs=3, action='append', metavar=("<component>", "<axis>", "<position>"), help=u"move the axis to the given position.") dm_grp.add_argument("--big-distance", dest="bigdist", action="store_true", default=False, help=u"flag needed to allow any move bigger than 10 mm.") dm_grpe.add_argument("--reference", dest="reference", nargs=2, action="append", metavar=("<component>", "<axis>"), help="runs the referencing procedure for the given axis.") dm_grpe.add_argument("--stop", "-S", dest="stop", action="store_true", default=False, help="immediately stop all the actuators in all directions.") dm_grpe.add_argument("--acquire", "-a", dest="acquire", nargs="+", metavar=("<component>", "data-flow"), help="acquire an image (default data-flow is \"data\")") dm_grp.add_argument("--output", "-o", dest="output", help="name of the file where the image should be saved " "after acquisition. The file format is derived from the extension " "(TIFF and HDF5 are supported).") dm_grpe.add_argument("--live", dest="live", nargs="+", metavar=("<component>", "data-flow"), help="display and update an image on the screen (default data-flow is \"data\")") options = parser.parse_args(args[1:]) # To allow printing unicode even with pipes ensure_output_encoding() # Cannot use the internal feature, because it doesn't support multiline if options.version: print (odemis.__fullname__ + " " + odemis.__version__ + "\n" + odemis.__copyright__ + "\n" + "Licensed under the " + odemis.__license__) return 0 # Set up logging before everything else if options.loglev < 0: logging.error("Log-level must be positive.") return 127 # TODO: allow to put logging level so low that nothing is ever output loglev_names = [logging.WARNING, logging.INFO, logging.DEBUG] loglev = loglev_names[min(len(loglev_names) - 1, options.loglev)] # change the log format to be more descriptive handler = logging.StreamHandler() logging.getLogger().setLevel(loglev) handler.setFormatter(logging.Formatter('%(asctime)s (%(module)s) %(levelname)s: %(message)s')) logging.getLogger().addHandler(handler) if loglev <= logging.DEBUG: # Activate also Pyro logging # TODO: options.logtarget pyrolog = logging.getLogger("Pyro4") pyrolog.setLevel(min(pyrolog.getEffectiveLevel(), logging.INFO)) # anything to do? if not any((options.check, options.kill, options.scan, options.list, options.stop, options.move, options.position, options.reference, options.listprop, options.setattr, options.upmd, options.acquire, options.live)): logging.error("No action specified.") return 127 if options.acquire is not None and options.output is None: logging.error("Name of the output file must be specified.") return 127 if options.setattr: for l in options.setattr: if len(l) < 3 or (len(l) - 1) % 2 == 1: logging.error("--set-attr expects component name and then a even number of arguments") if options.upmd: for l in options.upmd: if len(l) < 3 or (len(l) - 1) % 2 == 1: logging.error("--update-metadata expects component name and then a even number of arguments") logging.debug("Trying to find the backend") status = get_backend_status() if options.check: logging.info("Status of back-end is %s", status) return status_to_xtcode[status] try: # scan needs to have the backend stopped if options.scan: if status == BACKEND_RUNNING: raise ValueError("Back-end running while trying to scan for devices") if isinstance(options.scan, basestring): scan(options.scan) else: scan() return 0 # check if there is already a backend running if status == BACKEND_STOPPED: raise IOError("No running back-end") elif status == BACKEND_DEAD: raise IOError("Back-end appears to be non-responsive.") logging.debug("Executing the actions") if options.kill: kill_backend() elif options.list: list_components(pretty=not options.machine) elif options.listprop is not None: list_properties(options.listprop, pretty=not options.machine) elif options.setattr is not None: for l in options.setattr: # C A B E F => C, {A: B, E: F} c = l[0] avs = dict(zip(l[1::2], l[2::2])) set_attr(c, avs) elif options.upmd is not None: for l in options.upmd: c = l[0] kvs = dict(zip(l[1::2], l[2::2])) update_metadata(c, kvs) # TODO: catch keyboard interrupt and stop the moves elif options.reference is not None: for c, a in options.reference: reference(c, a) elif options.position is not None: moves = merge_moves(options.position) for c, m in moves.items(): move_abs(c, m, check_distance=(not options.bigdist)) elif options.move is not None: moves = merge_moves(options.move) for c, m in moves.items(): move(c, m, check_distance=(not options.bigdist)) elif options.stop: stop_move() elif options.acquire is not None: component = options.acquire[0] if len(options.acquire) == 1: dataflows = ["data"] else: dataflows = options.acquire[1:] filename = options.output.decode(sys.getfilesystemencoding()) acquire(component, dataflows, filename) elif options.live is not None: component = options.live[0] if len(options.live) == 1: dataflow = "data" elif len(options.live) == 2: dataflow = options.acquire[2] else: raise ValueError("Live command accepts only one data-flow") live_display(component, dataflow) except KeyboardInterrupt: logging.info("Interrupted before the end of the execution") return 1 except ValueError as exp: logging.error("%s", exp) return 127 except IOError as exp: logging.error("%s", exp) return 129 except Exception: logging.exception("Unexpected error while performing action.") return 130 return 0
def main(args): """ Handles the command line arguments args is the list of arguments passed return (int): value to return to the OS as program exit code """ # arguments handling parser = argparse.ArgumentParser(prog="odemis-cli", description=odemis.__fullname__) parser.add_argument('--version', dest="version", action='store_true', help="show program's version number and exit") opt_grp = parser.add_argument_group('Options') opt_grp.add_argument("--log-level", dest="loglev", metavar="<level>", type=int, default=0, help="set verbosity level (0-2, default = 0)") opt_grp.add_argument("--machine", dest="machine", action="store_true", default=False, help="display in a machine-friendly way (i.e., no pretty printing)") dm_grp = parser.add_argument_group('Microscope management') dm_grpe = dm_grp.add_mutually_exclusive_group() dm_grpe.add_argument("--kill", "-k", dest="kill", action="store_true", default=False, help="kill the running back-end") dm_grpe.add_argument("--check", dest="check", action="store_true", default=False, help="check for a running back-end (only returns exit code)") dm_grpe.add_argument("--scan", dest="scan", const=True, default=False, nargs="?", metavar="class", help="scan for possible devices to connect (the " "back-end must be stopped). Optionally class name of " "a specific hardware to scan can be specified.") dm_grpe.add_argument("--list", "-l", dest="list", action="store_true", default=False, help="list the components of the microscope") dm_grpe.add_argument("--list-prop", "-L", dest="listprop", metavar="<component>", help="list the properties of a component. Use '*' to list all the components.") dm_grpe.add_argument("--set-attr", "-s", dest="setattr", nargs="+", action='append', metavar=("<component>", "<attribute>"), help="set the attribute of a component. First the component name, " "then a series of attribute/value to be set. " "(Lists are delimited by commas, dictionary keys are delimited by colon)") dm_grpe.add_argument("--update-metadata", "-u", dest="upmd", nargs="+", action='append', metavar=("<component>", "<key>"), help="update the metadata entry of a component. First the component name, " "then a series of key/value to be set. " "(Lists are delimited by commas)") dm_grpe.add_argument("--move", "-m", dest="move", nargs=3, action='append', metavar=("<component>", "<axis>", "<distance>"), help=u"move the axis by the given amount (µm for distances).") dm_grpe.add_argument("--position", "-p", dest="position", nargs=3, action='append', metavar=("<component>", "<axis>", "<position>"), help=u"move the axis to the given position.") dm_grp.add_argument("--big-distance", dest="bigdist", action="store_true", default=False, help=u"flag needed to allow any move bigger than 10 mm.") dm_grpe.add_argument("--reference", dest="reference", nargs=2, action="append", metavar=("<component>", "<axis>"), help="runs the referencing procedure for the given axis.") dm_grpe.add_argument("--stop", "-S", dest="stop", action="store_true", default=False, help="immediately stop all the actuators in all directions.") dm_grpe.add_argument("--acquire", "-a", dest="acquire", nargs="+", metavar=("<component>", "data-flow"), help="acquire an image (default data-flow is \"data\")") dm_grp.add_argument("--output", "-o", dest="output", help="name of the file where the image should be saved " "after acquisition. The file format is derived from the extension " "(TIFF and HDF5 are supported).") dm_grpe.add_argument("--live", dest="live", nargs="+", metavar=("<component>", "data-flow"), help="display and update an image on the screen (default data-flow is \"data\")") # To allow printing unicode even with pipes ensure_output_encoding() options = parser.parse_args(args[1:]) # Cannot use the internal feature, because it doesn't support multiline if options.version: print(odemis.__fullname__ + " " + odemis.__version__ + "\n" + odemis.__copyright__ + "\n" + "Licensed under the " + odemis.__license__) return 0 # Set up logging before everything else if options.loglev < 0: logging.error("Log-level must be positive.") return 127 # TODO: allow to put logging level so low that nothing is ever output loglev_names = [logging.WARNING, logging.INFO, logging.DEBUG] loglev = loglev_names[min(len(loglev_names) - 1, options.loglev)] # change the log format to be more descriptive handler = logging.StreamHandler() logging.getLogger().setLevel(loglev) handler.setFormatter(logging.Formatter('%(asctime)s (%(module)s) %(levelname)s: %(message)s')) logging.getLogger().addHandler(handler) if loglev <= logging.DEBUG: # Activate also Pyro logging # TODO: options.logtarget pyrolog = logging.getLogger("Pyro4") pyrolog.setLevel(min(pyrolog.getEffectiveLevel(), logging.INFO)) # anything to do? if not any((options.check, options.kill, options.scan, options.list, options.stop, options.move, options.position, options.reference, options.listprop, options.setattr, options.upmd, options.acquire, options.live)): logging.error("No action specified.") return 127 if options.acquire is not None and options.output is None: logging.error("Name of the output file must be specified.") return 127 if options.setattr: for l in options.setattr: if len(l) < 3 or (len(l) - 1) % 2 == 1: logging.error("--set-attr expects component name and then a even number of arguments") return 127 if options.upmd: for l in options.upmd: if len(l) < 3 or (len(l) - 1) % 2 == 1: logging.error("--update-metadata expects component name and then a even number of arguments") return 127 logging.debug("Trying to find the backend") status = get_backend_status() if options.check: logging.info("Status of back-end is %s", status) return status_to_xtcode[status] try: # scan needs to have the backend stopped if options.scan: if status == BACKEND_RUNNING: raise ValueError("Back-end running while trying to scan for devices") if isinstance(options.scan, basestring): scan(options.scan) else: scan() return 0 # check if there is already a backend running if status == BACKEND_STOPPED: raise IOError("No running back-end") elif status == BACKEND_DEAD: raise IOError("Back-end appears to be non-responsive.") logging.debug("Executing the actions") if options.kill: kill_backend() elif options.list: list_components(pretty=not options.machine) elif options.listprop is not None: list_properties(options.listprop, pretty=not options.machine) elif options.setattr is not None: for l in options.setattr: # C A B E F => C, {A: B, E: F} c = l[0] avs = dict(zip(l[1::2], l[2::2])) set_attr(c, avs) elif options.upmd is not None: for l in options.upmd: c = l[0] kvs = dict(zip(l[1::2], l[2::2])) update_metadata(c, kvs) # TODO: catch keyboard interrupt and stop the moves elif options.reference is not None: for c, a in options.reference: reference(c, a) elif options.position is not None: moves = merge_moves(options.position) for c, m in moves.items(): move_abs(c, m, check_distance=(not options.bigdist)) elif options.move is not None: moves = merge_moves(options.move) for c, m in moves.items(): move(c, m, check_distance=(not options.bigdist)) elif options.stop: stop_move() elif options.acquire is not None: component = options.acquire[0] if len(options.acquire) == 1: dataflows = ["data"] else: dataflows = options.acquire[1:] if isinstance(options.output, unicode): # python3 filename = options.output else: # python2 filename = options.output.decode(sys.getfilesystemencoding()) acquire(component, dataflows, filename) elif options.live is not None: component = options.live[0] if len(options.live) == 1: dataflow = "data" elif len(options.live) == 2: dataflow = options.acquire[2] else: raise ValueError("Live command accepts only one data-flow") live_display(component, dataflow) except KeyboardInterrupt: logging.info("Interrupted before the end of the execution") return 1 except ValueError as exp: logging.error("%s", exp) return 127 except IOError as exp: logging.error("%s", exp) return 129 except Exception: logging.exception("Unexpected error while performing action.") return 130 return 0
def tearDownClass(cls): # turn off everything when the testing finished. if driver.get_backend_status() == driver.BACKEND_RUNNING: test.stop_backend()