/* * TimeTable.cpp * * Created on: 5 août 2013 * Author: CS */ #include "TimeTable.hh" #include "TimeUtil.hh" #include "TimeTableCatalogUtil.hh" #include #include #include #define CURL_STATICLIB #include #include #include namespace TimeTableCatalog { log4cxx::LoggerPtr TimeTable::_logger( log4cxx::Logger::getLogger("AMDA-Kernel.TimeTable")); TimeTable::TimeTable() : _creationDate(0), _timeFormat(TimeTable::TIME_FORMAT::UNKNOWN){ } TimeTable::TimeTable(TIME_FORMAT pFormat) : _creationDate(0), _timeFormat(pFormat) { } TimeTable::~TimeTable() { } // ------------------------------- PUBLIC ------------------------------ int TimeTable::getIntervalNumber() const { return _intervals.size(); } std::unique_ptr TimeTable::merge( const std::vector& pTimeTableList) { std::unique_ptr ptt(new TimeTable); // --------------------- METADATA --------------------- // -- name std::vector names; for (auto tt : pTimeTableList) { names.push_back(tt._name); } ptt->_name = join(names, "_u_"); // -- no historic // -- creation date time_t ptr; ptt->_creationDate = time(&ptr); // -- description std::string description = "Union between "; description += join(names, ", "); ptt->_description.push_back(description); // ------------------- INTERVALS ------------------- // add all intervals into the same TT ptt->group(pTimeTableList); // sort the interval according to their start date ptt->sort(); /*for (TimeInterval interval : ptt->_intervals) { std::cout << interval; }*/ std::unique_ptr> mergedIntervals = merge( ptt->_intervals); // fill new TT intervals with merged intervals ptt->_intervals.clear(); ptt->_intervals.insert(ptt->_intervals.end(), mergedIntervals->begin(), mergedIntervals->end()); return ptt; } std::unique_ptr TimeTable::intersect( const std::vector& pTimeTableList) { std::unique_ptr ptt(new TimeTable); // ---------------- METADATA --------------------- // -- name std::vector names; for (auto tt : pTimeTableList) { names.push_back(tt._name); } ptt->_name = join(names, "_i_"); // -- no historic // -- creation date time_t ptr; ptt->_creationDate = time(&ptr); // -- description std::string description = "Intersection between "; description += join(names, ", "); ptt->_description.push_back(description); // ---------------- INTERVALS ------------------- if (pTimeTableList.empty()) { return ptt; } // one interval list is empty, no intersection available for (auto tt : pTimeTableList) { if (tt.getIntervalNumber() == 0) { return ptt; } } // create tmp container for intersected time intervals std::vector intersetedIntervals; auto tmpTT = pTimeTableList[0]; for (size_t i = 1; i < pTimeTableList.size(); ++i) { tmpTT.sort(); intersetedIntervals.clear(); intersect(tmpTT, pTimeTableList[i], intersetedIntervals); tmpTT._intervals.clear(); tmpTT._intervals.insert(tmpTT._intervals.end(), intersetedIntervals.begin(), intersetedIntervals.end()); } // fill new TT intervals with merged intervals ptt->_intervals.clear(); ptt->_intervals.insert(ptt->_intervals.end(), intersetedIntervals.begin(), intersetedIntervals.end()); return ptt; } std::unique_ptr TimeTable::antiintersect( const std::vector& pTimeTableList) { std::unique_ptr ptt(new TimeTable); // ---------------- METADATA --------------------- // -- name std::vector names; for (auto tt : pTimeTableList) { names.push_back(tt._name); } ptt->_name = join(names, "_ai_"); // -- no historic // -- creation date time_t ptr; ptt->_creationDate = time(&ptr); // -- description std::string description = "Anti'intersection between "; description += join(names, ", "); ptt->_description.push_back(description); // ---------------- INTERVALS ------------------- if (pTimeTableList.empty()) { return ptt; } // create tmp container for anti'intersected time intervals // do anti'intersect for each tt with all other tt in list // Means, we have in list tt1, tt2 and tt3 // add into the resulting interval list // all non intersection between tt1/tt2, tt1/tt3 and tt2/tt3 // then make union between intervals std::vector antiIntersetedIntervals; TimeTable tt1, tt2; for (size_t i = 0; i < pTimeTableList.size(); ++i) { tt1._intervals.clear(); tt1._intervals.insert(tt1._intervals.end(), pTimeTableList[i]._intervals.begin(), pTimeTableList[i]._intervals.end()); tt1.sort(); for (size_t j = i + 1; j < pTimeTableList.size(); ++j) { tt2._intervals.clear(); tt2._intervals.insert(tt2._intervals.end(), pTimeTableList[j]._intervals.begin(), pTimeTableList[j]._intervals.end()); tt2.sort(); antiintersect(tt1, tt2, antiIntersetedIntervals); } } sort(antiIntersetedIntervals); // merge intervals std::unique_ptr> mergedIntervals = merge( antiIntersetedIntervals); // fill new TT intervals with merged intervals ptt->_intervals.clear(); ptt->_intervals.insert(ptt->_intervals.end(), mergedIntervals->begin(), mergedIntervals->end()); return ptt; } void TimeTable::group(const std::vector& pTimeTableList) { for (TimeTable tt : pTimeTableList) { _intervals.insert(_intervals.end(), tt._intervals.begin(), tt._intervals.end()); } } void TimeTable::sort() { sort(_intervals); } void TimeTable::addInterval(const TimeInterval& pInterval) { _intervals.push_back(pInterval); } /** * Unset current TT. */ void TimeTable::clear() { _intervals.clear(); _name = std::string(); _description.clear(); _creationDate = -1; _history = std::string(); _timeFormat = TIME_FORMAT::UNKNOWN; } /** * Downloads timetable file in tmp directory. */ std::string TimeTable::download(const std::string& pPath) { std::string localPath; std::string tmpPath(pPath); std::transform(tmpPath.begin(), tmpPath.end(), tmpPath.begin(), ::tolower); if (!boost::starts_with(tmpPath, "http:") && !boost::starts_with(tmpPath, "https:")) { return pPath; } // download file CURL *pCurl; CURLcode codes; const char *url = pPath.c_str(); // get tt name to create temp file as tmp_ size_t endOfPath = pPath.find_last_of("/"); if (endOfPath == std::string::npos) { endOfPath = pPath.find_last_of("=/\\"); } std::string tmpFile = "./tmp_" + pPath.substr(endOfPath + 1); // do download pCurl = curl_easy_init(); if (pCurl) { FILE *fptr = fopen(tmpFile.c_str(), "wb"); if (fptr) { curl_easy_setopt(pCurl, CURLOPT_URL, url); curl_easy_setopt(pCurl, CURLOPT_WRITEFUNCTION, write_data); curl_easy_setopt(pCurl, CURLOPT_SSL_VERIFYPEER, false); curl_easy_setopt(pCurl, CURLOPT_SSL_VERIFYHOST, false); curl_easy_setopt(pCurl, CURLOPT_WRITEDATA, fptr); // create buffer to get potential error string std::vector errBuf(1024); curl_easy_setopt(pCurl, CURLOPT_ERRORBUFFER, &errBuf[0]); codes = curl_easy_perform(pCurl); curl_easy_cleanup(pCurl); fclose(fptr); if (codes == CURLE_OK) { localPath = tmpFile; } else { LOG4CXX_ERROR(_logger, "Unable to download " + pPath + " : " + errBuf[0]); } } else { LOG4CXX_ERROR(_logger, "Unable to download " + pPath + " : not found."); } } else { LOG4CXX_ERROR(_logger, "Unable to download " + pPath + " : cUrl cannot be initialized."); } // else, do nothing // return local file or empty string if not downloaded return localPath; } // ------------------------------- PRIVATE ------------------------------ /** * Fills an interval list with all intersections between the two given time tables. */ void TimeTable::intersect(const TimeTable& ptt1, const TimeTable& ptt2, std::vector& pIntersetedIntervals) { size_t indexInTT2 = 0; size_t indexInTT1 = 0; for (; indexInTT1 < ptt1._intervals.size(); ++indexInTT1) { auto interval1 = ptt1._intervals[indexInTT1]; for (; indexInTT2 < ptt2._intervals.size(); ++indexInTT2) { auto interval2 = ptt2._intervals[indexInTT2]; // suppose two intervals // B1 E1 in tt1 // B2 E2 in tt2 // if B1 < E2 or B2 < E1, we have an intersection // all possible intersections // tt1 | ____ | ____ | _____ | ___ // tt2 | ____ | ___ | ___ | _____ if ((std::max(interval2._startTime, interval1._startTime) < std::min(interval1._stopTime, interval2._stopTime))) { // here we have an intersection TimeInterval intersectedInterval( std::max(interval1._startTime, interval2._startTime), std::min(interval1._stopTime, interval2._stopTime)); pIntersetedIntervals.push_back(intersectedInterval); // keep interval1 if larger to find potential other intersection if (interval1._stopTime > interval2._stopTime) { --indexInTT1; ++indexInTT2; } break; } else if (interval1._stopTime < interval2._startTime) { // interval1 is before interval2 // go on to next tt1 interval break; } // otherwise, continue to loop on tt2 } } } /** * Fills an interval list with all non intersections between the two given time tables. */ void TimeTable::antiintersect(const TimeTable& ptt1, const TimeTable& ptt2, std::vector& pAntiIntersectedIntervals) { size_t indexInTT1 = 0; size_t indexInSecondTT = 0; for (; indexInTT1 < ptt1._intervals.size(); ++indexInTT1) { auto interval1 = ptt1._intervals[indexInTT1]; // no intervals remaining in tt2, break the loop and // just save the rest of tt1 if (indexInSecondTT == ptt2._intervals.size()) { break; } for (; indexInSecondTT < ptt2._intervals.size(); ++indexInSecondTT) { auto interval2 = ptt2._intervals[indexInSecondTT]; if ((std::max(interval2._startTime, interval1._startTime) <= std::min(interval1._stopTime, interval2._stopTime))) { // here we have an intersection TimeInterval intersectedInterval1( std::min(interval1._startTime, interval2._startTime), std::max(interval1._startTime, interval2._startTime)); pAntiIntersectedIntervals.push_back(intersectedInterval1); TimeInterval intersectedInterval2( std::min(interval1._stopTime, interval2._stopTime), std::max(interval1._stopTime, interval2._stopTime)); pAntiIntersectedIntervals.push_back(intersectedInterval2); indexInSecondTT++; break; } else { // no intersection between the two intervals, // save the first interval and // search for a possible intersection with the second one if (interval1._startTime <= interval2._startTime) { pAntiIntersectedIntervals.push_back(interval1); // .. go to next interval in tt1 break; } else { pAntiIntersectedIntervals.push_back(interval2); // .. back to current tt1 interval indexInTT1--; } } } } // here, all intervals of tt1 or tt2 have been handled // save the rest of tt1 if (indexInTT1 < ptt1._intervals.size()) { for (; indexInTT1 < ptt1._intervals.size(); ++indexInTT1) { auto interval1 = ptt1._intervals[indexInTT1]; pAntiIntersectedIntervals.push_back(interval1); } } else { // or save the rest of tt2 for (; indexInSecondTT < ptt2._intervals.size(); ++indexInSecondTT) { auto interval2 = ptt2._intervals[indexInSecondTT]; pAntiIntersectedIntervals.push_back(interval2); } } } /** * Returns a merged interval list. */ std::unique_ptr> TimeTable::merge( const std::vector& pTimeIntervalsList) { // create tmp container for merged time intervals std::unique_ptr> mergedIntervals( new std::vector); if (!pTimeIntervalsList.empty()) { auto currentInterval = pTimeIntervalsList.front(); for (auto interval : pTimeIntervalsList) { // merge current interval with previous one if start date // is inside previous interval if (interval._startTime <= currentInterval._stopTime) { currentInterval._stopTime = std::max(interval._stopTime, currentInterval._stopTime); } else { // intervals are merged, next interval is outside // add interval to TT mergedIntervals->push_back(currentInterval); currentInterval = interval; } } // write last interval mergedIntervals->push_back(currentInterval); } return mergedIntervals; } void TimeTable::sort(std::vector& pIntervals) { std::sort(pIntervals.begin(), pIntervals.end()); } // ------------------------------- EXTERN ------------------------------ std::string join(const std::vector& pvalues, const std::string& pseparator) { std::string res; for (size_t i = 0; i < pvalues.size(); ++i) { res += pvalues[i]; if (i < pvalues.size() - 1) { res += pseparator; } } return res; } size_t write_data(void *ptr, size_t size, size_t nmemb, FILE *stream) { size_t written; written = fwrite(ptr, size, nmemb, stream); return written; } std::ostream& operator <<(std::ostream& os, const TimeInterval& interval) { writeISOTime(interval._startTime, TimeTable::TIME_FORMAT::YYYYMMDDThhmmssmsk, os); os << " "; writeISOTime(interval._stopTime, TimeTable::TIME_FORMAT::YYYYMMDDThhmmssmsk, os); os << std::endl; return os; } } /* namespace TimeTableCatalog */