/* * CMHbookPublisher.h * * Created on: Jul 24, 2012 * Author: rryan * * The object of template was to be a simple add on to the existing * C Mini H book program. This add-on when created in an existing main * will listen for requests from the RootSpy system and will publish the * histograms located in the passed array to be displayed in RootSpy. * * This project was given to CNU intern Rob Ryan by Elliot Wolin and * David Lawrence. * * Example: * int main(int narg, char *argv[]) { * histi // The array histi would be the array histi or histf * // defined in CMHbook.h * string UDL = "cMsg://localhost/cMsg/rootspy"; * CMHbookPublisher *go = new CMHbookPublisher(histi, UDL); * cout << "enter anything to quit" << endl; * string wait; * cin >> wait; * } * * This sudo code would publish requests from RootSpy such as request histograms list * and request histogram. The UDL needs to be to correct UDL of the RootSpy server * and the parameter histi in this case would be an array of UThisti histograms as * defined in uthbook.h however histf and UThistf types are also supported. * */ #include #include #include #include #include #include // shared memory #include // exit() etc #include // shmem_* stuff, and mmap() #include #include #include #include // Includes CMHbook UThist structs #include "Uthbook.h" // Includes cMsg functions #include using namespace std; using namespace cmsg; // Extension of the cMsg Call Back class template class CMHbookPublisher: public cMsgCallback { public: CMHbookPublisher(string udl = ""); virtual ~CMHbookPublisher(); // Subclass which allows for only the necessary data to be sent when // "list hists" is asked class mHistogram { public: string name; string title; string type; string path; }; /* * Prepares an ASCII character string to package into a cMsg * * The ASCII string is formatted as follows: * * dataType,title,xbin,xmin,xmax,ybin,ymin,ymax,0, * xunderflow,0,yunderflow,yoverflow,0,xoverflow,0, * * followed by the data. * * The 0's are values for the xunderyunder, xunderyover, * xover,yunder, and xoveryover values not stored in the uthist structs * * Since UThisti does not store 8 underflow/overflow values the * extra data points are set to zero. * * The variables ifi2_book, ifi2_fill, nwtitle, dx, and dy from the * uthist structs are lost in this conversion as they are not necessary * to convert to ROOT histograms */ string toCSV(int id, T* floatHist); protected: // The necessary method for cMsgCallBack. Listens for requests such as // "list hists" and "get hist" void callback(cMsgMessage *msg, void *userObject); private: vector subscription_handles; vector *finalhists; pthread_t mythread; string finalsender; cMsg *cMsgSys; string myname; // Vector to catch and store globally the passed histogram array vector histList; // shared memory // function to access shared memory void accessSharedMemory(); // gets a histogram in shared memory T* getSharedHistogram(int id); // Method which collects and converts the UThist objects to mHistogram // objects to be published for RootSpy void AddMHBObjectsToList(vector &hinfos); // Method to refresh the global list of Histograms from the passed pointer void refreshHistList(); }; template CMHbookPublisher::CMHbookPublisher(string myUDL) { accessSharedMemory(); // The 2000 value comes from the constant NHIST defined in uthbook.h // assuming that histi or histf defined in CMHbook.h would be passed. // This loop checks each value in the incoming array to see if it is // a valid histogram and then stores the good ones in wrapper classes // in the globally accessible vector, histList. // fill histList with correct values or find new way to send data for (int i = 1; i < 2000; i++) { if (uthistOffsetArray[1] != 0) { if (uthistOffsetArray[i] != 0) { T* pointer = (T*) ((char*) uthistOffsetArray + uthistOffsetArray[i - 1]); histList.push_back(pointer); } } else { break; } } // Create a unique name for producer char hostname[256]; gethostname(hostname, 256); char str[512]; sprintf(str, "%s_%d", hostname, getpid()); myname = string(str); // Determine UDL for connecting to cMsg server // The preference given is: // 1. udl passed to us (if not set to ") // 2. environment variable CMHBOOK_UDL if set // 3. hardwired UDL (cMsg://localhost/cMsg/cmhbook) // // If "" is specified, then check for 2 then 3 if (myUDL == "") { const char *CMHBOOK_UDL = getenv("CMHBOOK_UDL"); if (CMHBOOK_UDL) { myUDL = CMHBOOK_UDL; } else { myUDL = "cMsg://127.0.0.1/cMsg/cmhbook"; } } // Connect to cMsg system string myName = myname; string myDescr = "Produce Mini H Book Histograms for RootSpy Program"; // the cMsg system object, where cMsgSys = new cMsg(myUDL, myName, myDescr); try { // all args are of type string cMsgSys->connect(); } catch (cMsgException e) { cout << endl << endl << endl << endl << "_______________ CMHBOOK unable to connect to cMsg system! __________________" << endl; cout << e.toString() << endl; cout << endl << endl << endl << endl; return; } cout << "---------------------------------------------------" << endl; cout << "cmhbook name: \"" << myname << "\"" << endl; cout << "---------------------------------------------------" << endl; // Subscribe to generic cmhbook info requests subscription_handles.push_back( cMsgSys->subscribe("rootspy", "*", this, NULL)); // Subscribe to cmhbook requests specific to us subscription_handles.push_back(cMsgSys->subscribe(myname, "*", this, NULL)); // Start cMsg system cMsgSys->start(); // Broadcast that we're here to anyone already listening cMsgMessage ImAlive; ImAlive.setSubject("cmhbook"); ImAlive.setType(myname); cMsgSys->send(&ImAlive); } template CMHbookPublisher::~CMHbookPublisher() { // Unsubscribe for (unsigned int i = 0; i < subscription_handles.size(); i++) { cMsgSys->unsubscribe(subscription_handles[i]); } // Stop cMsg system cMsgSys->stop(); } template string CMHbookPublisher::toCSV(int id, T *hist) { cout << "Incorrect CSV format" << endl; } template<> string CMHbookPublisher::toCSV(int id, UThisti *intHist) { stringstream ss; ss << 'i' << "," << intHist->title << "," << intHist->entries << "," << intHist->nbinx << "," << intHist->xmin << "," << intHist->xmax << "," << intHist->nbiny << "," << intHist->ymin << "," << intHist->ymax << "," << 0 << "," << intHist->xunderflow << "," << 0 << "," << intHist->yunderflow << "," << intHist->yoverflow << "," << 0 << "," << intHist->xoverflow << "," << 0 << ","; int i, k; if (intHist->nbiny < 1) { for (i = 0; i < intHist->nbinx - 1; i++) { ss << uthijShared(id, i, 0) << ","; } ss << ss << uthijShared(id, i, 0) << ends; } else { for (i = 0; i < intHist->nbinx - 1; i++) { for (k = 0; k < intHist->nbiny; k++) { ss << uthijShared(id, i, k) << ","; } } for (k = 0; k < intHist->nbiny - 1; k++) { ss << uthijShared(id, i, k) << ","; } ss << uthijShared(id, i, k) << ends; } return ss.str(); } template<> string CMHbookPublisher::toCSV(int id, UThistf* floatHist) { stringstream ss; // since UThistf does not store 8 underflow/overflow values the // extra data points are set to zero ss << 'f' << "," << floatHist->title << "," << floatHist->entries << "," << floatHist->nbinx << "," << floatHist->xmin << "," << floatHist->xmax << "," << floatHist->nbiny << "," << floatHist->ymin << "," << floatHist->ymax << "," << 0 << "," << floatHist->xunderflow << "," << 0 << "," << floatHist->yunderflow << "," << floatHist->yoverflow << "," << 0 << "," << floatHist->xoverflow << "," << 0 << ","; int i, k; if (floatHist->nbiny < 1) { for (i = 0; i < floatHist->nbinx - 1; i++) { ss << uthijShared(id, i, 0) << ","; } ss << ss << uthijShared(id, i, 0) << ends; } else { for (i = 0; i < floatHist->nbinx - 1; i++) { for (k = 0; k < floatHist->nbiny; k++) { ss << uthijShared(id, i, k) << ","; } } for (k = 0; k < floatHist->nbiny - 1; k++) { ss << uthijShared(id, i, k) << ","; } ss << uthijShared(id, i, floatHist->nbiny - 1) << ends; } return ss.str(); } template void CMHbookPublisher::callback(cMsgMessage *msg, void *userObject) { if (!msg) return; cout << "Received message -- Subject:" << msg->getSubject() << " Type:" << msg->getType() << " Text:" << msg->getText() << endl; // The convention here is that the message "type" always constrains the // unique name of the sender and therefore should be the "subject" to // which any response should be sent. string sender = msg->getType(); // The actual command is always sent in the text of the message string cmd = msg->getText(); // Prepare to send a response (this may or may not be needed below, depending on the command) cMsgMessage response; response.setSubject(sender); response.setType(myname); // Dispatch command if (cmd == "who's there?") { response.setText("I am here"); cMsgSys->send(&response); delete msg; return; } //====================================================================== if (cmd == "list hists") { // Add histograms to list vector hin; vector &hinfos = hin; AddMHBObjectsToList(hinfos); // If any histograms were found, copy their info into the message vector hist_names; vector hist_types; vector hist_paths; vector hist_titles; if (hinfos.size() > 0) { // Copy elements of hinfo objects into individual vectors for (unsigned int i = 0; i < hinfos.size(); i++) { hist_names.push_back(hinfos[i].name); hist_types.push_back(hinfos[i].type); hist_paths.push_back(hinfos[i].path); hist_titles.push_back(hinfos[i].title); } response.add("hist_names", hist_names); response.add("hist_types", hist_types); response.add("hist_paths", hist_paths); response.add("hist_titles", hist_titles); } else { response.add("hist_names", hist_names); response.add("hist_types", hist_types); response.add("hist_paths", hist_paths); response.add("hist_titles", hist_titles); } response.setText("hists list"); cMsgSys->send(&response); delete msg; return; } //====================================================================== //====================================================================== if (cmd == "get hist") { // Get name of requested histogram string holdTitl = msg->getString("hnamepath"); stringstream sp(holdTitl); string incomingTitle; getline(sp, incomingTitle, '/'); bool histNotFound = true; for (int id = 0; id < histList.size(); id++) { if (histList[id]->title == incomingTitle) { string csvHist = toCSV(id, histList[id]); stringstream ss(csvHist); stringstream sp; string stringPiece; char type; getline(ss, stringPiece, ','); sp.str(stringPiece); sp >> type; string namePath = histList[id]->title; response.add("type", "cmhbook"); response.add("csv", csvHist); response.add("hnamepath", holdTitl); if (histList[id]->nbiny < 1 && type == 'i') { response.add("convert", "TH1I"); } else if (histList[id]->nbiny > 0 && type == 'i') { response.add("convert", "TH2I"); } else if (histList[id]->nbiny < 1 && type == 'f') { response.add("convert", "TH1F"); } else if (histList[id]->nbiny > 0 && type == 'f') { response.add("convert", "TH2F"); } histNotFound = false; break; } } if (histNotFound) { response.add("type", ""); response.add("hnamepath", ""); } response.setText("histogram"); cMsgSys->send(&response); delete msg; return; } delete msg; } template void CMHbookPublisher::accessSharedMemory() { // creating the shared memory object -- shmem_open() // O_RDONLY allows the Consumer to only read the file // S_IRWXU | S_IRGRP, allows the user to read write and execute // and the group to read only shm = shm_open(shmPath, O_RDONLY, S_IRWXU | S_IRGRP); if (shm < 0) { perror("In shmem_open()"); } uthistOffsetArray = (unsigned long *) mmap(NULL, shared_seg_size, PROT_READ, MAP_PRIVATE, shm, 0); if (uthistOffsetArray == NULL) { perror("In mmap()"); } for (int i = 1; i < NHIST; i++) { if (uthistOffsetArray[i] == 0) { shared_seg_size = uthistOffsetArray[i - 1]; indexOfEndOfMem = i-1; break; } } uthistOffsetArray = (unsigned long *) mmap(NULL, shared_seg_size, PROT_READ, MAP_PRIVATE, shm, 0); } template T* CMHbookPublisher::getSharedHistogram(int id) { UThistf *histToReturn = NULL; if (id < NHIST) { if (uthistOffsetArray[id] > 0 && uthistOffsetArray[id + 1] != 0) { histToReturn = (UThistf*) ((char*) uthistOffsetArray + uthistOffsetArray[id]); } else { printf("Hist %i does not exist.", id); } return histToReturn; } else { printf("Hist %i does not exist.", id); return NULL; } } template void CMHbookPublisher::AddMHBObjectsToList(vector &hinfos) { refreshHistList(); for (int i = 0; i < histList.size(); i++) { mHistogram copy; copy.name = "cmhbook"; copy.path = histList[i]->title; copy.title = histList[i]->title; if (histList[i]->nbiny < 1) { copy.type = "1D"; } else { copy.type = "2D"; } hinfos.push_back(copy); } } template void CMHbookPublisher::refreshHistList() { histList.clear(); for (int i = 1; i < 2000; i++) { if (uthistOffsetArray[1] != 0) { if (uthistOffsetArray[i] != 0) { T* pointer = (T*) ((char*) uthistOffsetArray + uthistOffsetArray[i - 1]); histList.push_back(pointer); } } else { break; } } }