From 825f784b782303f3400dbb879995ee5c09863da6 Mon Sep 17 00:00:00 2001 From: Yudong Sun Date: Tue, 15 Mar 2022 16:17:54 +0100 Subject: [PATCH 01/13] Reorganize --- INSTALL.md => archive/INSTALL.md | 0 .../conda-environment.32bit.yml | 0 github_setup.sh => archive/github_setup.sh | 0 requirements.txt => archive/requirements.txt | 0 todo.txt => archive/todo.txt | 0 windows-setup.txt => archive/windows-setup.txt | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename INSTALL.md => archive/INSTALL.md (100%) rename conda-environment.32bit.yml => archive/conda-environment.32bit.yml (100%) rename github_setup.sh => archive/github_setup.sh (100%) rename requirements.txt => archive/requirements.txt (100%) rename todo.txt => archive/todo.txt (100%) rename windows-setup.txt => archive/windows-setup.txt (100%) diff --git a/INSTALL.md b/archive/INSTALL.md similarity index 100% rename from INSTALL.md rename to archive/INSTALL.md diff --git a/conda-environment.32bit.yml b/archive/conda-environment.32bit.yml similarity index 100% rename from conda-environment.32bit.yml rename to archive/conda-environment.32bit.yml diff --git a/github_setup.sh b/archive/github_setup.sh similarity index 100% rename from github_setup.sh rename to archive/github_setup.sh diff --git a/requirements.txt b/archive/requirements.txt similarity index 100% rename from requirements.txt rename to archive/requirements.txt diff --git a/todo.txt b/archive/todo.txt similarity index 100% rename from todo.txt rename to archive/todo.txt diff --git a/windows-setup.txt b/archive/windows-setup.txt similarity index 100% rename from windows-setup.txt rename to archive/windows-setup.txt From c66c8a3de1fbe0df70e11a0d20f62b551607078c Mon Sep 17 00:00:00 2001 From: Yudong Sun Date: Tue, 15 Mar 2022 18:06:38 +0100 Subject: [PATCH 02/13] cli app setup --- launch_m2.bat | 3 + src/cli-app/cli.py | 75 ++++++++++++++++++++++ src/cli-app/m2-app.py | 117 ++++++++++++++++++++++++++++++++++ src/{gui => cli-app}/plan.txt | 0 4 files changed, 195 insertions(+) create mode 100644 launch_m2.bat create mode 100644 src/cli-app/cli.py create mode 100644 src/cli-app/m2-app.py rename src/{gui => cli-app}/plan.txt (100%) diff --git a/launch_m2.bat b/launch_m2.bat new file mode 100644 index 0000000..31e3b58 --- /dev/null +++ b/launch_m2.bat @@ -0,0 +1,3 @@ +@echo off +call %ProgramData%\Anaconda3\Scripts\activate.bat nanosquared +python ./src/cli-app/m2-app.py \ No newline at end of file diff --git a/src/cli-app/cli.py b/src/cli-app/cli.py new file mode 100644 index 0000000..35098bf --- /dev/null +++ b/src/cli-app/cli.py @@ -0,0 +1,75 @@ +import os, sys +import distutils.util + +# https://stackoverflow.com/a/287944/3211506 +class bcolors: + HEADER = '\033[95m' + OKBLUE = '\033[94m' + OKCYAN = '\033[96m' + OKGREEN = '\033[92m' + WARNING = '\033[93m' + FAIL = '\033[91m' + ENDC = '\033[0m' + BOLD = '\033[1m' + UNDERLINE = '\033[4m' + +class CLI(): + COLORS = bcolors + GAP = f"\033[95m==>>>\033[0m " + + def __init__(self) -> None: + pass + + @staticmethod + def clear_screen(): + os.system('cls' if os.name == 'nt' else 'clear') + + @staticmethod + def print_sep(): + print("======================") + + @staticmethod + def options(question, options, default): + assert (default in options) or (default is None), "ERROR: default not in options" + + prompt = f"[Default = {default}]" if default is not None else "" + + while True: + try: + resp = input(CLI.GAP + question + " " + prompt + " > ").strip().lower() + if default is not None and resp == '': + return default + else: + if resp not in options: + raise ValueError + return resp + except ValueError: + print("ERROR: Please respond with one of the options.") + except EOFError: + print("Encountered EOF, exiting...") + sys.exit() + + # https://gist.github.com/garrettdreyfus/8153571?permalink_comment_id=3263216#gistcomment-3263216 + @staticmethod + def whats_it_gonna_be_boy(question, default='no') -> bool: + if default is None: + prompt = " [y/n]" + elif default == 'yes': + prompt = " [Y/n]" + elif default == 'no': + prompt = " [y/N]" + else: + raise ValueError(f"Unknown setting '{default}' for default.") + + while True: + try: + resp = input(CLI.GAP + question + prompt + " > ").strip().lower() + if default is not None and resp == '': + return (default == 'yes') + else: + return bool(distutils.util.strtobool(resp)) + except ValueError: + print("ERROR: Please respond with 'yes' or 'no' (or 'y' or 'n').\n") + except EOFError: + print("Encountered EOF, exiting...") + sys.exit() \ No newline at end of file diff --git a/src/cli-app/m2-app.py b/src/cli-app/m2-app.py new file mode 100644 index 0000000..850ae35 --- /dev/null +++ b/src/cli-app/m2-app.py @@ -0,0 +1,117 @@ +from email.policy import default +import os, sys +from matplotlib.style import available +import serial.tools.list_ports + +from cli import CLI + +try: + import nanosquared +except ModuleNotFoundError as e: + base_dir = os.path.dirname(os.path.realpath(__file__)) + root_dir = os.path.abspath(os.path.join(base_dir, "..")) + sys.path.insert(0, root_dir) + + import nanosquared + +# https://asciiflow.com/ + +def setup(): + while True: + print(f"{CLI.COLORS.HEADER}==== SETUP ===={CLI.COLORS.ENDC}") + print("\nIn the following questions, pressing Enter will enter the default option, which is indicated in capital letters.") + print("\nIn development mode, no actual devices are required.\nAll function calls will therefore be simulated.") + devMode = CLI.whats_it_gonna_be_boy("Run in development mode?") + + print("\nNanoScan and WinCamD Beam Profilers are supported. \nyes = NanoScan, no = WinCamD") + useNanoScan = CLI.whats_it_gonna_be_boy("Use NanoScan?", default = 'yes') + + ports = serial.tools.list_ports.comports() + + print("\nAvailable COM Ports") + available_ports = [] + default_port = None + for port, desc, hwid in sorted(ports): + port_num = port[3:] + available_ports.append(port_num) + + if "communication" in desc.lower() or "comm" in desc.lower(): + default_port = port_num + + print("| {}: {} [{}]".format(port, desc, hwid)) + + if len(available_ports) < 1: + print("Error: No COM ports available. Exiting...") + sys.exit() + + if default_port is None: + default_port = available_ports[0] + + comPort = int(CLI.options("Which COM Port for stage?", options = available_ports, default = default_port)) + + print(f"{CLI.COLORS.HEADER}Obtained:\n--- devMode : {devMode}\n--- Profiler : {'NanoScan' if useNanoScan else 'WinCamD'}\n--- COM Port : COM{comPort}{CLI.COLORS.ENDC}") + confirm = CLI.whats_it_gonna_be_boy(f"Proceed?", default = "yes") + + if confirm: + break + CLI.clear_screen() + + return devMode, useNanoScan, comPort + +CLI.clear_screen() +print(f""" + ┌──────────────────────────────────────┐ + │ │ + │ {CLI.COLORS.OKCYAN}Welcome to M² Measurement Wizard{CLI.COLORS.ENDC} │ + │ │ + │ Made 2021-2022, Yudong Sun │ + │ │ + └──────────────────────────────────────┘ +""") + +devMode, useNanoScan, comPort = setup() + +cfg = { "port" : f"COM{comPort}" } + +cam = nanosquared.cameras.nanoscan.NanoScan if useNanoScan else nanosquared.cameras.wincamd.WinCamD + +print("Got it! Initialising...") +with cam(devMode = devMode) as n: + with nanosquared.stage.controller.GSC01(devMode = devMode, devConfig = cfg) as s: + with nanosquared.measurement.measure.Measurement(devMode = devMode, camera = n, controller = s) as M: + print(f"{CLI.COLORS.OKGREEN}Initialisation done!{CLI.COLORS.ENDC}") + print("") + ic = CLI.whats_it_gonna_be_boy("Launch Interactive Console?") + + meta = { + "Wavelength": "2300 nm", + "Lens": "f = 250mm CaF2 lens" + } + M.take_measurements(precision = 10, metadata = meta) + # incl. auto find beam waist and rayleigh length + # Measurement data will be saved under + # ``repo/data/M2/_.dat`` + # together with the metadata + + # To save to a specific file, use: + # `M.take_measurements(precision = 10, metadata = meta, writeToFile = "path/to/file")` + # or run + # `M.write_to_file("path/to/file", metadata = meta) + + # Explicit options + res = M.fit_data(axis = M.camera.AXES.X, wavelength = 2300, \ + mode = MsqFitter.M2_MODE, useODR = False, xerror = None) + print(f"X-Axis") + print(f"Fit Result:\t{res}") + print(f"M-squared:\t{M.fitter.m_squared}") + fig, ax = M.fitter.getPlotOfFit() + fig.show() + + res = M.fit_data(axis = M.camera.AXES.Y, wavelength = 2300) # Use defaults (same as above) + print(f"Y-Axis") + print(f"Fit Result:\t{res}") + print(f"M-squared:\t{M.fitter.m_squared}") + fig, ax = M.fitter.getPlotOfFit() + fig.show() +# import code; code.interact(local=locals()) + \ No newline at end of file diff --git a/src/gui/plan.txt b/src/cli-app/plan.txt similarity index 100% rename from src/gui/plan.txt rename to src/cli-app/plan.txt From f765924b3aa3ff4f7290cb23f9a0c56b7e2e6580 Mon Sep 17 00:00:00 2001 From: Yudong Sun Date: Tue, 15 Mar 2022 18:09:43 +0100 Subject: [PATCH 03/13] fix error where controller exits in devmode --- src/nanosquared/stage/controller.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/nanosquared/stage/controller.py b/src/nanosquared/stage/controller.py index 4d22af3..3a018c8 100644 --- a/src/nanosquared/stage/controller.py +++ b/src/nanosquared/stage/controller.py @@ -713,8 +713,7 @@ def stop(self, emergency: bool = False): return self.safesend(f"L:{self.axis}") def closeDevice(self): - if self.dev.isOpen(): - self.dev.close() + return super().closeDevice() def safesend(self, *args, **kwargs): if self.devMode: From 4c78ef4c7422cbf57dee3ee1925df8f55a77f670 Mon Sep 17 00:00:00 2001 From: Yudong Sun Date: Tue, 15 Mar 2022 18:11:20 +0100 Subject: [PATCH 04/13] fix error where nanoscan exits in devmode --- src/nanosquared/cameras/nanoscan.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/nanosquared/cameras/nanoscan.py b/src/nanosquared/cameras/nanoscan.py index ef3374c..e2799d8 100644 --- a/src/nanosquared/cameras/nanoscan.py +++ b/src/nanosquared/cameras/nanoscan.py @@ -239,7 +239,8 @@ def waitForData(self) -> bool: # return x def __exit__(self, e_type, e_val, traceback): - self.NS.__exit__(e_type, e_val, traceback) + if not self.devMode: + self.NS.__exit__(e_type, e_val, traceback) return super(NanoScan, self).__exit__(e_type, e_val, traceback) class NanoScanDLL(Client64): From bb2aaed8d1a9b5eef9196a55e7ea4d842d2c3b15 Mon Sep 17 00:00:00 2001 From: Yudong Sun Date: Tue, 15 Mar 2022 18:21:32 +0100 Subject: [PATCH 05/13] fix some typing assertion errors --- src/nanosquared/measurement/measure.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/nanosquared/measurement/measure.py b/src/nanosquared/measurement/measure.py index fb9310f..7ff688a 100644 --- a/src/nanosquared/measurement/measure.py +++ b/src/nanosquared/measurement/measure.py @@ -68,8 +68,9 @@ def __init__(self, if camera is None: camera = WinCamD(devMode = devMode) - assert isinstance(camera, Camera), f"Camera ({camera}) is not recognized" - assert isinstance(controller, Controller), f"Controller ({controller}) is not recognized" + # https://stackoverflow.com/questions/10582774/python-why-can-isinstance-return-false-when-it-should-return-true + assert isinstance(type(camera), type(Camera)), f"Camera ({camera}) is not recognized" + assert isinstance(type(controller), type(Controller)), f"Controller ({controller}) is not recognized" self.controller = controller self.camera = camera From f642787a2039e3549bb21e48e0b6942064d345c8 Mon Sep 17 00:00:00 2001 From: Yudong Sun Date: Tue, 15 Mar 2022 18:32:24 +0100 Subject: [PATCH 06/13] basic function of cli-app implemented --- src/cli-app/m2-app.py | 71 ++++++++++++++++++++++++------------------- 1 file changed, 39 insertions(+), 32 deletions(-) diff --git a/src/cli-app/m2-app.py b/src/cli-app/m2-app.py index 850ae35..797a0cb 100644 --- a/src/cli-app/m2-app.py +++ b/src/cli-app/m2-app.py @@ -21,6 +21,7 @@ def setup(): print(f"{CLI.COLORS.HEADER}==== SETUP ===={CLI.COLORS.ENDC}") print("\nIn the following questions, pressing Enter will enter the default option, which is indicated in capital letters.") print("\nIn development mode, no actual devices are required.\nAll function calls will therefore be simulated.") + print("Use this mode if you only want to fit.") devMode = CLI.whats_it_gonna_be_boy("Run in development mode?") print("\nNanoScan and WinCamD Beam Profilers are supported. \nyes = NanoScan, no = WinCamD") @@ -75,43 +76,49 @@ def setup(): cam = nanosquared.cameras.nanoscan.NanoScan if useNanoScan else nanosquared.cameras.wincamd.WinCamD -print("Got it! Initialising...") +print(f"{CLI.COLORS.OKGREEN}Got it! Initialising...{CLI.COLORS.ENDC}") with cam(devMode = devMode) as n: with nanosquared.stage.controller.GSC01(devMode = devMode, devConfig = cfg) as s: with nanosquared.measurement.measure.Measurement(devMode = devMode, camera = n, controller = s) as M: print(f"{CLI.COLORS.OKGREEN}Initialisation done!{CLI.COLORS.ENDC}") print("") ic = CLI.whats_it_gonna_be_boy("Launch Interactive Console?") + + if ic: + CLI.print_sep() + print(f"\n{CLI.COLORS.OKCYAN}with nanosquared.measurement.measure.Measurement(devMode = {devMode}) as M{CLI.COLORS.ENDC}") + import code; code.interact(local=locals()) + else: + if not devMode: + while True: + meta = { + "Wavelength": "2300 nm", + "Lens": "f = 250mm CaF2 lens" + } + M.take_measurements(precision = 10, metadata = meta) + + res = M.fit_data(axis = M.camera.AXES.X, wavelength = 2300) + print(f"X-Axis") + print(f"Fit Result:\t{res}") + print(f"M-squared:\t{M.fitter.m_squared}") + fig, ax = M.fitter.getPlotOfFit() + fig.show() + + res = M.fit_data(axis = M.camera.AXES.Y, wavelength = 2300) # Use defaults (same as above) + print(f"Y-Axis") + print(f"Fit Result:\t{res}") + print(f"M-squared:\t{M.fitter.m_squared}") + fig, ax = M.fitter.getPlotOfFit() + fig.show() + + break + # Measurement done, launch interactive? + # if not, take another measurement? + # no = exit, yes = redo + else: + print("Assuming you want to fit...") + + + - meta = { - "Wavelength": "2300 nm", - "Lens": "f = 250mm CaF2 lens" - } - M.take_measurements(precision = 10, metadata = meta) - # incl. auto find beam waist and rayleigh length - # Measurement data will be saved under - # ``repo/data/M2/_.dat`` - # together with the metadata - - # To save to a specific file, use: - # `M.take_measurements(precision = 10, metadata = meta, writeToFile = "path/to/file")` - # or run - # `M.write_to_file("path/to/file", metadata = meta) - - # Explicit options - res = M.fit_data(axis = M.camera.AXES.X, wavelength = 2300, \ - mode = MsqFitter.M2_MODE, useODR = False, xerror = None) - print(f"X-Axis") - print(f"Fit Result:\t{res}") - print(f"M-squared:\t{M.fitter.m_squared}") - fig, ax = M.fitter.getPlotOfFit() - fig.show() - - res = M.fit_data(axis = M.camera.AXES.Y, wavelength = 2300) # Use defaults (same as above) - print(f"Y-Axis") - print(f"Fit Result:\t{res}") - print(f"M-squared:\t{M.fitter.m_squared}") - fig, ax = M.fitter.getPlotOfFit() - fig.show() -# import code; code.interact(local=locals()) \ No newline at end of file From 0fc61741fc0c2ee29b4c1bac35a8ed6969b2c152 Mon Sep 17 00:00:00 2001 From: Yudong Sun Date: Tue, 15 Mar 2022 19:09:34 +0100 Subject: [PATCH 07/13] measurement part done? --- src/cli-app/cli.py | 58 +++++++++++++++++++++++++++++++++++++++++ src/cli-app/m2-app.py | 60 +++++++++++++++++++++++++++++++------------ 2 files changed, 101 insertions(+), 17 deletions(-) diff --git a/src/cli-app/cli.py b/src/cli-app/cli.py index 35098bf..7fbc1a7 100644 --- a/src/cli-app/cli.py +++ b/src/cli-app/cli.py @@ -28,6 +28,64 @@ def clear_screen(): def print_sep(): print("======================") + @staticmethod + def getPositiveNonZeroFloat(question, default = None) -> float: + + prompt = f"[Default = {default}]" if default is not None else "" + + while True: + try: + resp = input(CLI.GAP + question + " " + prompt + " > ").strip() + if default is not None and resp == '': + return default + else: + resp = float(resp) + if resp <= 0: + raise ValueError + return resp + except ValueError: + print("ERROR: Please respond with a positive number/float.") + except EOFError: + print("Encountered EOF, exiting...") + sys.exit() + + @staticmethod + def getIntWithLimit(question, default = None, lowerlimit: int = 1) -> int: + """Gets an integer that is no lower than the `lowerlimit` + + Parameters + ---------- + question : str + Question to ask + default : int, optional + Default value, by default None + lowerlimit : int, optional + Lowest acceptable integer, by default 1 + + Returns + ------- + int + Received input + """ + + prompt = f"[Default = {default}]" if default is not None else "" + + while True: + try: + resp = input(CLI.GAP + question + " " + prompt + " > ").strip() + if default is not None and resp == '': + return default + else: + resp = int(resp) + if resp <= lowerlimit: + raise ValueError + return resp + except ValueError: + print(f"ERROR: Please respond with an integer that is at least {lowerlimit}") + except EOFError: + print("Encountered EOF, exiting...") + sys.exit() + @staticmethod def options(question, options, default): assert (default in options) or (default is None), "ERROR: default not in options" diff --git a/src/cli-app/m2-app.py b/src/cli-app/m2-app.py index 797a0cb..659bcc5 100644 --- a/src/cli-app/m2-app.py +++ b/src/cli-app/m2-app.py @@ -83,40 +83,66 @@ def setup(): print(f"{CLI.COLORS.OKGREEN}Initialisation done!{CLI.COLORS.ENDC}") print("") ic = CLI.whats_it_gonna_be_boy("Launch Interactive Console?") + + def launchInteractive(): + print(f"\n{CLI.COLORS.OKCYAN}with nanosquared.measurement.measure.Measurement(devMode = {devMode}) as M{CLI.COLORS.ENDC}") + import code; code.interact(local=locals()) if ic: CLI.print_sep() - print(f"\n{CLI.COLORS.OKCYAN}with nanosquared.measurement.measure.Measurement(devMode = {devMode}) as M{CLI.COLORS.ENDC}") - import code; code.interact(local=locals()) + launchInteractive() else: if not devMode: + print("Assuming you want to take a measurement...") while True: + while True: + wavelength = CLI.getPositiveNonZeroFloat("Laser Wavelength (nm) ?") + precision = CLI.getIntWithLimit("Precision of search? (pulses) ?", default = 10, lowerlimit = 2) + other = input(CLI.GAP + "Other metadata > ") + + print(f"{CLI.COLORS.HEADER}Obtained:\n--- Wavelength : {wavelength} nm\n--- Precision : {precision} pps\n--- Other Metadata : {other}{CLI.COLORS.ENDC}") + confirm = CLI.whats_it_gonna_be_boy(f"Proceed?", default = "yes") + + if confirm: + break + meta = { - "Wavelength": "2300 nm", - "Lens": "f = 250mm CaF2 lens" + "Wavelength" : f"{wavelength} nm", + "Precision (pps)" : precision, + "Metadata" : other } - M.take_measurements(precision = 10, metadata = meta) + M.take_measurements(precision = precision, metadata = meta) - res = M.fit_data(axis = M.camera.AXES.X, wavelength = 2300) - print(f"X-Axis") - print(f"Fit Result:\t{res}") - print(f"M-squared:\t{M.fitter.m_squared}") + print(f"{CLI.COLORS.OKGREEN}Done!{CLI.COLORS.ENDC}") + + print(f"{CLI.COLORS.OKGREEN}Fitting data (X-Axis)...{CLI.COLORS.ENDC}") + res = M.fit_data(axis = M.camera.AXES.X, wavelength = wavelength) + print(f"{CLI.COLORS.OKGREEN}=== X-Axis ==={CLI.COLORS.ENDC}") + print(f"{CLI.COLORS.OKGREEN}=== Fit Result{CLI.COLORS.ENDC}: \t{res}") + print(f"{CLI.COLORS.OKGREEN}=== M-squared{CLI.COLORS.ENDC}: \t{M.fitter.m_squared}") fig, ax = M.fitter.getPlotOfFit() fig.show() - + + print(f"{CLI.COLORS.OKGREEN}Fitting data (Y-Axis)...{CLI.COLORS.ENDC}") res = M.fit_data(axis = M.camera.AXES.Y, wavelength = 2300) # Use defaults (same as above) - print(f"Y-Axis") - print(f"Fit Result:\t{res}") - print(f"M-squared:\t{M.fitter.m_squared}") + print(f"{CLI.COLORS.OKGREEN}=== Y-Axis ==={CLI.COLORS.ENDC}") + print(f"{CLI.COLORS.OKGREEN}=== Fit Result{CLI.COLORS.ENDC}: \t{res}") + print(f"{CLI.COLORS.OKGREEN}=== M-squared{CLI.COLORS.ENDC}: \t{M.fitter.m_squared}") fig, ax = M.fitter.getPlotOfFit() fig.show() - break - # Measurement done, launch interactive? - # if not, take another measurement? - # no = exit, yes = redo + print(f"{CLI.COLORS.OKGREEN}All Done!{CLI.COLORS.ENDC}") + + ic2 = CLI.whats_it_gonna_be_boy("Launch Interactive Console?") + if ic2: + launchInteractive() + + anothermeasurement = CLI.whats_it_gonna_be_boy("Take another measurement?") + if not anothermeasurement: + break else: print("Assuming you want to fit...") + From cf2ecec672423813d2a5cfbef82946db86d5fd38 Mon Sep 17 00:00:00 2001 From: Yudong Sun Date: Tue, 15 Mar 2022 19:32:57 +0100 Subject: [PATCH 08/13] cli app kinda done --- src/cli-app/cli.py | 5 ++ src/cli-app/m2-app.py | 73 +++++++++++++++++++++++--- src/nanosquared/measurement/measure.py | 4 +- 3 files changed, 73 insertions(+), 9 deletions(-) diff --git a/src/cli-app/cli.py b/src/cli-app/cli.py index 7fbc1a7..6034f64 100644 --- a/src/cli-app/cli.py +++ b/src/cli-app/cli.py @@ -28,6 +28,11 @@ def clear_screen(): def print_sep(): print("======================") + @staticmethod + def presskeycont(): + input("Press Enter to continue...") + return + @staticmethod def getPositiveNonZeroFloat(question, default = None) -> float: diff --git a/src/cli-app/m2-app.py b/src/cli-app/m2-app.py index 659bcc5..87460a1 100644 --- a/src/cli-app/m2-app.py +++ b/src/cli-app/m2-app.py @@ -1,3 +1,11 @@ +#!/usr/bin/env python3 + +# Made 2022, Sun Yudong +# yudong.sun [at] mpq.mpg.de / yudong [at] outlook.de + +# Possible Improvements +# - Different fitting methods + from email.policy import default import os, sys from matplotlib.style import available @@ -66,6 +74,7 @@ def setup(): │ {CLI.COLORS.OKCYAN}Welcome to M² Measurement Wizard{CLI.COLORS.ENDC} │ │ │ │ Made 2021-2022, Yudong Sun │ + │ github.com/sunjerry019/nanosquared │ │ │ └──────────────────────────────────────┘ """) @@ -81,15 +90,21 @@ def setup(): with nanosquared.stage.controller.GSC01(devMode = devMode, devConfig = cfg) as s: with nanosquared.measurement.measure.Measurement(devMode = devMode, camera = n, controller = s) as M: print(f"{CLI.COLORS.OKGREEN}Initialisation done!{CLI.COLORS.ENDC}") + + print("") + CLI.print_sep() + print(f"{CLI.COLORS.FAIL}IMPT{CLI.COLORS.ENDC}\n{CLI.COLORS.FAIL}IMPT{CLI.COLORS.ENDC}: If you happen to quit halfway through, use the Task Manager > Processes to ensure that no NanoScanII.exe instances are running before restarting this wizard.\n{CLI.COLORS.FAIL}IMPT{CLI.COLORS.ENDC}") + CLI.print_sep() print("") + ic = CLI.whats_it_gonna_be_boy("Launch Interactive Console?") def launchInteractive(): + CLI.print_sep() print(f"\n{CLI.COLORS.OKCYAN}with nanosquared.measurement.measure.Measurement(devMode = {devMode}) as M{CLI.COLORS.ENDC}") import code; code.interact(local=locals()) if ic: - CLI.print_sep() launchInteractive() else: if not devMode: @@ -118,16 +133,18 @@ def launchInteractive(): print(f"{CLI.COLORS.OKGREEN}Fitting data (X-Axis)...{CLI.COLORS.ENDC}") res = M.fit_data(axis = M.camera.AXES.X, wavelength = wavelength) print(f"{CLI.COLORS.OKGREEN}=== X-Axis ==={CLI.COLORS.ENDC}") - print(f"{CLI.COLORS.OKGREEN}=== Fit Result{CLI.COLORS.ENDC}: \t{res}") - print(f"{CLI.COLORS.OKGREEN}=== M-squared{CLI.COLORS.ENDC}: \t{M.fitter.m_squared}") + print(f"{CLI.COLORS.OKGREEN}=== Fit Result{CLI.COLORS.ENDC}: {res}") + print(f"{CLI.COLORS.OKGREEN}=== M-squared{CLI.COLORS.ENDC} : {M.fitter.m_squared}") fig, ax = M.fitter.getPlotOfFit() fig.show() + + CLI.presskeycont() print(f"{CLI.COLORS.OKGREEN}Fitting data (Y-Axis)...{CLI.COLORS.ENDC}") - res = M.fit_data(axis = M.camera.AXES.Y, wavelength = 2300) # Use defaults (same as above) + res = M.fit_data(axis = M.camera.AXES.Y, wavelength = wavelength) # Use defaults (same as above) print(f"{CLI.COLORS.OKGREEN}=== Y-Axis ==={CLI.COLORS.ENDC}") - print(f"{CLI.COLORS.OKGREEN}=== Fit Result{CLI.COLORS.ENDC}: \t{res}") - print(f"{CLI.COLORS.OKGREEN}=== M-squared{CLI.COLORS.ENDC}: \t{M.fitter.m_squared}") + print(f"{CLI.COLORS.OKGREEN}=== Fit Result{CLI.COLORS.ENDC}: {res}") + print(f"{CLI.COLORS.OKGREEN}=== M-squared{CLI.COLORS.ENDC} : {M.fitter.m_squared}") fig, ax = M.fitter.getPlotOfFit() fig.show() @@ -142,9 +159,49 @@ def launchInteractive(): break else: print("Assuming you want to fit...") - + while True: + while True: + wavelength = CLI.getPositiveNonZeroFloat("Laser Wavelength (nm) ?") + print(f"{CLI.COLORS.HEADER}Obtained:\n--- Wavelength : {wavelength} nm{CLI.COLORS.ENDC}") + confirm = CLI.whats_it_gonna_be_boy(f"Proceed?", default = "yes") + if confirm: + break + + while True: + try: + filename = input(CLI.GAP + "Filename > ") + M.read_from_file(filename = filename) + break + except OSError as e: + print(f"OSError: {e}. Try again.") + + print(f"{CLI.COLORS.OKGREEN}Fitting data (X-Axis)...{CLI.COLORS.ENDC}") + res = M.fit_data(axis = M.camera.AXES.X, wavelength = wavelength) + print(f"{CLI.COLORS.OKGREEN}=== X-Axis ==={CLI.COLORS.ENDC}") + print(f"{CLI.COLORS.OKGREEN}=== Fit Result{CLI.COLORS.ENDC}: {res}") + print(f"{CLI.COLORS.OKGREEN}=== M-squared{CLI.COLORS.ENDC} : {M.fitter.m_squared}") + fig, ax = M.fitter.getPlotOfFit() + fig.show() + CLI.presskeycont() + + print(f"{CLI.COLORS.OKGREEN}Fitting data (Y-Axis)...{CLI.COLORS.ENDC}") + res = M.fit_data(axis = M.camera.AXES.Y, wavelength = wavelength) # Use defaults (same as above) + print(f"{CLI.COLORS.OKGREEN}=== Y-Axis ==={CLI.COLORS.ENDC}") + print(f"{CLI.COLORS.OKGREEN}=== Fit Result{CLI.COLORS.ENDC}: {res}") + print(f"{CLI.COLORS.OKGREEN}=== M-squared{CLI.COLORS.ENDC} : {M.fitter.m_squared}") + fig, ax = M.fitter.getPlotOfFit() + fig.show() + - \ No newline at end of file + print(f"{CLI.COLORS.OKGREEN}All Done!{CLI.COLORS.ENDC}") + + ic2 = CLI.whats_it_gonna_be_boy("Launch Interactive Console?") + if ic2: + launchInteractive() + + anotherfit = CLI.whats_it_gonna_be_boy("Fit another?") + if not anotherfit: + break \ No newline at end of file diff --git a/src/nanosquared/measurement/measure.py b/src/nanosquared/measurement/measure.py index 7ff688a..885c87a 100644 --- a/src/nanosquared/measurement/measure.py +++ b/src/nanosquared/measurement/measure.py @@ -302,7 +302,7 @@ def write_to_file(self, writeToFile: Optional[str] = None, metadata: Optional[di return pfad - def read_from_file(self, filename: str): + def read_from_file(self, filename: str, raiseError = False): """Read from a file written by `self.write_to_file()` Parameters @@ -314,6 +314,8 @@ def read_from_file(self, filename: str): f = open(filename, 'r') except OSError as e: self.log(f"Unable to read file: {filename}: OSError {e}", logging.WARN) + if raiseError: + raise OSError(e) return # We assume the format position[mm] x_diam[um] dx_diam[um] y_diam[um] dy_diam[um] From bf254c597aa441dca464af71f5fe77d530e3a554 Mon Sep 17 00:00:00 2001 From: Yudong Sun Date: Tue, 15 Mar 2022 19:33:59 +0100 Subject: [PATCH 09/13] add possible improvements --- src/cli-app/m2-app.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cli-app/m2-app.py b/src/cli-app/m2-app.py index 87460a1..aecad6f 100644 --- a/src/cli-app/m2-app.py +++ b/src/cli-app/m2-app.py @@ -5,6 +5,8 @@ # Possible Improvements # - Different fitting methods +# - Provide option to choose which way the beam is coming in +# - Some proper way of breaking operations from email.policy import default import os, sys From b5157e272a7aa5dea311b6984ca71d0019e5b69b Mon Sep 17 00:00:00 2001 From: Yudong Sun Date: Tue, 15 Mar 2022 19:37:06 +0100 Subject: [PATCH 10/13] update readme --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 565417d..e6ffdcf 100644 --- a/README.md +++ b/README.md @@ -220,6 +220,10 @@ All logging is provided by the `LoggerMixIn` class under [`src/nanosquared/commo If you are adding modules to the codebase, it is recommended to inherit the `LoggerMixIn` class. ## Usage +**To start the quick-and-dirty CLI Application, simply double click on `launch_m2.bat`.** + +**The environment `nanosquared` needs to be set up with Anaconda.** + If you are using a `pip`-installed version, simply do: ```python import nanosquared From f997799d2ed37e51e8c15d8b1b2c20d6d7c541f7 Mon Sep 17 00:00:00 2001 From: Yudong Sun Date: Tue, 15 Mar 2022 19:39:24 +0100 Subject: [PATCH 11/13] add comment about reinstalling if not up-to-date --- src/cli-app/m2-app.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/cli-app/m2-app.py b/src/cli-app/m2-app.py index aecad6f..190c504 100644 --- a/src/cli-app/m2-app.py +++ b/src/cli-app/m2-app.py @@ -17,6 +17,8 @@ try: import nanosquared + print("Using pip-installed version (may not be up-to-date).") + print("Do `pip install .` to update the installed version with the one in the repository.") except ModuleNotFoundError as e: base_dir = os.path.dirname(os.path.realpath(__file__)) root_dir = os.path.abspath(os.path.join(base_dir, "..")) From ac2ad66b03b883657c525b6ecbc8774f7f591ccb Mon Sep 17 00:00:00 2001 From: Yudong Sun Date: Tue, 15 Mar 2022 19:40:14 +0100 Subject: [PATCH 12/13] add comment about reinstalling if not up-to-date 2 --- src/cli-app/m2-app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cli-app/m2-app.py b/src/cli-app/m2-app.py index 190c504..d93d9b6 100644 --- a/src/cli-app/m2-app.py +++ b/src/cli-app/m2-app.py @@ -18,7 +18,7 @@ try: import nanosquared print("Using pip-installed version (may not be up-to-date).") - print("Do `pip install .` to update the installed version with the one in the repository.") + print("If you have recently updated the repository, do `pip install .` to update the installed version with the one in the repository.") except ModuleNotFoundError as e: base_dir = os.path.dirname(os.path.realpath(__file__)) root_dir = os.path.abspath(os.path.join(base_dir, "..")) From b604d027bc758faeaa0aea41f0d4379377a69be6 Mon Sep 17 00:00:00 2001 From: Yudong Sun Date: Tue, 15 Mar 2022 19:40:40 +0100 Subject: [PATCH 13/13] update version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 1aaa849..e572360 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setuptools.setup( name="nanosquared", - version="0.1.1-alpha", + version="0.2", author="Yudong Sun", author_email="yudong.sun@mpq.mpg.de", description="Automated M-Squared Measurement",