Go to the documentation of this file.
48 _timestampUpdate(
true)
250 std::map<std::string, float> statistics;
259 std::map<int, std::pair<std::map<std::string, float>,
double> > statistics;
268 std::map<int, std::vector<int> > wmStates;
323 std::map<int, Signature*> signatures;
324 std::map<int, VisualWord*> visualWords;
337 if(signatures.size() || visualWords.size())
342 if(signatures.size())
350 for(std::map<int, Signature *>::iterator
iter=signatures.begin();
iter!=signatures.end(); ++
iter)
357 if(visualWords.size())
365 for(std::map<int, VisualWord *>::iterator
iter=visualWords.begin();
iter!=visualWords.end(); ++
iter)
367 delete (*iter).second;
410 std::list<Signature *> toSave;
411 std::list<Signature *> toUpdate;
414 for(std::vector<Signature *>::const_iterator
i=signatures.begin();
i!=signatures.end();++
i)
418 toUpdate.push_back(*
i);
422 toSave.push_back(*
i);
440 std::list<VisualWord *> toSave;
441 std::list<VisualWord *> toUpdate;
444 for(std::vector<VisualWord *>::const_iterator
i=words.begin();
i!=words.end();++
i)
448 toUpdate.push_back(*
i);
452 toSave.push_back(*
i);
485 const cv::Mat & ground,
486 const cv::Mat & obstacles,
487 const cv::Mat &
empty,
489 const cv::Point3f & viewpoint)
494 data.setOccupancyGrid(ground, obstacles,
empty, cellSize, viewpoint);
497 data.gridGroundCellsCompressed(),
498 data.gridObstacleCellsCompressed(),
499 data.gridEmptyCellsCompressed(),
537 this->
loadQuery(dictionary, lastStateOnly);
552 std::list<Signature*> signatures;
553 std::set<int> loadedFromTrashSet;
555 if(loadedFromTrash && loadedFromTrashSet.size())
557 *loadedFromTrash =
true;
559 if(!signatures.empty())
561 return signatures.front();
566 std::list<Signature *> & signatures,
567 std::set<int> * loadedFromTrash)
571 std::list<int> ids = signIds;
572 bool valueFound =
false;
575 for(std::list<int>::iterator
iter = ids.begin();
iter != ids.end();)
580 if(sIter->first == *
iter)
582 signatures.push_back(sIter->second);
597 loadedFromTrash->insert(*
iter);
620 std::set<int> ids = wordIds;
621 std::map<int, VisualWord*>::iterator wIter;
622 std::list<VisualWord *> puttedBack;
627 for(std::set<int>::iterator
iter = ids.begin();
iter != ids.end();)
634 puttedBack.push_back(wIter->second);
653 else if(puttedBack.size())
661 std::list<Signature *> signatures;
662 signatures.push_back(signature);
663 this->
loadNodeData(signatures, images, scan, userData, occupancyGrid);
666 void DBDriver::loadNodeData(std::list<Signature *> & signatures,
bool images,
bool scan,
bool userData,
bool occupancyGrid)
const
673 for(std::list<Signature *>::iterator
iter=signatures.begin();
iter!=signatures.end(); ++
iter)
689 bool images,
bool scan,
bool userData,
bool occupancyGrid)
const
698 ((!images || !
s->sensorData().imageCompressed().empty()) &&
699 (!scan || !
s->sensorData().laserScanCompressed().isEmpty()) &&
700 (!userData || !
s->sensorData().userDataCompressed().empty()) &&
701 (!occupancyGrid ||
s->sensorData().gridCellSize() != 0.0f))))
706 data.setRGBDImage(cv::Mat(), cv::Mat(), std::vector<CameraModel>());
714 data.setUserData(cv::Mat());
718 data.setOccupancyGrid(cv::Mat(), cv::Mat(), cv::Mat(), 0, cv::Point3f());
728 std::list<Signature *> signatures;
730 signatures.push_back(&tmp);
732 data = signatures.front()->sensorData();
739 std::vector<CameraModel> & models,
740 std::vector<StereoCameraModel> & stereoModels)
const
749 stereoModels =
_trashSignatures.at(signatureId)->sensorData().stereoCameraModels();
795 std::vector<float> & velocity,
809 groundTruthPose =
_trashSignatures.at(signatureId)->getGroundTruthPose().clone();
820 found = this->
getNodeInfoQuery(signatureId, pose, mapId, weight, label, stamp, groundTruthPose,
velocity, gps, sensors);
835 for(std::map<int, Link>::const_iterator nIter =
s->getLinks().begin();
836 nIter!=
s->getLinks().end();
841 links.insert(*nIter);
846 links.insert(
s->getLandmarks().begin(),
s->getLandmarks().end());
887 void DBDriver::getAllNodeIds(std::set<int> & ids,
bool ignoreChildren,
bool ignoreBadSignatures,
bool ignoreIntermediateNodes)
const
895 bool hasNeighbors = !ignoreChildren;
898 for(std::map<int, Link>::const_iterator nIter = sIter->second->getLinks().begin();
899 nIter!=sIter->second->getLinks().end();
910 if(hasNeighbors && (!ignoreIntermediateNodes || sIter->second->getWeight() != -1))
912 ids.insert(sIter->first);
922 this->
getAllNodeIdsQuery(ids, ignoreChildren, ignoreBadSignatures, ignoreIntermediateNodes);
934 bool hasNeighbors = !ignoreChildren;
937 for(std::map<int, Link>::const_iterator nIter = sIter->second->getLinks().begin();
938 nIter!=sIter->second->getLinks().end();
949 if(hasNeighbors && (!ignoreIntermediateNodes || sIter->second->getWeight() != -1))
951 poses.insert(std::make_pair(sIter->first, sIter->second->getPose()));
977 links.erase(
iter->first);
978 for(std::map<int, Link>::const_iterator jter=
iter->second->getLinks().begin();
979 jter!=
iter->second->getLinks().end();
982 if(!ignoreNullLinks || jter->second.isValid())
984 links.insert(std::make_pair(
iter->first, jter->second));
989 for(std::map<int, Link>::const_iterator jter=
iter->second->getLandmarks().begin();
990 jter!=
iter->second->getLandmarks().end();
993 if(!ignoreNullLinks || jter->second.isValid())
995 links.insert(std::make_pair(
iter->first, jter->second));
1016 int statisticsId = 0;
1020 if(statisticsId >
id)
1086 std::map<int, Link>::const_iterator kter = sIter->second->getLandmarks().find(landmarkId);
1087 if(kter != sIter->second->getLandmarks().end())
1089 nodes.insert(std::make_pair(sIter->second->id(), kter->second));
1101 UWARN(
"Can't search with an empty label!");
1114 if(sIter->second->getLabel().compare(label) == 0)
1116 idFound = sIter->first;
1136 UWARN(
"Can't search with an empty label!");
1146 if(!sIter->second->getLabel().empty())
1148 labels.insert(std::make_pair(sIter->first, sIter->second->getLabel()));
1163 int databaseMemUsed,
1170 std::stringstream query;
1176 query <<
"INSERT INTO Info(STM_size,last_sign_added,process_mem_used,database_mem_used,dictionary_size,parameters) values("
1178 << lastSignAdded <<
","
1179 << processMemUsed <<
","
1180 << databaseMemUsed <<
","
1181 << dictionarySize <<
","
1182 "'" << param.c_str() <<
"');";
1186 query <<
"INSERT INTO Statistics(STM_size,last_sign_added,process_mem_used,database_mem_used,dictionary_size,parameters) values("
1188 << lastSignAdded <<
","
1189 << processMemUsed <<
","
1190 << databaseMemUsed <<
","
1191 << dictionarySize <<
","
1192 "'" << param.c_str() <<
"');";
1197 query <<
"INSERT INTO Statistics(STM_size,last_sign_added,process_mem_used,database_mem_used,dictionary_size) values("
1199 << lastSignAdded <<
","
1200 << processMemUsed <<
","
1201 << databaseMemUsed <<
","
1202 << dictionarySize <<
");";
1261 const cv::Mat & cloud,
1262 const std::vector<std::vector<std::vector<RTABMAP_PCL_INDEX> > > & polygons,
1263 #
if PCL_VERSION_COMPARE(>=, 1, 8, 0)
1266 const std::vector<std::vector<Eigen::Vector2f> > & texCoords,
1268 const cv::Mat & textures)
const
1276 std::vector<std::vector<std::vector<RTABMAP_PCL_INDEX> > > * polygons,
1277 #
if PCL_VERSION_COMPARE(>=, 1, 8, 0)
1280 std::vector<std::vector<Eigen::Vector2f> > * texCoords,
1282 cv::Mat * textures)
const
1291 const std::string & fileName,
1292 const std::set<int> & idsInput,
1293 const std::map<int, Signature *> & otherSignatures)
1297 if(!fileName.empty())
1301 fopen_s(&fout, fileName.c_str(),
"w");
1303 fout = fopen(fileName.c_str(),
"w");
1308 UERROR(
"Cannot open file %s!", fileName.c_str());
1313 if(idsInput.size() == 0)
1316 UDEBUG(
"ids.size()=%d", ids.size());
1317 for(std::map<int, Signature*>::const_iterator
iter=otherSignatures.begin();
iter!=otherSignatures.end(); ++
iter)
1319 ids.insert(
iter->first);
1327 const char * colorG =
"green";
1328 const char * colorP =
"pink";
1329 const char * colorNM =
"blue";
1330 UINFO(
"Generating map with %d locations", ids.size());
1331 fprintf(fout,
"digraph G {\n");
1332 for(std::set<int>::iterator
i=ids.begin();
i!=ids.end(); ++
i)
1334 if(otherSignatures.find(*
i) == otherSignatures.end())
1337 std::multimap<int, Link> links;
1341 for(std::multimap<int, Link>::iterator
iter = links.begin();
iter!=links.end(); ++
iter)
1343 int weightNeighbor = 0;
1344 if(otherSignatures.find(
iter->first) == otherSignatures.end())
1350 weightNeighbor = otherSignatures.find(
iter->first)->second->getWeight();
1355 fprintf(fout,
" \"%d\\n%d\" -> \"%d\\n%d\"\n",
1364 fprintf(fout,
" \"%d\\n%d\" -> \"%d\\n%d\" [label=\"M\", fontcolor=%s, fontsize=8];\n",
1371 else if(
iter->first >
id)
1374 fprintf(fout,
" \"%d\\n%d\" -> \"%d\\n%d\" [label=\"L\", fontcolor=%s, fontsize=8];\n",
1384 fprintf(fout,
" \"%d\\n%d\" -> \"%d\\n%d\" [label=\"C\", fontcolor=%s, fontsize=8];\n",
1394 for(std::map<int, Signature*>::const_iterator
i=otherSignatures.begin();
i!=otherSignatures.end(); ++
i)
1396 if(ids.find(
i->first) != ids.end())
1398 int id =
i->second->id();
1399 const std::multimap<int, Link> & links =
i->second->getLinks();
1400 int weight =
i->second->getWeight();
1401 for(std::multimap<int, Link>::const_iterator
iter = links.begin();
iter!=links.end(); ++
iter)
1403 int weightNeighbor = 0;
1407 weightNeighbor =
s->getWeight();
1416 fprintf(fout,
" \"%d\\n%d\" -> \"%d\\n%d\"\n",
1424 fprintf(fout,
" \"%d\\n%d\" -> \"%d\\n%d\" [label=\"M\", fontcolor=%s, fontsize=8];\n",
1431 else if(
iter->first >
id)
1434 fprintf(fout,
" \"%d\\n%d\" -> \"%d\\n%d\" [label=\"L\", fontcolor=%s, fontsize=8];\n",
1441 else if(
iter->first !=
id)
1444 fprintf(fout,
" \"%d\\n%d\" -> \"%d\\n%d\" [label=\"C\", fontcolor=%s, fontsize=8];\n",
1454 fprintf(fout,
"}\n");
1456 UINFO(
"Graph saved to \"%s\" (Tip: $ neato -Tpdf \"%s\" -o out.pdf)", fileName.c_str(), fileName.c_str());
DBDriver(const ParametersMap ¶meters=ParametersMap())
std::map< int, VisualWord * > _trashVisualWords
void loadWords(const std::set< int > &wordIds, std::list< VisualWord * > &vws)
std::map< int, Transform > loadOptimizedPoses(Transform *lastlocalizationPose=0) const
void getNodeData(int signatureId, SensorData &data, bool images=true, bool scan=true, bool userData=true, bool occupancyGrid=true) const
virtual int getTotalNodesSizeQuery() const =0
bool getCalibration(int signatureId, std::vector< CameraModel > &models, std::vector< StereoCameraModel > &stereoModels) const
void getNodesObservingLandmark(int landmarkId, std::map< int, Link > &nodes) const
long getNodesMemoryUsed() const
void beginTransaction() const
void updateLink(const Link &link)
ParametersMap getLastParameters() const
void addStatistics(const Statistics &statistics, bool saveWmState) const
virtual long getStatisticsMemoryUsedQuery() const =0
std::string format(const std::string &str, const std::vector< std::string > &find, const std::vector< std::string > &replace)
virtual bool isConnectedQuery() const =0
virtual int getTotalDictionarySizeQuery() const =0
long getImagesMemoryUsed() const
bool openConnection(const std::string &url, bool overwritten=false)
int getLastNodesSize() const
virtual void save2DMapQuery(const cv::Mat &map, float xMin, float yMin, float cellSize) const =0
virtual void loadSignaturesQuery(const std::list< int > &ids, std::list< Signature * > &signatures) const =0
virtual void getLastIdQuery(const std::string &tableName, int &id, const std::string &fieldName="id") const =0
void addLink(const Link &link)
virtual int getLastNodesSizeQuery() const =0
virtual long getGridsMemoryUsedQuery() const =0
std::map< int, Signature * > _trashSignatures
virtual void updateCalibrationQuery(int nodeId, const std::vector< CameraModel > &models, const std::vector< StereoCameraModel > &stereoModels) const =0
virtual std::map< int, std::vector< int > > getAllStatisticsWmStatesQuery() const =0
Signature * loadSignature(int id, bool *loadedFromTrash=0)
virtual bool getCalibrationQuery(int signatureId, std::vector< CameraModel > &models, std::vector< StereoCameraModel > &stereoModels) const =0
virtual void getAllOdomPosesQuery(std::map< int, Transform > &poses, bool ignoreChildren, bool ignoreIntermediateNodes) const =0
virtual void updateDepthImageQuery(int nodeId, const cv::Mat &image, const std::string &format) const =0
void join(bool killFirst=false)
virtual ParametersMap getLastParametersQuery() const =0
std::map< int, std::vector< int > > getAllStatisticsWmStates() const
void executeNoResult(const std::string &sql) const
virtual void updateLaserScanQuery(int nodeId, const LaserScan &scan) const =0
void updateOccupancyGrid(int nodeId, const cv::Mat &ground, const cv::Mat &obstacles, const cv::Mat &empty, float cellSize, const cv::Point3f &viewpoint)
virtual cv::Mat loadOptimizedMeshQuery(std::vector< std::vector< std::vector< RTABMAP_PCL_INDEX > > > *polygons, std::vector< std::vector< Eigen::Vector2f > > *texCoords, cv::Mat *textures) const =0
virtual long getDepthImagesMemoryUsedQuery() const =0
virtual void addStatisticsQuery(const Statistics &statistics, bool saveWmState) const =0
void loadNodeData(Signature *signature, bool images=true, bool scan=true, bool userData=true, bool occupancyGrid=true) const
std::map< std::string, float > getStatistics(int nodeId, double &stamp, std::vector< int > *wmState=0) const
virtual void addLinkQuery(const Link &link) const =0
std::map< std::string, std::string > ParametersMap
void loadLastNodes(std::list< Signature * > &signatures) const
virtual void loadQuery(VWDictionary *dictionary, bool lastStateOnly=true) const =0
#define ULOGGER_DEBUG(...)
long getUserDataMemoryUsed() const
Basic mathematics functions.
virtual void loadNodeDataQuery(std::list< Signature * > &signatures, bool images=true, bool scan=true, bool userData=true, bool occupancyGrid=true) const =0
virtual long getImagesMemoryUsedQuery() const =0
long getDepthImagesMemoryUsed() const
static bool parse(const ParametersMap ¶meters, const std::string &key, bool &value)
void save2DMap(const cv::Mat &map, float xMin, float yMin, float cellSize) const
virtual void savePreviewImageQuery(const cv::Mat &image) const =0
virtual bool connectDatabaseQuery(const std::string &url, bool overwritten=false)=0
std::vector< std::string > labels
virtual std::map< int, std::pair< std::map< std::string, float >, double > > getAllStatisticsQuery() const =0
int getLastDictionarySize() const
virtual void updateLinkQuery(const Link &link) const =0
virtual long getUserDataMemoryUsedQuery() const =0
bool uContains(const std::list< V > &list, const V &value)
virtual long getWordsMemoryUsedQuery() const =0
int getTotalNodesSize() const
long getStatisticsMemoryUsed() const
std::vector< K > uKeys(const std::multimap< K, V > &mm)
virtual void parseParameters(const ParametersMap ¶meters)
int uStrNumCmp(const std::string &a, const std::string &b)
void getLastMapId(int &mapId) const
virtual void loadLinksQuery(int signatureId, std::multimap< int, Link > &links, Link::Type type=Link::kUndef) const =0
Some conversion functions.
virtual void getWeightQuery(int signatureId, int &weight) const =0
cv::Mat loadPreviewImage() const
void updateCalibration(int nodeId, const std::vector< CameraModel > &models, const std::vector< StereoCameraModel > &stereoModels)
void getLastWordId(int &id) const
virtual long getNodesMemoryUsedQuery() const =0
Velocity3 velocity(const NavState &X, OptionalJacobian< 3, 9 > H)
virtual void getAllLabelsQuery(std::map< int, std::string > &labels) const =0
std::string _targetVersion
bool getNodeInfo(int signatureId, Transform &pose, int &mapId, int &weight, std::string &label, double &stamp, Transform &groundTruthPose, std::vector< float > &velocity, GPS &gps, EnvSensors &sensors) const
virtual void getNodesObservingLandmarkQuery(int landmarkId, std::map< int, Link > &nodes) const =0
virtual long getFeaturesMemoryUsedQuery() const =0
virtual unsigned long getMemoryUsedQuery() const =0
#define UASSERT(condition)
std::string getDatabaseVersion() const
V uValue(const std::map< K, V > &m, const K &key, const V &defaultValue=V())
void getNodeIdByLabel(const std::string &label, int &id) const
static std::string serialize(const ParametersMap ¶meters)
bool getLaserScanInfo(int signatureId, LaserScan &info) const
std::map< int, std::pair< std::map< std::string, float >, double > > getAllStatistics() const
long getWordsMemoryUsed() const
virtual int getLastDictionarySizeQuery() const =0
void uAppend(std::list< V > &list, const std::list< V > &newItems)
long getCalibrationsMemoryUsed() const
virtual void executeNoResultQuery(const std::string &sql) const =0
void emptyTrashes(bool async=false)
void getAllLabels(std::map< int, std::string > &labels) const
void loadLinks(int signatureId, std::multimap< int, Link > &links, Link::Type type=Link::kUndef) const
#define UASSERT_MSG(condition, msg_str)
void getLastNodeIds(std::set< int > &ids) const
long getLinksMemoryUsed() const
std::string UTILITE_EXPORT uFormat(const char *fmt,...)
void removeLink(int from, int to)
ULogger class and convenient macros.
virtual cv::Mat loadPreviewImageQuery() const =0
unsigned long getMemoryUsed() const
void getAllOdomPoses(std::map< int, Transform > &poses, bool ignoreChildren=false, bool ignoreIntermediateNodes=false) const
std::map< EnvSensor::Type, EnvSensor > EnvSensors
virtual long getLinksMemoryUsedQuery() const =0
virtual void updateQuery(const std::list< Signature * > &signatures, bool updateTimestamp) const =0
void getWeight(int signatureId, int &weight) const
void saveOrUpdate(const std::vector< Signature * > &signatures)
virtual void updateOccupancyGridQuery(int nodeId, const cv::Mat &ground, const cv::Mat &obstacles, const cv::Mat &empty, float cellSize, const cv::Point3f &viewpoint) const =0
UMutex _dbSafeAccessMutex
virtual long getCalibrationsMemoryUsedQuery() const =0
virtual bool getDatabaseVersionQuery(std::string &version) const =0
virtual void saveOptimizedPosesQuery(const std::map< int, Transform > &optimizedPoses, const Transform &lastlocalizationPose) const =0
virtual void getAllLinksQuery(std::multimap< int, Link > &links, bool ignoreNullLinks, bool withLandmarks) const =0
iterator iter(handle obj)
cv::Mat loadOptimizedMesh(std::vector< std::vector< std::vector< RTABMAP_PCL_INDEX > > > *polygons=0, std::vector< std::vector< Eigen::Vector2f > > *texCoords=0, cv::Mat *textures=0) const
virtual void disconnectDatabaseQuery(bool save=true, const std::string &outputUrl="")=0
void addInfoAfterRun(int stMemSize, int lastSignAdded, int processMemUsed, int databaseMemUsed, int dictionarySize, const ParametersMap ¶meters) const
const char * c_str(Args &&...args)
Wrappers of STL for convenient functions.
void asyncSave(Signature *s)
long getFeaturesMemoryUsed() const
cv::Mat load2DMap(float &xMin, float &yMin, float &cellSize) const
virtual void getLastNodeIdsQuery(std::set< int > &ids) const =0
virtual std::map< int, Transform > loadOptimizedPosesQuery(Transform *lastlocalizationPose=0) const =0
virtual bool getNodeInfoQuery(int signatureId, Transform &pose, int &mapId, int &weight, std::string &label, double &stamp, Transform &groundTruthPose, std::vector< float > &velocity, GPS &gps, EnvSensors &sensors) const =0
virtual void loadLastNodesQuery(std::list< Signature * > &signatures) const =0
void getLastNodeId(int &id) const
void updateLaserScan(int nodeId, const LaserScan &scan)
long getLaserScansMemoryUsed() const
void updateDepthImage(int nodeId, const cv::Mat &image, const std::string &format)
void load(VWDictionary *dictionary, bool lastStateOnly=true) const
void getAllNodeIds(std::set< int > &ids, bool ignoreChildren=false, bool ignoreBadSignatures=false, bool ignoreIntermediateNodes=false) const
void savePreviewImage(const cv::Mat &image) const
virtual bool getLaserScanInfoQuery(int signatureId, LaserScan &info) const =0
std::vector< V > uValues(const std::multimap< K, V > &mm)
void generateGraph(const std::string &fileName, const std::set< int > &ids=std::set< int >(), const std::map< int, Signature * > &otherSignatures=std::map< int, Signature * >())
void saveOptimizedPoses(const std::map< int, Transform > &optimizedPoses, const Transform &lastlocalizationPose) const
virtual void getInvertedIndexNiQuery(int signatureId, int &ni) const =0
virtual cv::Mat load2DMapQuery(float &xMin, float &yMin, float &cellSize) const =0
virtual void getNodeIdByLabelQuery(const std::string &label, int &id) const =0
int getTotalDictionarySize() const
virtual void loadWordsQuery(const std::set< int > &wordIds, std::list< VisualWord * > &vws) const =0
void getInvertedIndexNi(int signatureId, int &ni) const
virtual void saveQuery(const std::list< Signature * > &signatures)=0
long getGridsMemoryUsed() const
virtual std::map< std::string, float > getStatisticsQuery(int nodeId, double &stamp, std::vector< int > *wmState) const =0
virtual void getAllNodeIdsQuery(std::set< int > &ids, bool ignoreChildren, bool ignoreBadSignatures, bool ignoreIntermediateNodes) const =0
virtual void saveOptimizedMeshQuery(const cv::Mat &cloud, const std::vector< std::vector< std::vector< RTABMAP_PCL_INDEX > > > &polygons, const std::vector< std::vector< Eigen::Vector2f > > &texCoords, const cv::Mat &textures) const =0
void loadSignatures(const std::list< int > &ids, std::list< Signature * > &signatures, std::set< int > *loadedFromTrash=0)
void getAllLinks(std::multimap< int, Link > &links, bool ignoreNullLinks=true, bool withLandmarks=false) const
virtual long getLaserScansMemoryUsedQuery() const =0
void closeConnection(bool save=true, const std::string &outputUrl="")
void saveOptimizedMesh(const cv::Mat &cloud, const std::vector< std::vector< std::vector< RTABMAP_PCL_INDEX > > > &polygons=std::vector< std::vector< std::vector< RTABMAP_PCL_INDEX > > >(), const std::vector< std::vector< Eigen::Vector2f > > &texCoords=std::vector< std::vector< Eigen::Vector2f > >(), const cv::Mat &textures=cv::Mat()) const
static DBDriver * create(const ParametersMap ¶meters=ParametersMap())
rtabmap
Author(s): Mathieu Labbe
autogenerated on Thu Feb 13 2025 03:44:52