diff --git a/AlgorithmModule/include/CheckUtil.hpp b/AlgorithmModule/include/CheckUtil.hpp index aa1fe60..e73edac 100644 --- a/AlgorithmModule/include/CheckUtil.hpp +++ b/AlgorithmModule/include/CheckUtil.hpp @@ -57,6 +57,10 @@ public: static std::string getCurTimeHMS(); static std::string Op_float2String(float nvalue); static int64_t getSnowId(); + // 当前日期(年月日, 8 位纯数字), 形如 "20261029", 便于存图文件名检索/排序 + static std::string getCurrentDateYMD(); + // 生成不重名的存图完整路径 = strDir + strPrefix + [ "_序号" ] + "_" + strName + strExt + static std::string GetUniqueImgPath(const std::string &strDir, const std::string &strPrefix, const std::string &strName, const std::string &strExt); static bool JudgRect(cv::Rect roi, int img_w, int img_h); static bool JudgRect_SZ(cv::Rect roi, int w, int h); static bool compareIgnoreCase(const std::string &str1, const std::string &str2); diff --git a/AlgorithmModule/src/CheckUtil.cpp b/AlgorithmModule/src/CheckUtil.cpp index 5168829..c86dfac 100644 --- a/AlgorithmModule/src/CheckUtil.cpp +++ b/AlgorithmModule/src/CheckUtil.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include "snowflake.hpp" int _sysmkdir_2(const std::string &dir) @@ -69,8 +70,10 @@ std::string CheckUtil::getCurrentDate() // 将 tv_sec 转换为 time_t 类型,代表自1970年1月1日起的秒数 time_t raw_time = tv.tv_sec; - // 使用 localtime 将 time_t 转换为 tm 结构体 - struct tm *time_info = localtime(&raw_time); + // 使用 localtime_r 转换为 tm 结构体。 + // 注意:localtime() 返回共享的静态缓冲区,多线程并发调用会互相覆盖(tm 内容串数据)。 + struct tm time_buf; + struct tm *time_info = localtime_r(&raw_time, &time_buf); // 使用字符串流格式化为 "YYYY-MM-DD" std::ostringstream oss; @@ -80,13 +83,34 @@ std::string CheckUtil::getCurrentDate() return oss.str(); // 返回日期字符串 } +std::string CheckUtil::getCurrentDateYMD() +{ + struct timeval tv; + gettimeofday(&tv, NULL); + + time_t raw_time = tv.tv_sec; + // localtime_r 为线程安全版本 + struct tm time_buf; + struct tm *time_info = localtime_r(&raw_time, &time_buf); + + // 固定 8 位 "YYYYMMDD", 便于文件名检索与排序 + std::ostringstream oss; + oss << std::setfill('0') + << std::setw(4) << (time_info->tm_year + 1900) + << std::setw(2) << (time_info->tm_mon + 1) + << std::setw(2) << time_info->tm_mday; + + return oss.str(); +} std::string CheckUtil::getCurTimeHMS() { struct timeval tv; gettimeofday(&tv, NULL); // 获取当前时间 time_t raw_time = tv.tv_sec; - struct tm *time_info = localtime(&raw_time); + // 使用 localtime_r(线程安全);localtime() 的静态缓冲会被其它线程并发覆盖 + struct tm time_buf; + struct tm *time_info = localtime_r(&raw_time, &time_buf); int millisec = tv.tv_usec / 1000; // 微秒转毫秒 @@ -120,6 +144,40 @@ int64_t CheckUtil::getSnowId() return 0; } +std::string CheckUtil::GetUniqueImgPath(const std::string &strDir, const std::string &strPrefix, const std::string &strName, const std::string &strExt) +{ + if (strDir.empty()) + { + return strDir + strPrefix + strName + strExt; + } + // 目录不存在时 O_CREAT 会失败, 先建目录(已存在时内部无副作用) + CreateDir(strDir); + + // 去重序号插在前缀之后: <年月日>[_序号]_<通道>_<用途>.png + // 同一产品的每个 strName 每轮只会写一次, 所以各文件序号天然同步: + // 第 1 轮 -> <年月日>_<名称>.png、第 2 轮 -> <年月日>_1_<名称>.png、第 3 轮 -> <年月日>_2_<名称>.png ... + const std::string strHead = strPrefix.empty() ? std::string("") : (strPrefix + "_"); + for (int i = 0; i < 100000; i++) + { + std::string strPath = strDir + strHead + (i == 0 ? std::string("") : std::to_string(i) + "_") + strName + strExt; + // O_EXCL: 文件不存在才能创建成功, 即是"原子占位", 并发安全 + int fd = ::open(strPath.c_str(), O_CREAT | O_EXCL | O_WRONLY, 0644); + if (fd >= 0) + { + ::close(fd); + return strPath; + } + if (errno != EEXIST) + { + // 非"已存在"类错误(权限/路径等)不再重试, 直接返回让 imwrite 把错误暴露出来 + printf("create img file[%s] error: %d %s\n", strPath.c_str(), errno, strerror(errno)); + return strPath; + } + } + // 序号用尽的兜底(实际不会发生) + return strDir + strHead + std::to_string(CheckUtil::getcurTime()) + "_" + strName + strExt; +} + bool CheckUtil::JudgRect(cv::Rect roi, int img_w, int img_h) { if (img_w <= 0 || img_h <= 0) diff --git a/AlgorithmModule/src/ImgCheckAnalysisy.cpp b/AlgorithmModule/src/ImgCheckAnalysisy.cpp index 3dae6e4..11cdad6 100644 --- a/AlgorithmModule/src/ImgCheckAnalysisy.cpp +++ b/AlgorithmModule/src/ImgCheckAnalysisy.cpp @@ -3366,8 +3366,6 @@ int ImgCheckAnalysisy::ConfigCheck(cv::Mat img) int ImgCheckAnalysisy::AI_Detect_Thread(const cv::Mat &img, cv::Mat &ResultImg) { - std::string cur_time_s = CheckUtil::getCurTimeHMS(); - std::shared_ptr pAIDet; // printf("=====>>>>AI_Detect_Thread m_strCurDetChannel %s \n", m_strCurDetChannel.c_str()); bool bUseDarkModel = false; @@ -3477,10 +3475,10 @@ int ImgCheckAnalysisy::AI_Detect_Thread(const cv::Mat &img, cv::Mat &ResultImg) if (m_pbaseCheckFunction->saveImg.bSaveAIDetImg && !ResultImg.empty()) { - std::string strSaveDir = "/home/aidlux/BOE/AI_Detect/" + DetImgInfo_shareP->strImgProductID + "/"; - CheckUtil::CreateDir(strSaveDir); - std::string strSavePath_in = strSaveDir + cur_time_s + "_" + strCameraName + "_in.png"; - std::string strSavePath_out = strSaveDir + cur_time_s + "_" + strCameraName + "_out.png"; + std::string strDatePre = CheckUtil::getCurrentDateYMD(); + std::string strSaveDir = "/home/aidlux/BOE/AI_Detect/" + DetImgInfo_shareP->strImgProductID + "/BM/"; + std::string strSavePath_in = CheckUtil::GetUniqueImgPath(strSaveDir, strDatePre, strCameraName, "_in.png"); + std::string strSavePath_out = CheckUtil::GetUniqueImgPath(strSaveDir, strDatePre, strCameraName, "_out.png"); CheckUtil::SaveImgAsync(strSavePath_in, m_CheckResult_shareP->cutSrcimg); CheckUtil::SaveImgAsync(strSavePath_out, ResultImg); } @@ -4305,10 +4303,9 @@ int ImgCheckAnalysisy::CheckTagByBigMask(const cv::Mat &img, const cv::Mat &bigm if (m_pbaseCheckFunction->saveImg.bSaveBQDetImg) { - std::string cur_time_s = CheckUtil::getCurTimeHMS(); - std::string strSaveDir = "/home/aidlux/BOE/AI_Detect/" + DetImgInfo_shareP->strImgProductID + "/"; - CheckUtil::CreateDir(strSaveDir); - std::string strSavePath_out = strSaveDir + cur_time_s + "_" + DetImgInfo_shareP->strCameraName + "_BQ_out.png"; + std::string strDatePre = CheckUtil::getCurrentDateYMD(); + std::string strSaveDir = "/home/aidlux/BOE/AI_Detect/" + DetImgInfo_shareP->strImgProductID + "/BM/"; + std::string strSavePath_out = CheckUtil::GetUniqueImgPath(strSaveDir, strDatePre, DetImgInfo_shareP->strCameraName, "_BQ_out.png"); CheckUtil::SaveImgAsync(strSavePath_out, mask); }