Line data Source code
1 : #include "otsdaq-utilities/ECLWriter/ECLSupervisor.h"
2 : #include "otsdaq/CgiDataUtilities/CgiDataUtilities.h"
3 : #include "otsdaq/ConfigurationInterface/ConfigurationManager.h"
4 : #include "otsdaq/Macros/CoutMacros.h"
5 : #include "otsdaq/MessageFacility/MessageFacility.h"
6 : #include "otsdaq/SOAPUtilities/SOAPCommand.h"
7 : #include "otsdaq/SOAPUtilities/SOAPParameters.h"
8 : #include "otsdaq/SOAPUtilities/SOAPUtilities.h"
9 : #include "otsdaq/TablePlugins/XDAQContextTable/XDAQContextTable.h"
10 : #include "otsdaq/XmlUtilities/HttpXmlDocument.h"
11 :
12 : #include <dirent.h> /*DIR and dirent*/
13 : #include <sys/stat.h> /*mkdir*/
14 :
15 : #include <xdaq/NamespaceURI.h>
16 :
17 : #include <iomanip>
18 : #include <iostream>
19 : #include "otsdaq/TableCore/TableGroupKey.h"
20 :
21 : using namespace ots;
22 :
23 : #undef __MF_SUBJECT__
24 : #define __MF_SUBJECT__ "ECL"
25 :
26 : #define XML_ADMIN_STATUS "logbook_admin_status"
27 : #define XML_STATUS "logbook_status"
28 : #define XML_MOST_RECENT_DAY "most_recent_day"
29 : #define XML_TIMEZONE_OFFSET "timezone_offset_hours"
30 : #define XML_CATEGORY_ROOT "categories"
31 : #define XML_CATEGORY "category"
32 : #define XML_ACTIVE_CATEGORY "active_category"
33 : #define XML_SAFE_URL "ecl_url"
34 : #define XML_RESPONSE_CATEGORY "response_category"
35 : #define XML_CATEGORY_CREATE "create_time"
36 : #define XML_CATEGORY_CREATOR "creator"
37 :
38 : #define XML_LOGBOOK_ENTRY "logbook_entry"
39 : #define XML_LOGBOOK_ENTRY_SUBJECT "logbook_entry_subject"
40 : #define XML_LOGBOOK_ENTRY_TEXT "logbook_entry_text"
41 : #define XML_LOGBOOK_ENTRY_FILE "logbook_entry_file"
42 : #define XML_LOGBOOK_ENTRY_TIME "logbook_entry_time"
43 : #define XML_LOGBOOK_ENTRY_CREATOR "logbook_entry_creator"
44 : #define XML_LOGBOOK_ENTRY_HIDDEN "logbook_entry_hidden"
45 : #define XML_LOGBOOK_ENTRY_HIDER "logbook_entry_hider"
46 : #define XML_LOGBOOK_ENTRY_HIDDEN_TIME "logbook_entry_hidden_time"
47 :
48 0 : XDAQ_INSTANTIATOR_IMPL(ECLSupervisor)
49 :
50 : //==============================================================================
51 0 : ECLSupervisor::ECLSupervisor(xdaq::ApplicationStub* stub) : CoreSupervisorBase(stub)
52 : {
53 0 : __SUP_COUT__ << "Constructor." << __E__;
54 :
55 0 : INIT_MF("." /*directory used is USER_DATA/LOG/.*/);
56 :
57 0 : xoap::bind(
58 : this, &ECLSupervisor::MakeSystemLogEntry, "MakeSystemLogEntry", XDAQ_NS_URI);
59 :
60 0 : init();
61 :
62 0 : __SUP_COUT__ << "Constructed." << __E__;
63 0 : } // end constructor
64 :
65 : //==============================================================================
66 0 : void ECLSupervisor::init(void)
67 : try
68 : {
69 : // do not put username/pw in saved/committed text files
70 0 : ECLUser_ = __ENV__("ECL_USER_NAME");
71 0 : ECLHost_ = __ENV__("ECL_URL"); // e.g. https://dbweb6.fnal.gov:8443/ECL/test_beam
72 0 : ECLPwd_ = __ENV__("ECL_PASSWORD");
73 0 : ECLCategory_ = __ENV__("ECL_CATEGORY");
74 0 : CategoryName_ = __ENV__("OTS_OWNER") + std::string(" ots");
75 :
76 : // Determine the timezone offset from UTC time in hours
77 : // Get the current time
78 0 : std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
79 :
80 : // Get the current time in UTC
81 0 : std::chrono::zoned_time<std::chrono::system_clock::duration> utcTime{"UTC", now};
82 :
83 : // Get the current local time using the current system's timezone
84 : std::chrono::zoned_time<std::chrono::system_clock::duration> localTime{
85 0 : std::chrono::current_zone(), now};
86 :
87 : // Calculate the difference between local time and UTC
88 0 : std::chrono::seconds offset = std::chrono::duration_cast<std::chrono::seconds>(
89 0 : localTime.get_local_time() - utcTime.get_local_time());
90 :
91 : // Convert the offset to hours and minutes
92 0 : timezoneHourOffset_ = offset.count() / 3600;
93 :
94 0 : __SUP_COUTV__(timezoneHourOffset_);
95 :
96 0 : eclConn_ = std::make_unique<ECLConnection>(ECLUser_, ECLPwd_, ECLHost_);
97 :
98 : } // end init()
99 0 : catch(const std::runtime_error& e)
100 : {
101 0 : __COUT_ERR__ << "ECL environment variables not setup: " << e.what() << __E__;
102 0 : ECLUser_ = ""; //clearing to disable
103 0 : }
104 :
105 : //==============================================================================
106 0 : ECLSupervisor::~ECLSupervisor(void) { destroy(); }
107 :
108 : //==============================================================================
109 0 : void ECLSupervisor::destroy(void)
110 : {
111 : // // called by destructor
112 : // delete theConfigurationManager_;
113 :
114 0 : __SUP_COUT__ << "Destructed." << __E__;
115 0 : } // end destroy()
116 :
117 : //==============================================================================
118 0 : void ECLSupervisor::defaultPage(xgi::Input* /*in*/, xgi::Output* out)
119 : {
120 0 : __COUT__ << " active category " << ECLCategory_ << std::endl;
121 : *out << "<!DOCTYPE HTML><html lang='en'><head><title>ots</title>"
122 0 : << ECLSupervisor::getIconHeaderString() <<
123 : // end show ots icon
124 : "</head>"
125 : << "<frameset col='100%' row='100%'><frame "
126 0 : "src='/WebPath/html/Logbook.html?urn="
127 0 : << this->getApplicationDescriptor()->getLocalId()
128 0 : << "&active_category=" << ECLCategory_ << "'></frameset></html>";
129 0 : } //end defaultPage()
130 :
131 : //==============================================================================
132 0 : std::string ECLSupervisor::getIconHeaderString(void)
133 : {
134 : // show ots icon
135 : // from http://www.favicon-generator.org/
136 0 : return "<link rel='apple-touch-icon' sizes='57x57' href='/WebPath/images/otsdaqIcons/apple-icon-57x57.png'>\
137 : <link rel='apple-touch-icon' sizes='60x60' href='/WebPath/images/otsdaqIcons/apple-icon-60x60.png'>\
138 : <link rel='apple-touch-icon' sizes='72x72' href='/WebPath/images/otsdaqIcons/apple-icon-72x72.png'>\
139 : <link rel='apple-touch-icon' sizes='76x76' href='/WebPath/images/otsdaqIcons/apple-icon-76x76.png'>\
140 : <link rel='apple-touch-icon' sizes='114x114' href='/WebPath/images/otsdaqIcons/apple-icon-114x114.png'>\
141 : <link rel='apple-touch-icon' sizes='120x120' href='/WebPath/images/otsdaqIcons/apple-icon-120x120.png'>\
142 : <link rel='apple-touch-icon' sizes='144x144' href='/WebPath/images/otsdaqIcons/apple-icon-144x144.png'>\
143 : <link rel='apple-touch-icon' sizes='152x152' href='/WebPath/images/otsdaqIcons/apple-icon-152x152.png'>\
144 : <link rel='apple-touch-icon' sizes='180x180' href='/WebPath/images/otsdaqIcons/apple-icon-180x180.png'>\
145 : <link rel='icon' type='image/png' sizes='192x192' href='/WebPath/images/otsdaqIcons/android-icon-192x192.png'>\
146 : <link rel='icon' type='image/png' sizes='144x144' href='/WebPath/images/otsdaqIcons/android-icon-144x144.png'>\
147 : <link rel='icon' type='image/png' sizes='48x48' href='/WebPath/images/otsdaqIcons/android-icon-48x48.png'>\
148 : <link rel='icon' type='image/png' sizes='72x72' href='/WebPath/images/otsdaqIcons/android-icon-72x72.png'>\
149 : <link rel='icon' type='image/png' sizes='32x32' href='/WebPath/images/otsdaqIcons/favicon-32x32.png'>\
150 : <link rel='icon' type='image/png' sizes='96x96' href='/WebPath/images/otsdaqIcons/favicon-96x96.png'>\
151 : <link rel='icon' type='image/png' sizes='16x16' href='/WebPath/images/otsdaqIcons/favicon-16x16.png'>\
152 : <link rel='manifest' href='/WebPath/images/otsdaqIcons/manifest.json'>\
153 : <meta name='msapplication-TileColor' content='#ffffff'>\
154 : <meta name='msapplication-TileImage' content='/WebPath/images/otsdaqIcons/ms-icon-144x144.png'>\
155 : <meta name='theme-color' content='#ffffff'>";
156 :
157 : } // end getIconHeaderString()
158 :
159 : //==============================================================================
160 : /// setSupervisorPropertyDefaults
161 : /// override to set defaults for supervisor property values (before user settings
162 : /// override)
163 0 : void ECLSupervisor::setSupervisorPropertyDefaults()
164 : {
165 0 : CorePropertySupervisorBase::setSupervisorProperty(
166 : CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.UserPermissionsThreshold,
167 0 : std::string() +
168 : "*=1 | CreateCategory=-1 | RemoveCategory=-1 | GetCategoryListAdmin=-1 "
169 0 : "| SetActiveCategory=-1" +
170 : " | AdminRemoveRestoreEntry=-1");
171 :
172 0 : CorePropertySupervisorBase::setSupervisorProperty(
173 : CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.AllowNoLoginRequestTypes,
174 : "RefreshLogbook | GetCategoryList");
175 :
176 0 : } //end setSupervisorPropertyDefaults()
177 :
178 : //==============================================================================
179 : /// forceSupervisorPropertyValues
180 : /// override to force supervisor property values (and ignore user settings)
181 0 : void ECLSupervisor::forceSupervisorPropertyValues()
182 : {
183 0 : CorePropertySupervisorBase::addSupervisorProperty(
184 : CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.AutomatedRequestTypes,
185 : "RefreshLogbook");
186 0 : CorePropertySupervisorBase::setSupervisorProperty(
187 : CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.NonXMLRequestTypes,
188 : "LogImage | LogReport");
189 0 : CorePropertySupervisorBase::addSupervisorProperty(
190 : CorePropertySupervisorBase::SUPERVISOR_PROPERTIES.RequireUserLockRequestTypes,
191 : "CreateCategory | RemoveCategory | PreviewEntry | AdminRemoveRestoreEntry");
192 0 : } //end forceSupervisorPropertyValues()
193 :
194 : //==============================================================================
195 : /// request
196 : /// Handles Web Interface requests to Logbook supervisor.
197 : /// Does not refresh cookie for automatic update checks.
198 0 : void ECLSupervisor::request(const std::string& requestType,
199 : cgicc::Cgicc& cgiIn,
200 : HttpXmlDocument& xmlOut,
201 : const WebUsers::RequestUserInfo& userInfo)
202 : {
203 0 : __COUTTV__(requestType);
204 :
205 : // Commands - Note: treat 'Category' as ECL Category
206 : // N/A CreateCategory
207 : // N/A RemoveCategory
208 : // GetCategoryList
209 : // SetActiveCategory
210 : // RefreshLogbook
211 : // PreviewEntry
212 : // ApproveEntry
213 : // N/A AdminRemoveRestoreEntry
214 :
215 : // to report to logbook admin status use
216 : // xmlOut.addTextElementToData(XML_ADMIN_STATUS,tempStr);
217 :
218 : if(0 && requestType == "CreateCategory")
219 : {
220 : // check that category directory does not exist, and it is not in xml list
221 : // create category (TODO - could set env variable ECLConnection/$ECL_CATEGORY)
222 : //
223 :
224 : // get creator name
225 : std::string creator = userInfo.username_;
226 :
227 : // createCategory(
228 : // CgiDataUtilities::postData(cgiIn, "Category"), creator, &xmlOut);
229 :
230 : __COUT__ << "Created" << std::endl;
231 : }
232 : else if(0 && requestType == "RemoveCategory")
233 : {
234 : // remove from xml list, but do not remove directory (requires manual delete so
235 : // mistakes aren't made)
236 : //(TODO - could unset env variable ECLConnection/$ECL_CATEGORY)
237 :
238 : // get remover name
239 : std::string remover = userInfo.username_;
240 : // removeCategory(
241 : // CgiDataUtilities::postData(cgiIn, "Category"), remover, &xmlOut);
242 : }
243 0 : else if(requestType == "GetCategoryList")
244 : {
245 : //allow all users to ECL categories, but tell GUI not admin since have no access to managing ECL posts directly
246 0 : xmlOut.addTextElementToData("is_admin", "0"); // indicate not an admin
247 0 : xmlOut.addTextElementToData("no_post_preview",
248 : "1"); // indicate no post preview, just post directly
249 0 : xmlOut.addTextElementToData("no_post_attachments",
250 : "1"); // indicate no post attachments supported
251 0 : getCategories(&xmlOut);
252 : }
253 0 : else if(requestType == "SetActiveCategory")
254 : {
255 : // check that category exists
256 : // set active category
257 :
258 : // if(userPermissions < ADMIN_PERMISSIONS_THRESHOLD)
259 : // {
260 : // xmlOut.addTextElementToData(XML_ADMIN_STATUS,"Error - Insufficient
261 : // permissions."); goto CLEANUP;
262 : // }
263 :
264 0 : webUserSetActiveCategory(CgiDataUtilities::postData(cgiIn, "Category"), &xmlOut);
265 : }
266 0 : else if(requestType == "RefreshLogbook")
267 : {
268 : // returns logbook for currently active category based on date and duration
269 : // parameters
270 :
271 0 : std::string Date = CgiDataUtilities::postData(cgiIn, "Date");
272 0 : uint32_t Duration = CgiDataUtilities::postDataAsInt(cgiIn, "Duration");
273 0 : std::string CategoryFilter = CgiDataUtilities::postData(cgiIn, "CategoryFilter");
274 :
275 0 : __COUTV__(CategoryFilter);
276 :
277 : time_t date;
278 0 : sscanf(Date.c_str(), "%li", &date); // scan for unsigned long
279 :
280 0 : __COUT__ << "date " << date << " duration " << Duration << std::endl;
281 0 : std::stringstream str;
282 0 : refreshLogbook(date,
283 : Duration,
284 : &xmlOut,
285 : (std::ostringstream*)&str,
286 0 : StringMacros::decodeURIComponent(CategoryFilter));
287 0 : __COUT__ << str.str() << std::endl;
288 0 : }
289 0 : else if(requestType == "PostEntry")
290 : {
291 : // NOTE: all input parameters for PostEntry will be attached to form
292 : // so use cgiIn(xxx) to get values.
293 :
294 0 : std::string EntryText = cgiIn("EntryText");
295 0 : __SUP_COUTV__(EntryText);
296 0 : std::string EntrySubject = cgiIn("EntrySubject");
297 0 : __SUP_COUTV__(EntrySubject);
298 :
299 : // get creator name
300 0 : std::string creator = userInfo.username_;
301 0 : __SUP_COUTV__(creator);
302 :
303 : //return; //to debug, this breaks link to ECL
304 0 : ECLEntry_t eclEntry;
305 0 : eclEntry.author(StringMacros::escapeString(ECLUser_));
306 0 : eclEntry.category(StringMacros::escapeString(ECLCategory_));
307 0 : eclEntry.subject(StringMacros::escapeString(EntrySubject));
308 :
309 0 : Form_t form;
310 0 : Field_t field;
311 0 : Form_t::field_sequence fields;
312 0 : std::string users = theRemoteWebUsers_.getActiveUserList();
313 :
314 0 : form.name("default"); // these form names must be created in advance? ... default
315 : // seems to have one field and be generic: 'text' field
316 :
317 : {
318 0 : std::stringstream ss;
319 0 : ss << "Author: " << creator << " (" << creator << ")" << __E__ << __E__;
320 0 : ss << "Message: " << __E__ << EntryText << __E__ << __E__;
321 :
322 0 : ss << "This was a Manual Log Entry from '" << CategoryName_ << "' at host '"
323 0 : << __ENV__("THIS_HOST") << "'" << __E__;
324 0 : ss << "Active ots users: " << users << __E__;
325 0 : ss << "USER_DATA: " << __ENV__("USER_DATA") << __E__;
326 : ss << "Uptime: "
327 0 : << StringMacros::getTimeDurationString(
328 0 : CorePropertySupervisorBase::getSupervisorUptime())
329 0 : << __E__;
330 : field =
331 0 : Field_t(StringMacros::escapeString(ss.str(), true /* keep white space */),
332 0 : "text");
333 0 : fields.push_back(field);
334 0 : }
335 :
336 0 : std::string retStr = "1"; //1 for success
337 0 : form.field(fields);
338 0 : eclEntry.form(form);
339 : try
340 : {
341 : // ECLConnection eclConn(ECLUser_, ECLPwd_, ECLHost_);
342 0 : if(!eclConn_->Post(eclEntry))
343 : {
344 0 : __SS__ << "Failure to post ECL entry." << __E__;
345 0 : __COUT_ERR__ << ss.str();
346 0 : retStr = ss.str();
347 0 : }
348 : }
349 0 : catch(const std::runtime_error& e)
350 : {
351 0 : __SS__ << "Exception caught during ECL message post and ECL connection: "
352 0 : << e.what();
353 0 : __COUT_ERR__ << ss.str();
354 0 : retStr = ss.str();
355 0 : }
356 :
357 0 : xmlOut.addTextElementToData(XML_STATUS, retStr);
358 0 : }
359 : // else if(requestType == "PreviewEntry")
360 : // {
361 : // // cleanup temporary folder
362 : // // NOTE: all input parameters for PreviewEntry will be attached to form
363 : // // so use cgiIn(xxx) to get values.
364 : // // increment number for each temporary preview, previewPostTempIndex_
365 : // // save entry and uploads to previewPath / previewPostTempIndex_ /.
366 :
367 : // cleanUpPreviews();
368 : // std::string EntryText = cgiIn("EntryText");
369 : // __COUT__ << "EntryText " << EntryText << std::endl << std::endl;
370 : // std::string EntrySubject = cgiIn("EntrySubject");
371 : // __COUT__ << "EntrySubject " << EntrySubject << std::endl << std::endl;
372 :
373 : // // get creator name
374 : // std::string creator = userInfo.username_;
375 :
376 : // savePostPreview(EntrySubject, EntryText, cgiIn.getFiles(), creator, &xmlOut);
377 : // // else xmlOut.addTextElementToData(XML_STATUS,"Failed - could not get username
378 : // // info.");
379 : // }
380 : // else if(requestType == "ApproveEntry")
381 : // {
382 : // // If Approve = "1", then previewed Log entry specified by PreviewNumber
383 : // // is moved to logbook
384 : // // Else the specified Log entry is deleted.
385 : // std::string PreviewNumber = CgiDataUtilities::postData(cgiIn, "PreviewNumber");
386 : // std::string Approve = CgiDataUtilities::postData(cgiIn, "Approve");
387 :
388 : // movePreviewEntry(PreviewNumber, Approve == "1", &xmlOut);
389 : // }
390 : // else if(requestType == "AdminRemoveRestoreEntry")
391 : // {
392 : // // if(userPermissions < ADMIN_PERMISSIONS_THRESHOLD)
393 : // // {
394 : // // xmlOut.addTextElementToData(XML_ADMIN_STATUS,"Error - Insufficient
395 : // // permissions."); goto CLEANUP;
396 : // // }
397 :
398 : // std::string EntryId = CgiDataUtilities::postData(cgiIn, "EntryId");
399 : // bool Hide = CgiDataUtilities::postData(cgiIn, "Hide") == "1" ? true : false;
400 :
401 : // // get creator name
402 : // std::string hider = userInfo.username_;
403 :
404 : // hideLogbookEntry(EntryId, Hide, hider);
405 :
406 : // xmlOut.addTextElementToData(XML_ADMIN_STATUS, "1"); // success
407 : // }
408 : else
409 : {
410 0 : __SUP_SS__ << "requestType Request, " << requestType
411 : << ", not recognized by the ECL Supervisor (was it intended for "
412 0 : "another Supervisor?)."
413 0 : << __E__;
414 0 : __SUP_SS_THROW__;
415 0 : }
416 0 : } //end request()
417 :
418 : //==============================================================================
419 : /// getCategories
420 : /// if xmlOut, then output categories to xml
421 : /// if out, then output to stream
422 0 : void ECLSupervisor::getCategories(HttpXmlDocument* xmlOut, std::ostringstream* out)
423 : {
424 0 : if(ECLUser_ == "" ||
425 0 : ECLHost_ == "") //ignore ECL when environment variables are not set
426 : {
427 0 : __SS__ << "No ECL user/host specified for logbook access." << __E__;
428 0 : __SS_THROW__;
429 0 : }
430 :
431 0 : std::string response, url = "/A/xml_category_list";
432 0 : eclConn_->Get(url, response);
433 0 : __COUTTV__(response);
434 :
435 0 : std::vector<std::string> exps;
436 0 : std::string name;
437 0 : size_t after = 0;
438 :
439 : //example response:
440 : // <?xml version="1.0" encoding="UTF-8"?>
441 : // <category_list>
442 : // <category path="Accelerator"/>
443 : // <category path="CRV"/>
444 : // <category path="CRV/Vertical Slice Test"/>
445 : // <category path="Calorimeter"/>
446 : // <category path="Cryogenics"/>
447 : // <category path="Cryogenics/Cryogenics Construction"/>
448 : // <category path="Cryogenics/Mu2e Controls"/>
449 : // <category path="Cryogenics/Mu2e ODH System"/>
450 : // <category path="Cryogenics/Mu2e Operations"/>
451 : // <category path="Cryogenics/Mu2e Vacuum"/>
452 : // <category path="Cryogenics/Muon Campus Operations"/>
453 : // <category path="Extinction Monitor"/>
454 : // <category path="Facilities / Building"/>
455 : // <category path="Global Run"/>
456 : // <category path="Leak checking"/>
457 : // <category path="Mu2e team member location"/>
458 : // <category path="Muon beamline"/>
459 : // <category path="Planning"/>
460 : // <category path="Production"/>
461 : // <category path="Production/MDC18"/>
462 : // <category path="Production/su2020"/>
463 : // <category path="Safety"/>
464 : // <category path="Solenoids"/>
465 : // <category path="Stopping Target Monitor"/>
466 : // <category path="TDAQ"/>
467 : // <category path="Tracker"/>
468 : // <category path="Tracker/VST"/>
469 : // <category path="Transfer Lines"/>
470 : // <category path="help"/>
471 : // <category path="test"/>
472 : // <category path="testbeam"/>
473 : // </category_list>
474 :
475 0 : while((name = StringMacros::extractXmlField(
476 0 : response, "category", 0, after, &after, "path=", "\"")) != "")
477 : {
478 0 : after +=
479 0 : std::string("category").size(); //move forward to prepare for next search
480 0 : __COUTTV__(name);
481 0 : exps.push_back(name);
482 : }
483 :
484 : // // check that category listing doesn't already exist
485 : // HttpXmlDocument expXml;
486 : // if(!expXml.loadXmlDocument((std::string)LOGBOOK_CATEGORY_LIST_PATH))
487 : // {
488 : // __COUT__ << "Fatal Error - Category database." << std::endl;
489 : // __COUT__ << "Creating empty category database." << std::endl;
490 :
491 : // expXml.addTextElementToData((std::string)XML_CATEGORY_ROOT);
492 : // expXml.saveXmlDocument((std::string)LOGBOOK_CATEGORY_LIST_PATH);
493 : // return;
494 : // }
495 :
496 : // expXml.getAllMatchingValues(XML_CATEGORY, exps);
497 :
498 0 : if(xmlOut)
499 : {
500 0 : xmlOut->addTextElementToData(XML_ACTIVE_CATEGORY, ECLCategory_);
501 0 : xmlOut->addTextElementToData(XML_SAFE_URL, eclConn_->getSafeURL());
502 : }
503 :
504 0 : for(unsigned int i = 0; i < exps.size(); ++i) // loop categories
505 : {
506 0 : if(xmlOut)
507 0 : xmlOut->addTextElementToData(XML_CATEGORY, exps[i]);
508 0 : if(out)
509 0 : *out << exps[i] << std::endl;
510 : }
511 0 : } //end getCategories()
512 :
513 : //==============================================================================
514 : /// webUserSetActiveCategory
515 : /// if category exists, set as active
516 : /// to clear active category set to ""
517 0 : void ECLSupervisor::webUserSetActiveCategory(std::string category,
518 : HttpXmlDocument* xmlOut)
519 : {
520 : // no check, just set
521 0 : ECLCategory_ = category;
522 0 : if(xmlOut)
523 0 : xmlOut->addTextElementToData(
524 0 : XML_ADMIN_STATUS, "Active category set to " + category + " successfully.");
525 0 : } //end webUserSetActiveCategory()
526 :
527 : //==============================================================================
528 : /// refreshLogbook
529 : /// returns all the logbook data for active category from starting date and back in
530 : /// time for duration total number of days.
531 : /// e.g. date = today, and duration = 1 returns logbook for today from active
532 : /// category The entries are returns from oldest to newest
533 0 : void ECLSupervisor::refreshLogbook(time_t date,
534 : size_t duration,
535 : HttpXmlDocument* xmlOut,
536 : std::ostringstream* out,
537 : std::string categoryFilter)
538 : {
539 0 : if(ECLUser_ == "" ||
540 0 : ECLHost_ == "") //ignore ECL when environment variables are not set
541 : {
542 0 : __SS__ << "No ECL user/host specified for logbook access." << __E__;
543 0 : __SS_THROW__;
544 0 : }
545 :
546 0 : if(categoryFilter == "")
547 0 : categoryFilter = ECLCategory_; // default to active category
548 0 : if(xmlOut)
549 0 : xmlOut->addTextElementToData(XML_ACTIVE_CATEGORY, ECLCategory_); // for success
550 0 : if(xmlOut)
551 0 : xmlOut->addTextElementToData(XML_RESPONSE_CATEGORY,
552 : categoryFilter); // for success
553 :
554 0 : int64_t mostRecentTime = 0;
555 : time_t baseTime;
556 :
557 0 : __COUTTV__(date);
558 0 : if(!date) // if date is 0 take most recent day and update it
559 0 : baseTime = time(0); // / (60 * 60 * 24);
560 : else
561 0 : baseTime = date - timezoneHourOffset_ * 60 * 60 +
562 : 1; //date is 12:00a GMT, so could give wrong day in local timezone
563 :
564 : if(0) //test xml_get
565 : {
566 : std::string response, url = "/E/xml_get?e=" + std::string("1843");
567 : __COUTV__(url);
568 : eclConn_->Get(url, response);
569 : __COUTV__(response);
570 : }
571 : if(0) //test xml_search
572 : {
573 : std::string response, url = "/E/xml_search?l=5"; //limit to 5
574 : // url += "&c=Facilities / Building";
575 : __COUTV__(url);
576 : eclConn_->Get(url, response);
577 : __COUTV__(response);
578 : }
579 : if(0) //test xml_search
580 : {
581 : std::string response, url = "/E/xml_search?l=5"; //limit to 5
582 : url +=
583 : "&c=" + StringMacros::encodeURIComponent("Facilities / Building"); //category
584 : __COUTV__(url);
585 : eclConn_->Get(url, response);
586 : __COUTV__(response);
587 : }
588 : if(0) //test xml_search
589 : {
590 : std::string response, url = "/E/xml_search?l=5"; //limit to 5
591 : url += "&c=" +
592 : StringMacros::encodeURIComponent("Facilities . Building"); //category
593 : __COUTV__(url);
594 : eclConn_->Get(url, response);
595 : __COUTV__(response);
596 : }
597 :
598 : //add all posts that match date/duration criteria
599 : {
600 0 : __COUTTV__(categoryFilter);
601 0 : std::string response, url = "/E/xml_search?"; //l=100"; //limit to 100
602 : // "&a=" + std::to_string(duration) + "days" + //after
603 : // "&b=" + baseTimeTmBuffer; //before
604 :
605 0 : bool applyCategoryFilter = false;
606 0 : bool applyInvertedCategoryFilter = false;
607 0 : std::vector<std::string> acceptCategories;
608 : //filter can start with * for all, or with ! for inverted selection (i.e. all without certain categories)
609 0 : if(categoryFilter.size() && categoryFilter[0] != '*' &&
610 0 : categoryFilter[0] != '!' && categoryFilter.find(',') == std::string::npos &&
611 0 : categoryFilter.find(" / ") == std::string::npos)
612 : url +=
613 0 : "l=100&c=" + StringMacros::encodeURIComponent(categoryFilter); //category
614 : else
615 : {
616 0 : if(duration > 14)
617 0 : url += "l=1000"; //get more so better chance to find in filter
618 : else
619 0 : url += "l=300"; //get more so better chance to find in filter
620 :
621 0 : applyCategoryFilter =
622 0 : (categoryFilter.size() && categoryFilter[0] != '*') ? true : false;
623 0 : applyInvertedCategoryFilter =
624 0 : (categoryFilter.size() && categoryFilter[0] == '!') ? true : false;
625 0 : if(applyCategoryFilter)
626 : {
627 0 : if(applyInvertedCategoryFilter) //skip 1st char
628 : acceptCategories =
629 0 : StringMacros::getVectorFromString(categoryFilter.substr(1));
630 : else
631 0 : acceptCategories = StringMacros::getVectorFromString(categoryFilter);
632 0 : __COUTTV__(StringMacros::vectorToString(acceptCategories));
633 : }
634 0 : __COUTTV__(applyInvertedCategoryFilter);
635 0 : __COUTTV__(applyCategoryFilter);
636 : }
637 :
638 : //apply date range
639 : {
640 0 : __COUTTV__(baseTime);
641 0 : __COUTTV__(duration);
642 :
643 0 : if(TTEST(30)) //debug date
644 : {
645 0 : for(size_t i = 0; i < 24; ++i)
646 : {
647 0 : __COUTT__ << "i-: " << i << " "
648 0 : << ((baseTime - i * 60 * 60) / (60 * 60 * 24));
649 :
650 0 : time_t modTime = baseTime - i * 60 * 60;
651 : std::tm* baseTimeTm =
652 0 : std::localtime(&modTime); // Convert to local time
653 : char translatedDate[256];
654 0 : strftime(
655 : translatedDate, sizeof(translatedDate), "%Y-%m-%d", baseTimeTm);
656 0 : __COUTTV__(translatedDate);
657 : }
658 :
659 0 : for(size_t i = 0; i < 24; ++i)
660 : {
661 0 : __COUTT__ << "i+: " << i << " "
662 0 : << ((baseTime + i * 60 * 60) / (60 * 60 * 24));
663 0 : time_t modTime = baseTime + i * 60 * 60;
664 : std::tm* baseTimeTm =
665 0 : std::localtime(&modTime); // Convert to local time
666 : char translatedDate[256];
667 0 : strftime(
668 : translatedDate, sizeof(translatedDate), "%Y-%m-%d", baseTimeTm);
669 0 : __COUTTV__(translatedDate);
670 : }
671 : }
672 :
673 : // add one day to calculate before
674 0 : baseTime += 1 * (60 * 60 * 24); //before is non-inclusive, after is inclusive
675 0 : std::tm* baseTimeTm = std::localtime(&baseTime); // Convert to local time
676 : char baseTimeTmBuffer[256];
677 0 : strftime(baseTimeTmBuffer, sizeof(baseTimeTmBuffer), "%Y-%m-%d", baseTimeTm);
678 0 : __COUTTV__(baseTimeTmBuffer);
679 0 : url += "&b=" + std::string(baseTimeTmBuffer) + "+00:00:00"; //before
680 :
681 : //now calculate after from duration in days
682 0 : baseTime -=
683 0 : duration * (60 * 60 * 24); //before is non-inclusive, after is inclusive
684 0 : baseTimeTm = std::localtime(&baseTime); // Convert to local time
685 0 : strftime(baseTimeTmBuffer, sizeof(baseTimeTmBuffer), "%Y-%m-%d", baseTimeTm);
686 0 : __COUTTV__(baseTimeTmBuffer);
687 0 : url += "&a=" + std::string(baseTimeTmBuffer) + "+00:00:00"; //after
688 : }
689 : // ECLConnection eclConn(ECLUser_, ECLPwd_, ECLHost_);
690 0 : eclConn_->Get(url, response);
691 0 : __COUTVS__(3, response);
692 :
693 : //example response:
694 : // <?xml version="1.0" encoding="UTF-8"?>
695 : // <entry_list ids_only="False">
696 :
697 : // <entry
698 : // id="2502"
699 : // author="mu2e_ots"
700 : // category="Global Run"
701 : // timestamp="12/05/2024 17:49:53"
702 : // html="yes"
703 : // formatted="no"
704 : // form="default"
705 : // images="0"
706 : // files="0">
707 : // <text>Message: &#010;Run stopped. Run &apos;105214&apos; duration so far of 00:11:57.35 seconds.&#010;&#010;This was a System Generated Log Entry from &apos;Mu2e ot>
708 : // <text-html><![CDATA[<pre class="html_safe_entry">Message: 
Run stopped. Run '105214' duration so far of 00:11:57.35 seconds.

This was a System Generated Log Entry from 'Mu2e>
709 : // <text-cdata><![CDATA[<pre class="html_safe_entry">Message: 
Run stopped. Run '105214' duration so far of 00:11:57.35 seconds.

This was a System Generated Log Entry from 'Mu2>
710 : // </entry>
711 : // <entry
712 : // id="2501"
713 : // author="mu2e_ots"
714 : // ...
715 :
716 : //and result to request is:
717 : // <XML_LOGBOOK_ENTRY>
718 : // <XML_LOGBOOK_ENTRY_TIME>
719 : // <XML_LOGBOOK_ENTRY_CREATOR>
720 : // <XML_LOGBOOK_ENTRY_SUBJECT>
721 : // <XML_LOGBOOK_ENTRY_TEXT>
722 : // <XML_LOGBOOK_ENTRY_FILE value=fileType0>
723 : // <XML_LOGBOOK_ENTRY_FILE value=fileType1> ...
724 : // </XML_LOGBOOK_ENTRY>
725 :
726 0 : std::string id, author, subject, category, timestamp, files, images, text;
727 0 : size_t after = 0, before = -1, entryCount = 0, lastBefore = -1;
728 : std::tm tm;
729 0 : std::string preText, postText;
730 :
731 0 : while((id = //StringMacros::extractXmlField(response, "entry", 0, after, &after,
732 0 : StringMacros::rextractXmlField(response,
733 : "entry",
734 : 0,
735 : before,
736 : &before, //e.g. for reverse order
737 : "id=",
738 0 : "\"")) != "")
739 : {
740 0 : __COUTVS__(2, id);
741 0 : ++entryCount;
742 :
743 0 : after = before;
744 :
745 0 : author = StringMacros::extractXmlField(
746 0 : response, "entry", 0, after, nullptr, "author=", "\"");
747 0 : subject = StringMacros::extractXmlField(
748 0 : response, "entry", 0, after, nullptr, "subject=", "\"");
749 0 : category = StringMacros::extractXmlField(
750 0 : response, "entry", 0, after, nullptr, "category=", "\"");
751 :
752 0 : if(applyCategoryFilter)
753 : {
754 0 : bool found = applyInvertedCategoryFilter;
755 0 : for(const auto& acceptCategory : acceptCategories)
756 0 : if(category == acceptCategory ||
757 0 : category.find(acceptCategory + "/") != std::string::npos)
758 : {
759 0 : found = !applyInvertedCategoryFilter;
760 0 : break;
761 : }
762 :
763 0 : if(!found)
764 : {
765 0 : __COUTS__(10)
766 0 : << "Skipping unaccepted category: " << category << __E__;
767 0 : lastBefore = before;
768 0 : --before; //move back to prepare for next search
769 0 : continue;
770 0 : }
771 : }
772 :
773 0 : timestamp = StringMacros::extractXmlField(
774 0 : response, "entry", 0, after, nullptr, "timestamp=", "\"");
775 0 : images = StringMacros::extractXmlField(
776 0 : response, "entry", 0, after, nullptr, "images=", "\"");
777 0 : files = StringMacros::extractXmlField(
778 0 : response, "entry", 0, after, nullptr, "files=", "\"");
779 :
780 0 : __COUTVS__(2, author);
781 0 : __COUTVS__(2, timestamp);
782 0 : tm = {}; //clear
783 0 : std::istringstream ss(timestamp);
784 : ss >>
785 : std::get_time(
786 0 : &tm, "%m/%d/%Y %H:%M:%S"); // Parse the string into the tm structure
787 0 : time_t t = std::mktime(&tm); // Convert tm structure to time_t
788 :
789 0 : __COUTVS__(2, t);
790 0 : __COUTVS__(2, mostRecentTime);
791 0 : if(!date && t > mostRecentTime)
792 0 : mostRecentTime = t; //track most recent entry
793 0 : __COUTVS__(2, mostRecentTime);
794 :
795 : size_t foundTextPos;
796 0 : text = StringMacros::extractXmlField(response,
797 : "pre class=\"html_safe_entry\"",
798 : 0,
799 : after,
800 : &foundTextPos,
801 : "",
802 0 : ">");
803 0 : __COUTVS__(2, text.size());
804 0 : if(text.size() == 0 || //if not found
805 0 : foundTextPos > lastBefore) //or found in different entry!
806 : {
807 0 : text = StringMacros::extractXmlField(
808 0 : response, "text", 0, after, nullptr, "", ">");
809 0 : __COUTVS__(2, text.size());
810 : }
811 0 : __COUTVS__(3, text);
812 0 : if(text.size() > std::string("Message: 
").size() && text[0] == 'M' &&
813 0 : text[6] == 'e' && text[7] == ':' && text[9] == '&' && text[10] == '#')
814 0 : text = text.substr(
815 0 : std::string("Message: 
").size()); //skip Message header
816 :
817 0 : preText = "";
818 0 : postText = "";
819 0 : bool needAttachments = false;
820 0 : if(atoi(images.c_str()))
821 : {
822 0 : needAttachments = true;
823 0 : preText += "Attached Images: " + images + "<br>";
824 : }
825 0 : if(atoi(files.c_str()))
826 : {
827 0 : needAttachments = true;
828 0 : preText += "Attached Files: " + files + "<br>";
829 : }
830 :
831 0 : if(needAttachments)
832 : {
833 0 : std::string response, url = "/E/xml_get?e=" + id;
834 : // ECLConnection eclConn(ECLUser_, ECLPwd_, ECLHost_);
835 0 : eclConn_->Get(url, response);
836 0 : __COUTVS__(3, response);
837 :
838 0 : size_t fileCount = atoi(files.c_str());
839 0 : __COUTV__(fileCount);
840 0 : size_t attachmentAfter = 0;
841 :
842 0 : if(fileCount)
843 0 : postText += "<br><br>=====> Attached Files:";
844 0 : for(size_t j = 0; j < fileCount; ++j)
845 : {
846 0 : __COUTTV__(attachmentAfter);
847 0 : std::string furl = StringMacros::extractXmlField(response,
848 : "attachment",
849 : 0,
850 : attachmentAfter,
851 : &attachmentAfter,
852 : "url=",
853 0 : "\"");
854 :
855 0 : if(eclConn_->safeURLisHTTPS())
856 : {
857 0 : __COUTS__(2) << "Force file URL to https: " << furl << __E__;
858 0 : if(furl.size() > 5 && furl[0] == 'h' && furl[1] == 't' &&
859 0 : furl[2] == 't' && furl[3] == 'p' && furl[4] == ':')
860 0 : furl.insert(4, 1, 's'); // insert 's' at index 4
861 : }
862 0 : __COUTVS__(2, furl);
863 0 : std::string fname = StringMacros::extractXmlField(response,
864 : "attachment",
865 : 0,
866 : attachmentAfter,
867 : nullptr,
868 : "filename=",
869 0 : "\"");
870 :
871 0 : __COUTVS__(2, fname);
872 0 : postText += "<br>";
873 0 : if(fileCount > 1)
874 0 : postText += "Attached File #" + std::to_string(j + 1) + ": ";
875 : postText +=
876 0 : "<a target='_blank' href='" + furl + "'>" + fname + "</a>";
877 0 : attachmentAfter += 20; //advance to next
878 0 : }
879 :
880 0 : size_t imageCount = atoi(images.c_str());
881 0 : __COUTVS__(3, imageCount);
882 0 : attachmentAfter = 0;
883 0 : if(imageCount)
884 0 : postText += "<br><br>=====> Attached Images:";
885 0 : for(size_t j = 0; j < imageCount; ++j)
886 : {
887 0 : __COUTTV__(attachmentAfter);
888 0 : attachmentAfter = response.find("type=\"image\"", attachmentAfter);
889 0 : if(attachmentAfter == std::string::npos)
890 0 : break;
891 0 : attachmentAfter = response.rfind("<attachment", attachmentAfter);
892 0 : if(attachmentAfter == std::string::npos)
893 0 : break;
894 :
895 0 : std::string furl = StringMacros::extractXmlField(response,
896 : "attachment",
897 : 0,
898 : attachmentAfter,
899 : nullptr,
900 : "full_url=",
901 0 : "\"");
902 :
903 0 : if(eclConn_->safeURLisHTTPS())
904 : {
905 0 : __COUTS__(2) << "Force file URL to https: " << furl << __E__;
906 0 : if(furl.size() > 5 && furl[0] == 'h' && furl[1] == 't' &&
907 0 : furl[2] == 't' && furl[3] == 'p' && furl[4] == ':')
908 0 : furl.insert(4, 1, 's'); // insert 's' at index 4
909 : }
910 0 : __COUTVS__(2, furl);
911 0 : std::string fname = StringMacros::extractXmlField(response,
912 : "attachment",
913 : 0,
914 : attachmentAfter,
915 : nullptr,
916 : "filename=",
917 0 : "\"");
918 :
919 0 : __COUTVS__(2, fname);
920 0 : postText += "<br>";
921 0 : if(imageCount > 1)
922 0 : postText += "Attached Image #" + std::to_string(j + 1) + ": ";
923 : postText +=
924 0 : "<a target='_blank' href='" + furl + "'>" + fname + "</a>";
925 :
926 0 : attachmentAfter += 50; //advance to next
927 0 : }
928 0 : } //end attachment handling
929 :
930 : // Lambda function to wrap links
931 0 : auto wrapLinks = [](const std::string& inputText) -> std::string {
932 0 : std::string result;
933 0 : std::string::size_type pos = 0;
934 : std::string::size_type start;
935 :
936 0 : while((start = inputText.find("http://", pos)) != std::string::npos ||
937 0 : (start = inputText.find("https://", pos)) != std::string::npos)
938 : {
939 : // Append text before the link
940 0 : result.append(inputText, pos, start - pos);
941 :
942 : // Find the end of the URL (stop at space or end of string)
943 : std::string::size_type end =
944 0 : inputText.find_first_of(" \t\n!<>(),\"\'", start);
945 0 : if(end == std::string::npos)
946 : {
947 0 : end = inputText.size();
948 : }
949 :
950 : // Extract the URL
951 0 : std::string url = inputText.substr(start, end - start);
952 :
953 : // Append the <a> tag
954 0 : result += "<a href=\"" + url + "\" target=\"_blank\">" + url + "</a>";
955 :
956 : // Move position to after the URL
957 0 : pos = end;
958 0 : }
959 :
960 : // Append any remaining text
961 0 : result.append(inputText, pos, inputText.size() - pos);
962 :
963 0 : return result;
964 0 : };
965 :
966 : text =
967 0 : preText + (preText.size() ? "\n<br>" : "") + wrapLinks(text) + postText;
968 0 : __COUTVS__(2, text);
969 :
970 0 : if(xmlOut)
971 : {
972 0 : auto entryParent = xmlOut->addTextElementToData(XML_LOGBOOK_ENTRY);
973 :
974 0 : xmlOut->addTextElementToParent(
975 0 : XML_LOGBOOK_ENTRY_TIME, std::to_string(t), entryParent);
976 0 : xmlOut->addTextElementToParent(
977 : XML_LOGBOOK_ENTRY_CREATOR, author, entryParent);
978 0 : xmlOut->addTextElementToParent(XML_LOGBOOK_ENTRY_TEXT, text, entryParent);
979 0 : xmlOut->addTextElementToParent(
980 : XML_LOGBOOK_ENTRY_SUBJECT,
981 0 : category + " - entry #" + id + " - " + subject,
982 : entryParent);
983 : }
984 :
985 0 : lastBefore = before;
986 0 : --before; //move back to prepare for next search
987 : // after += std::string("entry").size(); //move forward to prepare for next search
988 :
989 0 : } //end primary entry extraction loop
990 :
991 0 : __COUTV__(entryCount);
992 0 : } //end add all posts that match
993 :
994 0 : if(xmlOut)
995 0 : xmlOut->addTextElementToData(XML_STATUS, "1"); // for success
996 0 : if(out)
997 0 : *out << __COUT_HDR_FL__ << "Today: " << time(0) / (60 * 60 * 24) << std::endl;
998 :
999 0 : if(TTEST(30))
1000 : {
1001 0 : __COUTTV__(mostRecentTime);
1002 0 : __COUTTV__(time(0));
1003 0 : __COUTTV__(time(0) - mostRecentTime);
1004 0 : __COUTTV__((time(0) - mostRecentTime) / (60 * 60 * 24));
1005 0 : __COUTTV__(timezoneHourOffset_);
1006 0 : for(size_t i = 0; i < 24; ++i)
1007 0 : __COUTT__ << "i: " << i << " "
1008 0 : << ((time(0) - i * 60 * 60 + timezoneHourOffset_ * 60 * 60) /
1009 0 : (60 * 60 * 24));
1010 : }
1011 :
1012 0 : if(xmlOut)
1013 : {
1014 0 : xmlOut->addNumberElementToData(XML_TIMEZONE_OFFSET, timezoneHourOffset_);
1015 :
1016 : if(0 &&
1017 : mostRecentTime) //always return 0 for ECL, because category filter may change, and may want live view of today..
1018 : {
1019 : int64_t mostRecentDayIndex =
1020 : (mostRecentTime + timezoneHourOffset_ * 60 * 60) / (60 * 60 * 24);
1021 : int64_t nowDayIndex =
1022 : (time(0) + timezoneHourOffset_ * 60 * 60) / (60 * 60 * 24);
1023 : if(TTEST(30))
1024 : {
1025 : __COUTTV__(mostRecentDayIndex);
1026 : __COUTTV__(nowDayIndex);
1027 : __COUTTV__(mostRecentTime);
1028 : __COUTTV__(((time(0) - mostRecentTime + timezoneHourOffset_ * 60 * 60) /
1029 : (60 * 60 * 24)));
1030 : }
1031 : __COUTTV__(nowDayIndex - mostRecentDayIndex);
1032 : xmlOut->addNumberElementToData(
1033 : XML_MOST_RECENT_DAY, //0 is today
1034 : nowDayIndex -
1035 : mostRecentDayIndex); // send most recent day index found in response
1036 : }
1037 : else
1038 0 : xmlOut->addNumberElementToData(XML_MOST_RECENT_DAY, 0);
1039 : }
1040 :
1041 0 : } //end refreshLogbook()
1042 :
1043 : //==============================================================================
1044 : /// xoap::MakeSystemLogEntry
1045 : /// make a system logbook entry into active category's logbook from Supervisor only
1046 : /// TODO: (how to enforce?)
1047 0 : xoap::MessageReference ECLSupervisor::MakeSystemLogEntry(xoap::MessageReference msg)
1048 : {
1049 0 : SOAPParameters parameters("EntryText");
1050 0 : parameters.addParameter("SubjectText");
1051 0 : SOAPUtilities::receive(msg, parameters);
1052 : std::string EntryText =
1053 0 : StringMacros::decodeURIComponent(parameters.getValue("EntryText"));
1054 : std::string SubjectText =
1055 0 : StringMacros::decodeURIComponent(parameters.getValue("SubjectText"));
1056 :
1057 0 : __COUT__ << "Received External Supervisor System Entry " << EntryText << std::endl;
1058 0 : __COUTV__(SubjectText);
1059 :
1060 0 : std::string retStr = "Success";
1061 :
1062 0 : if(ECLUser_ == "" ||
1063 0 : ECLHost_ == "") //ignore ECL when environment variables are not set
1064 : {
1065 0 : __COUT_INFO__ << "No ECL user/host specified for log entry: " << EntryText
1066 0 : << __E__;
1067 :
1068 : // fill return parameters
1069 0 : SOAPParameters retParameters("Status", retStr);
1070 :
1071 0 : return SOAPUtilities::makeSOAPMessageReference("SystemLogEntryStatusResponse",
1072 0 : retParameters);
1073 0 : }
1074 :
1075 0 : ECLEntry_t eclEntry;
1076 0 : eclEntry.author(StringMacros::escapeString(ECLUser_));
1077 0 : eclEntry.category(StringMacros::escapeString(ECLCategory_));
1078 0 : eclEntry.subject(StringMacros::escapeString(SubjectText));
1079 :
1080 0 : Form_t form;
1081 0 : Field_t field;
1082 0 : Form_t::field_sequence fields;
1083 0 : std::string users = theRemoteWebUsers_.getActiveUserList();
1084 :
1085 0 : form.name("default"); // these form names must be created in advance? ... default
1086 : // seems to have one field and be generic: 'text' field
1087 :
1088 : {
1089 0 : std::stringstream ss;
1090 0 : ss << "Message: " << __E__ << EntryText << __E__ << __E__;
1091 0 : ss << "This was a System Generated Log Entry from '" << CategoryName_
1092 0 : << "' at host '" << __ENV__("THIS_HOST") << "'" << __E__;
1093 0 : ss << "Active ots users: " << users << __E__;
1094 0 : ss << "USER_DATA: " << __ENV__("USER_DATA") << __E__;
1095 : ss << "Uptime: "
1096 0 : << StringMacros::getTimeDurationString(
1097 0 : CorePropertySupervisorBase::getSupervisorUptime())
1098 0 : << __E__;
1099 0 : field = Field_t(StringMacros::escapeString(ss.str(), true /* keep white space */),
1100 0 : "text");
1101 0 : fields.push_back(field);
1102 0 : }
1103 :
1104 0 : form.field(fields);
1105 0 : eclEntry.form(form);
1106 : try
1107 : {
1108 : // ECLConnection eclConn(ECLUser_, ECLPwd_, ECLHost_);
1109 0 : if(!eclConn_->Post(eclEntry))
1110 : {
1111 0 : __COUT_ERR__ << "Failure to post ECL entry." << __E__;
1112 0 : retStr = "Failure";
1113 : }
1114 : }
1115 0 : catch(const std::runtime_error& e)
1116 : {
1117 0 : __SS__ << "Exception caught during Logbook ECL connection: " << e.what();
1118 : ss << "\n\nIf the problem persists, you can turn off ECL communication by "
1119 : "clearing the environment variable ECL_USER_NAME (e.g. "
1120 : << "export ECL_USER_NAME="
1121 : ") at the terminal and restarting ots, or you can permanently disable the "
1122 : "requirement by editing the Configuration Tree and setting Gateway "
1123 : "Supervisor --> State Machine --> "
1124 : "RequireUserLogInputOnConfigureTransition/"
1125 0 : "RequireUserLogInputOnRunTransition = false.";
1126 0 : __COUT_ERR__ << ss.str();
1127 0 : retStr = ss.str();
1128 0 : }
1129 :
1130 : // fill return parameters
1131 0 : SOAPParameters retParameters("Status", retStr);
1132 :
1133 0 : return SOAPUtilities::makeSOAPMessageReference("SystemLogEntryStatusResponse",
1134 0 : retParameters);
1135 0 : } // end MakeSystemLogEntry()
|