exptdriver.cc

Go to the documentation of this file.
00001 
00003 
00004 //
00005 // Copyright (c) 1999-2004 California Institute of Technology
00006 // Copyright (c) 2004-2007 University of Southern California
00007 // Rob Peters <rjpeters at usc dot edu>
00008 //
00009 // created: Tue May 11 13:33:50 1999
00010 // commit: $Id: exptdriver.cc 10065 2007-04-12 05:54:56Z rjpeters $
00011 // $HeadURL: file:///lab/rjpeters/svnrepo/code/trunk/groovx/src/visx/exptdriver.cc $
00012 //
00013 // --------------------------------------------------------------------
00014 //
00015 // This file is part of GroovX.
00016 //   [http://ilab.usc.edu/rjpeters/groovx/]
00017 //
00018 // GroovX is free software; you can redistribute it and/or modify it
00019 // under the terms of the GNU General Public License as published by
00020 // the Free Software Foundation; either version 2 of the License, or
00021 // (at your option) any later version.
00022 //
00023 // GroovX is distributed in the hope that it will be useful, but
00024 // WITHOUT ANY WARRANTY; without even the implied warranty of
00025 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00026 // General Public License for more details.
00027 //
00028 // You should have received a copy of the GNU General Public License
00029 // along with GroovX; if not, write to the Free Software Foundation,
00030 // Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
00031 //
00033 
00034 #ifndef GROOVX_VISX_EXPTDRIVER_CC_UTC20050626084017_DEFINED
00035 #define GROOVX_VISX_EXPTDRIVER_CC_UTC20050626084017_DEFINED
00036 
00037 #include "visx/exptdriver.h"
00038 
00039 #include "io/ioproxy.h"
00040 #include "io/ioutil.h"
00041 #include "io/readutils.h"
00042 #include "io/writeutils.h"
00043 
00044 #include "nub/log.h"
00045 #include "nub/ref.h"
00046 
00047 #include "tcl/eventloop.h"
00048 #include "tcl/interp.h"
00049 
00050 #include "tcl-gfx/toglet.h"
00051 #include "tcl-io/tclprocwrapper.h"
00052 
00053 #include "rutz/error.h"
00054 #include "rutz/fstring.h"
00055 #include "rutz/iter.h"
00056 #include "rutz/sfmt.h"
00057 #include "rutz/timeformat.h"
00058 #include "rutz/unixcall.h"
00059 
00060 #include "visx/tlistutils.h"
00061 
00062 #include <cstdlib> // for getenv()
00063 #include <cstring> // for strncmp()
00064 #include <string>
00065 #include <sys/stat.h> // for mode_t constants S_IRUSR etc.
00066 #include <unistd.h> // for sleep()
00067 
00068 #define GVX_DYNAMIC_TRACE_EXPR ExptDriver::tracer.status()
00069 #include "rutz/trace.h"
00070 #include "rutz/debug.h"
00071 GVX_DBG_REGISTER
00072 
00073 using rutz::fstring;
00074 
00075 rutz::tracer ExptDriver::tracer;
00076 
00078 //
00079 // File scope declarations
00080 //
00082 
00083 namespace
00084 {
00085   const io::version_id EXPTDRIVER_SVID = 6;
00086 }
00087 
00089 //
00090 // ExptDriver::Impl class definition
00091 //
00093 
00094 class ExptDriver::Impl
00095 {
00096 private:
00097   Impl(const Impl&);
00098   Impl& operator=(const Impl&);
00099 
00100 public:
00101   Impl() :
00102     interp(tcl::event_loop::interp()),
00103     widget(),
00104     hostname(""),
00105     subject(""),
00106     beginDate(""),
00107     endDate(""),
00108     autosaveFile("__autosave_file"),
00109     filePrefix("expt"),
00110     infoLog(),
00111     autosavePeriod(10),
00112     doWhenComplete(new tcl::ProcWrapper(interp)),
00113     numTrialsCompleted(0),
00114     createTime(rutz::time::wall_clock_now()),
00115     fileTimestamp(rutz::format_time(createTime, "%Y%b%d_%H%M%S"))
00116   {}
00117 
00118   ~Impl() {}
00119 
00120   void addLogInfo(const char* message)
00121   {
00122     const fstring date_string =
00123       rutz::format_time(rutz::time::wall_clock_now());
00124 
00125     infoLog += rutz::sfmt("@%s %s\n",
00126                           date_string.c_str(), message).c_str();
00127   }
00128 
00129   //
00130   // data members
00131   //
00132 
00133   tcl::interpreter interp;
00134 
00135   nub::soft_ref<Toglet> widget;
00136 
00137   fstring hostname;     // Host computer on which Expt was begun
00138   fstring subject;      // Id of subject on whom Expt was performed
00139   fstring beginDate;    // Date(+time) when Expt was begun
00140   fstring endDate;      // Date(+time) when Expt was stopped
00141   fstring autosaveFile; // Filename used for autosaves
00142   fstring filePrefix;   // String used as filename prefix for output files
00143 
00144   std::string infoLog;
00145 
00146   int autosavePeriod;
00147 
00148   nub::ref<tcl::ProcWrapper> doWhenComplete;
00149 
00150   unsigned int numTrialsCompleted;
00151 
00152   rutz::time createTime;// Timestamp of when the ExptDriver is created
00153   fstring fileTimestamp;// Timestamp used in filenames
00154 };
00155 
00157 //
00158 // ExptDriver creator method definitions
00159 //
00161 
00162 ExptDriver::ExptDriver() :
00163   rep(new Impl)
00164 {
00165 GVX_TRACE("ExptDriver::ExptDriver");
00166 
00167   rep->addLogInfo(tcl::event_loop::command_line().c_str());
00168 }
00169 
00170 ExptDriver::~ExptDriver() throw()
00171 {
00172 GVX_TRACE("ExptDriver::~ExptDriver");
00173   delete rep;
00174 }
00175 
00176 io::version_id ExptDriver::class_version_id() const
00177 {
00178 GVX_TRACE("ExptDriver::class_version_id");
00179   return EXPTDRIVER_SVID;
00180 }
00181 
00182 void ExptDriver::read_from(io::reader& reader)
00183 {
00184 GVX_TRACE("ExptDriver::read_from");
00185 
00186   reader.ensure_version_id("ExptDriver", 6,
00187                            "Try cvs tag xml_conversion_20040526",
00188                            SRC_POS);
00189 
00190   reader.read_value("hostname", rep->hostname);
00191   reader.read_value("subject", rep->subject);
00192   reader.read_value("beginDate", rep->beginDate);
00193   reader.read_value("endDate", rep->endDate);
00194   reader.read_value("autosaveFile", rep->autosaveFile);
00195   reader.read_value("autosavePeriod", rep->autosavePeriod);
00196   {
00197     rutz::fstring tmp;
00198     reader.read_value("infoLog", tmp);
00199     rep->infoLog = tmp.c_str();
00200   }
00201   reader.read_owned_object("doWhenComplete", rep->doWhenComplete);
00202   reader.read_value("filePrefix", rep->filePrefix);
00203 
00204   reader.read_base_class("ElementContainer",
00205                        io::make_proxy<ElementContainer>(this));
00206 }
00207 
00208 void ExptDriver::write_to(io::writer& writer) const
00209 {
00210 GVX_TRACE("ExptDriver::write_to");
00211 
00212   writer.ensure_output_version_id("ExptDriver", EXPTDRIVER_SVID, 6,
00213                               "Try groovx0.8a7", SRC_POS);
00214 
00215   writer.write_value("hostname", rep->hostname);
00216   writer.write_value("subject", rep->subject);
00217   writer.write_value("beginDate", rep->beginDate);
00218   writer.write_value("endDate", rep->endDate);
00219   writer.write_value("autosaveFile", rep->autosaveFile);
00220   writer.write_value("autosavePeriod", rep->autosavePeriod);
00221   writer.write_value("infoLog", rutz::fstring(rep->infoLog.c_str()));
00222   writer.write_owned_object("doWhenComplete", rep->doWhenComplete);
00223   writer.write_value("filePrefix", rep->filePrefix);
00224 
00225   writer.write_base_class("ElementContainer",
00226                         io::make_const_proxy<ElementContainer>(this));
00227 }
00228 
00229 
00231 //
00232 // ExptDriver's Element interface
00233 //
00235 
00236 const nub::soft_ref<Toglet>& ExptDriver::getWidget() const
00237 {
00238 GVX_TRACE("ExptDriver::getWidget");
00239   return rep->widget;
00240 }
00241 
00242 void ExptDriver::vxRun(Element& /*parent*/)
00243 {
00244 GVX_TRACE("currentElement");
00245   /* FIXME */ GVX_ASSERT(false);
00246 }
00247 
00248 void ExptDriver::vxEndTrialHook()
00249 {
00250 GVX_TRACE("ExptDriver::vxEndTrialHook");
00251 
00252   ++(rep->numTrialsCompleted);
00253 
00254   // Determine whether an autosave is necessary now. An autosave is
00255   // requested only if the autosave period is positive, and the number of
00256   // completed trials is evenly divisible by the autosave period.
00257   if ( rep->autosavePeriod <= 0 ||
00258        (rep->numTrialsCompleted % rep->autosavePeriod) != 0 ||
00259        rep->autosaveFile.is_empty() )
00260     return;
00261 
00262   dbg_eval_nl(3, rep->autosaveFile.c_str());
00263   io::save_gvx(nub::ref<io::serializable>(this), rep->autosaveFile);
00264 }
00265 
00266 void ExptDriver::vxAllChildrenFinished()
00267 {
00268 GVX_TRACE("ExptDriver::vxAllChildrenFinished");
00269 
00270   nub::log( "ExptDriver::vxAllChildrenFinished" );
00271 
00272   rep->addLogInfo("Experiment complete.");
00273 
00274   vxHalt();
00275 
00276   storeData();
00277 
00278   nub::logging::add_obj_scope(*rep->doWhenComplete);
00279   rep->doWhenComplete->invoke(""); // Call the user-defined callback
00280   nub::logging::remove_obj_scope(*rep->doWhenComplete);
00281 
00282   nub::logging::remove_obj_scope(*this);
00283 }
00284 
00286 //
00287 // ExptDriver accessor + manipulator method definitions
00288 //
00290 
00291 const fstring& ExptDriver::getAutosaveFile() const
00292 {
00293 GVX_TRACE("ExptDriver::getAutosaveFile");
00294   return rep->autosaveFile;
00295 }
00296 
00297 void ExptDriver::setAutosaveFile(const fstring& str)
00298 {
00299 GVX_TRACE("ExptDriver::setAutosaveFile");
00300   rep->autosaveFile = str;
00301 }
00302 
00303 const fstring& ExptDriver::getFilePrefix() const
00304 {
00305 GVX_TRACE("ExptDriver::getFilePrefix");
00306   return rep->filePrefix;
00307 }
00308 
00309 void ExptDriver::setFilePrefix(const fstring& str)
00310 {
00311 GVX_TRACE("ExptDriver::setFilePrefix");
00312   rep->filePrefix = str;
00313 }
00314 
00315 void ExptDriver::claimLogFile() const
00316 {
00317 GVX_TRACE("ExptDriver::claimLogFile");
00318   nub::logging::set_log_filename
00319     (rutz::sfmt("%s_%s.log",
00320                 rep->filePrefix.c_str(),
00321                 rep->fileTimestamp.c_str()));
00322 }
00323 
00324 int ExptDriver::getAutosavePeriod() const
00325 {
00326 GVX_TRACE("ExptDriver::getAutosavePeriod");
00327   return rep->autosavePeriod;
00328 }
00329 
00330 void ExptDriver::setAutosavePeriod(int period)
00331 {
00332 GVX_TRACE("ExptDriver::setAutosavePeriod");
00333   rep->autosavePeriod = period;
00334 }
00335 
00336 const char* ExptDriver::getInfoLog() const
00337 {
00338 GVX_TRACE("ExptDriver::getInfoLog");
00339   return rep->infoLog.c_str();
00340 }
00341 
00342 fstring ExptDriver::getDoWhenComplete() const
00343 {
00344 GVX_TRACE("ExptDriver::getDoWhenComplete");
00345   return rep->doWhenComplete->fullSpec();
00346 }
00347 
00348 void ExptDriver::setDoWhenComplete(const fstring& script)
00349 {
00350 GVX_TRACE("ExptDriver::setDoWhenComplete");
00351   rep->doWhenComplete->define("", script);
00352 }
00353 
00354 void ExptDriver::setWidget(const nub::soft_ref<Toglet>& widg)
00355 {
00356 GVX_TRACE("ExptDriver::setWidget");
00357   rep->widget = widg;
00358 }
00359 
00360 void ExptDriver::edBeginExpt()
00361 {
00362 GVX_TRACE("ExptDriver::edBeginExpt");
00363 
00364   if (isComplete())
00365     {
00366       throw rutz::error("Can't begin experiment: "
00367                         "there are no pending elements", SRC_POS);
00368     }
00369 
00370   rep->addLogInfo("Beginning experiment.");
00371 
00372   const fstring cwd = rutz::unixcall::getcwd();
00373 
00374   rep->beginDate = rutz::format_time(rutz::time::wall_clock_now());
00375   rep->hostname = getenv("HOSTNAME");
00376   rep->subject = cwd;
00377   rep->numTrialsCompleted = 0;
00378 
00379   nub::logging::reset(); // to clear any existing timer scopes
00380   nub::logging::add_obj_scope(*this);
00381 
00382   claimLogFile();
00383 
00384   nub::log(rutz::sfmt("expt begin: %s", rep->beginDate.c_str()));
00385   nub::log(rutz::sfmt("hostname: %s", rep->hostname.c_str()));
00386   nub::log(rutz::sfmt("cwd: %s", cwd.c_str()));
00387   nub::log(rutz::sfmt("cmdline: %s",
00388                       tcl::event_loop::command_line().c_str()));
00389 
00390   currentElement()->vxRun(*this);
00391 }
00392 
00393 void ExptDriver::edResumeExpt()
00394 {
00395 GVX_TRACE("ExptDriver::edResumeExpt");
00396 
00397   if (isComplete())
00398     {
00399       throw rutz::error("Can't resume experiment: "
00400                         "there are no pending elements", SRC_POS);
00401     }
00402 
00403   currentElement()->vxRun(*this);
00404 }
00405 
00406 #if 0
00407 // This is disabled right now because it interacts badly with top-level
00408 // Toglet windows that have override_direct=true... neither the Toglet nor
00409 // the tk_messageBox are unable to receive any events, so the application
00410 // appears to lock up
00411 void ExptDriver::pause()
00412 {
00413 GVX_TRACE("ExptDriver::pause");
00414 
00415   // Halt the experiment, then pop up a pause window. When the user
00416   // dismisses the window, the experiment will resume.
00417 
00418   fstring pauseMsgCmd
00419     ("tk_messageBox -default ok -icon info "
00420      "-title \"Pause\" -type ok "
00421      "-message \"Experiment paused. Click OK to continue.\";\n");
00422 
00423   vxHalt();
00424 
00425   rep->addLogInfo("Experiment paused.");
00426 
00427   rep->interp.eval(pauseMsgCmd);
00428 
00429   tcl::interpreter::clear_event_queue();
00430 
00431   rep->widget->fullClearscreen();
00432   rep->widget->fullClearscreen();
00433 
00434   ::sleep(2);
00435 
00436   rep->widget->fullClearscreen();
00437   rep->widget->fullClearscreen();
00438 
00439   tcl::interpreter::clear_event_queue();
00440 
00441   rep->addLogInfo("Resuming experiment.");
00442 
00443   edResumeExpt();
00444 }
00445 #endif
00446 
00447 namespace
00448 {
00449   // Check if fname exists as a file; if so, then rename it to a
00450   // unique name so that at the end of this call, no file exists with
00451   // the given fname.
00452   void renameFileIfExists(const fstring& fname)
00453   {
00454     struct stat buf;
00455 
00456     if (stat(fname.c_str(), &buf) != 0)
00457       {
00458         // no file exists at fname, so we don't need to do anything
00459         return;
00460       }
00461 
00462     fstring newname = fname;
00463 
00464     int i = 0;
00465 
00466     while (stat(newname.c_str(), &buf) == 0)
00467       {
00468         newname = rutz::sfmt("%s.bkp%d", fname.c_str(), i++);
00469       }
00470 
00471     rutz::unixcall::rename(fname.c_str(), newname.c_str());
00472   }
00473 }
00474 
00475 void ExptDriver::storeData()
00476 {
00477 GVX_TRACE("ExptDriver::storeData");
00478 
00479   // The experiment and a summary of the responses to it are written to
00480   // files with unique filenames.
00481 
00482   const rutz::time timestamp = rutz::time::wall_clock_now();
00483 
00484   rep->endDate = rutz::format_time(timestamp);
00485 
00486   // Write the main experiment file
00487   const fstring expt_filename =
00488     rutz::sfmt("%s_%s.gvx", rep->filePrefix.c_str(),
00489                rep->fileTimestamp.c_str());
00490   renameFileIfExists(expt_filename);
00491   io::save_gvx(nub::ref<io::serializable>(this), expt_filename.c_str());
00492   nub::log( rutz::sfmt("wrote file %s", expt_filename.c_str()) );
00493 
00494   // Write the responses file
00495   const fstring resp_filename =
00496     rutz::sfmt("%s_%s.resp", rep->filePrefix.c_str(),
00497                rep->fileTimestamp.c_str());
00498   renameFileIfExists(resp_filename);
00499   TlistUtils::writeResponses(resp_filename.c_str());
00500   nub::log( rutz::sfmt("wrote file %s", resp_filename.c_str()) );
00501 
00502   // Change file access modes to allow read-only by all
00503   const mode_t datafile_mode = S_IRUSR | S_IRGRP | S_IROTH;
00504 
00505   rutz::unixcall::chmod(expt_filename.c_str(), datafile_mode);
00506   rutz::unixcall::chmod(resp_filename.c_str(), datafile_mode);
00507 
00508   rep->addLogInfo("Experiment saved.");
00509 }
00510 
00511 static const char __attribute__((used)) vcid_groovx_visx_exptdriver_cc_utc20050626084017[] = "$Id: exptdriver.cc 10065 2007-04-12 05:54:56Z rjpeters $ $HeadURL: file:
00512 #endif // !GROOVX_VISX_EXPTDRIVER_CC_UTC20050626084017_DEFINED

The software described here is Copyright (c) 1998-2005, Rob Peters.
This page was generated Wed Dec 3 06:49:41 2008 by Doxygen version 1.5.5.