#pragma rtGlobals=3 // Use modern global access method and strict wave access. #pragma version = 1.40 #pragma IgorVersion = 6.2 #pragma ModuleName = PearlElog // author: matthias.muntwiler@psi.ch // 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. // You may obtain a copy of the License at // http://www.apache.org/licenses/LICENSE-2.0 /// @file /// @brief interface for writing ELOG entries with Igor graphs as attachment. /// @ingroup ArpesPackage /// /// /// 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. /// /// 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 /// elog -h [-p port] [-d subdir] /// Location where elogd is running /// -l logbook Name of logbook /// -s Use SSL for communication /// [-v] For verbose output /// [-w password] Write password defined on server /// [-u username password] User name and password /// [-f ] Up to 50 attachments /// -a = Up to 50 attributes /// [-r ] Reply to existing message /// [-q] Quote original text on reply /// [-e ] Edit existing message /// [-x] Suppress email notification /// [-n 0|1|2] Encoding: 0:ELcode,1:plain,2:HTML /// -m ] | ///@endverbatim /// /// /// @author matthias muntwiler, matthias.muntwiler@psi.ch /// /// @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 /// http://www.apache.org/licenses/LICENSE-2.0 /// @namespace PearlElog /// @brief interface for writing ELOG entries with Igor graphs as attachment. /// /// PearlElog is declared in @ref pearl-elog.ipf. static strconstant package_name = "pearl_elog" static strconstant package_path = "root:packages:pearl_elog:" /// main function to initialize ELOG and to open an ELOG panel. /// /// this function takes care of all necessary initialization, configuration, and preferences. /// if a panel exists, it will be moved to the front. /// /// @param logbook name of the logbook /// 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) elog_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 win_name = PearlElogPanel(logbook) STRUCT WMWinHookStruct s s.eventCode = 0 s.winName = win_name elog_panel_hook(s) endif endif end /// save preferences and recent values before Igor opens a new experiment. static function IgorBeforeNewHook(igorApplicationNameStr) string igorApplicationNameStr save_prefs() cleanup_temp_files() return 0 end /// save preferences and recent values before Igor quits. static function IgorQuitHook(igorApplicationNameStr) string igorApplicationNameStr save_prefs() cleanup_temp_files() return 0 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(clean=1) load_prefs() endif return 0 end static constant kdfRoot = 0 static constant kdfVolatile = 1 static constant kdfPersistent = 2 static constant kdfTemplates = 3 /// get the package, logbook, or template 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(name, category) string name variable category dfref df_package = $package_path 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_parent endif end /// initialize the package data folder. /// /// the data folder is initialized with a default, local configuration without any logbooks. /// the server configuration should be set in the preferences. /// /// @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 if (ParamIsDefault(clean)) clean = 0 endif dfref savedf = getdatafolderdfr() 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 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 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 = "" variable /g loglevel = 3 setdatafolder savedf return 0 end /// setup PEARL template logbooks. /// /// template logbooks for PEARL. /// /// @remark this function is specific to the setup at PEARL. /// function elog_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 // attributes (persistent) // available attributes 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;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 string /g required_attributes = "author;project;sample;source;task;technique;valid" // 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;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 newdatafolder /o/s Calculations // attributes (persistent) // available attributes 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_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 = "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 end /// 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 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) 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 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 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. /// 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. /// 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 setdatafolder df_persistent_parent if (CheckName(name, 11) != 0) setdatafolder savedf Abort "invalid logbook name" return -1 endif 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 // 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 df = get_elog_df("", kdfPersistent) if (!ParamIsDefault(elog_path)) svar /sdfr=df g_elog_path = elog_path g_elog_path = elog_path endif if (!ParamIsDefault(hostname)) svar /sdfr=df g_hostname = hostname g_hostname = hostname endif if (!ParamIsDefault(port)) nvar /sdfr=df g_port = port g_port = port endif if (!ParamIsDefault(subdir)) svar /sdfr=df g_subdir = subdir g_subdir = subdir endif 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. /// /// saves everything under the persistent folder of the package. /// static function save_prefs() dfref saveDF = GetDataFolderDFR() dfref df = get_elog_df("", kdfPersistent) if (DataFolderRefStatus(df) == 1) string fullPath = SpecialDirPath("Packages", 0, 0, 0) fullPath += package_name NewPath/O/C/Q tempPackagePrefsPath, fullPath fullPath += ":preferences.pxp" SetDataFolder df SaveData /O /Q /R fullPath KillPath/Z tempPackagePrefsPath endif SetDataFolder saveDF end /// load persistent package data from the preferences file. /// /// the preferences file is an Igor packed experiment file in a special preferences folder static function load_prefs() dfref saveDF = GetDataFolderDFR() variable result = -1 init_package() setdatafolder get_elog_df("", kdfPersistent) string fullPath = SpecialDirPath("Packages", 0, 0, 0) fullPath += package_name GetFileFolderInfo /Q /Z fullPath if (V_Flag == 0) // Disk directory exists? fullPath += ":preferences.pxp" GetFileFolderInfo /Q /Z fullPath if (V_Flag == 0) // Preference file exist? LoadData /O /R /Q fullPath init_volatile_vars() result = 0 endif endif SetDataFolder saveDF return result end /// get a list of configured logbooks or templates. /// /// 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 if (ParamIsDefault(templates)) templates = 0 endif 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 = "" 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 /// /// @returns 0 if all required attributes are present and enumerated items are correct. /// non-zero if a violation is detected. /// /// @todo /// function currently not implemented, always returns 0 function elog_validate_attributes(logbook, attributes) string logbook // name of the logbook (as in igor folder name) string attributes // key=value list of attributes, semicolon separated variable result = 0 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 string message variable encoding string graphs variable replyto if (ParamIsDefault(encoding)) encoding = 1 endif if (ParamIsDefault(graphs)) graphs = "" endif if (ParamIsDefault(replyto)) replyto = 0 endif dfref savedf = getdatafolderdfr() dfref df_general = get_elog_df("", kdfPersistent) dfref df_volatile = get_elog_df(logbook, kdfVolatile) variable result = 0 nvar /sdfr=df_volatile msg_id nvar /sdfr=df_general loglevel if (elog_validate_attributes(logbook,attributes) != 0) if (loglevel >= 2) print "ELOG: failed to validate attributes." endif result = -3 endif string cmd = prepare_command_line(logbook) if (strlen(cmd) == 0) if (loglevel >= 2) print "ELOG: failed to prepare command line." endif result = -2 endif if (replyto >= 1) cmd += " -r " + num2str(replyto) endif cmd += " -n " + num2str(encoding) variable nattr = ItemsInList(attributes, ";") variable iattr string sattr 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 if (result == 0) string cmd_graphs = prepare_graph_attachments(graphs) cmd += " " + cmd_graphs endif if ((result == 0) && (strlen(message) > 0)) string messagefile = create_message_file(message) 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 if (loglevel >= 4) print "ELOG: sent message " + num2str(id) endif else if (loglevel >= 2) print "ELOG: sending message failed." endif result = -4 endif else if (loglevel >= 2) print "ELOG: failed to create temporary message file." endif result = -1 endif endif setdatafolder savedf return result end /// add one or more graphs to an existing ELOG entry /// /// @param logbook name of the target logbook /// @param id identification number of the existing entry /// @param graphs names of graph windows to be added as attachments, semicolon separated /// /// @warning this will delete all existing attachments of the entry! function elog_add_attachment(logbook, id, graphs) string logbook variable id // existing entry ID string graphs // names of graph windows to be added as attachments, semicolon separated dfref savedf = getdatafolderdfr() dfref df_general = get_elog_df("", kdfPersistent) dfref df_volatile = get_elog_df(logbook, kdfVolatile) variable result = 0 nvar /sdfr=df_volatile msg_id nvar /sdfr=df_general loglevel string cmd = prepare_command_line(logbook) if (strlen(cmd) == 0) result = -2 // error: invalid/missing command line endif cmd += " -e " + num2str(id) if (result == 0) string cmd_graphs = prepare_graph_attachments(graphs) if (strlen(cmd_graphs) == 0) result = -3 // error: invalid/missing graphs endif endif if (result == 0) cmd += " " + cmd_graphs cmd += " > \"" + get_log_path() + "\"" string cmd_file_path = create_cmd_file(cmd) ExecuteScriptText cmd_file_path id = parse_result() if (id > 0) msg_id = id if (loglevel >= 4) print "ELOG: attached graphs to message " + num2str(id) endif else if (loglevel >= 2) print "ELOG: failed to attach graphs." endif result = -4 // error: elog returned error endif endif setdatafolder savedf return result end /// format the ELOG command and essential address arguments. /// /// the following arguments are included (from global variables) if applicable: /// host name, port, SSL, sub-dir, username, password /// the result string does not include leading or trailing space /// /// @param logbook name of the target logbook static function /s prepare_command_line(logbook) string logbook dfref df_general = get_elog_df("", kdfPersistent) dfref df_persistent = get_elog_df(logbook, kdfPersistent) dfref df_volatile = get_elog_df(logbook, kdfVolatile) 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) endif if ((svar_exists(subdir)) && (strlen(subdir) > 0)) cmd += " -d " + subdir endif cmd += " -l \"" + logbook + "\"" if ((nvar_exists(ssl)) && (ssl != 0)) cmd += " -s" endif //cmd += " -w " + password if (svar_exists(username) && svar_exists(password) && (strlen(username) > 0) && (strlen(password) > 0)) cmd += " -u " + username + " " + password endif if (loglevel >= 5) print cmd endif 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 /// and returns the arguments to the elog command to attach the files. /// the result string does not include leading or trailing space /// /// @param graphs names of graph windows to be added as attachments, semicolon separated /// static function /s prepare_graph_attachments(graphs) string graphs // names of graph windows to be added as attachments, semicolon separated string cmd = "" 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) cmd += " -f \"" + graph_path + "\"" endif endfor return cmd end static function /s get_timestamp(sep) string sep Variable now = DateTime string dat = ReplaceString("-", Secs2Date(DateTime, -2), "") string tim = ReplaceString(":", Secs2Time(DateTime, 3), "") return dat + sep + tim 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) path += "elog_temp_message.txt" variable f1 Open f1 as path fprintf f1, message Close f1 else path = "" endif return path end 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) temp_graph_files = AddListItem(path, temp_graph_files, ";", inf) else path = "" endif else path = "" endif return path end static function /s create_cmd_file(cmd) string cmd string path = SpecialDirPath("Temporary", 0, 1, 0) variable len = strlen(path) if (numtype(len) == 0) path += "elog_temp_cmd.bat" variable f1 Open f1 as path fprintf f1, cmd Close f1 else path = "" endif return path end static function /s get_log_path() string path = SpecialDirPath("Temporary", 0, 1, 0) variable len = strlen(path) if (numtype(len) == 0) path += "elog.log" else path = "" endif return path end /// 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" static strconstant elog_parse_id = "ID=%u" /// parse the result file from an elog invokation. /// /// @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) 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 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 data folders. /// call init_package() and load_prefs() once before creating panels. /// /// @param logbook name of the target logbook /// function /s PearlElogPanel(logbook) string logbook dfref savedf = getdatafolderdfr() 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 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 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 variable height = 0 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={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={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={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 break endswitch endfor 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={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={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={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={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={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) 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 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 fSize=10, fStyle=0, textRGB=(0,0,0) RenameWindow #,Message string nb_name = win_name + "#Message" SetActiveSubwindow ## // restore recently used attributes and message svar /z /sdfr=df_persistent recent if (svar_exists(recent) && (strlen(recent) > 0)) set_panel_attributes(win_name, recent) endif svar /z /sdfr=df_persistent recent_message 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 end static function elog_panel_hook(s) STRUCT WMWinHookStruct &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={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={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={51,b_top} break endswitch return hookResult // 0 if nothing done, else 1 end static constant kAttachColSel = 0 static constant kAttachColTitle = 1 static constant kAttachColName = 2 /// 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) // 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 // 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) Redimension /n=(k+1,3) attach_list, attach_sel //InsertPoints /M=0 k, 1, attach_list, attach_sel attach_list[k][kAttachColSel] = "" attach_list[k][kAttachColTitle] = "" attach_list[k][kAttachColName] = s attach_sel[k][kAttachColSel] = 32 attach_sel[k][kAttachColTitle] = 0 attach_sel[k][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 static function bp_submit(ba) : ButtonControl STRUCT WMButtonAction &ba switch( ba.eventCode ) case 2: // mouse up string logbook = GetUserData(ba.win, "", "logbook") string attributes string message string graphs attributes = get_panel_attributes(ba.win) message = get_panel_message(ba.win) graphs = get_panel_graphs(ba.win) variable id if (cmpstr(ba.ctrlName, "b_reply") == 0) // Reply button ControlInfo /w=$ba.win sv_id id = v_value else // Submit button id = 0 endif if ((elog_validate_attributes(logbook, attributes) == 0) && (strlen(message) > 0)) variable result result = elog_create_entry(logbook, attributes, message, graphs=graphs, replyto=id) if (result == 0) dfref df = get_elog_df(logbook, kdfPersistent) svar /sdfr=df recent recent = attributes svar /sdfr=df recent_message recent_message = message else abort "Submission failed. Error code " + num2str(result) + "." endif else abort "Submission failed due to missing/invalid attribute." endif break case -1: // control being killed break endswitch return 0 end /// select top graph window for attachment static function bp_attach_top(ba) : ButtonControl STRUCT WMButtonAction &ba switch( ba.eventCode ) case 2: // mouse up string graphs = WinName(0, 1, 1) set_panel_graphs(ba.win, graphs) break case -1: // control being killed break endswitch 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 switch( ba.eventCode ) case 2: // mouse up string logbook = GetUserData(ba.win, "", "logbook") string graphs graphs = get_panel_graphs(ba.win) variable id ControlInfo /w=$ba.win sv_id id = v_value // TODO : is there a way around this restriction? DoAlert /T="ELOG" 1, "This operation will replace all existing attachments. Do you want to continue?" if ((id > 0) && (v_flag == 1)) variable result result = elog_add_attachment(logbook, id, graphs) if (result != 0) abort "Submission failed. Error code " + num2str(result) + "." endif else abort "Submission failed due to missing/invalid attribute." endif break case -1: // control being killed break endswitch 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 switch( ba.eventCode ) case 2: // mouse up set_panel_attributes(ba.win, "", clear=1) set_panel_message(ba.win, "") set_panel_graphs(ba.win, "") break case -1: // control being killed break endswitch 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"), ";") return windowname end /// get a list of attributes from the fields of the ELOG panel. /// /// @param windowname window name of the ELOG panel /// if empty, use default name "PearlElogPanel" /// /// @return list of attributes to in the format "key1=value1;key2=value2". /// static function /s get_panel_attributes(windowname) string windowname if (strlen(windowname) == 0) windowname = get_default_panel_name() endif if (strlen(windowname) == 0) return "" endif string controls = ControlNameList(windowname, ";") string attributes = "" string control string attribute variable ico variable nco = ItemsInList(controls, ";") for (ico = 0; ico < nco; ico += 1) control = StringFromList(ico, controls, ";") attribute = GetUserData(windowname, control, "attribute") if (strlen(attribute) > 0) ControlInfo /w=$windowname $control switch(v_flag) case 2: // checkbox attributes = ReplaceNumberByKey(attribute, attributes, v_value, "=", ";") break case 3: // popupmenu case 5: // setvariable attributes = ReplaceStringByKey(attribute, attributes, s_value, "=", ";") break endswitch endif endfor return attributes end /// set the fields of the ELOG panel /// /// @param windowname window name of the ELOG panel /// if empty, use default name "PearlElogPanel" /// /// @param attributes list of attributes to set (format "key1=value1;key2=value2") /// /// @param clear what to do if a key is missing in attributes? /// @arg 0 (default) leave the field unchanged /// @arg 1 clear the field /// static function /s set_panel_attributes(windowname, attributes, [clear]) string windowname string attributes variable clear if (strlen(windowname) == 0) windowname = get_default_panel_name() endif if (strlen(windowname) == 0) return "" endif if (ParamIsDefault(clear)) clear = 0 endif string path string controls = ControlNameList(windowname, ";") string control string attribute string value variable numval variable ico variable nco = ItemsInList(controls, ";") for (ico = 0; ico < nco; ico += 1) control = StringFromList(ico, controls, ";") attribute = GetUserData(windowname, control, "attribute") if (strlen(attribute)) value = StringByKey(attribute, attributes, "=", ";") if (strlen(value) || clear) ControlInfo /w=$windowname $control switch(v_flag) case 2: // checkbox numval = NumberByKey(attribute, attributes, "=", ";") if ((numtype(numval) != 0) && clear) numval = 0 endif if (numtype(numval) == 0) CheckBox $control, value=numval, win=$windowname endif break case 3: // popupmenu PopupMenu $control, popvalue=value, win=$windowname break case 5: // setvariable SetVariable /z $control, value= _STR:value, win=$windowname break endswitch endif endif endfor return attributes end /// get the message field of the ELOG panel /// /// @param windowname window name of the ELOG panel /// if empty, use default name "PearlElogPanel" /// /// @return message text /// static function /s get_panel_message(windowname) string windowname if (strlen(windowname) == 0) windowname = get_default_panel_name() endif if (strlen(windowname) == 0) return "" endif string nb = windowname + "#Message" notebook $nb selection={startOfFile, endOfFile} getselection notebook, $nb, 2 return s_selection end /// set the message field of the ELOG panel /// /// @param windowname window name of the ELOG panel /// if empty, use default name "PearlElogPanel" /// /// @param message message text that can be passed to the @c Notebook operation. /// /// @return original message (unchanged) /// static function /s set_panel_message(windowname, message) string windowname string message if (strlen(windowname) == 0) windowname = get_default_panel_name() endif string nb = windowname + "#Message" notebook $nb selection={startOfFile, endOfFile},text=message return message 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. /// 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 if (strlen(windowname) == 0) return "" endif 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 = "" 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 string graphs if (strlen(windowname) == 0) windowname = get_default_panel_name() endif if (strlen(windowname) == 0) return "" endif 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 end