From 9a65d269842796ee07d3c0920173cc6859aab3e0 Mon Sep 17 00:00:00 2001 From: matthias muntwiler Date: Thu, 2 Feb 2017 15:31:13 +0100 Subject: [PATCH] updates: scaling of pshell data, matrix preview, elog panel - elog panel supports multiple attachments - matrix (omicron STM) data file preview in data explorer - various improvements for the scaling of pshell data --- pearl/pearl-data-explorer.ipf | 218 +++++-- pearl/pearl-elog.ipf | 558 ++++++++++++++---- pearl/pearl-matrix-import.ipf | 900 +++++++++++++++++++++++++++++ pearl/pearl-pshell-import.ipf | 79 ++- pearl/pearl-scienta-preprocess.ipf | 96 +++ 5 files changed, 1680 insertions(+), 171 deletions(-) create mode 100644 pearl/pearl-matrix-import.ipf diff --git a/pearl/pearl-data-explorer.ipf b/pearl/pearl-data-explorer.ipf index e44a185..755d863 100644 --- a/pearl/pearl-data-explorer.ipf +++ b/pearl/pearl-data-explorer.ipf @@ -1,11 +1,14 @@ #pragma rtGlobals=3 // Use modern global access method and strict wave access. #pragma IgorVersion = 6.1 #pragma ModuleName = PearlDataExplorer -#pragma version = 1.43 +#pragma version = 1.50 #include "pearl-area-import" #include "pearl-area-profiles" #include "pearl-area-display" #include "pearl-pshell-import" +#if exists("MFR_OpenResultFile") +#include "pearl-matrix-import" +#endif // copyright (c) 2013-16 Paul Scherrer Institut // @@ -20,7 +23,10 @@ /// /// /// preview and import panel for PEARL data: -/// scienta analyser, prosilica cameras, s-scans, otf-scans +/// @arg area detector (HDF5) files from scienta analyser and prosilica cameras (if HDF5.xop is installed). +/// @arg igor text files from s-scans and otf-scans. +/// @arg pshell (HDF5) data files (if HDF5.xop is installed). +/// @arg matrix STM files (if MatrixFileReader.xop is installed). /// @namespace PearlDataExplorer /// @brief preview and import panel for PEARL data @@ -33,6 +39,7 @@ static strconstant package_path = "root:packages:pearl_explorer:" static strconstant ks_filematch_adh5 = "*.h5" static strconstant ks_filematch_pshell = "psh*.h5" static strconstant ks_filematch_itx = "*.itx" +static strconstant ks_filematch_mtrx = "*_mtrx" function pearl_data_explorer() init_package() @@ -140,31 +147,64 @@ static function load_prefs() return result end +/// check whether a file can be imported by this module. +/// +/// the file type is determined by the extension of the file name. +/// +/// @return file type +/// @arg 0 not a recognized file type +/// @arg 1 PShell file (HDF5, name starts with psh_) +/// @arg 2 area detector HDF5 file +/// @arg 3 Igor text (itx) file +/// @arg 4 Matrix STM file (*_mtrx) +/// +static function pearl_file_type(filename) + string filename + + if (StringMatch(filename, ks_filematch_pshell)) + return 1 + elseif (StringMatch(filename, ks_filematch_adh5)) + return 2 + elseif (StringMatch(filename, ks_filematch_itx)) + return 3 +#if exists("MFR_OpenResultFile") + elseif (StringMatch(filename, ks_filematch_mtrx)) + return 4 +#endif + else + return 0 + endif +end + +/// read a list of PEARL files from the file system +/// +/// wtFiles and wSelectedFiles in the package data folder are updated. +/// only files for which pearl_file_type() returns non-zero are listed. +/// static function update_filelist() dfref saveDF = GetDataFolderDFR() - string hdf_files, itx_files, all_files + string all_files wave /t wtFiles = $(package_path + "wtFiles") wave wSelectedFiles = $(package_path + "wSelectedFiles") variable nn PathInfo pearl_explorer_filepath if (v_flag == 1) - hdf_files = IndexedFile(pearl_explorer_filepath, -1, ".h5") - itx_files = IndexedFile(pearl_explorer_filepath, -1, ".itx") - all_files = hdf_files + itx_files - all_files = SortList(hdf_files + itx_files, ";", 4) + all_files = IndexedFile(pearl_explorer_filepath, -1, "????") nn = ItemsInList(all_files) else all_files = "" nn = 0 endif - redimension /n=(nn) wtFiles, wSelectedFiles - if (nn > 0) - wtFiles = StringFromList(p, all_files) - wSelectedFiles = 0 - endif + make /n=(nn) /t /free wtAllFiles + wtAllFiles = StringFromList(p, all_files) + Extract /o /t wtAllFiles, wtFiles, pearl_file_type(wtAllFiles[p]) + Sort /A /R wtFiles, wtFiles + + redimension /n=(numpnts(wtFiles)) wSelectedFiles + wSelectedFiles = 0 setdatafolder saveDF end @@ -216,14 +256,24 @@ static function preview_file(filename) string filename dfref saveDF = GetDataFolderDFR() - - if (StringMatch(filename, ks_filematch_pshell)) - wave /z image = preview_pshell_file(filename) - elseif (StringMatch(filename, ks_filematch_adh5)) - wave /z image = preview_hdf_file(filename) - elseif (StringMatch(filename, ks_filematch_itx)) - wave /z image = preview_itx_file(filename) - endif + + variable ft = pearl_file_type(filename) + switch(ft) + case 1: + wave /z image = preview_pshell_file(filename) + break + case 2: + wave /z image = preview_hdf_file(filename) + break + case 3: + wave /z image = preview_itx_file(filename) + break + case 4: + wave /z image = preview_mtrx_file(filename) + break + default: + wave /z image = $"" + endswitch if (WaveExists(image)) string graphname = show_preview_graph(image) @@ -240,9 +290,10 @@ static function preview_file(filename) endif setdatafolder saveDF + return 0 end -/// load the preview of a PShell HDF5 file (not implemented). +/// load the preview of a PShell HDF5 file. /// /// the preview is an arbitrary detector image extracted from the file, see adh5_load_preview(). /// the preview is loaded to the preview_image wave in the pear_explorer data folder. @@ -354,6 +405,63 @@ static function /wave preview_itx_file(filename) return preview_image end +/// load the preview of a Matrix STM file. +/// +/// the preview is loaded to the preview_image wave in the pearl_explorer data folder. +/// +/// the s_file_info string is updated with information about the scan dimensions. +/// +/// this function requires the MatrixFileReader.xop and pearl-matrix-import.ipf to be loaded. +/// otherwise it will return an empty wave reference. +/// +/// @param filename name of a file in the directory specified by the pearl_explorer_filepath path object. +/// +/// @return wave reference of the preview image. +/// empty wave reference if the function failed. +/// +static function /wave preview_mtrx_file(filename) + string filename + +#if exists("MFR_OpenResultFile") + dfref saveDF = GetDataFolderDFR() + setdatafolder $package_path + variable /g V_MatrixFileReaderOverwrite = 1 + variable /g V_MatrixFileReaderFolder = 0 + variable /g V_MatrixFileReaderDouble = 0 + svar s_preview_file + svar s_preview_source + string datanames + string dataname + datanames = mtrx_load_preview("preview", "pearl_explorer_filepath", filename) + if (strlen(datanames) > 0) + s_preview_file = filename + + dataname = StringFromList(0, datanames) + wave data = $dataname + duplicate /o $dataname, preview_image + s_preview_source = StringByKey("Dataset", note(data), "=", "\r") + + svar /z s_file_info + if (svar_exists(s_file_info)) + s_file_info = "" + endif + + variable i + variable n = ItemsInList(datanames) + string s + for (i = 0; i < n; i += 1) + s = StringFromList(i, datanames) + killwaves /z $s + endfor + endif + wave /z preview_image + setdatafolder saveDF +#else + wave /z preview_image = $"" +#endif + return preview_image +end + static function extract_preview_image(data, preview) // extracts a preview image from a wave of arbitrary dimension wave data @@ -862,22 +970,32 @@ static function load_file(filename, [options]) dfref saveDF = GetDataFolderDFR() - if (StringMatch(filename, ks_filematch_pshell)) - if (ParamIsDefault(options)) - load_pshell_file(filename) - else - load_pshell_file(filename, options=options) - endif - elseif (StringMatch(filename, ks_filematch_adh5)) - if (ParamIsDefault(options)) - load_hdf_file(filename) - else - load_hdf_file(filename, options=options) - endif - elseif (StringMatch(filename, ks_filematch_itx)) - load_itx_file(filename) - endif - + variable ft = pearl_file_type(filename) + switch(ft) + case 1: + if (ParamIsDefault(options)) + load_pshell_file(filename) + else + load_pshell_file(filename, options=options) + endif + break + case 2: + if (ParamIsDefault(options)) + load_hdf_file(filename) + else + load_hdf_file(filename, options=options) + endif + break + case 3: + load_itx_file(filename) + break + case 4: + load_mtrx_file(filename) + break + default: + break + endswitch + setdatafolder saveDF end @@ -1080,6 +1198,32 @@ static function /df load_itx_file(filename, [options]) return actDF end +/// load a matrix (STM) data file +/// +/// +static function /df load_mtrx_file(filename, [options]) + string filename + string options + + dfref saveDF = GetDataFolderDFR() + dfref dataDF = $"" + +#if exists("MFR_OpenResultFile") + setdatafolder root: + string datasets = "" + datasets = mtrx_load_file("pearl_explorer_filepath", filename) + if (strlen(datasets) > 0) + string /g pearl_explorer_import = "load_mtrx_file" + string s1 = StringFromList(0, datasets) + wave w1 = $s1 + dataDF = GetWavesDataFolderDFR(w1) + endif +#endif + + setdatafolder saveDF + return dataDF +end + function /s itx_suggest_foldername(filename, [ignoredate,sourcename,unique]) // suggests the name of a data folder based on a file name // if the file name follows the naming convention source-date-index.extension, diff --git a/pearl/pearl-elog.ipf b/pearl/pearl-elog.ipf index bd498b6..37c1373 100644 --- a/pearl/pearl-elog.ipf +++ b/pearl/pearl-elog.ipf @@ -1,5 +1,5 @@ #pragma rtGlobals=3 // Use modern global access method and strict wave access. -#pragma version = 1.31 +#pragma version = 1.40 #pragma IgorVersion = 6.2 #pragma ModuleName = PearlElog @@ -16,13 +16,38 @@ /// @ingroup ArpesPackage /// /// -/// the functions in this module support the following work flows: -/// 1. send a preview of a selected measurement to ELOG. -/// 2. send any Igor graph to ELOG. (CLI and GUI) -/// 3. direct access to all ELOG parameters. (CLI only) +/// the functions in this module support the following ELOG features: +/// - submit new entries and replies to existing entries. +/// - text field, list box, and check box attributes. +/// - attach any Igor graph to ELOG. +/// - configurable logbook templates for logbooks that share the same configuration. +/// - common server configurations available on the ELOG command line +/// (hostname, port, SSL, username, password, sub-directory). +/// - not specific to the configuration at PEARL. +/// PEARL code is concentrated in the elog_init_pearl_templates() function. +/// - the configuration of the ELOG server and logbooks +/// as well as the most recently used attributes are persisted in the preference file. /// -/// the configuration of the ELOG server and logbooks (except user name and password) -/// as well as the most recently used attributes are persisted in the preference file. +/// usage: +/// 1. the administrator of the ELOG server creates logbook templates +/// according to the configuration of the logbooks. +/// the templates are written in Igor code. +/// 2. the user opens logbooks via the _Open ELOG panel_ menu item. +/// before first use, select a template and enter a name for the logbook. +/// the new logbook is written to the preference file, +/// and can afterwards be opened directly. +/// 3. if the server requires a user name and password, +/// click the login button. +/// 4. edit the message, attributes and attachments as necessary, and submit to ELOG. +/// 5. log out before saving the experiment to clear the password. +/// +/// @attention the user name and password are stored in the global data tree of an experiment. +/// it is not possible to handle passwords safely in Igor. +/// they can be read by anyone having access to an open Igor experiment or a saved experiment file +/// (unless the password is reset before saving). +/// therefore: +/// - use a password for the ELOG server which is different from your other passwords. +/// - clear the password (logout button in the panel) before saving an experiment. /// /// elog command line ///@verbatim @@ -43,12 +68,6 @@ /// -m ] | ///@endverbatim /// -/// user name and password are used if configured in the package data folder. -/// there is currently no separate user interface. -/// -/// @attention some functions in this module refer specifically to the ELOG configuration at PEARL. -/// -/// @todo ask for logbook, username and password before opening the panel. /// /// @author matthias muntwiler, matthias.muntwiler@psi.ch /// @@ -82,7 +101,7 @@ function pearl_elog(logbook) load_prefs() string templates = list_logbooks(templates=1) if (ItemsInList(templates) < 1) - init_pearl_templates() + elog_init_pearl_templates() endif endif @@ -95,7 +114,11 @@ function pearl_elog(logbook) if (strlen(WinList(win_name, ";", "")) > 0) DoWindow /F $win_name else - PearlElogPanel(logbook) + win_name = PearlElogPanel(logbook) + STRUCT WMWinHookStruct s + s.eventCode = 0 + s.winName = win_name + elog_panel_hook(s) endif endif end @@ -104,6 +127,7 @@ end static function IgorBeforeNewHook(igorApplicationNameStr) string igorApplicationNameStr save_prefs() + cleanup_temp_files() return 0 end @@ -111,6 +135,7 @@ end static function IgorQuitHook(igorApplicationNameStr) string igorApplicationNameStr save_prefs() + cleanup_temp_files() return 0 end @@ -234,7 +259,7 @@ end /// /// @remark this function is specific to the setup at PEARL. /// -static function init_pearl_templates() +function elog_init_pearl_templates() dfref savedf = getdatafolderdfr() dfref df_root = get_elog_df("", kdfRoot) @@ -247,10 +272,10 @@ static function init_pearl_templates() // attributes (persistent) // available attributes - string /g attributes = "author;project;sample;source;task;technique;valid;file" + string /g attributes = "author;project;sample;source;task;technique;file;valid;" // controls corresponding to attributes // prefix determines the control type: sv_ = setvariable (string), pm_ = popup menu, cb = check box - string /g controls = "sv_author;sv_project;sv_sample;pm_source;pm_task;pm_technique;cb_valid;sv_file" + string /g controls = "sv_author;sv_project;sv_sample;pm_source;pm_task;pm_technique;sv_file;cb_valid;" // attributes with fixed options, value item declares the options string string /g options = "source=sources;task=tasks;technique=techniques" // attributes which must be defined @@ -258,8 +283,8 @@ static function init_pearl_templates() // option lists string /g sources = "Manual Entry;PShell;Scienta Data;SScan Data;Prosilica Data;OTF Data;Beamline Status;LEED Data;QMS Data;Matrix Data;Igor Pro;Other" - string /g tasks = "Measurement;Sample Preparation;Sample Storage;Optimization;Analysis;Development;Maintenance;Test;Comment;Other" - string /g techniques = "XPS;UPS;XPD;XAS;XMCD;PhD;ARUPS;LEED;AES;STM;STS;QMS;MBE;Test;Other" + string /g tasks = "Measurement;Optimization;Analysis;Sample Preparation;Sample Storage;Comment;Development;Maintenance;Test;Other" + string /g techniques = "XPS;UPS;XPD;XAS;XMCD;PhD;ARUPS;STM;STS;LEED;AES;QMS;MBE;Sputter/Anneal;Test;Other" // Calculations template setdatafolder df_templates @@ -267,18 +292,38 @@ static function init_pearl_templates() // attributes (persistent) // available attributes - string /g attributes = "author;project;sample;program;revision;machine;job;source path;result path;valid" + string /g attributes = "author;project;sample;program;revision;machine;job;experiment;source path;result path;valid" // controls corresponding to attributes // prefix determines the control type: sv_ = setvariable (string), pm_ = popup menu, cb = check box - string /g controls = "sv_author;sv_project;sv_sample;pm_program;sv_revision;pm_machine;sv_job;sv_sourcepath;sv_resultpath;cb_valid" + string /g controls = "sv_author;sv_project;sv_sample;pm_program;sv_revision;pm_machine;sv_job;sv_experiment;sv_sourcepath;sv_resultpath;cb_valid" // attributes with fixed options, value item declares the options string string /g options = "program=programs;machine=machines" // attributes which must be defined string /g required_attributes = "author;project;sample" // option lists - string /g programs = "DMSUP;EDAC;MSC;MUFPOT;SSC" - string /g machines = "llcx;Merlin;PC" + string /g programs = "PMSCO;EDAC;MSC;SSC;MUFPOT;DMSUP;Other" + string /g machines = "PC;VM;Ra;Merlin;llcx;Other" + + // System template + setdatafolder df_templates + newdatafolder /o/s System + + // attributes (persistent) + // available attributes + string /g attributes = "author;type;system;source;file" + // controls corresponding to attributes + // prefix determines the control type: sv_ = setvariable (string), pm_ = popup menu, cb = check box + string /g controls = "sv_author;pm_type;pm_system;pm_source;sv_file" + // attributes with fixed options, value item declares the options string + string /g options = "type=types;system=systems;source=sources" + // attributes which must be defined + string /g required_attributes = "author;type;system" + + // option lists + string /g types = "Installation;Repair;Maintenance;Test;Commissioning;Bakeout;Incident;Cool-down;Warm-up;Storage;Other" + string /g systems = "Vacuum;Control System;BL;XA;XP;SA;SP;T;LL;Monochromator;Carving;Scienta;STM;PC-Scienta;PC-Matrix;PC-Console;PC-Console-Win;PC-XP;EPS;LAC;Desiccator" + string /g sources = "Manual Entry;PShell;Scienta Data;SScan Data;Prosilica Data;OTF Data;Beamline Status;LEED Data;QMS Data;Matrix Data;Igor Pro;Other" setdatafolder savedf return 0 @@ -303,6 +348,11 @@ static function init_volatile_vars() variable nlb = ItemsInList(logbooks) variable ilb + SetDataFolder df_volatile_root + if (exists("temp_graph_files") != 2) + string /g temp_graph_files = "" + endif + for (ilb = 0; ilb < nlb; ilb += 1) logbook = StringFromList(ilb, logbooks) @@ -322,6 +372,13 @@ static function init_volatile_vars() if (exists("msg_id") != 2) variable /g msg_id = 0 endif + if (exists("att_list") != 1) + make /n=(0,3) /t /o attach_list + make /n=(0,3) /i /o attach_sel + endif + if (exists("url") != 2) + string /g url = "" + endif endfor SetDataFolder savedf @@ -347,6 +404,9 @@ end /// create a new empty logbook or duplicate from a template. /// /// @param name name of the new logbook. +/// if the logbook exists, the existing logbook folder is killed +/// and replaced by a new one. +/// this may fail if a window is still open. /// /// @param template name of the template. /// if empty string, a new empty logbook is created. @@ -368,6 +428,10 @@ function elog_create_logbook(name, [template]) if (strlen(template) > 0) dfref df_template = get_elog_df(template, kdfTemplates) + dfref df_existing = get_elog_df(name, kdfPersistent) + if (DataFolderRefStatus(df_existing)) + KillDataFolder /Z df_existing + endif DuplicateDataFolder df_template, df_persistent_parent:$name else NewDataFolder /o/s df_persistent_parent:$name @@ -695,7 +759,6 @@ function elog_create_entry(logbook, attributes, message, [encoding, graphs, repl endif result = -4 endif - cleanup_temp_files() else if (loglevel >= 2) print "ELOG: failed to create temporary message file." @@ -759,7 +822,6 @@ function elog_add_attachment(logbook, id, graphs) endif result = -4 // error: elog returned error endif - cleanup_temp_files() endif setdatafolder savedf @@ -817,6 +879,39 @@ static function /s prepare_command_line(logbook) return cmd end +/// format the URL for display to the user +/// +/// +/// @param logbook name of the target logbook +/// +static function /s format_url(logbook) + string logbook + + dfref df_general = get_elog_df("", kdfPersistent) + + svar /sdfr=df_general hostname + nvar /sdfr=df_general port + nvar /sdfr=df_general ssl + svar /sdfr=df_general subdir + + string cmd = "" + if ((nvar_exists(ssl)) && (ssl != 0)) + cmd += "https://" + else + cmd += "http://" + endif + cmd += hostname + if ((nvar_exists(port)) && (port > 0)) + cmd += ":" + num2str(port) + endif + if ((svar_exists(subdir)) && (strlen(subdir) > 0)) + cmd += "/" + subdir + endif + cmd += "/" + logbook + + return cmd +end + /// prepare screenshots of graph windows for attachments /// /// prepares the attachment files from Igor graph windows @@ -875,13 +970,18 @@ static function /s create_graph_file(graphname, fileindex) string graphname variable fileindex + dfref df_volatile_root = get_elog_df("", kdfVolatile) + svar /sdfr=df_volatile_root temp_graph_files + string path = SpecialDirPath("Temporary", 0, 1, 0) string ts = get_timestamp("_") variable len = strlen(path) if (numtype(len) == 0) path += "elog_" + ts + "_" + num2str(fileindex) + ".png" SavePICT /B=72 /E=-5 /M /O /W=(0,0,8,6) /WIN=$graphname /Z as path - if (v_flag != 0) + if (v_flag == 0) + temp_graph_files = AddListItem(path, temp_graph_files, ";", inf) + else path = "" endif else @@ -921,12 +1021,30 @@ static function /s get_log_path() return path end -static function /s cleanup_temp_files() - string path = SpecialDirPath("Temporary", 0, 1, 0) - string cmd - sprintf cmd, "del \"%s\elog*.*\"", path - //ExecuteScriptText cmd - return path +/// delete temporary files created by the ELOG module. +/// +/// this deletes all temporary graph files that are referenced by the volatile temp_graph_files list. +/// temp_graph_files is a semicolon-delimited string. +/// items are added by create_graph_file(). +/// +/// this function should be called before a new experiment is loaded or igor quits. +/// +static function cleanup_temp_files() + dfref df_volatile_root = get_elog_df("", kdfVolatile) + if (DataFolderRefStatus(df_volatile_root)) + svar /sdfr=df_volatile_root /z temp_graph_files + if (SVAR_Exists(temp_graph_files)) + variable nfi = ItemsInList(temp_graph_files) + variable ifi + string sfi + for (ifi = 0; ifi < nfi; ifi += 1) + sfi = StringFromList(ifi, temp_graph_files) + DeleteFile /Z sfi + endfor + temp_graph_files = "" + endif + endif + return 0 end static strconstant elog_success_msg = "Message successfully transmitted" @@ -1048,14 +1166,17 @@ function /s PearlElogPanel(logbook) string win_name = logbook + "ElogPanel" string win_title = "ELOG " + logbook - NewPanel /K=1 /N=$win_name /W=(600,200,926,494) as win_title + NewPanel /K=1 /N=$win_name /W=(600,200,1200,700) as win_title win_name = s_name ModifyPanel /w=$win_name cbRGB=(52224,52224,65280) svar /sdfr=df_persistent attributes svar /sdfr=df_persistent controls svar /sdfr=df_persistent options - + wave /t /sdfr=df_volatile attach_list + wave /sdfr=df_volatile attach_sel + svar /sdfr=df_volatile url + variable iattr variable nattr = ItemsInList(attributes, ";") string s_attr @@ -1066,35 +1187,28 @@ function /s PearlElogPanel(logbook) string options_path string variable_path variable ypos = 2 + variable height = 0 - Button b_login,win=$win_name, pos={264,ypos},size={46,20},proc=PearlElog#bp_login,title="Login" - Button b_login,win=$win_name, help={"Enter user name and password."} - Button b_login,win=$win_name, fcolor=(56576,60928,47872) - - Button b_logout,win=$win_name, pos={264,ypos},size={46,20},proc=PearlElog#bp_logout,title="Logout" - Button b_logout,win=$win_name, help={"Clear user name and password."} - Button b_logout,win=$win_name, fcolor=(56576,60928,47872), disable=3 - for (iattr = 0; iattr < nattr; iattr += 1) s_attr = StringFromList(iattr, attributes, ";") s_control = StringFromList(iattr, controls, ";") strswitch(s_control[0,1]) case "sv": - SetVariable $s_control, win=$win_name, pos={0,ypos}, size={260,16}, bodyWidth=200 + SetVariable $s_control, win=$win_name, pos={0,ypos}, size={300,16}, bodyWidth=230 SetVariable $s_control, win=$win_name, title=s_attr, value= _STR:"" SetVariable $s_control, win=$win_name, userdata(attribute)=s_attr ypos += 18 break case "pm": options_path = persistent_path + StringByKey(s_attr, options, "=", ";") - PopupMenu $s_control, win=$win_name, pos={0,ypos}, size={260,21}, bodyWidth=200 + PopupMenu $s_control, win=$win_name, pos={0,ypos}, size={300,21}, bodyWidth=230 PopupMenu $s_control, win=$win_name, title=s_attr PopupMenu $s_control, win=$win_name, mode=1, popvalue="Test", value= #options_path PopupMenu $s_control, win=$win_name, userdata(attribute)=s_attr ypos += 23 break case "cb": - CheckBox $s_control, win=$win_name, pos={60,ypos}, size={260,14} + CheckBox $s_control, win=$win_name, pos={70,ypos}, size={300,14} CheckBox $s_control, win=$win_name, title=s_attr, value= 1 CheckBox $s_control, win=$win_name, userdata(attribute)=s_attr ypos += 17 @@ -1102,48 +1216,81 @@ function /s PearlElogPanel(logbook) endswitch endfor - PopupMenu pm_attach,win=$win_name, pos={0,ypos},size={260,21},bodyWidth=200,title="Attachment" - PopupMenu pm_attach,win=$win_name, mode=1,popvalue="(none)",value=PearlElog#pm_list_attach_items() - PopupMenu pm_attach,win=$win_name, help={"Choose any visible Igor graph for attachment."} - Button b_select_attach_top,win=$win_name, pos={264,ypos},size={60,20},proc=PearlElog#bp_select_attach_top,title="Top Graph" - Button b_select_attach_top,win=$win_name, help={"Select top graph window."} - Button b_select_attach_top,win=$win_name, fcolor=(56576,60928,47872) + TitleBox t_attach, win=$win_name, pos={308,5}, size={70,14}, title="Attachments", frame=0 + height = ypos - 21 - 4 + ListBox lb_attach, win=$win_name, pos={308,21}, size={264,height} + ListBox lb_attach, win=$win_name, listWave=attach_list + ListBox lb_attach, win=$win_name, mode=1, selWave=attach_sel, selRow=-1 + ListBox lb_attach, win=$win_name, widths={20,160,80} + ListBox lb_attach, win=$win_name, help={"Choose graphs to attach to the message."} + + Button b_attach_top, win=$win_name, pos={420,2}, size={40,18}, title="top" + Button b_attach_top, win=$win_name, fcolor=(56576,60928,47872) + Button b_attach_top, win=$win_name, proc=PearlElog#bp_attach_top + Button b_attach_top, win=$win_name, help={"Select top graph for attachment."} + Button b_attach_all, win=$win_name, pos={460,2}, size={40,18}, title="all" + Button b_attach_all, win=$win_name, fcolor=(56576,60928,47872) + Button b_attach_all, win=$win_name, proc=PearlElog#bp_attach_allnone + Button b_attach_all, win=$win_name, help={"Select all graphs for attachment."} + Button b_attach_none, win=$win_name, pos={500,2}, size={40,18}, title="none" + Button b_attach_none, win=$win_name, fcolor=(56576,60928,47872) + Button b_attach_none, win=$win_name, proc=PearlElog#bp_attach_allnone + Button b_attach_none, win=$win_name, help={"Deselect all attachments."} + Button b_save_graphs, win=$win_name, pos={540,2}, size={40,18}, title="save" + Button b_save_graphs, win=$win_name, fcolor=(56576,60928,47872) + Button b_save_graphs, win=$win_name, proc=PearlElog#bp_save_graphs + Button b_save_graphs, win=$win_name, help={"Save selected graphs as PNG bitmap files."} + Button b_attach_up, win=$win_name, pos={576,20}, size={20,20}, title="\\W517" + Button b_attach_up, win=$win_name, fcolor=(56576,60928,47872) + Button b_attach_up, win=$win_name, proc=PearlElog#bp_attach_updown + Button b_attach_up, win=$win_name, help={"Move selected graph up."} + Button b_attach_dw, win=$win_name, pos={576,40}, size={20,20}, title="\\W523" + Button b_attach_dw, win=$win_name, fcolor=(56576,60928,47872) + Button b_attach_dw, win=$win_name, proc=PearlElog#bp_attach_updown + Button b_attach_dw, win=$win_name, help={"Move selected graph down."} ypos += 246-160 - Button b_submit,win=$win_name, pos={60,ypos},size={46,20},proc=PearlElog#bp_submit,title="Submit" + Button b_submit,win=$win_name, pos={70,ypos},size={46,20},proc=PearlElog#bp_submit,title="Submit" Button b_submit,win=$win_name, help={"Submit form data to ELOG (new entry)."} Button b_submit,win=$win_name, fcolor=(56576,60928,47872) - - Button b_clear,win=$win_name, pos={110,ypos},size={46,20},proc=PearlElog#bp_clear,title="Clear" + Button b_clear,win=$win_name, pos={120,ypos},size={46,20},proc=PearlElog#bp_clear,title="Clear" Button b_clear,win=$win_name, help={"Clear the form fields"} Button b_clear,win=$win_name, fcolor=(56576,60928,47872) ypos += 272-246 variable_path = volatile_path + "msg_id" - SetVariable sv_id,win=$win_name, pos={46,ypos},size={109,16},bodyWidth=94 + SetVariable sv_id,win=$win_name, pos={51,ypos},size={119,16},bodyWidth=77 SetVariable sv_id,win=$win_name, title="ID",value=$variable_path SetVariable sv_id,win=$win_name, help={"ID of last submitted message, or message to attach or reply to."} + TitleBox t_host, win=$win_name, pos={170,ypos+4}, size={112.00,14.00}, frame=0 + TitleBox t_host, win=$win_name, variable=url + ypos += 270-272 - Button b_attach,win=$win_name, pos={160,ypos},size={48,20},proc=PearlElog#bp_attach,title="Attach" + Button b_attach,win=$win_name, pos={170,ypos},size={48,20},proc=PearlElog#bp_attach,title="Attach" Button b_attach,win=$win_name, help={"Attach the selected graph to an existing ELOG entry (correct ID required)."} Button b_attach,win=$win_name, fcolor=(56576,60928,47872) - - Button b_reply,win=$win_name, pos={210,ypos},size={48,20},proc=PearlElog#bp_submit,title="Reply" + Button b_reply,win=$win_name, pos={220,ypos},size={48,20},proc=PearlElog#bp_submit,title="Reply" Button b_reply,win=$win_name, help={"Submit form data to ELOG as a reply to an existing message (correct ID required)."} Button b_reply,win=$win_name, fcolor=(56576,60928,47872) - - ypos += 184-270 - TitleBox t_message,win=$win_name, pos={0,ypos},size={58,16},fixedSize=1,frame=0,anchor=RT,title="Message" + Button b_login,win=$win_name, pos={550,ypos},size={46,20},proc=PearlElog#bp_login,title="Login" + Button b_login,win=$win_name, help={"Enter user name and password."} + Button b_login,win=$win_name, fcolor=(56576,60928,47872) + Button b_logout,win=$win_name, pos={550,ypos},size={46,20},proc=PearlElog#bp_logout,title="Logout" + Button b_logout,win=$win_name, help={"Clear user name and password."} + Button b_logout,win=$win_name, fcolor=(56576,60928,47872), disable=3 SetWindow $win_name, hook(elogPanelHook)=PearlElog#elog_panel_hook SetWindow $win_name, userdata(logbook)=logbook - DefineGuide UGH0={FT,ypos},UGV0={FL,60},UGH1={FB,-52},UGV1={FR,-2} + ypos += 160-270 + TitleBox t_message,win=$win_name, pos={10,ypos},size={58,16},fixedSize=1,frame=0,anchor=RT,title="Message" + DefineGuide UGH0={FT,ypos},UGV0={FL,70},UGH1={FB,-52},UGV1={FR,-2} NewNotebook /F=0 /N=Message /OPTS=3 /W=(115,404,345,341)/FG=(UGV0,UGH0,UGV1,UGH1) /HOST=# Notebook kwTopWin, defaultTab=20, statusWidth=0, autoSave=0 - Notebook kwTopWin font="Arial", fSize=10, fStyle=0, textRGB=(0,0,0) + Notebook kwTopWin fSize=10, fStyle=0, textRGB=(0,0,0) RenameWindow #,Message + string nb_name = win_name + "#Message" SetActiveSubwindow ## // restore recently used attributes and message @@ -1155,6 +1302,7 @@ function /s PearlElogPanel(logbook) if (svar_exists(recent_message) && (strlen(recent_message) > 0)) set_panel_message(win_name, recent_message) endif + Notebook $nb_name selection={startOfFile,startOfFile}, findText={"",1} setdatafolder savedf return win_name @@ -1166,49 +1314,152 @@ static function elog_panel_hook(s) Variable hookResult = 0 switch(s.eventCode) + case 0: // activate + string logbook = GetUserData(s.winName, "", "logbook") + if (strlen(logbook) > 0) + dfref df_volatile = get_elog_df(logbook, kdfVolatile) + svar /sdfr=df_volatile url + url = format_url(logbook) + update_attach_items(logbook) + endif + break case 6: // resize // move bottom-aligned controls when the window is resized variable b_top = s.winRect.bottom + 4 - Button b_submit,pos={60,b_top} - Button b_clear,pos={110,b_top} + Button b_submit,pos={70,b_top} + Button b_clear,pos={120,b_top} + TitleBox t_host, pos={170,b_top+4} b_top += 24 - Button b_attach,pos={160,b_top} - Button b_reply,pos={210,b_top} + Button b_attach,pos={170,b_top} + Button b_reply,pos={220,b_top} + Button b_login, pos={550,b_top} + Button b_logout, pos={550,b_top} b_top += 2 - SetVariable sv_id,pos={46,b_top} + SetVariable sv_id,pos={51,b_top} break endswitch return hookResult // 0 if nothing done, else 1 end -/// get a list of graph windows for a popup menu. -static function /s pm_list_attach_items() +static constant kAttachColSel = 0 +static constant kAttachColTitle = 1 +static constant kAttachColName = 2 - // get graph names +/// update the list of attachments +static function update_attach_items(logbook) + string logbook + + dfref savedf = getdatafolderdfr() + dfref df_volatile = get_elog_df(logbook, kdfVolatile) + wave /t /sdfr=df_volatile attach_list + wave /sdfr=df_volatile attach_sel + + if (!waveexists(attach_list)) + return -1 + endif string names = WinList("*", ";", "WIN:1;VISIBLE:1") names = SortList(names, ";", 16) - - // get corresponding graph titles - variable nnames = ItemsInList(names, ";") - variable iname - string name - string item - string items = "" - - // format each entry like "name: title" - for (iname = 0; iname < nnames; iname += 1) - name = StringFromList(iname, names, ";") - getwindow /z $name, wtitle - if (v_flag == 0) - item = ReplaceString(";", s_value, ", ") // title - item = item + " (" + name + ")" - items = AddListItem(item, items, ";", inf) + + // remove closed graphs + variable i + variable k + variable n = DimSize(attach_list, 0) + string s + for (i = n-1; i >= 0; i -= 1) + s = attach_list[i][kAttachColName] + if (WhichListItem(s, names) < 0) + DeletePoints /M=0 i, 1, attach_list, attach_sel endif endfor - items = AddListItem("(none)", items, ";") - return items + // add new graphs + n = ItemsInList(names) + for (i = 0; i < n; i += 1) + s = StringFromList(i, names) + FindValue /text=s /txop=4 /z attach_list + if (v_value < 0) + k = DimSize(attach_list, 0) + InsertPoints /M=0 k, 1, attach_list, attach_sel + attach_list[i][kAttachColSel] = "" + attach_list[i][kAttachColTitle] = "" + attach_list[k][kAttachColName] = s + attach_sel[i][kAttachColSel] = 32 + attach_sel[i][kAttachColTitle] = 0 + attach_sel[i][kAttachColName] = 0 + endif + endfor + + // update titles + n = DimSize(attach_list, 0) + for (i = n-1; i >= 0; i -= 1) + s = attach_list[i][kAttachColName] + getwindow /z $s, wtitle + if (v_flag == 0) + attach_list[i][kAttachColTitle] = s_value + else + attach_list[i][kAttachColTitle] = s + endif + endfor + + setdatafolder savedf + return 0 +end + +/// move an attachment item in the list of attachments +static function move_attach_item(logbook, item, distance) + string logbook + variable item + variable distance + + dfref savedf = getdatafolderdfr() + dfref df_volatile = get_elog_df(logbook, kdfVolatile) + wave /t /sdfr=df_volatile attach_list + wave /sdfr=df_volatile attach_sel + variable n = DimSize(attach_list, 0) + variable dest = item + distance + + if ((item >= 0) && (item < n) && (dest >= 0) && (dest < n)) + string name = attach_list[item][kAttachColName] + variable sel = attach_sel[item][kAttachColSel] + DeletePoints /M=0 item, 1, attach_list, attach_sel + InsertPoints /M=0 dest, 1, attach_list, attach_sel + attach_list[dest][kAttachColName] = name + update_attach_items(logbook) + attach_sel[dest][kAttachColSel] = sel + endif +end + +/// button procedure for the attachment up and down buttons +static function bp_attach_updown(ba) : ButtonControl + STRUCT WMButtonAction &ba + + switch( ba.eventCode ) + case 2: // mouse up + string logbook = GetUserData(ba.win, "", "logbook") + ControlInfo /w=$ba.win lb_attach + variable row = v_value + dfref df = $s_datafolder + wave /t /sdfr=df attach_list = $s_value + if (cmpstr(ba.ctrlName, "b_attach_up") == 0) + // up button + if (row >= 1) + move_attach_item(logbook, row, -1) + ListBox lb_attach, win=$ba.win, selRow=(row-1) + endif + else + // down button + if (row < DimSize(attach_list, 0) - 1) + move_attach_item(logbook, row, +1) + ListBox lb_attach, win=$ba.win, selRow=(row+1) + endif + endif + break + case -1: // control being killed + break + endswitch + + return 0 end /// button procedure for the Submit and Reply buttons @@ -1259,7 +1510,7 @@ static function bp_submit(ba) : ButtonControl end /// select top graph window for attachment -static function bp_select_attach_top(ba) : ButtonControl +static function bp_attach_top(ba) : ButtonControl STRUCT WMButtonAction &ba switch( ba.eventCode ) @@ -1274,6 +1525,28 @@ static function bp_select_attach_top(ba) : ButtonControl return 0 end +/// select/deselect all graph windows for attachment +static function bp_attach_allnone(ba) : ButtonControl + STRUCT WMButtonAction &ba + + switch( ba.eventCode ) + case 2: // mouse up + string logbook = GetUserData(ba.win, "", "logbook") + dfref df_volatile = get_elog_df(logbook, kdfVolatile) + wave /sdfr=df_volatile attach_sel + if (cmpstr(ba.ctrlName, "b_attach_all") == 0) + attach_sel[][kAttachColSel] = attach_sel[p][kAttachColSel] | 16 + else + attach_sel[][kAttachColSel] = attach_sel[p][kAttachColSel] & ~16 + endif + break + case -1: // control being killed + break + endswitch + + return 0 +end + static function bp_attach(ba) : ButtonControl STRUCT WMButtonAction &ba @@ -1307,6 +1580,34 @@ static function bp_attach(ba) : ButtonControl return 0 end +static function bp_save_graphs(ba) : ButtonControl + STRUCT WMButtonAction &ba + + switch( ba.eventCode ) + case 2: // mouse up + string logbook = GetUserData(ba.win, "", "logbook") + string graphs = get_panel_graphs(ba.win) + variable ngraphs = ItemsInList(graphs, ";") + + variable igraph + string sgraph + string graph_path + for (igraph = 0; igraph < ngraphs; igraph += 1) + sgraph = StringFromList(igraph, graphs, ";") + graph_path = create_graph_file(sgraph, igraph) + if (strlen(graph_path) > 0) + print graph_path + endif + endfor + + break + case -1: // control being killed + break + endswitch + + return 0 +end + static function bp_clear(ba) : ButtonControl STRUCT WMButtonAction &ba @@ -1522,15 +1823,14 @@ end /// get the names of the graphs selected for attachment /// +/// @param windowname panel window name /// @returns a semicolon-separated list, /// or the empty string if the selection is not valid. /// -/// in the current version, the function returns at most one graph. -/// in future versions, the function may return more than one graph. -/// static function /s get_panel_graphs(windowname) string windowname // panel window name + dfref savedf = getdatafolderdfr() if (strlen(windowname) == 0) windowname = get_default_panel_name() endif @@ -1538,24 +1838,38 @@ static function /s get_panel_graphs(windowname) return "" endif - ControlInfo /W=$windowname pm_attach - - // menu item has the form: "title (name)" - string graphname = ReplaceString(")", StringFromList(ItemsInList(s_value, "(") - 1, s_value, "("), "") + string logbook = GetUserData(windowname, "", "logbook") + dfref df_volatile = get_elog_df(logbook, kdfVolatile) + wave /t /sdfr=df_volatile attach_list + wave /sdfr=df_volatile attach_sel string graphs = "" - string windows = WinList(graphname, ";", "WIN:1") - if (ItemsInList(windows) == 1) - graphs = graphname - else - graphs = "" - endif + string windows = "" + string graphname + + variable n = DimSize(attach_sel, 0) + variable i + for (i = 0; i < n; i += 1) + if (attach_sel[i][kAttachColSel] & 16) + graphname = attach_list[i][kAttachColName] + windows = WinList(graphname, ";", "WIN:1") + if (ItemsInList(windows) == 1) + graphs = AddListItem(graphname, graphs, ";", inf) + endif + endif + endfor return graphs end +/// update selection of graphs for attachment +/// +/// @param windowname panel window name. looks for default panel if empty. +/// +/// @param graphs semicolon-separated list of names of graph windows to select for attachment. +/// static function /s set_panel_graphs(windowname, graphs) - string windowname // panel window name. looks for default panel if empty. - string graphs // name of graph window to select for attachment. + string windowname + string graphs if (strlen(windowname) == 0) windowname = get_default_panel_name() @@ -1564,23 +1878,21 @@ static function /s set_panel_graphs(windowname, graphs) return "" endif - // the panel supports only one graph - string graph = StringFromList(0, graphs, ";") - - string items = pm_list_attach_items() - string item - string itemname - variable nitems = ItemsInList(items, ";") - variable iitem - for (iitem = 0; iitem < nitems; iitem += 1) - item = StringFromList(iitem, items, ";") - itemname = ReplaceString(")", StringFromList(ItemsInList(item, "(") - 1, item, "("), "") - if (cmpstr(graph, itemname) == 0) - iitem += 1 - PopupMenu pm_attach mode=iitem, win=$windowname - break + string logbook = GetUserData(windowname, "", "logbook") + update_attach_items(logbook) + dfref df_volatile = get_elog_df(logbook, kdfVolatile) + wave /t /sdfr=df_volatile attach_list + wave /sdfr=df_volatile attach_sel + + variable n = DimSize(attach_sel, 0) + variable i + string graphname + for (i = 0; i < n; i += 1) + graphname = attach_list[i][kAttachColName] + if (WhichListItem(graphname, graphs)>= 0) + attach_sel[i][kAttachColSel] = 48 + else + attach_sel[i][kAttachColSel] = 32 endif endfor - - return graph end \ No newline at end of file diff --git a/pearl/pearl-matrix-import.ipf b/pearl/pearl-matrix-import.ipf new file mode 100644 index 0000000..82294a8 --- /dev/null +++ b/pearl/pearl-matrix-import.ipf @@ -0,0 +1,900 @@ +#pragma rtGlobals=3 +#pragma version = 1.00 +#pragma IgorVersion = 6.36 +#pragma ModuleName = PearlMatrixImport + +// author: matthias.muntwiler@psi.ch +// Copyright (c) 2016 Paul Scherrer Institut + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 + +/// @file +/// @brief data file import for omicron matrix (STM) files +/// +/// the matrix file import requires the matrix file reader XOP by thomas braun +/// (http://www.igorexchange.com/project/matrixFileReader) +/// which in turn requires an installation of vernissage by omicron nanotechnology. +/// +/// @warning EXPERIMENTAL +/// the matrix import module and its interface may change radically in future revisions! +/// +/// @author matthias muntwiler, matthias.muntwiler@psi.ch +/// +/// @copyright 2016 Paul Scherrer Institut @n +/// Licensed under the Apache License, Version 2.0 (the "License"); @n +/// you may not use this file except in compliance with the License. @n +/// You may obtain a copy of the License at +/// http://www.apache.org/licenses/LICENSE-2.0 + +/// @namespace PearlMatrixImport +/// @brief data file import for omicron matrix (STM) files +/// +/// PearlMatrixImport is declared in @ref pearl-matrix-import.ipf. + +static strconstant package_name = "pearl_matrix_import" +static strconstant package_path = "root:packages:pearl_matrix_import:" + +static strconstant ks_filematch_mtrx = "*_mtrx" + +/// initialize the package data folder. +/// +/// +static function init_package() + + dfref savedf = getdatafolderdfr() + + setdatafolder root: + newdatafolder /o/s packages + newdatafolder /o/s $package_name + + variable /g loglevel = 3 + string /g dataFilePath = "" + string /g resultFilePath = "" + variable /g runCycle = 0 + variable /g scanCycle = 0 + string /g channelName = "" + variable /g brickletID = 0 + variable /g V_MatrixFileReaderOverwrite = 1 + variable /g V_MatrixFileReaderFolder = 0 + variable /g V_MatrixFileReaderDouble = 0 + + setdatafolder savedf + return 0 +end + +/// check that the package data folder exists +/// +/// initialize the package if the folder does not exist. +/// +static function check_package_folder() + dfref df_pack = $(package_path) + if (DataFolderRefStatus(df_pack)) + svar /sdfr=df_pack /z resultFilePath + if (!svar_exists(resultFilePath)) + init_package() + endif + else + init_package() + endif +end + +/// initialize the package and reload preferences after an experiment is loaded. +static function AfterFileOpenHook(refNum,file,pathName,type,creator,kind) + Variable refNum,kind + String file,pathName,type,creator + if( (kind >= 1) && (kind <= 2)) + init_package() + //load_prefs() + endif + return 0 +end + +/// open a matrix file that was dropped into Igor. +/// +/// preliminary implementation. +/// this should rather load the entire file and display a preview. +/// graph windows should be reused by subsequent loads. +/// also decide on a data saving location. +/// +static function BeforeFileOpenHook(refNum,fileName,path,type,creator,kind) + Variable refNum,kind + String fileName,path,type,creator + + Variable handledOpen = 0 + if (StringMatch(fileName, ks_filematch_mtrx)) + setdatafolder root: + newdatafolder /o /s matrix + mtrx_load_preview("matrix", path, fileName) + handledOpen = 1 + endif + return handledOpen +End + +/// generate elog message from bricklet metadata +/// +/// @param metadata two-column text wave +/// +function /s matrix_format_elog_message(metadata) + wave /t metadata + + string key + string value + variable nkeys = dimsize(metadata, 0) + variable ikey + string message_keys + message_keys = "resultFileName;sampleName;channelName;" + message_keys += "XYScanner.Points.value;XYScanner.Raster_Time.value;XYScanner.Raster_Time.unit;XYScanner.Width.value;XYScanner.Width.unit;XYScanner.Height.value;XYScanner.Height.unit;" + message_keys += "GapVoltageControl.Voltage.value;GapVoltageControl.Voltage.unit;" + message_keys += "Regulator.Loop_Gain_1_I.value;Regulator.Loop_Gain_1_I.unit;Regulator.Setpoint_1.value;Regulator.Setpoint_1.unit;" + message_keys += "Spectroscopy.Device_1_Start.value;Spectroscopy.Device_1_Start.unit;Spectroscopy.Spectroscopy_Mode.value;" + string message + + message_keys = "" + for (ikey = 0; ikey < nkeys; ikey += 1) + key = metadata[ikey][0] + value = metadata[ikey][1] + if (WhichListItem(key, message_keys) >= 0) + message += key + " = " + value + "\r" + endif + endfor +end + +function matrix_preview_2d(data, metadata) + wave data + wave /t metadata + + Display + AppendImage data + ModifyImage data ctab= {*,*,Mud,0} + ModifyGraph margin(left)=30,margin(bottom)=30,margin(top)=5,margin(right)=5,height={Plan,1,left,bottom} + ModifyGraph mirror=2 + ModifyGraph nticks=3 + ModifyGraph axThick=0.5 + ModifyGraph btLen=4 + +end + +/// load the preview of a Matrix data file +/// +/// the preview is loaded to the preview_image wave in the pearl_explorer data folder. +/// +/// the s_file_info string is updated with information about the scan dimensions. +/// +/// @param filename name of a file in the directory specified by the pearl_explorer_filepath path object. +/// +/// @return wave reference of the preview image +/// +static function /wave preview_matrix_file(filename) + string filename + + dfref saveDF = GetDataFolderDFR() + setdatafolder $package_path + svar s_preview_file + svar s_preview_source + mtrx_load_preview("preview_image", "pearl_explorer_filepath", filename) + s_preview_file = filename + s_preview_source = "" + wave /z preview_image + + svar /z s_file_info + if (! svar_exists(s_file_info)) + string /g s_file_info + endif + if (strlen(s_preview_file) > 0) + s_file_info = mtrx_load_info("pearl_explorer_filepath", filename) + else + s_file_info = "" + endif + + setdatafolder saveDF + return preview_image +end + +/// from matrixfilereader help +Structure errorCode + int32 SUCCESS + int32 UNKNOWN_ERROR + int32 ALREADY_FILE_OPEN + int32 EMPTY_RESULTFILE + int32 FILE_NOT_READABLE + int32 NO_NEW_BRICKLETS + int32 WRONG_PARAMETER + int32 INTERNAL_ERROR_CONVERTING_DATA + int32 NO_FILE_OPEN + int32 INVALID_RANGE + int32 WAVE_EXIST +EndStructure + +/// from matrixfilereader help +static Function initStruct(errorCode) + Struct errorCode &errorCode + + errorCode.SUCCESS =0 + errorCode.UNKNOWN_ERROR=10001 + errorCode.ALREADY_FILE_OPEN=10002 + errorCode.EMPTY_RESULTFILE=10004 + errorCode.FILE_NOT_READABLE=10008 + errorCode.NO_NEW_BRICKLETS=10016 + errorCode.WRONG_PARAMETER=10032 + errorCode.INTERNAL_ERROR_CONVERTING_DATA=10064 + errorCode.NO_FILE_OPEN=10128 + errorCode.INVALID_RANGE=10256 + errorCode.WAVE_EXIST=10512 + +end + +/// load all data from a Matrix data file. +/// +function mtrx_load_all() + + struct errorCode errorCode + initStruct(errorCode) + +#if exists("MFR_OpenResultFile") + MFR_OpenResultFile + if(V_flag != errorCode.SUCCESS) + MFR_GetXOPErrorMessage + return -1 + endif + + MFR_GetBrickletData + if(V_flag != errorCode.SUCCESS) + MFR_GetXOPErrorMessage + return -1 + endif + + MFR_GetBrickletMetaData + if(V_flag != errorCode.SUCCESS) + MFR_GetXOPErrorMessage + return -1 + endif + + return 0 +#else + return -1 +#endif +end + +/// parse matrix file names +/// +/// parse matrix file names for result name, run cycle, scan cycle, and channel. +/// +/// @param fileName matrix result or data file name (without path). +/// +/// @param resultFile (out) base name of the result file. +/// append "_%04u.mtrx" to get the actual result file. +/// we do not know the chain link number at this stage. +/// +/// @param runCycle (out) run cycle number. necessary to look up the bricklet ID. +/// +/// @param scanCycle (out) scan cycle number. necessary to look up the bricklet ID. +/// +/// @param channel (out) channel name. +/// +/// @return file type +/// @arg 0 result file (logbook) +/// @arg 1 result data file (bricklet) +/// +/// result file names look like: +/// default_2015Apr20-124353_STM-STM_AtomManipulation_0001.mtrx, +/// default_2015Apr20-124353_STM-STM_AtomManipulation_0002.mtrx, etc. +/// the function returns the first part up to the experiment name ("AtomManipulation" in the examples). +/// all other return values set to defaults and must not be regarded. +/// +/// result data files look like: +/// default_2015Apr20-124353_STM-STM_AtomManipulation--136_1.Aux1(V)_mtrx, +/// default_2015Apr20-124353_STM-STM_AtomManipulation--136_1.I(V)_mtrx, +/// default_2015Apr20-124353_STM-STM_AtomManipulation--14_1.I_mtrx, +/// default_2015Apr20-124353_STM-STM_AtomManipulation--14_1.Z_mtrx, etc. +/// the function returns all results as described in the parameter list. +/// +function mtrx_parse_filename(fileName, resultFile, runCycle, scanCycle, channel) + string fileName + string &resultFile + variable &runCycle + variable &scanCycle + string &channel + + variable fileType = 0 + resultFile = "" + channel = "" + runCycle = 0 + scanCycle = 0 + + string regexp = "" + string index1 = "" + string index2 = "" + string extension = "" + + if (StringMatch(fileName, "*.mtrx")) + regexp = "(.+)_([[:digit:]]+)\.(.+)" + SplitString /E=regexp fileName, resultFile, index1, extension + fileType = 0 + else + regexp = "(.+)--([[:digit:]]+)_([[:digit:]]+)\.((.+)_mtrx)" + SplitString /E=regexp fileName, resultFile, index1, index2, extension, channel + fileType = 1 + runCycle = str2num(index1) + scanCycle = str2num(index2) + endif + + return fileType +end + +/// split a matrix filename and return the first three parts +/// +/// we assume that the second (third) part contains the date (time). +/// the parts are separated by dash or underscore. +/// +function /s mtrx_split_filename(fileName, prefix, datepart, timepart) + string fileName + string &prefix + string &datepart + string &timepart + + string regexp + regexp = "([[:alpha:][:digit:]]+)[-_]([[:alpha:][:digit:]]+)[-_]([[:alpha:][:digit:]]+)[-_].+" + SplitString /E=regexp fileName, prefix, datepart, timepart + return datepart +end + +/// create or look up a data folder based on a matrix file name. +/// +/// the name of the folder is mtrx_date_time, where date and time are parsed from the file name. +/// for this to work, the file name must consist of at least three parts that are separated by dash or underscore. +/// the second (third) part contains the date (time). +/// date and time are copied as strings. +/// +/// if the data folder exists, a reference to the existing folder is returned. +/// +/// @param fileName name of the result or data file. +/// +/// @param df_base (optional) base data folder. +/// default: current folder. +/// +/// @return reference of the newly created or existing data folder. +/// +function /df mtrx_create_folder(fileName, [df_base]) + string fileName + dfref df_base + + if (ParamIsDefault(df_base)) + df_base = GetDataFolderDFR() + endif + + string prefix + string datepart + string timepart + string folderName + + mtrx_split_filename(fileName, prefix, datepart, timepart) + folderName = "mtrx_" + datepart + "_" + timepart + folderName = CleanupName(folderName, 0) + + dfref df_save = GetDataFolderDFR() + setdatafolder root: + newdatafolder /o /s $foldername + dfref df = GetDataFolderDFR() + + setdatafolder df_save + return df +end + +/// create a data folder for bricklet data. +/// +/// the name of the folder is, for example "r23s2" where the first (second) number is the run (scan) cycle. +/// run cycle and scan cycle numbers are taken from the open matrix file unless overridden by optional arguments. +/// +/// if the data folder exists, a reference to the existing folder is returned. +/// if one of the run or scan cycle numbers is lower than 1, the base folder is returned. +/// +/// @param df_base (optional) base data folder. +/// default: current folder. +/// +/// @param runCycle (optional) run cycle number. must be >= 1. +/// default: from last mtrx_open_file call. +/// +/// @param scanCycle (optional) scan cycle number. must be >= 1. +/// default: from last mtrx_open_file call. +/// +/// @return reference of the newly created or existing data folder. +/// +function /df mtrx_get_cycle_folder([df_base, runCycle, scanCycle]) + dfref df_base + variable runCycle + variable scanCycle + + dfref df_save = GetDataFolderDFR() + dfref df_pack = $(package_path) + if (ParamIsDefault(df_base)) + df_base = GetDataFolderDFR() + endif + if (ParamIsDefault(runCycle)) + nvar /sdfr=df_pack defRunCycle = runCycle + runCycle = defRunCycle + endif + if (ParamIsDefault(scanCycle)) + nvar /sdfr=df_pack defScanCycle = scanCycle + scanCycle = defScanCycle + endif + + string dfname + if ((runCycle >= 1) && (scanCycle >= 1)) + sprintf dfname, "r%us%u", runCycle, scanCycle + setdatafolder df_base + dfref df = $dfname + if (DataFolderRefStatus(df) == 0) + newdatafolder $dfname + dfref df = $dfname + endif + else + dfref df = df_base + endif + + setdatafolder df_save + return df +end + +/// find out bricklet ID of a file +/// +/// @warning EXPERIMENTAL +/// the code of this function is inefficient. +/// the function may be removed in a later version. +/// +/// @param resultFile base name of result file without chain link number and extension. +/// as returned by mtrx_parse_filename. +/// +/// @param runCycle requested run cycle. +/// 0 = first available. +/// +/// @param scanCycle requested scan cycle. +/// 0 = first available. +/// +/// @param channel channel name. for example: "I", "Z", "Aux(V)", etc. +/// empty string: first available. +/// +/// @return bricklet ID, or -1 if an error occurred. +/// +function mtrx_file_brickletID(resultFile, runCycle, scanCycle, channel) + string resultFile + variable runCycle + variable scanCycle + string channel + + dfref df_overview = NewFreeDataFolder() + variable link = 1 + variable id = -1 + variable idx = 0 + + string resultFileName +#if exists("MFR_OpenResultFile") + struct errorCode errorCode + initStruct(errorCode) + do + sprintf resultFileName, "%s_%04u.mtrx", resultFile, link + MFR_OpenResultFile /K resultFileName + if(V_flag != errorCode.SUCCESS) + return -1 + endif + MFR_CreateOverviewTable /DEST=df_overview + // dimension labels are: brickletID, scanCycleCount, runCycleCount, sequenceID, dimension, channelName + wave /t /sdfr=df_overview overviewTable + make /n=(dimsize(overviewTable, 0)) /i /u /free runcycles, scancycles, ids, match + make /n=(dimsize(overviewTable, 0)) /t /free channels + ids = str2num(overviewtable[p][%brickletID]) + if (runcycle > 0) + runcycles = str2num(overviewtable[p][%runCycleCount]) + else + runcycles = runcycle + endif + if (scancycle > 0) + scancycles = str2num(overviewtable[p][%scanCycleCount]) + else + scancycles = scancycle + endif + if (strlen(channel) > 0) + channels = overviewTable[p][%channelName] + else + channels = channel + endif + Extract /FREE ids, match_ids, (scancycles == scanCycle) && (runcycles == runCycle) && (cmpstr(channels, channel) == 0) + if (numpnts(match_ids) > 0) + id = match_ids[0] + else + link += 1 + endif + while (id < 0) +#endif + return id +end + +/// open a matrix result or data file +/// +/// this function opens a matrix result file (.mtrx) or data file (.*_mtrx). +/// +/// if a data file is selected, the function locates the corresponding result file, opens it, +/// and looks up the bricklet ID of the data file. +/// if a result file is selected, the function opens it but does not look up bricklet IDs. +/// +/// the result file remains open and can be accessed using the mtrx_ functions or MFR_ operations. +/// once a result file is open, you can easily access any bricklets linked to it, +/// i.e., any run cycle, scan cycle, and channel. +/// +/// the function stores information about the opened file in a global package data folder. +/// if the same result file is opened again later, the information is reused and the file not read again. +/// this may cause problems if the file has been modified in the meantime, +/// or if the cached data become corrupt for some reason. +/// the function detects if a data file is not linked in the open result file, and updates the cache. +/// in other situations it may be necessary to force a reload. +/// +/// @todo fix possible cache issues, add an option to override the cache. +/// +/// @param pathName igor path name or empty string. +/// +/// @param fileName file name, with or without path, or empty string. +/// +/// @return file type +/// @arg 0 result file (logbook) +/// @arg 1 result data file (bricklet) +/// @arg -1 error, no data loaded +/// @arg -2 matrixfilereader.xop not installed +/// +function mtrx_open_file(pathName, fileNameOrPath) + string pathName + string fileNameOrPath + + check_package_folder() + dfref df_save = GetDataFolderDFR() + dfref df_pack = $(package_path) + svar /sdfr=df_pack dataFilePath + svar /sdfr=df_pack resultFilePath + nvar /sdfr=df_pack runCycle + nvar /sdfr=df_pack scanCycle + svar /sdfr=df_pack channelName + nvar /sdfr=df_pack brickletID + + string loc_resultFileName + string loc_resultFilePath + variable loc_runCycle + variable loc_scanCycle + string loc_channelName + + // make sure we have a valid and complete file path + GetFileFolderInfo /P=$pathName /Q /Z=2 fileNameOrPath + string filePath + if ((v_flag == 0) && (v_isFile)) + filePath = s_path + else + return -1 + endif + + // get base file name + string fileName + string fileDir + string baseFileName + variable fileType + fileName = ParseFilePath(0, filePath, ":", 1, 0) + fileDir = ParseFilePath(1, filePath, ":", 1, 0) + fileType = mtrx_parse_filename(fileName, baseFileName, loc_runCycle, loc_scanCycle, loc_channelName) + + variable link = 1 + variable id = -1 + variable result = -1 + variable using_cache = 0 + + struct errorCode errorCode + initStruct(errorCode) + do + sprintf loc_resultFileName, "%s_%04u.mtrx", baseFileName, link + loc_resultFilePath = fileDir + loc_resultFileName +#if exists("MFR_OpenResultFile") + if ((strlen(resultFilePath) == 0) || (cmpstr(loc_resultFilePath, resultFilePath) != 0)) + MFR_OpenResultFile /K loc_resultFilePath + if(V_flag != errorCode.SUCCESS) + MFR_GetXOPErrorMessage + result = -1 + break + endif + resultFilePath = loc_resultFilePath + if (fileType == 1) + dataFilePath = filePath + else + dataFilePath = "" + endif + runCycle = 0 + scanCycle = 0 + channelName = "" + brickletID = 0 + MFR_CreateOverviewTable /DEST=df_pack + if(V_flag != errorCode.SUCCESS) + MFR_GetXOPErrorMessage + result = -1 + break + endif + using_cache = 0 + else + using_cache = 1 + endif +#else + print "matrixfilereader.xop not installed" + result = -2 + break +#endif + // dimension labels are: brickletID, scanCycleCount, runCycleCount, sequenceID, dimension, channelName + wave /t /sdfr=df_pack overviewTable + make /n=(dimsize(overviewTable, 0)) /i /u /o df_pack:runCycles, df_pack:scanCycles, df_pack:ids + make /n=(dimsize(overviewTable, 0)) /t /o df_pack:channels + wave /sdfr=df_pack ids, runCycles, scanCycles + wave /t /sdfr=df_pack channels + ids = str2num(overviewtable[p][%brickletID]) + runCycles = str2num(overviewtable[p][%runCycleCount]) + scanCycles = str2num(overviewtable[p][%scanCycleCount]) + channels = overviewTable[p][%channelName] + result = fileType + + // if a data file is opened, make sure we found the right result file + if ((loc_runCycle > 0) && (loc_scanCycle > 0)) + Extract /FREE ids, match_ids, (runCycles == loc_runCycle) && (scanCycles == loc_scanCycle) && (cmpstr(channels, loc_channelName) == 0) + if (numpnts(match_ids) > 0) + id = match_ids[0] + runCycle = loc_runCycle + scanCycle = loc_scanCycle + channelName = loc_channelName + brickletID = id + break + elseif (using_cache) + resultFilePath = "" + else + link += 1 + endif + else + break + endif + while (id < 0) + + return result +end + +/// load a preview image from a Matrix data file. +/// +/// the data wave is loaded into the current data folder. +/// +/// @param destName destination wave name. the wave is created in the current data folder. +/// +/// @param pathName igor symbolic path name. can be empty if the path is specified in FileName or a dialog box should be displayed +/// +/// @param fileName if empty a dialog box shows up +/// the file name must adhere to the format +/// "{prefix}-{date}-{time}-{anything}--{run_cycle}_{scan_cycle}.{extension}". +/// the first three seperators can alternatively be underscores. +/// it may be necessary to change the configuration of the Matrix application. +/// +/// @param traces (currently not used) semicolon-separated list of preferred traces. +/// the items of the list are match strings for the Igor StringMatch function. +/// only the first matching trace is loaded from the file. +/// default: "*Up;*Down;*ReUp;*ReDown;" +/// +/// @return semicolon-separated list of loaded waves including partial path from current data folder. +/// +function /s mtrx_load_preview(destName, pathName, fileName, [traces]) + string destName + string pathName + string fileName + string traces + + if (ParamIsDefault(traces)) + traces = "*Up;*Down;*ReUp;*ReDown;" + endif + + dfref df_save = GetDataFolderDFR() + + string datanames = "" + string datapaths = "" + variable filestatus = mtrx_open_file(pathName, fileName) + if (filestatus != 1) + return "" + endif + + dfref df_pack = $(package_path) + svar /sdfr=df_pack dataFilePath + svar /sdfr=df_pack resultFilePath + nvar /sdfr=df_pack runCycle + nvar /sdfr=df_pack scanCycle + svar /sdfr=df_pack channelName + nvar /sdfr=df_pack brickletID + +#if exists("MFR_OpenResultFile") + dfref df_data = df_save + variable /g df_data:V_MatrixFileReaderOverwrite = 1 + variable /g df_data:V_MatrixFileReaderFolder = 0 + variable /g df_data:V_MatrixFileReaderDouble = 0 + struct errorCode errorCode + initStruct(errorCode) + MFR_GetBrickletData /N=destName /R=(brickletID) /S=2 /DEST=df_data + if(V_flag == errorCode.SUCCESS) + datanames = S_waveNames + variable i + variable n = ItemsInList(datanames) + string s + s = StringFromList(0, datanames) + wave data = $s + mtrx_scale_dataset(data) + if (WaveDims(data) == 2) + subtract_line_bg(data) + endif + datapaths = AddListItem(GetWavesDataFolder(data, 4), datapaths, ";", inf) + for (i = 1; i < n; i += 1) + s = StringFromList(i, datanames) + killwaves /z $s + endfor + else + MFR_GetXOPErrorMessage + endif + //MFR_GetBrickletMetaData /N=ANickName /R=(brickletID) // /DEST=dfref +#else + print "matrixfilereader.xop not installed" +#endif + + setdatafolder df_save + return datapaths +end + +/// load all data from a Matrix data file. +/// +/// the data wave is loaded into a sub-subfolder the current data folder. +/// the relative path has the format ":mtrx_{date}_{time}:r{run_cycle}s{scan_cycle}", +/// where the parameters {date}, {time}, {run_cycle} and {scan_cycle} are copied from the file name. +/// the file name must be formatted according to the specifications set out below. +/// +/// @param pathName igor symbolic path name. can be empty if the path is specified in FileName or a dialog box should be displayed +/// +/// @param fileName if empty a dialog box shows up +/// the file name must adhere to the format +/// "{prefix}-{date}-{time}-{anything}--{run_cycle}_{scan_cycle}.{extension}". +/// the first three seperators can alternatively be underscores. +/// it may be necessary to change the configuration of the Matrix application. +/// +/// @param traces (currently not used) semicolon-separated list of preferred traces. +/// the items of the list are match strings for the Igor StringMatch function. +/// only matching traces are loaded from the file. +/// default: "*Up;*Down;*ReUp;*ReDown;" +/// +/// @return semicolon-separated list of loaded waves including partial path from current data folder. +/// +function /s mtrx_load_file(pathName, fileName, [traces]) + string pathName + string fileName + string traces + + if (ParamIsDefault(traces)) + traces = "*Up;*Down;*ReUp;*ReDown;" + endif + + dfref df_save = GetDataFolderDFR() + + string datanames = "" + string datapaths = "" + variable filestatus = mtrx_open_file(pathName, fileName) + if (filestatus != 1) + return "" + endif + + dfref df_pack = $(package_path) + svar /sdfr=df_pack dataFilePath + svar /sdfr=df_pack resultFilePath + nvar /sdfr=df_pack runCycle + nvar /sdfr=df_pack scanCycle + svar /sdfr=df_pack channelName + nvar /sdfr=df_pack brickletID + +#if exists("MFR_OpenResultFile") + string resultFileName = ParseFilePath(0, resultFilePath, ":", 1, 0) + dfref df_result = mtrx_create_folder(resultFileName, df_base=df_save) + dfref df_data = mtrx_get_cycle_folder(df_base = df_result) + variable /g df_data:V_MatrixFileReaderOverwrite = 1 + variable /g df_data:V_MatrixFileReaderFolder = 0 + variable /g df_data:V_MatrixFileReaderDouble = 0 + + struct errorCode errorCode + initStruct(errorCode) + string name + name = CleanupName(channelName, 0) + MFR_GetBrickletData /N=name /R=(brickletID) /DEST=df_data + if(V_flag == errorCode.SUCCESS) + datanames = S_waveNames + variable i + variable n = ItemsInList(datanames) + string s + for (i = 0; i < n; i += 1) + s = StringFromList(i, datanames) + wave /sdfr=df_data data = $s + mtrx_scale_dataset(data) + datapaths = AddListItem(GetWavesDataFolder(data, 4), datapaths, ";", inf) + endfor + else + MFR_GetXOPErrorMessage + endif + //MFR_GetBrickletMetaData /N=ANickName /R=(brickletID) // /DEST=dfref +#else + print "matrixfilereader.xop not installed" +#endif + + setdatafolder df_save + return datapaths +end + +function mtrx_scale_dataset(data) + wave data + + dfref df_pack = $(package_path) + nvar /sdfr=df_pack runCycle + nvar /sdfr=df_pack scanCycle + svar /sdfr=df_pack channelName + nvar /sdfr=df_pack brickletID + + string scanDir = StringFromList(2, NameOfWave(data), "_") + if (WaveDims(data) == 2) + Note data, "AxisLabelX=X" + Note data, "AxisLabelY=Y" + endif + Note data, "AxisLabelD=" + channelName + string title + sprintf title, "%u-%u %s %s", runCycle, scanCycle, channelName, scanDir + Note data, "Dataset=" + title +end + +/// load descriptive info from a Matrix data file. +/// +/// the info string lists the following information for each scan contained in the file: +/// - path of the scan group inside the file. +/// - number of scan positions. +/// - dataset names of scan positioners. +/// - dataset names of detectors. +/// +/// @param APathName igor symbolic path name. can be empty if the path is specified in AFileName or a dialog box should be displayed +/// +/// @param AFileName if empty a dialog box shows up +/// +/// @return newline terminated string. +/// +function /s mtrx_load_info(APathName, AFileName) + string APathName + string AFileName + + dfref saveDF = GetDataFolderDFR() + dfref fileDF = NewFreeDataFolder() + setdatafolder fileDF + + variable fileID + string filepath + string scanpaths + variable nscans + variable iscan + string scanpath + string info = "" + + + setdatafolder saveDF + return info +end + +/// remove linear background line-by-line +/// +function subtract_line_bg(img) + wave img + + variable nx = dimsize(img, 0) + variable ny = dimsize(img, 1) + variable iy + make /n=(nx) /free line, fit + for (iy = 0; iy < ny; iy += 1) + line = img[p][iy] + if (numtype(sum(line)) == 0) + CurveFit /N /Q /NTHR=0 line line /D=fit + img[][iy] = line[p] - fit[p] + endif + endfor +end diff --git a/pearl/pearl-pshell-import.ipf b/pearl/pearl-pshell-import.ipf index 08a9d75..585f5cc 100644 --- a/pearl/pearl-pshell-import.ipf +++ b/pearl/pearl-pshell-import.ipf @@ -1539,6 +1539,35 @@ function ps_detect_scale(ax, lo, hi, un) lo[%$kScanDimLabel] = scanner[0] hi[%$kScanDimLabel] = scanner[numpnts(scanner)-1] ax[%$kScanDimLabel] = NameOfWave(scanner) + strswitch(NameOfWave(scanner)) + case "Eph": + ax[%$kScanDimLabel] = "photon energy" + un[%$kScanDimLabel] = "eV" + break + case "ManipulatorX": + case "ManipulatorY": + case "ManipulatorZ": + case "FocusYTrans": + case "FocusZTrans": + case "RefocusYTrans": + case "RefocusZTrans": + case "ExitSlitY": + un[%$kScanDimLabel] = "mm" + case "ExitSlit": + un[%$kScanDimLabel] = "µm" + break + case "ManipulatorTheta": + case "ManipulatorTilt": + case "ManipulatorPhi": + un[%$kScanDimLabel] = "°" + case "FocusXRot": + case "FocusYRot": + case "FocusZRot": + case "RefocusXRot": + case "RefocusYRot": + case "RefocusZRot": + un[%$kScanDimLabel] = "mrad" + endswitch endif endif end @@ -1600,8 +1629,24 @@ function ps_scale_dataset_2(data, ax, lo, hi, un) Note data, "AxisLabelZ=" + ax[%$sdim] endif - setscale d 0, 0, un[%$kDataDimLabel], data - Note data, "AxisLabelD=" + ax[%$kDataDimLabel] + string data_unit = un[%$kDataDimLabel] + string data_label = ax[%$kDataDimLabel] + if (cmpstr(data_unit, "arb.") == 0) + strswitch(NameOfWave(data)) + case "SampleCurrent": + case "RefCurrent": + case "AuxCurrent": + data_unit = "A" + data_label = "current" + break + case "MachineCurrent": + data_unit = "mA" + data_label = "current" + break + endswitch + endif + setscale d 0, 0, data_unit, data + Note data, "AxisLabelD=" + data_label Note data, "Dataset=" + NameOfWave(data) end @@ -1674,21 +1719,35 @@ function /s psh5_load_reduced(ANickName, APathName, AFileName, reduction_func, r variable ig = 0 variable ng = ItemsInList(s_scanpaths) - string sg + string scanpath string folder + string positioners + string positioner + string positionerpath - sg = StringFromList(ig, s_scanpaths) - folder = ReplaceString("/", sg, "") + scanpath = StringFromList(ig, s_scanpaths) + folder = ReplaceString("/", scanpath, "") folder = ReplaceString(" ", folder, "") folder = CleanupName(folder, 0) setdatafolder fileDF newdatafolder /s /o $folder dfref dataDF = GetDataFolderDFR() - psh5_load_scan_meta(fileID, sg) + positioners = psh5_load_scan_meta(fileID, scanpath) newdatafolder /s /o attr - psh5_load_scan_attrs(fileID, sg) + killwaves /a/z + psh5_load_scan_attrs(fileID, scanpath) setdatafolder dataDF - wavenames = psh5_load_dataset_reduced(fileID, sg, "ScientaImage", reduction_func, reduction_param, progress=progress) + wave /t /z ScanWritables + if (waveexists(ScanWritables) && (numpnts(ScanWritables) >= 1)) + positioner = ScanWritables[0] + if (strlen(positioner) > 0) + positionerpath = scanpath + "/" + positioner + positionerpath = ReplaceString("//", positionerpath, "/") + HDF5LoadData /O /Q /Z fileID, positionerpath + endif + endif + setdatafolder dataDF + wavenames = psh5_load_dataset_reduced(fileID, scanpath, "ScientaImage", reduction_func, reduction_param, progress=progress) psh5_close_file(fileID) endif @@ -1803,9 +1862,7 @@ function /s psh5_load_dataset_reduced(fileID, scanpath, datasetname, reduction_f ny = di.dims[idy] nz = di.dims[idz] nt = di.dims[idt] - make /n=(nx,ny,nz,nt) /o $datawavename /wave=data - - // adjust nz and nt *after* making the data wave + // adjust singleton dimensions nz = max(nz, 1) nt = max(nt, 1) nzt = nz * nt diff --git a/pearl/pearl-scienta-preprocess.ipf b/pearl/pearl-scienta-preprocess.ipf index 2805f77..e35292f 100644 --- a/pearl/pearl-scienta-preprocess.ipf +++ b/pearl/pearl-scienta-preprocess.ipf @@ -703,3 +703,99 @@ function scienta_poly_bg(w, e, a): fitfunc return bg * (base + pk1 + pk2 + pk3) end + +/// parameter dialog for the redim_linbg_reduction() function +/// +/// @param param parameter string in a key1=value1;key2=value2;... list. +/// the parameter string is passed by reference. +/// see redim_linbg_reduction() for a description of parameters. +/// +/// @return zero if the user clicked OK, non-zero if the user clicked Cancel. +/// +function prompt_redim_linbg_reduction(param) + string ¶m + + variable Lcrop = NumberByKey("Lcrop", param, "=", ";") + variable Lsize = NumberByKey("Lsize", param, "=", ";") + variable Hcrop = NumberByKey("Hcrop", param, "=", ";") + variable Hsize = NumberByKey("Hsize", param, "=", ";") + variable Cpos = NumberByKey("Cpos", param, "=", ";") + variable Csize = NumberByKey("Csize", param, "=", ";") + + prompt Lcrop, "Lower cropping region" + prompt Hcrop, "Upper cropping region" + prompt Lsize, "Lower background region" + prompt Hsize, "Upper background region" + prompt Cpos, "Center position" + prompt Csize, "Center integration region" + + doprompt "redim_linbg_reduction Parameters", lcrop, hcrop, lsize, hsize, cpos, csize + if (v_flag == 0) + param = ReplaceNumberByKey("Lcrop", param, Lcrop, "=", ";") + param = ReplaceNumberByKey("Lsize", param, Lsize, "=", ";") + param = ReplaceNumberByKey("Hcrop", param, Hcrop, "=", ";") + param = ReplaceNumberByKey("Hsize", param, Hsize, "=", ";") + param = ReplaceNumberByKey("Cpos", param, Cpos, "=", ";") + param = ReplaceNumberByKey("Csize", param, Csize, "=", ";") + endif + + return v_flag +end + +/// linear background reduction function for incorrectly dimensioned scienta image +/// +/// if the energy step size does not divide the energy range to an integer number, +/// the scienta image is exported with the wrong array size. +/// this can be fixed by redimensioning the array. +/// +/// the current implementation works in the case where dimension 0 needs to be incremented. +/// the function may be generalized to dimension 1 and/or decrementing by additional parameters. +/// it is not known yet whether a generalization is needed or whether it can cover all cases. +/// +/// background subtraction and peak integration is the same as by the int_linbg_reduction() function. +/// +/// @param source source wave +/// Scienta detector image, energy axis along X, angle axis along Y +/// +/// @param dest1 destination wave 1 +/// +/// @param dest2 destination wave 2 +/// each wave is a one-dimensional intensity distribution +/// the function may redimension these waves to one of the image dimensions +/// (it must be clear to the user which dimension this is). +/// the meaning of dest1 and dest2 is up to the particular function, +/// e.g. dest1 could hold the mean value and dest2 the one-sigma error, +/// or dest1 could hold the X-profile, and dest2 the Y-profile. +/// +/// @param param parameter string in a key1=value1;key2=value2;... list. +/// the parameter string is passed by reference. +/// +/// all region parameters are relative to the image size (0...1). +/// @arg Lcrop size of the lower cropping region +/// @arg Hcrop size of the upper cropping region +/// @arg Lsize size of the lower background integration region +/// @arg Hsize size of the upper background integration region +/// @arg Cpos center position of the of the peak integration region +/// @arg Csize size of the peak integration region +/// +/// typical values (peak centered on detector, FWHM ~ 20 % of image) +/// Lcrop=0.11;Hcrop=0.11;Lsize=0.2;Hsize=0.2;Cpos=0.5;Csize=0.2 +/// +/// @return zero if successful, non-zero if an error occurs. +/// +threadsafe function redim_linbg_reduction(source, dest1, dest2, param) + wave source + wave dest1, dest2 + string ¶m + + variable nx = dimsize(source, 0) + variable ny = dimsize(source, 1) + + duplicate /free source, source_redim + redimension /n=(nx * ny) source_redim + nx += 1 + redimension /n=(nx, ny) source_redim + + return int_linbg_reduction(source_redim, dest1, dest2, param) +end +