diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..7d605cc --- /dev/null +++ b/.gitattributes @@ -0,0 +1,20 @@ +.git* export-ignore +#*.ipf diff=igorpro +# git diff --check should output something useful +*.ipf whitespace=indent-with-non-tab,tabwidth=4 +# mark files as binary +# you can also add -delta if you regularly +# commit large files of these types +*.ibw binary +*.xop binary +*.pxp binary +*.pxt binary +*.uxp binary +*.uxt binary +*.ihf binary +*.ifn binary +*.ift binary + +# automatic end of line normalization +*.ipf eol=lf + diff --git a/.gitignore b/.gitignore index 85da8ee..302d42c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ *.ipfT* doc/html/* doc/latex/* + diff --git a/doc/Doxyfile b/doc/config.dox similarity index 99% rename from doc/Doxyfile rename to doc/config.dox index 840dea4..6ec14eb 100644 --- a/doc/Doxyfile +++ b/doc/config.dox @@ -38,7 +38,7 @@ PROJECT_NAME = "PEARL Procedures" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = +PROJECT_NUMBER = $(REVISION) # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a @@ -1647,7 +1647,7 @@ EXTRA_PACKAGES = # to HTML_HEADER. # This tag requires that the tag GENERATE_LATEX is set to YES. -LATEX_HEADER = +LATEX_HEADER = src/header.tex # The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the # generated LaTeX document. The footer should contain everything after the last diff --git a/doc/makefile b/doc/makefile index 60929aa..aa64ebb 100644 --- a/doc/makefile +++ b/doc/makefile @@ -11,7 +11,7 @@ DOX=doxygen DOXOPTS= LATEX_DIR=latex -REVISION=$(shell git describe --always --tags --dirty --long) +REVISION=$(shell git describe --always --tags --dirty --long || echo "unknown, "`date +"%F %T %z"`) export REVISION all: docs @@ -19,7 +19,7 @@ all: docs docs: doxygen pdf doxygen: - $(DOX) $(DOXOPTS) Doxyfile + $(DOX) $(DOXOPTS) config.dox pdf: doxygen -$(MAKE) -C $(LATEX_DIR) @@ -27,3 +27,4 @@ pdf: doxygen clean: -rm latex/* -rm html/* + diff --git a/doc/src/header.tex b/doc/src/header.tex new file mode 100644 index 0000000..48a20a2 --- /dev/null +++ b/doc/src/header.tex @@ -0,0 +1,149 @@ +% Latex header for doxygen 1.8.9.1 +% +% To generate this file, call: +% doxygen -w latex header.tex footer.tex doxygen.sty +% and substitute the placeholders: +% $title, $datetime, $date, $doxygenversion, $projectname, $projectnumber, $projectbrief, $projectlogo +% (or diff and merge with previous version) +% +\documentclass[twoside]{book} + +% Packages required by doxygen +\usepackage{fixltx2e} +\usepackage{calc} +\usepackage{doxygen} +\usepackage[export]{adjustbox} % also loads graphicx +\usepackage{graphicx} +\usepackage[utf8]{inputenc} +\usepackage{makeidx} +\usepackage{multicol} +\usepackage{multirow} +\PassOptionsToPackage{warn}{textcomp} +\usepackage{textcomp} +\usepackage[nointegrals]{wasysym} +\usepackage[table]{xcolor} + +% Font selection +\usepackage[T1]{fontenc} +\usepackage[scaled=.90]{helvet} +\usepackage{courier} +\usepackage{amssymb} +\usepackage{sectsty} +\renewcommand{\familydefault}{\sfdefault} +\allsectionsfont{% + \fontseries{bc}\selectfont% + \color{darkgray}% +} +\renewcommand{\DoxyLabelFont}{% + \fontseries{bc}\selectfont% + \color{darkgray}% +} +\newcommand{\+}{\discretionary{\mbox{\scriptsize$\hookleftarrow$}}{}{}} + +% Page & text layout +\usepackage{geometry} +\geometry{% + a4paper,% + top=2.5cm,% + bottom=2.5cm,% + left=2.5cm,% + right=2.5cm% +} +\tolerance=750 +\hfuzz=15pt +\hbadness=750 +\setlength{\emergencystretch}{15pt} +\setlength{\parindent}{0cm} +\setlength{\parskip}{0.2cm} +\makeatletter +\renewcommand{\paragraph}{% + \@startsection{paragraph}{4}{0ex}{-1.0ex}{1.0ex}{% + \normalfont\normalsize\bfseries\SS@parafont% + }% +} +\renewcommand{\subparagraph}{% + \@startsection{subparagraph}{5}{0ex}{-1.0ex}{1.0ex}{% + \normalfont\normalsize\bfseries\SS@subparafont% + }% +} +\makeatother + +% Headers & footers +\usepackage{fancyhdr} +\pagestyle{fancyplain} +\fancyhead[LE]{\fancyplain{}{\bfseries\thepage}} +\fancyhead[CE]{\fancyplain{}{}} +\fancyhead[RE]{\fancyplain{}{\bfseries\leftmark}} +\fancyhead[LO]{\fancyplain{}{\bfseries\rightmark}} +\fancyhead[CO]{\fancyplain{}{}} +\fancyhead[RO]{\fancyplain{}{\bfseries\thepage}} +\fancyfoot[LE]{\fancyplain{}{}} +\fancyfoot[CE]{\fancyplain{}{}} +\fancyfoot[RE]{\fancyplain{}{\bfseries\scriptsize $projectnumber}} +\fancyfoot[LO]{\fancyplain{}{\bfseries\scriptsize $projectnumber}} +\fancyfoot[CO]{\fancyplain{}{}} +\fancyfoot[RO]{\fancyplain{}{}} +\renewcommand{\footrulewidth}{0.4pt} +\renewcommand{\chaptermark}[1]{% + \markboth{#1}{}% +} +\renewcommand{\sectionmark}[1]{% + \markright{\thesection\ #1}% +} + +% Indices & bibliography +\usepackage{natbib} +\usepackage[titles]{tocloft} +\setcounter{tocdepth}{3} +\setcounter{secnumdepth}{5} +\makeindex + +% Hyperlinks (required, but should be loaded last) +\usepackage{ifpdf} +\ifpdf + \usepackage[pdftex,pagebackref=true]{hyperref} +\else + \usepackage[ps2pdf,pagebackref=true]{hyperref} +\fi +\hypersetup{% + colorlinks=true,% + linkcolor=blue,% + citecolor=blue,% + unicode% +} + +% Custom commands +\newcommand{\clearemptydoublepage}{% + \newpage{\pagestyle{empty}\cleardoublepage}% +} + +%===== C O N T E N T S ===== + +\begin{document} + +% Titlepage & ToC +\hypersetup{pageanchor=false, + bookmarks=true, + bookmarksnumbered=true, + pdfencoding=unicode + } +\pagenumbering{roman} +\begin{titlepage} +\vspace*{7cm} +\begin{center}% +{\Large $projectname}\\ +\vspace*{1cm} +{\large $projectbrief }\\ +\vspace*{0.5cm} +{\small Version $projectnumber}\\ +\vspace*{0.5cm} +{\small $datetime}\\ +\end{center} +\end{titlepage} +\clearemptydoublepage +\tableofcontents +\clearemptydoublepage +\pagenumbering{arabic} +\hypersetup{pageanchor=true} + +%--- Begin generated contents --- diff --git a/doc/src/mainpage.dox b/doc/src/mainpage.dox index 36437b0..6a53a9b 100644 --- a/doc/src/mainpage.dox +++ b/doc/src/mainpage.dox @@ -21,7 +21,7 @@ Users of PEARL Procedures are requested to coordinate and share the development Please read and respect the respective license agreements. \author Matthias Muntwiler, -\version This documentation is compiled from revision $(REVISION). +\version This documentation is compiled from version $(REVISION). \copyright 2009-2016 by [Paul Scherrer Institut](http://www.psi.ch) \copyright Licensed under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) */ diff --git a/pearl/pearl-anglescan-process.ipf b/pearl/pearl-anglescan-process.ipf index e8a87d2..94d5e03 100644 --- a/pearl/pearl-anglescan-process.ipf +++ b/pearl/pearl-anglescan-process.ipf @@ -1893,13 +1893,12 @@ end /// /// @param x, y projected Cartesian coordinate /// -/// @param projection mapping function from polar to cartesian coordinates +/// @param projection mapping function from polar to cartesian coordinates. +/// projections 0-2 have no effect on the azimuthal coordinate. /// @arg 0 linear /// @arg 1 stereographic (default) /// @arg 2 azimuthal /// -/// projections 0-2 have no effect on the azimuthal coordinate -/// /// @param zeroAngle zeroAngleWhere parameter of polar graphs /// @arg 0 (default) zero is at the 3 o'clock position /// @arg 180 zero is at the 9 o'clock position diff --git a/pearl/pearl-elog.ipf b/pearl/pearl-elog.ipf index dec6c79..bd498b6 100644 --- a/pearl/pearl-elog.ipf +++ b/pearl/pearl-elog.ipf @@ -1,11 +1,10 @@ #pragma rtGlobals=3 // Use modern global access method and strict wave access. -#pragma version = 1.2 +#pragma version = 1.31 #pragma IgorVersion = 6.2 #pragma ModuleName = PearlElog -// $Id$ // author: matthias.muntwiler@psi.ch -// Copyright (c) 2013-15 Paul Scherrer Institut +// Copyright (c) 2013-16 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. @@ -16,6 +15,7 @@ /// @brief interface for writing ELOG entries with Igor graphs as attachment. /// @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) @@ -47,11 +47,12 @@ /// there is currently no separate user interface. /// /// @attention some functions in this module refer specifically to the ELOG configuration at PEARL. -/// logbook and attributes are specific to the ELOG configuration at PEARL. +/// +/// @todo ask for logbook, username and password before opening the panel. /// /// @author matthias muntwiler, matthias.muntwiler@psi.ch /// -/// @copyright 2013-15 Paul Scherrer Institut @n +/// @copyright 2013-16 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 @@ -71,24 +72,31 @@ static strconstant package_path = "root:packages:pearl_elog:" /// if a panel exists, it will be moved to the front. /// /// @param logbook name of the logbook -/// if empty, the ELOG package is initialized (overwriting existing data) but no panel is opened. +/// if empty, the user is prompted to select or create a logbook by elog_prompt_logbook(). +/// /// function pearl_elog(logbook) string logbook + if (init_package() == 0) + load_prefs() + string templates = list_logbooks(templates=1) + if (ItemsInList(templates) < 1) + init_pearl_templates() + endif + endif + + if (strlen(logbook) == 0) + logbook = elog_prompt_logbook() + endif + string win_name = logbook + "ElogPanel" if (strlen(logbook) > 0) if (strlen(WinList(win_name, ";", "")) > 0) DoWindow /F $win_name else - if (init_package() == 0) - load_prefs() - endif PearlElogPanel(logbook) endif - else - init_package(clean=1) - load_prefs() endif end @@ -117,96 +125,145 @@ static function AfterFileOpenHook(refNum,file,pathName,type,creator,kind) return 0 end -/// returns the package data folder or logbook datafolder +static constant kdfRoot = 0 +static constant kdfVolatile = 1 +static constant kdfPersistent = 2 +static constant kdfTemplates = 3 + +/// get the package, logbook, or template datafolder. /// -/// @param logbook name of logbook, or empty string for package datafolder +/// @param name name of logbook or template, or empty string for respective parent folder. +/// +/// @param category parameter category: +/// @arg kdfRoot package root +/// @arg kdfVolatile volatile +/// @arg kdfPersistent persistent +/// @arg kdfTemplates template /// /// @returns data folder reference -static function /df get_elog_df(logbook) - string logbook +/// +static function /df get_elog_df(name, category) + string name + variable category dfref df_package = $package_path - if (strlen(logbook) > 0) - dfref df_logbook = df_package:$logbook + dfref df_persistent = df_package:persistent + dfref df_volatile = df_package:volatile + + switch(category) + case kdfRoot: + dfref df_parent = df_package + break + case kdfPersistent: + dfref df_parent = df_persistent + break + case kdfTemplates: + dfref df_parent = df_persistent:templates + break + case kdfVolatile: + dfref df_parent = df_volatile + break + default: + Abort "get_elog_df: undefined data folder category." + endswitch + + if ((strlen(name) > 0) && (category >= 1)) + if (category == kdfTemplates) + dfref df_logbooks = df_parent + else + dfref df_logbooks = df_parent:logbooks + endif + dfref df_logbook = df_logbooks:$name return df_logbook else - return df_package + return df_parent endif end /// initialize the package data folder. /// -/// the data folder is initialized with a default, local configuration and the PEARL logbooks. +/// the data folder is initialized with a default, local configuration without any logbooks. /// the server configuration should be set in the preferences. /// -/// @remark this function is specific to the setup at PEARL. +/// @param clean decides what to do if the package configuration exists. +/// @arg 0 (default) keep existing configuration. +/// @arg 1 overwrite existing configuration. +/// static function init_package([clean]) - - variable clean // 0 (default) = do not overwrite existing data - // 1 = clean configuration, overwrite existing data + variable clean if (ParamIsDefault(clean)) clean = 0 endif dfref savedf = getdatafolderdfr() - dfref basedf = get_elog_df("") - if ((clean == 0) && (DataFolderRefStatus(dfr) == 1)) + dfref df_root = get_elog_df("", kdfRoot) + if ((clean == 0) && (DataFolderRefStatus(df_root) == 1)) return 1 endif setdatafolder root: newdatafolder /o/s packages newdatafolder /o/s $package_name - dfref basedf = getdatafolderdfr() + dfref df_package_root = getdatafolderdfr() + newdatafolder /o/s volatile + dfref df_volatile = getdatafolderdfr() + newdatafolder /o logbooks + setdatafolder df_package_root + newdatafolder /o/s persistent + dfref df_persistent = getdatafolderdfr() + newdatafolder /o logbooks + newdatafolder /o templates - // common configuration (persistent except username and password) + // common configuration + setdatafolder df_persistent string /g elog_path = "c:\\program files (x86)\\ELOG\\elog.exe" string /g hostname = "localhost" variable /g port = 0 // 0 = unspecified (default) variable /g ssl = 0 // 0 = plain text (incl. passwords), 1 = secure connection string /g subdir = "" - string /g username = "" - string /g password = "" + variable /g loglevel = 3 - // list of configured logbooks - // there must be a sub-folder for each of these - string /g logbooks = "Experiments;Calculations" + setdatafolder savedf + return 0 +end - // Experiments logbook - setdatafolder basedf +/// setup PEARL template logbooks. +/// +/// template logbooks for PEARL. +/// +/// @remark this function is specific to the setup at PEARL. +/// +static function init_pearl_templates() + dfref savedf = getdatafolderdfr() + + dfref df_root = get_elog_df("", kdfRoot) + dfref df_persistent = get_elog_df("", kdfPersistent) + dfref df_templates = get_elog_df("", kdfTemplates) + + // Experiments template + setdatafolder df_templates newdatafolder /o/s Experiments - // ELOG logbook name - string /g logbook = "Experiments" // attributes (persistent) // available attributes - string /g attributes = "author;project;sample;source;task;technique;valid" + string /g attributes = "author;project;sample;source;task;technique;valid;file" // 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" + string /g controls = "sv_author;sv_project;sv_sample;pm_source;pm_task;pm_technique;cb_valid;sv_file" // attributes with fixed options, value item declares the options string string /g options = "source=sources;task=tasks;technique=techniques" // attributes which must be defined string /g required_attributes = "author;project;sample;source;task;technique;valid" // option lists - string /g sources = "Manual Entry;Scienta Data;SScan Data;Prosilica Data;OTF Data;Beamline Status;LEED Data;QMS Data;Matrix Data;Igor Pro;Other" + 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" - // usage data (persistent) - string /g recent = "" - string /g recent_message = "" - - // run-time variables (volatile) - variable /g msg_id - - // Calculations logbook - setdatafolder basedf + // Calculations template + setdatafolder df_templates newdatafolder /o/s Calculations - // ELOG logbook name - string /g logbook = "Calculations" // attributes (persistent) // available attributes @@ -223,53 +280,220 @@ static function init_package([clean]) string /g programs = "DMSUP;EDAC;MSC;MUFPOT;SSC" string /g machines = "llcx;Merlin;PC" - // usage data (persistent) - string /g recent = "" - string /g recent_message = "" - - // run-time variables (volatile) - variable /g msg_id - setdatafolder savedf return 0 end -/// set module configuration parameters +/// initialize volatile variables. +/// +/// create and initialize all volatile variables for the configured notebooks. +/// values of existing variables are not changed. +/// +/// this function must be called after new logbooks have been configured, +/// specifically by elog_create_logbook() and load_prefs(). +/// +static function init_volatile_vars() + dfref savedf = GetDataFolderDFR() + + dfref df_volatile_root = get_elog_df("", kdfVolatile) + dfref df_volatile_parent = df_volatile_root:logbooks + + string logbooks = list_logbooks() + string logbook + variable nlb = ItemsInList(logbooks) + variable ilb + + for (ilb = 0; ilb < nlb; ilb += 1) + logbook = StringFromList(ilb, logbooks) + + SetDataFolder df_volatile_parent + if (DataFolderExists(logbook)) + SetDataFolder $logbook + else + NewDataFolder /o/s $logbook + endif + + if (exists("username") != 2) + string /g username = "" + endif + if (exists("password") != 2) + string /g password = "" + endif + if (exists("msg_id") != 2) + variable /g msg_id = 0 + endif + endfor + + SetDataFolder savedf + return 0 +end + +/// @var persistent:loglevel +/// @brief filter history messages by log level. +/// +/// messages are printed to the history only if their level exceeds this setting. +/// this is a global variable in the "persistent" folder of the package configuration. +/// +/// @arg 0 none. do not print any messages. no messages have this level. +/// @arg 1 critical. severe error with possible data corruption. +/// @arg 2 error. a function did not complete successfully. +/// @arg 3 warning. everything worked fine, but some attention of the user is required. +/// @arg 4 info. status message which may be useful to the normal user. +/// @arg 5 debug. status message which may be useful to the developer. +/// + +/// create a new logbook. +/// +/// create a new empty logbook or duplicate from a template. +/// +/// @param name name of the new logbook. +/// +/// @param template name of the template. +/// if empty string, a new empty logbook is created. +/// +function elog_create_logbook(name, [template]) + string name + string template + + if (ParamIsDefault(template)) + template = "" + endif + + dfref savedf = getdatafolderdfr() + dfref df_root = get_elog_df("", kdfRoot) + dfref df_persistent_root = get_elog_df("", kdfPersistent) + dfref df_persistent_parent = df_persistent_root:logbooks + dfref df_volatile_root = get_elog_df("", kdfVolatile) + dfref df_volatile_parent = df_volatile_root:logbooks + + if (strlen(template) > 0) + dfref df_template = get_elog_df(template, kdfTemplates) + DuplicateDataFolder df_template, df_persistent_parent:$name + else + NewDataFolder /o/s df_persistent_parent:$name + + // ELOG logbook name + string /g logbook = name + // attributes (persistent) + // available attributes + string /g attributes = "" + // controls corresponding to attributes + // prefix determines the control type: sv_ = setvariable (string), pm_ = popup menu, cb = check box + string /g controls = "" + // attributes with fixed options, value item declares the options string + string /g options = "" + // attributes which must be defined + string /g required_attributes = "" + endif + + // usage data (persistent) + setdatafolder get_elog_df(name, kdfPersistent) + string /g recent = "" + string /g recent_message = "" + + init_volatile_vars() + + setdatafolder savedf + return 0 +end + +/// set global module configuration parameters +/// function elog_config([elog_path, hostname, port, subdir]) string elog_path string hostname variable port string subdir - dfref saveDF = GetDataFolderDFR() + dfref df = get_elog_df("", kdfPersistent) if (!ParamIsDefault(elog_path)) - svar g_elog_path = $(package_path + "elog_path") + svar /sdfr=df g_elog_path = elog_path g_elog_path = elog_path endif if (!ParamIsDefault(hostname)) - svar g_hostname = $(package_path + "hostname") + svar /sdfr=df g_hostname = hostname g_hostname = hostname endif if (!ParamIsDefault(port)) - nvar g_port = $(package_path + "port") + nvar /sdfr=df g_port = port g_port = port endif if (!ParamIsDefault(subdir)) - svar g_subdir = $(package_path + "subdir") + svar /sdfr=df g_subdir = subdir g_subdir = subdir endif - - setdatafolder savedf +end + +/// set username and password for login to a logbook +/// +/// the username and password are stored (in plain text) in global strings under the selected logbook folder. +/// this is necessary for sending data to the ELOG server. +/// +/// call elog_logout() to clear the password variables and to avoid unintended use of your credentials. +/// +/// @warning +/// igor does not have a built-in mechanism to protect passwords. +/// user names and passwords are stored in plain text in the data folder tree. +/// as such they are saved to experiment files and preferences. +/// +/// @param logbook name of the target logbook. +/// +function elog_login(logbook, username, password) + string logbook + string username + string password + + dfref df = get_elog_df(logbook, kdfVolatile) + svar /sdfr=df g_username=username + svar /sdfr=df g_password=password + g_username = username + g_password = password +end + +/// clear username and password of a logbook or all logbooks. +/// +/// the username and password are stored (in plain text) in global strings under the selected logbook folder. +/// this function resets the username and password strings. +/// +/// @param logbook name of the target logbook. +/// if empty, the passwords of all logbooks are cleared. +/// +function elog_logout(logbook) + string logbook + + dfref df = get_elog_df(logbook, kdfVolatile) + if (strlen(logbook) > 0) + svar /z /sdfr=df g_username=username + svar /z /sdfr=df g_password=password + if (svar_exists(g_username)) + g_username = "" + endif + if (svar_exists(g_password)) + g_password = "" + endif + else + dfref df2 = df:logbooks + variable nlb = CountObjectsDFR(df2, 4) + variable ilb + string slb + for (ilb = 0; ilb < nlb; ilb += 1) + slb = GetIndexedObjNameDFR(df2, 4, ilb) + if (strlen(slb) > 0) + elog_logout(slb) + endif + endfor + endif end /// save persistent package data to the preferences file. /// -/// currently saves everything under the package folder. +/// saves everything under the persistent folder of the package. +/// static function save_prefs() dfref saveDF = GetDataFolderDFR() - dfref df = get_elog_df("") + dfref df = get_elog_df("", kdfPersistent) if (DataFolderRefStatus(df) == 1) string fullPath = SpecialDirPath("Packages", 0, 0, 0) fullPath += package_name @@ -290,9 +514,9 @@ static function load_prefs() dfref saveDF = GetDataFolderDFR() variable result = -1 - setdatafolder root: - NewDataFolder /O/S packages - NewDataFolder /O/S $package_name + init_package() + setdatafolder get_elog_df("", kdfPersistent) + string fullPath = SpecialDirPath("Packages", 0, 0, 0) fullPath += package_name @@ -302,6 +526,7 @@ static function load_prefs() GetFileFolderInfo /Q /Z fullPath if (V_Flag == 0) // Preference file exist? LoadData /O /R /Q fullPath + init_volatile_vars() result = 0 endif endif @@ -310,104 +535,41 @@ static function load_prefs() return result end -/// add a copy of the given graph to ELOG, prompting for attributes (deprecated) +/// get a list of configured logbooks or templates. /// -/// @deprecated -/// this function is specific to the PEARL/Experiments logbook. -/// it cannot be customized without major efforts. -/// it will be removed in a future version. -function elog_add_graph_prompt(graphname, [replyto]) - string graphname - variable replyto +/// this is list of data folder names under persistent:logbooks (or persistent:templates). +/// the function does not check whether the folders contain valid data. +/// +/// @param templates select whether logbooks (0, default) or templates (1) are returned. +/// +/// @return semicolon-separated list of logbooks +/// +static function /s list_logbooks([templates]) + variable templates - print "executing deprecated function elog_add_graph_prompt." - - dfref savedf = getdatafolderdfr() - //setdatafolder root:packages:elog - - string attributes - string message = "" - svar mru_attributes = $(package_path + "attributes") - attributes = mru_attributes - - variable result = elog_prompt_attributes(attributes, message) - if (result == 0) - if (ParamIsDefault(replyto)) - result = elog_create_entry("Experiments", attributes, message, graphs=graphname) - else - result = elog_create_entry("Experiments", attributes, message, graphs=graphname, replyto=replyto) - endif - if (result == 0) - mru_attributes = attributes - endif + if (ParamIsDefault(templates)) + templates = 0 endif - setdatafolder savedf - return result -end - -/// prompt for attributes (deprecated) -/// -/// @deprecated -/// this function is specific to the PEARL/Experiments logbook. -/// it cannot be customized without major efforts. -/// it will be removed in a future version. -function elog_prompt_attributes(attributes, message) - string &attributes - string &message - - print "executing deprecated function elog_prompt_attributes." - - string author - string project - string sample - string source - string task - string technique - string file - variable valid - string loc_message - - author = StringByKey("Author", attributes, "=", ";") - project = StringByKey("Project", attributes, "=", ";") - sample = StringByKey("Sample", attributes, "=", ";") - source = StringByKey("Source", attributes, "=", ";") - task = StringByKey("Task", attributes, "=", ";") - technique = StringByKey("Technique", attributes, "=", ";") - file = StringByKey("File", attributes, "=", ";") - valid = NumberByKey("Valid", attributes, "=", ";") - loc_message = message - - svar sources = $(package_path + "sources") - svar tasks = $(package_path + "tasks") - svar techniques = $(package_path + "techniques") - - prompt author, "Author" - prompt project, "Project" - prompt sample, "Sample" - prompt source, "Source", popup, sources - prompt task, "Task", popup, tasks - prompt technique, "Technique", popup, techniques - prompt file, "File" - prompt valid, "Valid" - prompt loc_message, "Message" - - doprompt "Create New ELOG Entry", author, project, sample, task, technique, file, loc_message - source = "Manual Entry" - - if (v_flag == 0) - attributes = ReplaceStringByKey("Author", attributes, author, "=", ";") - attributes = ReplaceStringByKey("Project", attributes, project, "=", ";") - attributes = ReplaceStringByKey("Sample", attributes, sample, "=", ";") - attributes = ReplaceStringByKey("Source", attributes, source, "=", ";") - attributes = ReplaceStringByKey("Task", attributes, task, "=", ";") - attributes = ReplaceStringByKey("Technique", attributes, technique, "=", ";") - attributes = ReplaceStringByKey("File", attributes, file, "=", ";") - //attributes = ReplaceNumberByKey("Valid", attributes, valid, "=", ";") - message = loc_message + dfref df_persistent = get_elog_df("", kdfPersistent) + if (templates) + dfref df_logbooks = df_persistent:templates + else + dfref df_logbooks = df_persistent:logbooks endif + string logbooks = "" - return v_flag + variable nlb = CountObjectsDFR(df_logbooks, 4) + variable ilb + string slb + for (ilb = 0; ilb < nlb; ilb += 1) + slb = GetIndexedObjNameDFR(df_logbooks, 4, ilb) + if (strlen(slb) > 0) + logbooks = AddListItem(slb, logbooks) + endif + endfor + + return SortList(logbooks, ";", 16) end /// validate attributes @@ -426,14 +588,36 @@ function elog_validate_attributes(logbook, attributes) return result end +/// create a new entry in ELOG +/// +/// this is the main function to create a new entry in a logbook. +/// +/// @param logbook name of the target logbook. +/// +/// @param attributes key=value list of attributes, semicolon separated. +/// +/// @param message free text part of the entry. +/// +/// @param encoding encoding of message, 0:ELcode, 1:plain (default), 2:HTML. +/// +/// @param graphs names of graph windows to be added as attachments, semicolon separated. +/// +/// @param replyto existing message ID (> 1) to follow up on. +/// 0 or default: start new thread. +/// +/// @return ID number of the new entry (> 0), or error code (< 0). +/// @arg -1: failed to save temporary message file. +/// @arg -2: invalid/missing command line. +/// @arg -3: invalid/missing attributes. +/// @arg -4: elog returned error +/// function elog_create_entry(logbook, attributes, message, [encoding, graphs, replyto]) string logbook - string attributes // key=value list of attributes, semicolon separated + string attributes string message - variable encoding // encoding of message, 0:ELcode, 1:plain (default), 2:HTML - string graphs // names of graph windows to be added as attachments, semicolon separated - variable replyto // existing message ID (> 1) to follow up on. - // 0 or default: start new thread + variable encoding + string graphs + variable replyto if (ParamIsDefault(encoding)) encoding = 1 @@ -446,19 +630,26 @@ function elog_create_entry(logbook, attributes, message, [encoding, graphs, repl endif dfref savedf = getdatafolderdfr() - setdatafolder $(package_path) - setdatafolder $(logbook) + dfref df_general = get_elog_df("", kdfPersistent) + dfref df_volatile = get_elog_df(logbook, kdfVolatile) variable result = 0 - nvar msg_id + nvar /sdfr=df_volatile msg_id + nvar /sdfr=df_general loglevel if (elog_validate_attributes(logbook,attributes) != 0) - result = -3 // error: invalid/missing attributes + if (loglevel >= 2) + print "ELOG: failed to validate attributes." + endif + result = -3 endif string cmd = prepare_command_line(logbook) if (strlen(cmd) == 0) - result = -2 // error: invalid/missing command line + if (loglevel >= 2) + print "ELOG: failed to prepare command line." + endif + result = -2 endif if (replyto >= 1) @@ -472,6 +663,7 @@ function elog_create_entry(logbook, attributes, message, [encoding, graphs, repl for (iattr = 0; (iattr < nattr) && (result == 0); iattr += 1) sattr = StringFromList(iattr, attributes, ";") if (strlen(StringFromList(1, sattr, "=")) > 0) + sattr = ReplaceString("%", sattr, "") cmd += " -a \"" + sattr + "\"" endif endfor @@ -486,19 +678,29 @@ function elog_create_entry(logbook, attributes, message, [encoding, graphs, repl if (strlen(messagefile) > 0) cmd += " -m \"" + messagefile + "\"" cmd += " > \"" + get_log_path() + "\"" + if (loglevel >= 5) + print cmd + endif string cmd_file_path = create_cmd_file(cmd) ExecuteScriptText cmd_file_path variable id = parse_result() if (id > 0) msg_id = id - print "ELOG: sent message " + num2str(id) + if (loglevel >= 4) + print "ELOG: sent message " + num2str(id) + endif else - print "ELOG: sending message failed." - result = -4 // error: elog returned error + if (loglevel >= 2) + print "ELOG: sending message failed." + endif + result = -4 endif cleanup_temp_files() else - result = -1 // error + if (loglevel >= 2) + print "ELOG: failed to create temporary message file." + endif + result = -1 endif endif @@ -519,11 +721,12 @@ function elog_add_attachment(logbook, id, graphs) string graphs // names of graph windows to be added as attachments, semicolon separated dfref savedf = getdatafolderdfr() - setdatafolder $(package_path) - setdatafolder $(logbook) + dfref df_general = get_elog_df("", kdfPersistent) + dfref df_volatile = get_elog_df(logbook, kdfVolatile) variable result = 0 - nvar msg_id + nvar /sdfr=df_volatile msg_id + nvar /sdfr=df_general loglevel string cmd = prepare_command_line(logbook) if (strlen(cmd) == 0) @@ -547,9 +750,13 @@ function elog_add_attachment(logbook, id, graphs) id = parse_result() if (id > 0) msg_id = id - print "ELOG: attached graphs to message " + num2str(id) + if (loglevel >= 4) + print "ELOG: attached graphs to message " + num2str(id) + endif else - print "ELOG: attaching graphs failed." + if (loglevel >= 2) + print "ELOG: failed to attach graphs." + endif result = -4 // error: elog returned error endif cleanup_temp_files() @@ -569,19 +776,24 @@ end static function /s prepare_command_line(logbook) string logbook - dfref savedf = getdatafolderdfr() - setdatafolder $(package_path) + dfref df_general = get_elog_df("", kdfPersistent) + dfref df_persistent = get_elog_df(logbook, kdfPersistent) + dfref df_volatile = get_elog_df(logbook, kdfVolatile) - svar elog_path - svar hostname - nvar port - nvar ssl - svar subdir - svar username - svar password + svar /sdfr=df_general elog_path + svar /sdfr=df_general hostname + nvar /sdfr=df_general port + nvar /sdfr=df_general ssl + svar /sdfr=df_general subdir + nvar /sdfr=df_general loglevel + svar /sdfr=df_volatile username + svar /sdfr=df_volatile password string cmd cmd = "\"" + elog_path + "\"" + if (loglevel >= 5) + cmd += " -v" + endif cmd += " -h " + hostname if ((nvar_exists(port)) && (port > 0)) cmd += " -p " + num2str(port) @@ -598,7 +810,10 @@ static function /s prepare_command_line(logbook) cmd += " -u " + username + " " + password endif - setdatafolder savedf + if (loglevel >= 5) + print cmd + endif + return cmd end @@ -613,9 +828,6 @@ end static function /s prepare_graph_attachments(graphs) string graphs // names of graph windows to be added as attachments, semicolon separated - dfref savedf = getdatafolderdfr() - setdatafolder $(package_path) - string cmd = "" variable ngraphs = ItemsInList(graphs, ";") variable igraph @@ -629,7 +841,6 @@ static function /s prepare_graph_attachments(graphs) endif endfor - setdatafolder savedf return cmd end @@ -644,6 +855,7 @@ end static function /s create_message_file(message) string message + message = ReplaceString("%", message, "") string path = SpecialDirPath("Temporary", 0, 1, 0) variable len = strlen(path) if (numtype(len) == 0) @@ -724,37 +936,103 @@ static strconstant elog_parse_id = "ID=%u" /// /// @returns the ID of the generated message, /// or a value <= 0 if an error occurred. +/// static function parse_result() + dfref df_general = get_elog_df("", kdfPersistent) + nvar /sdfr=df_general loglevel + string path = get_log_path() string line = "" + string output = "" + variable success = 0 + variable id = -1 + string part1 = "" + string part2 = "" variable len = strlen(path) if (numtype(len) == 0) variable f1 Open /R/Z f1 as path if (v_flag == 0) - FReadLine f1, line + do + FReadLine f1, line + if (strlen(line) > 0) + part1 = StringFromList(0, line, ",") + part2 = ReplaceString(" ", StringFromList(1, line, ","), "") + success = cmpstr(part1, elog_success_msg) == 0 + if (success) + sscanf part2, elog_parse_id, id + endif + else + break + endif + output += line + while(!success) Close f1 endif endif - - variable success = 0 - variable id = -1 - if (strlen(line) > 0) - string part1 = StringFromList(0, line, ",") - string part2 = ReplaceString(" ", StringFromList(1, line, ","), "") - success = cmpstr(part1, elog_success_msg) == 0 - if (success) - sscanf part2, elog_parse_id, id - endif + if (loglevel >= 5) + print output endif return id end +/// prompt to open or create a logbook +/// +function /s elog_prompt_logbook() + string logbooks = list_logbooks(templates=0) + logbooks = AddListItem("(new)", logbooks) + string templates = list_logbooks(templates=1) + templates = AddListItem("(none)", templates) + + string logbook = StringFromList(0, logbooks) + string template = StringFromList(0, logbooks) + string name = "" + string username = "" + string password = "" + + prompt logbook, "logbook", popup logbooks + prompt template, "template", popup templates + prompt name, "new logbook name" + + doprompt "select logbook", logbook, template, name + if (!v_flag) + if (cmpstr(logbook, "(new)") == 0) + elog_create_logbook(name, template=template) + logbook = name + endif + else + logbook = "" + endif + return logbook +end + +/// prompt the user for login to a logbook +/// +function elog_prompt_login(logbook) + string logbook + + string logbooks = list_logbooks(templates=0) + + string username = "" + string password = "" + + prompt logbook, "logbook", popup logbooks + prompt username, "user name" + prompt password, "password (blank to log out)" + + doprompt "log in to logbook", logbook, username, password + if (!v_flag) + elog_login(logbook, username, password) + endif + + return v_flag +end + /// open a new panel for submitting data to ELOG. /// -/// this function creates only the panel but not the necessary package data folder. +/// this function creates only the panel but not the necessary data folders. /// call init_package() and load_prefs() once before creating panels. /// /// @param logbook name of the target logbook @@ -763,9 +1041,10 @@ function /s PearlElogPanel(logbook) string logbook dfref savedf = getdatafolderdfr() - setdatafolder $(package_path) - setdatafolder $(logbook) - string df_path = getdatafolder(1) + dfref df_general = get_elog_df("", kdfPersistent) + dfref df_persistent = get_elog_df(logbook, kdfPersistent) + dfref df_volatile = get_elog_df(logbook, kdfVolatile) + string win_name = logbook + "ElogPanel" string win_title = "ELOG " + logbook @@ -773,19 +1052,29 @@ function /s PearlElogPanel(logbook) win_name = s_name ModifyPanel /w=$win_name cbRGB=(52224,52224,65280) - svar attributes - svar controls - svar options + svar /sdfr=df_persistent attributes + svar /sdfr=df_persistent controls + svar /sdfr=df_persistent options variable iattr variable nattr = ItemsInList(attributes, ";") string s_attr string s_control string s_option + string persistent_path = GetDataFolder(1, df_persistent) + string volatile_path = GetDataFolder(1, df_volatile) string options_path string variable_path variable ypos = 2 + 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, ";") @@ -797,7 +1086,7 @@ function /s PearlElogPanel(logbook) ypos += 18 break case "pm": - options_path = df_path + StringByKey(s_attr, options, "=", ";") + 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, title=s_attr PopupMenu $s_control, win=$win_name, mode=1, popvalue="Test", value= #options_path @@ -830,7 +1119,7 @@ function /s PearlElogPanel(logbook) Button b_clear,win=$win_name, fcolor=(56576,60928,47872) ypos += 272-246 - variable_path = df_path + "msg_id" + 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, title="ID",value=$variable_path SetVariable sv_id,win=$win_name, help={"ID of last submitted message, or message to attach or reply to."} @@ -858,11 +1147,11 @@ function /s PearlElogPanel(logbook) SetActiveSubwindow ## // restore recently used attributes and message - svar /z /sdfr=get_elog_df(logbook) recent + svar /z /sdfr=df_persistent recent if (svar_exists(recent) && (strlen(recent) > 0)) set_panel_attributes(win_name, recent) endif - svar /z /sdfr=get_elog_df(logbook) recent_message + svar /z /sdfr=df_persistent recent_message if (svar_exists(recent_message) && (strlen(recent_message) > 0)) set_panel_message(win_name, recent_message) endif @@ -950,9 +1239,10 @@ static function bp_submit(ba) : ButtonControl variable result result = elog_create_entry(logbook, attributes, message, graphs=graphs, replyto=id) if (result == 0) - svar /sdfr=get_elog_df(logbook) recent + dfref df = get_elog_df(logbook, kdfPersistent) + svar /sdfr=df recent recent = attributes - svar /sdfr=get_elog_df(logbook) recent_message + svar /sdfr=df recent_message recent_message = message else abort "Submission failed. Error code " + num2str(result) + "." @@ -1033,6 +1323,41 @@ static function bp_clear(ba) : ButtonControl return 0 end +static function bp_login(ba) : ButtonControl + STRUCT WMButtonAction &ba + + switch( ba.eventCode ) + case 2: // mouse up + string logbook = GetUserData(ba.win, "", "logbook") + if (elog_prompt_login(logbook) == 0) + Button b_login, win=$ba.win, disable=3 + Button b_logout, win=$ba.win, disable=0 + endif + break + case -1: // control being killed + break + endswitch + + return 0 +end + +static function bp_logout(ba) : ButtonControl + STRUCT WMButtonAction &ba + + switch( ba.eventCode ) + case 2: // mouse up + string logbook = GetUserData(ba.win, "", "logbook") + elog_logout(logbook) + Button b_login, win=$ba.win, disable=0 + Button b_logout, win=$ba.win, disable=3 + break + case -1: // control being killed + break + endswitch + + return 0 +end + static function /s get_default_panel_name() string windowname windowname = StringFromList(0, WinList("*ElogPanel*", ";", "WIN:64"), ";") diff --git a/pearl/pearl-menu.ipf b/pearl/pearl-menu.ipf index 1002b6e..5dd5430 100644 --- a/pearl/pearl-menu.ipf +++ b/pearl/pearl-menu.ipf @@ -55,10 +55,8 @@ menu "PEARL" end submenu "Services" - PearlMenuEnableFunc("pearl_elog") + "ELOG Experiments", /Q, pearl_elog("Experiments") - help = {"Create entries in ELOG experiments logbook"} - PearlMenuEnableFunc("pearl_elog") + "ELOG Calculations", /Q, pearl_elog("Calculations") - help = {"Create entries in ELOG calculations logbook"} + PearlMenuEnableFunc("pearl_elog") + "Open ELOG Panel", /Q, pearl_elog("") + help = {"Open an ELOG panel to send entries to an ELOG logbook"} end submenu "Sample Preparation" diff --git a/pearl/pearl-pshell-import.ipf b/pearl/pearl-pshell-import.ipf index 04720bf..f0d1042 100644 --- a/pearl/pearl-pshell-import.ipf +++ b/pearl/pearl-pshell-import.ipf @@ -637,6 +637,7 @@ function /s psh5_load_scan_preview(fileID, scanpath, [set_scale]) string datasets = psh5_list_scan_datasets(fileID, scanpath) string datasetname = "" + variable index // todo: this should be generalized if (strsearch(datasets, "ScientaImage", 0) >= 0) datasetname = "ScientaImage" @@ -644,17 +645,24 @@ function /s psh5_load_scan_preview(fileID, scanpath, [set_scale]) datasetname = "ScientaSpectrum" elseif (strsearch(datasets, "ScientaEnergyDistribution", 0) >= 0) datasetname = "ScientaEnergyDistribution" + elseif (strsearch(datasets, "ImageEnergyDistribution", 0) >= 0) + datasetname = "ImageEnergyDistribution" elseif (strsearch(datasets, "Counts", 0) >= 0) datasetname = "Counts" elseif (strsearch(datasets, "SampleCurrent", 0) >= 0) datasetname = "SampleCurrent" else datasetname = StringFromList(0, datasets) + index = ItemsInList(datasetname, "/") - 1 + datasetname = StringFromList(index, datasetname, "/") endif + if (strlen(datasetname) == 0) + return "" + endif + string datasetpath datasetpath = scanpath + "/" + datasetname datasetpath = ReplaceString("//", datasetpath, "/") - STRUCT HDF5DataInfo di // Defined in HDF5 Browser.ipf. InitHDF5DataInfo(di) variable err = HDF5DatasetInfo(fileID, datasetpath, 0, di) @@ -667,6 +675,10 @@ function /s psh5_load_scan_preview(fileID, scanpath, [set_scale]) if (di.ndims < 3) HDF5LoadData /O /Q /Z fileID, datasetpath dataname = StringFromList(0, S_waveNames) + wave /z data = $dataname + if (waveexists(data)) + ps_set_dimlabels(data) + endif else variable dim2start = 0 variable dim2count = 1 @@ -1000,10 +1012,12 @@ function ps_set_dimlabels(data) setdimlabel 0, -1, $kEnergyDimLabel, data break case "ImageAngleDistribution": + case "ScientaAngleDistribution": setdimlabel 0, -1, $kScanDimLabel, data setdimlabel 1, -1, $kAngleDimLabel, data break case "ImageEnergyDistribution": + case "ScientaEnergyDistribution": setdimlabel 0, -1, $kScanDimLabel, data setdimlabel 1, -1, $kEnergyDimLabel, data break diff --git a/pearl/preferences/pearl_elog/preferences.pxp b/pearl/preferences/pearl_elog/preferences.pxp index 5713cd1..8909c97 100644 Binary files a/pearl/preferences/pearl_elog/preferences.pxp and b/pearl/preferences/pearl_elog/preferences.pxp differ