#pragma rtGlobals=3 // Use modern global access method and strict wave access. #pragma version = 1.2 #pragma IgorVersion = 6.2 #pragma ModuleName = PearlElog // $Id$ // author: matthias.muntwiler@psi.ch // Copyright (c) 2013-15 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 work flows: /// 1. send a preview of a selected measurement to ELOG. /// 2. send any Igor graph to ELOG. (CLI and GUI) /// 3. direct access to all ELOG parameters. (CLI only) /// /// the configuration of the ELOG server and logbooks (except user name and password) /// as well as the most recently used attributes are persisted in the preference file. /// /// 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 /// /// user name and password are used if configured in the package data folder. /// there is currently no separate user interface. /// /// @attention some functions in this module refer specifically to the ELOG configuration at PEARL. /// logbook and attributes are specific to the ELOG configuration at PEARL. /// /// @author matthias muntwiler, matthias.muntwiler@psi.ch /// /// @copyright 2013-15 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 ELOG package is initialized (overwriting existing data) but no panel is opened. /// function pearl_elog(logbook) string logbook 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 /// save preferences and recent values before Igor opens a new experiment. static function IgorBeforeNewHook(igorApplicationNameStr) string igorApplicationNameStr save_prefs() return 0 end /// save preferences and recent values before Igor quits. static function IgorQuitHook(igorApplicationNameStr) string igorApplicationNameStr save_prefs() 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 /// returns the package data folder or logbook datafolder /// /// @param logbook name of logbook, or empty string for package datafolder /// /// @returns data folder reference static function /df get_elog_df(logbook) string logbook dfref df_package = $package_path if (strlen(logbook) > 0) dfref df_logbook = df_package:$logbook return df_logbook else return df_package endif end /// initialize the package data folder. /// /// the data folder is initialized with a default, local configuration and the PEARL logbooks. /// the server configuration should be set in the preferences. /// /// @remark this function is specific to the setup at PEARL. static function init_package([clean]) variable clean // 0 (default) = do not overwrite existing data // 1 = clean configuration, overwrite existing data if (ParamIsDefault(clean)) clean = 0 endif dfref savedf = getdatafolderdfr() dfref basedf = get_elog_df("") if ((clean == 0) && (DataFolderRefStatus(dfr) == 1)) return 1 endif setdatafolder root: newdatafolder /o/s packages newdatafolder /o/s $package_name dfref basedf = getdatafolderdfr() // common configuration (persistent except username and password) 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 = "" // list of configured logbooks // there must be a sub-folder for each of these string /g logbooks = "Experiments;Calculations" // Experiments logbook setdatafolder basedf 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" // 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" // 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 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 newdatafolder /o/s Calculations // ELOG logbook name string /g logbook = "Calculations" // attributes (persistent) // available attributes string /g attributes = "author;project;sample;program;revision;machine;job;source path;result path;valid" // controls corresponding to attributes // prefix determines the control type: sv_ = setvariable (string), pm_ = popup menu, cb = check box string /g controls = "sv_author;sv_project;sv_sample;pm_program;sv_revision;pm_machine;sv_job;sv_sourcepath;sv_resultpath;cb_valid" // attributes with fixed options, value item declares the options string string /g options = "program=programs;machine=machines" // attributes which must be defined string /g required_attributes = "author;project;sample" // option lists string /g programs = "DMSUP;EDAC;MSC;MUFPOT;SSC" string /g machines = "llcx;Merlin;PC" // 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 function elog_config([elog_path, hostname, port, subdir]) string elog_path string hostname variable port string subdir dfref saveDF = GetDataFolderDFR() if (!ParamIsDefault(elog_path)) svar g_elog_path = $(package_path + "elog_path") g_elog_path = elog_path endif if (!ParamIsDefault(hostname)) svar g_hostname = $(package_path + "hostname") g_hostname = hostname endif if (!ParamIsDefault(port)) nvar g_port = $(package_path + "port") g_port = port endif if (!ParamIsDefault(subdir)) svar g_subdir = $(package_path + "subdir") g_subdir = subdir endif setdatafolder savedf end /// save persistent package data to the preferences file. /// /// currently saves everything under the package folder. static function save_prefs() dfref saveDF = GetDataFolderDFR() dfref df = get_elog_df("") 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 setdatafolder root: NewDataFolder /O/S packages NewDataFolder /O/S $package_name 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 result = 0 endif endif SetDataFolder saveDF return result end /// add a copy of the given graph to ELOG, prompting 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_add_graph_prompt(graphname, [replyto]) string graphname variable replyto 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 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 endif return v_flag 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 function elog_create_entry(logbook, attributes, message, [encoding, graphs, replyto]) string logbook string attributes // key=value list of attributes, semicolon separated 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 if (ParamIsDefault(encoding)) encoding = 1 endif if (ParamIsDefault(graphs)) graphs = "" endif if (ParamIsDefault(replyto)) replyto = 0 endif dfref savedf = getdatafolderdfr() setdatafolder $(package_path) setdatafolder $(logbook) variable result = 0 nvar msg_id if (elog_validate_attributes(logbook,attributes) != 0) result = -3 // error: invalid/missing attributes endif string cmd = prepare_command_line(logbook) if (strlen(cmd) == 0) result = -2 // error: invalid/missing command line 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) 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() + "\"" 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) else print "ELOG: sending message failed." result = -4 // error: elog returned error endif cleanup_temp_files() else result = -1 // error 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() setdatafolder $(package_path) setdatafolder $(logbook) variable result = 0 nvar msg_id 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 print "ELOG: attached graphs to message " + num2str(id) else print "ELOG: attaching graphs failed." result = -4 // error: elog returned error endif cleanup_temp_files() 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 savedf = getdatafolderdfr() setdatafolder $(package_path) svar elog_path svar hostname nvar port nvar ssl svar subdir svar username svar password string cmd cmd = "\"" + elog_path + "\"" 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 setdatafolder savedf 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 dfref savedf = getdatafolderdfr() setdatafolder $(package_path) 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 setdatafolder savedf 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 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 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) 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 static function /s cleanup_temp_files() string path = SpecialDirPath("Temporary", 0, 1, 0) string cmd sprintf cmd, "del \"%s\elog*.*\"", path //ExecuteScriptText cmd return path 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() string path = get_log_path() string line = "" variable len = strlen(path) if (numtype(len) == 0) variable f1 Open /R/Z f1 as path if (v_flag == 0) FReadLine f1, line 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 endif return id end /// open a new panel for submitting data to ELOG. /// /// this function creates only the panel but not the necessary package data folder. /// 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() setdatafolder $(package_path) setdatafolder $(logbook) string df_path = getdatafolder(1) string win_name = logbook + "ElogPanel" string win_title = "ELOG " + logbook NewPanel /K=1 /N=$win_name /W=(600,200,926,494) as win_title win_name = s_name ModifyPanel /w=$win_name cbRGB=(52224,52224,65280) svar attributes svar controls svar options variable iattr variable nattr = ItemsInList(attributes, ";") string s_attr string s_control string s_option string options_path string variable_path variable ypos = 2 for (iattr = 0; iattr < nattr; iattr += 1) s_attr = StringFromList(iattr, attributes, ";") s_control = StringFromList(iattr, controls, ";") strswitch(s_control[0,1]) case "sv": SetVariable $s_control, win=$win_name, pos={0,ypos}, size={260,16}, bodyWidth=200 SetVariable $s_control, win=$win_name, title=s_attr, value= _STR:"" SetVariable $s_control, win=$win_name, userdata(attribute)=s_attr ypos += 18 break case "pm": options_path = df_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 PopupMenu $s_control, win=$win_name, userdata(attribute)=s_attr ypos += 23 break case "cb": CheckBox $s_control, win=$win_name, pos={60,ypos}, size={260,14} CheckBox $s_control, win=$win_name, title=s_attr, value= 1 CheckBox $s_control, win=$win_name, userdata(attribute)=s_attr ypos += 17 break endswitch endfor PopupMenu pm_attach,win=$win_name, pos={0,ypos},size={260,21},bodyWidth=200,title="Attachment" PopupMenu pm_attach,win=$win_name, mode=1,popvalue="(none)",value=PearlElog#pm_list_attach_items() PopupMenu pm_attach,win=$win_name, help={"Choose any visible Igor graph for attachment."} Button b_select_attach_top,win=$win_name, pos={264,ypos},size={60,20},proc=PearlElog#bp_select_attach_top,title="Top Graph" Button b_select_attach_top,win=$win_name, help={"Select top graph window."} Button b_select_attach_top,win=$win_name, fcolor=(56576,60928,47872) ypos += 246-160 Button b_submit,win=$win_name, pos={60,ypos},size={46,20},proc=PearlElog#bp_submit,title="Submit" Button b_submit,win=$win_name, help={"Submit form data to ELOG (new entry)."} Button b_submit,win=$win_name, fcolor=(56576,60928,47872) Button b_clear,win=$win_name, pos={110,ypos},size={46,20},proc=PearlElog#bp_clear,title="Clear" Button b_clear,win=$win_name, help={"Clear the form fields"} Button b_clear,win=$win_name, fcolor=(56576,60928,47872) ypos += 272-246 variable_path = df_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."} ypos += 270-272 Button b_attach,win=$win_name, pos={160,ypos},size={48,20},proc=PearlElog#bp_attach,title="Attach" Button b_attach,win=$win_name, help={"Attach the selected graph to an existing ELOG entry (correct ID required)."} Button b_attach,win=$win_name, fcolor=(56576,60928,47872) Button b_reply,win=$win_name, pos={210,ypos},size={48,20},proc=PearlElog#bp_submit,title="Reply" Button b_reply,win=$win_name, help={"Submit form data to ELOG as a reply to an existing message (correct ID required)."} Button b_reply,win=$win_name, fcolor=(56576,60928,47872) ypos += 184-270 TitleBox t_message,win=$win_name, pos={0,ypos},size={58,16},fixedSize=1,frame=0,anchor=RT,title="Message" SetWindow $win_name, hook(elogPanelHook)=PearlElog#elog_panel_hook SetWindow $win_name, userdata(logbook)=logbook DefineGuide UGH0={FT,ypos},UGV0={FL,60},UGH1={FB,-52},UGV1={FR,-2} NewNotebook /F=0 /N=Message /OPTS=3 /W=(115,404,345,341)/FG=(UGV0,UGH0,UGV1,UGH1) /HOST=# Notebook kwTopWin, defaultTab=20, statusWidth=0, autoSave=0 Notebook kwTopWin font="Arial", fSize=10, fStyle=0, textRGB=(0,0,0) RenameWindow #,Message SetActiveSubwindow ## // restore recently used attributes and message svar /z /sdfr=get_elog_df(logbook) recent if (svar_exists(recent) && (strlen(recent) > 0)) set_panel_attributes(win_name, recent) endif svar /z /sdfr=get_elog_df(logbook) recent_message if (svar_exists(recent_message) && (strlen(recent_message) > 0)) set_panel_message(win_name, recent_message) endif setdatafolder savedf return win_name end static function elog_panel_hook(s) STRUCT WMWinHookStruct &s Variable hookResult = 0 switch(s.eventCode) case 6: // resize // move bottom-aligned controls when the window is resized variable b_top = s.winRect.bottom + 4 Button b_submit,pos={60,b_top} Button b_clear,pos={110,b_top} b_top += 24 Button b_attach,pos={160,b_top} Button b_reply,pos={210,b_top} b_top += 2 SetVariable sv_id,pos={46,b_top} break endswitch return hookResult // 0 if nothing done, else 1 end /// get a list of graph windows for a popup menu. static function /s pm_list_attach_items() // get graph names string names = WinList("*", ";", "WIN:1;VISIBLE:1") names = SortList(names, ";", 16) // get corresponding graph titles variable nnames = ItemsInList(names, ";") variable iname string name string item string items = "" // format each entry like "name: title" for (iname = 0; iname < nnames; iname += 1) name = StringFromList(iname, names, ";") getwindow /z $name, wtitle if (v_flag == 0) item = ReplaceString(";", s_value, ", ") // title item = item + " (" + name + ")" items = AddListItem(item, items, ";", inf) endif endfor items = AddListItem("(none)", items, ";") return items 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) svar /sdfr=get_elog_df(logbook) recent recent = attributes svar /sdfr=get_elog_df(logbook) 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_select_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 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_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 /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 /// /// @returns a semicolon-separated list, /// or the empty string if the selection is not valid. /// /// in the current version, the function returns at most one graph. /// in future versions, the function may return more than one graph. /// static function /s get_panel_graphs(windowname) string windowname // panel window name if (strlen(windowname) == 0) windowname = get_default_panel_name() endif if (strlen(windowname) == 0) return "" endif ControlInfo /W=$windowname pm_attach // menu item has the form: "title (name)" string graphname = ReplaceString(")", StringFromList(ItemsInList(s_value, "(") - 1, s_value, "("), "") string graphs = "" string windows = WinList(graphname, ";", "WIN:1") if (ItemsInList(windows) == 1) graphs = graphname else graphs = "" endif return graphs end static function /s set_panel_graphs(windowname, graphs) string windowname // panel window name. looks for default panel if empty. string graphs // name of graph window to select for attachment. if (strlen(windowname) == 0) windowname = get_default_panel_name() endif if (strlen(windowname) == 0) return "" endif // the panel supports only one graph string graph = StringFromList(0, graphs, ";") string items = pm_list_attach_items() string item string itemname variable nitems = ItemsInList(items, ";") variable iitem for (iitem = 0; iitem < nitems; iitem += 1) item = StringFromList(iitem, items, ";") itemname = ReplaceString(")", StringFromList(ItemsInList(item, "(") - 1, item, "("), "") if (cmpstr(graph, itemname) == 0) iitem += 1 PopupMenu pm_attach mode=iitem, win=$windowname break endif endfor return graph end