Files
x11ma/script/beamline_alignment/02_beamline_configuration.py
2025-12-17 09:49:26 +01:00

630 lines
23 KiB
Python

# This script contains seveal user functions:
# 1 bml_save_current_settings()
# 2 bml_current_settings_for_functions()
# 3 bml_display_current_settings()
# 4 bml_restore_saved_settings()
# 5 bml_display_saved_settings()
# 6 bml_show_difference_to_saved_settings()
# A bit more in detail:
# 1 bml_save_current_settings()
# 2 save_data_and_config(default_dir=DATA_DIR, default_name=None):
# 3 select_components(selected_components=None) - is called by the following functions)
# 4 bml_display_current_settings(selected_components=None)
# 5 bml_restore_saved_settings(selected_components=None)
# 6 bml_display_saved_settings(selected_components=None)
# 7 bml_show_difference_to_saved_settings(selected_components=None)
# see also Python Projekte/Strings in Datei speichern
from collections import OrderedDict
import os
import json
import shutil
from javax.swing import JFrame, JFileChooser
from javax.swing.filechooser import FileNameExtensionFilter
from java.io import File
BEAMLINE_PARAMETERS_FILE = "/home/gac-x11ma/pshell/home/script/beamline_alignment/bml_align_params.json"
DATA_FILE = "/home/gac-x11ma/pshell/home/data/2025_11/20251113/Monocam_AU_131.json"
DATA_DIR = "/home/gac-x11ma/network_drives/home/Data/2025_11/"
#DATA_FILE = "/sls/X11MA/data/X11MA/scans/2511/0081.json"
#DATA_FILE = "/home/gac-x11ma/pshell/home/data/2025_11/20251113/Monocam_AU_131.json"
#DATA_DIR = "/home/gac-x11ma/pshell/home/data/2025_11/"
def save_data_and_config(default_dir=DATA_DIR, default_name=None, save_dialog=True):
"""
Offnet einen JSON-Datei-Dialog.
Parameter:
default_dir (str): Optionaler Standardordner
default_name (str): Optionaler Standard-Dateiname (nur relevant beim Speichern)
save_dialog (bool): True = "Speichern unter", False = "Offnen"
Ruckgabe:
tuple (full_path, name_without_ext) oder (None, None), falls abgebrochen
"""
frame = JFrame()
frame.setAlwaysOnTop(True)
chooser = JFileChooser()
# Default-Ordner setzen
if default_dir:
chooser.setCurrentDirectory(File(default_dir))
# Default-Dateiname nur beim Speichern
if save_dialog and default_dir and default_name:
chooser.setSelectedFile(File(default_dir, default_name))
# JSON-Filter
json_filter = FileNameExtensionFilter("JSON Dateien (*.json)", ["json"])
chooser.setFileFilter(json_filter)
# Dialog anzeigen
if save_dialog:
result = chooser.showSaveDialog(frame)
else:
result = chooser.showOpenDialog(frame)
if result == JFileChooser.APPROVE_OPTION:
file = chooser.getSelectedFile()
path = file.getAbsolutePath()
# Nur beim Speichern: sicherstellen, dass .json angehangt wird
if save_dialog and not path.lower().endswith(".json"):
path += ".json"
name_without_ext = os.path.splitext(os.path.basename(path))[0]
return path, name_without_ext
else:
return None, None
def bml_save_current_settings():
# Allows the user to save relevant beamline parameters in aJSON file.
path, name_only = save_data_and_config(default_dir=DATA_DIR, default_name=None, save_dialog=True)
# 1 Check if the user cancelled
if path is None:
print("Aborted by user.")
return # exit the function immediately
# 2 Get the parent folder of the file
folder = os.path.dirname(path)
#print(folder)
# 3 Check if the folder exists
if not os.path.isdir(folder):
print("Aborted. Folder: " + folder + " does not exist.")
return # exit the function cleanly
# 4 Mapping from component + parameter to individual read string (Dictionary)
# Each component contains its parameters, with the corresponding read string
# and optional tolerance
with open(BEAMLINE_PARAMETERS_FILE, "r") as f:
machine_parameters = json.load(f, object_pairs_hook=OrderedDict)
parameters = {}
nan_occurred = False
# 5 read each parameter from the machoine
for comp, param_dict in machine_parameters.items():
print("\nComponent: " + comp)
parameters[comp] = {}
for p, info in param_dict.items():
cmd = info["read_cmd"]
# print(cmd)
tol = info["tolerance"]
# print(tol)
try:
value = caget(cmd)
if isinstance(value, str):
display_value = value
else:
# value = string(value) c ensure float
display_value = str(value)
print(comp + "_" + p + " = " + display_value)
# print(value)
except:
# print("Problem")
# value = float('nan')
nan_occurred = True
print(comp + p + " = NaN")
parameters[comp][p] = {"value": value, "tolerance": tol}
# 6 Save parameters to JSON file
try:
with open(path, "w") as f:
json.dump(parameters, f, indent=4, allow_nan=True)
print("Parameters successfully saved in: " + path)
except:
print("Error while saving file.")
# Warn if NaN occurred
if nan_occurred:
print("There were NaN-Values")
return path, name_only
def bml_save_scan_settings_json(path):
# 1 Mapping from component + parameter to individual read string (Dictionary)
# Each component contains its parameters, with the corresponding read string
# and optional tolerance
with open(BEAMLINE_PARAMETERS_FILE, "r") as f:
machine_parameters = json.load(f, object_pairs_hook=OrderedDict)
parameters = {}
nan_occurred = False
# 2 read each parameter from the machoine
for comp, param_dict in machine_parameters.items():
print("\nComponent: " + comp)
parameters[comp] = {}
for p, info in param_dict.items():
cmd = info["read_cmd"]
# print(cmd)
tol = info["tolerance"]
# print(tol)
try:
value = caget(cmd)
if isinstance(value, str):
display_value = value
else:
# value = string(value) c ensure float
display_value = str(value)
print(comp + "_" + p + " = " + display_value)
# print(value)
except:
# print("Problem")
# value = float('nan')
nan_occurred = True
print(comp + p + " = NaN")
parameters[comp][p] = {"value": value, "tolerance": tol}
# 3 Save parameters to JSON file
file_path = path + "/bml_settings_after_scan.json"
try:
with open(file_path, "w") as f:
json.dump(parameters, f, indent=4, allow_nan=True)
print("Parameters successfully saved in: " + file_path)
except:
print("Error while saving file.")
# Warn if NaN occurred
if nan_occurred:
print("There were NaN-Values")
def bml_current_settings_for_functions():
# 4 Mapping from component + parameter to individual read string (Dictionary)
# Each component contains its parameters, with the corresponding read string
# and optional tolerance
with open(BEAMLINE_PARAMETERS_FILE, "r") as f:
machine_parameters = json.load(f, object_pairs_hook=OrderedDict)
parameters = {}
nan_occurred = False
# 5 read each parameter from the machoine
for comp, param_dict in machine_parameters.items():
print("\nComponent: " + comp)
parameters[comp] = {}
for p, info in param_dict.items():
cmd = info["read_cmd"]
# print(cmd)
tol = info["tolerance"]
# print(tol)
try:
value = caget(cmd)
if isinstance(value, str):
display_value = value
else:
# value = string(value) c ensure float
display_value = str(value)
print(comp + "_" + p + " = " + display_value)
# print(value)
except:
# print("Problem")
# value = float('nan')
nan_occurred = True
print(comp + p + " = NaN")
parameters[comp][p] = {"value": value, "tolerance": tol}
return parameters
def select_components(selected_components=None):
# Allows the user (or another function) to select one
# or more components either interactively or
# by providing a list. It validates the selection against
# the available components in the JSON file.
msg = "Select components: "
# 1 Load machine parameters
try:
with open(BEAMLINE_PARAMETERS_FILE, "r") as f:
machine_parameters = json.load(f, object_pairs_hook=OrderedDict)
except:
print("Error loading machine_parameters.json")
return None
# 2 Extract component names
all_components = list(machine_parameters.keys())
# print("Available components:")
# for comp in all_components:
# print("- " + comp)
# 3 determine components to display
if selected_components is None:
# user didn't specify -> use all from JSON
presented_to_select = all_components[:]
presented_to_select.append("all")
selected = get_string(msg, default=None, alternatives=presented_to_select, password=False)
# print(selected)
if selected is None:
print("Selection aborted by user.")
return None
elif selected == "all":
# Display components
print("Selected components:")
for comp in all_components:
print("- " + comp)
return all_components
else:
selected_components = [selected] if not isinstance(selected, list) else selected
print("Selected components:")
for comp in selected_components:
print("- " + comp)
return selected_components
elif selected_components == "all":
# Display components
print("Selected components:")
for comp in all_components:
print("- " + comp)
return all_components
else:
# ensure it is a list
if not isinstance(selected_components, list):
print("Error: selected_components must be a list of the form \"[\"Diag\", \"CMU\", ...]\" or \"all\".")
return
# validate each component
invalid = [c for c in selected_components if c not in all_components]
if invalid:
print("Error: Invalid component(s):")
for comp in invalid:
print("- " + comp)
print("Valid components are:")
for comp in all_components:
print("- " + comp)
return None
else:
print("Selected components:")
for comp in selected_components:
print("- " + comp)
return selected_components
def bml_display_current_settings(selected_components=None):
# Displays parameters for selected components.
# - Uses select_components() to choose components.
# - Prints each parameter with its value.
# - If a parameter value is missing or cannot be read, prints NaN.
# 1 Get the selected component
selection = select_components(selected_components)
if selection is None:
return # user cancelled or invalid input
# 2 Load the machine poarametrers JSON
try:
with open(BEAMLINE_PARAMETERS_FILE, "r") as f:
machine_parameters = json.load(f, object_pairs_hook=OrderedDict)
except:
print("Error loading machine_parameters.json")
return
# 3 Loop through the components and display their parameters
for comp in selection:
print("\nComponent: " + comp)
params = machine_parameters.get(comp, {})
if not params:
print(" No parameters found.")
continue
for key, param_info in params.items():
# param_info enthalt z.B. den read_cmd
read_cmd = param_info.get("read_cmd") # JSON muss fur jeden Parameter den entsprechenden Befehl enthalten
try:
display_value = caget(read_cmd)
print(key + " = " + str(display_value))
except:
display_value = "NaN"
# print(" " + key + ": " + display_value)
def bml_restore_saved_settings(selected_components=None):
# Restore parameter values from a previously saved JSON file.
# The user selects which components to restore, and only parameters
# marked as "restorable": true in machine_parameters.json will be written back.
restore_file, name_only = save_data_and_config(default_dir=DATA_DIR, default_name=None, save_dialog=False)
# 1 Select file
# restore_file = get_string(msg, default = DATA_FILE, alternatives = None, password = False)
# if path is None:
# print("Restore aborted: no file specified.")
# return
try:
with open(restore_file, "r") as f:
saved_data = json.load(f, object_pairs_hook=OrderedDict)
except:
print("Error loading restore file:", restore_file)
return
# 2 Load machine parameters
try:
with open(BEAMLINE_PARAMETERS_FILE, "r") as f:
machine_parameters = json.load(f, object_pairs_hook=OrderedDict)
except:
print("Error loading machine_parameters.json")
return
# 3 Select components
selection = select_components(selected_components)
if selection is None:
return # user cancelled or invalid input
# 4 Load the machine parameters JSON
try:
with open(BEAMLINE_PARAMETERS_FILE, "r") as f:
machine_parameters = json.load(f, object_pairs_hook=OrderedDict)
except:
print("Error loading machine_parameters.json")
return
# 5 Restore loop
for comp in selection:
# print("\nComponent: " + comp)
params = machine_parameters.get(comp, {})
if not params:
print(" No parameters found.")
continue
saved_params = saved_data.get(comp, {})
if not saved_params:
print(" No saved values for this component.")
continue
for key, param_info in params.items():
if not param_info.get("restorable", False):
continue
# get saved value
saved_entry = saved_params.get(key, None)
if saved_entry is not None and 'value' in saved_entry:
saved_value = saved_entry['value']
else:
saved_value = "NaN"
# param_info enthalt z.B. den read_cmd
read_cmd = param_info.get("read_cmd") # JSON muss fur jeden Parameter den entsprechenden Befehl enthalten
write_cmd = param_info.get("write_cmd") # JSON muss fur jeden Parameter den entsprechenden Befehl enthalten
tolerance = param_info.get("tolerance")
try:
display_value = caget(read_cmd)
# print(read_cmd + " = " + str(display_value) + ", saved value = " + str(saved_value))
# difference = float(display_value) - float(saved_value)
# compute difference if possible
if saved_value != "NaN" and display_value != "NaN":
try:
difference = abs(float(display_value) - float(saved_value))
except:
difference = "NaN"
else:
difference = "NaN"
# print(read_cmd + " = " + str(display_value) + ", diff = " + str(round(difference, 5)) + ", tolerance = " + str(round(tolerance, 5)))
if float(difference) > float(tolerance):
print(key + " - Not OK. Set to " + str(round(saved_value, 5)))
write_cmd_new = "\"" + write_cmd + "\""
# print(write_cmd_new)
caput(write_cmd, float(saved_value))
check_restored_value = caget(read_cmd)
print(key + " - Reads now: " + str(check_restored_value))
else:
print(key + " - OK.")
except:
display_value = "NaN"
# print(" " + key + ": " + display_value)
def bml_display_saved_settings(selected_components=None):
# Diplay parameter values from a previously saved JSON file.
# The user selects which components to restore, and only parameters
# marked as "restorable": true in machine_parameters.json will be written back.
# msg = "Open *.json configuration file ..."
restore_file, name_only = save_data_and_config(default_dir=DATA_DIR, default_name=None, save_dialog=False)
# 1 Select file
# restore_file = get_string(msg, default = "/sls/X11MA/data/X11MA/scans/2511/0012.json", alternatives = None, password = False)
# path = get_string(msg, default = DATA_FILE, alternatives = None, password = False)
# if restore_file is None:
# print("Restore aborted: no file specified.")
# return
try:
with open(restore_file, "r") as f:
saved_data = json.load(f, object_pairs_hook=OrderedDict)
except:
print("Error loading restore file:", restore_file)
return
# 2 Load machine parameters
try:
with open(BEAMLINE_PARAMETERS_FILE, "r") as f:
machine_parameters = json.load(f, object_pairs_hook=OrderedDict)
except:
print("Error loading machine_parameters.json")
return
# 3 Select components
selection = select_components(selected_components)
if selection is None:
return # user cancelled or invalid input
# 4 Load the machine parameters JSON
try:
with open(BEAMLINE_PARAMETERS_FILE, "r") as f:
machine_parameters = json.load(f, object_pairs_hook=OrderedDict)
except:
print("Error loading machine_parameters.json")
return
# 5 Display loop
for comp in selection:
print("\nComponent: " + comp)
params = machine_parameters.get(comp, {})
if not params:
print(" No parameters found.")
continue
saved_params = saved_data.get(comp, {})
if not saved_params:
print(" No saved values for this component.")
continue
for key, param_info in params.items():
# if not param_info.get("restorable", False):
# continue
# get saved value
saved_entry = saved_params.get(key, None)
if saved_entry is not None and 'value' in saved_entry:
saved_value = saved_entry['value']
else:
saved_value = "NaN"
print(key + " was: " + str(saved_value))
def bml_show_difference_to_saved_settings(selected_components=None):
# Restore parameter values from a previously saved JSON file.
# The user selects which components to restore, and only parameters
# marked as "restorable": true in machine_parameters.json will be written back.
restore_file, name_only = save_data_and_config(default_dir=DATA_DIR, default_name=None, save_dialog=False)
# restore_file = get_string(msg, default = "/sls/X11MA/data/X11MA/scans/2511/0012.json", alternatives = None, password = False)
if restore_file is None:
print("Restore aborted: no file specified.")
return
try:
with open(restore_file, "r") as f:
saved_data = json.load(f, object_pairs_hook=OrderedDict)
except:
print("Error loading restore file:", restore_file)
return
# 2 Load machine parameters
try:
with open(BEAMLINE_PARAMETERS_FILE, "r") as f:
machine_parameters = json.load(f, object_pairs_hook=OrderedDict)
except:
print("Error loading machine_parameters.json")
return
# 3 Select components
selection = select_components(selected_components)
if selection is None:
return # user cancelled or invalid input
# 4 Load the machine parameters JSON
try:
with open(BEAMLINE_PARAMETERS_FILE, "r") as f:
machine_parameters = json.load(f, object_pairs_hook=OrderedDict)
except:
print("Error loading machine_parameters.json")
return
# 5 Restore loop
for comp in selection:
print("\nComponent: " + comp)
params = machine_parameters.get(comp, {})
if not params:
print(" No parameters found.")
continue
saved_params = saved_data.get(comp, {})
if not saved_params:
print(" No saved values for this component.")
continue
for key, param_info in params.items():
# if not param_info.get("restorable", False):
# continue
# get saved value
saved_entry = saved_params.get(key, None)
if saved_entry is not None and 'value' in saved_entry:
saved_value = saved_entry['value']
else:
saved_value = "NaN"
# param_info enthalt z.B. den read_cmd
read_cmd = param_info.get("read_cmd") # JSON muss fur jeden Parameter den entsprechenden Befehl enthalten
# write_cmd = param_info.get("write_cmd") # JSON muss fur jeden Parameter den entsprechenden Befehl enthalten
tolerance = param_info.get("tolerance")
try:
display_value = caget(read_cmd)
# print(read_cmd + " = " + str(display_value) + ", saved value = " + str(saved_value))
# difference = float(display_value) - float(saved_value)
# compute difference if possible
if saved_value != "NaN" and display_value != "NaN":
try:
difference = abs(float(display_value) - float(saved_value))
except:
difference = "NaN"
else:
difference = "NaN"
# print(read_cmd + " = " + str(display_value) + ", diff = " + str(round(difference, 5)) + ", tolerance = " + str(round(tolerance, 5)))
if float(difference) > float(tolerance):
print(key + " - Differs from saved value: " + str(round(saved_value, 5)))
# write_cmd_new = "\"" + write_cmd + "\""
# print(write_cmd_new)
# caput(write_cmd, float(saved_value))a
# check_restored_value = caget(read_cmd)
# print(key + " - Reads now: " + str(check_restored_value))
else:
print(key + " - OK.")
except:
display_value = "NaN"
# print(" " + key + ": " + display_value)