#include "stdafx.h" #include "CEXMemFileQueue.h" #include #include #include #include #include #include #include #include #include // 获取精确到秒的时间字符串YYYYMMDDhhmmssSSS std::string formatTimeAsString(int lIndexEx) { SYSTEMTIME st; GetLocalTime(&st); std::ostringstream oss; // 年份 oss << std::setw(4) << std::setfill('0') << (st.wYear % 10000); // 月份和日期 oss << std::setw(2) << std::setfill('0') << st.wMonth; oss << std::setw(2) << std::setfill('0') << st.wDay; // 小时、分钟和秒 oss << std::setw(2) << std::setfill('0') << st.wHour; oss << std::setw(2) << std::setfill('0') << st.wMinute; oss << std::setw(2) << std::setfill('0') << st.wSecond; //__timeb64 tb; //_ftime64_s(&tb); // 获取当前时间,包括毫秒 //oss << std::setw(3) << std::setfill('0') << tb.millitm; // 毫秒,三位数,前面补零 oss << std::setw(6) << std::setfill('0') << lIndexEx; // 防止文件名冲突,增加一个索引 return oss.str(); } //获取一个指定目录下所有的文件,按文件名排序 lType表示获取类型,1仅文件,2仅文件夹,0所有 static void traverse_directory_ansi(const char* directory_path, std::vector& files, int lType) { char search_path[MAX_PATH]; strcpy_s(search_path, directory_path); strcat_s(search_path, "\\*"); WIN32_FIND_DATAA find_data; HANDLE hFind = FindFirstFileA(search_path, &find_data); if (hFind == INVALID_HANDLE_VALUE) { std::cerr << "Failed to open directory: " << directory_path << std::endl; return; } do { if (strcmp(find_data.cFileName, ".") == 0 || strcmp(find_data.cFileName, "..") == 0) { continue; } char full_path[MAX_PATH]; strcpy_s(full_path, directory_path); strcat_s(full_path, "\\"); strcat_s(full_path, find_data.cFileName); if (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { //暂不支持递归子文件夹 if (1 != lType) files.push_back(find_data.cFileName); } else { if (2 != lType) files.push_back(find_data.cFileName); } } while (FindNextFileA(hFind, &find_data) != 0); FindClose(hFind); // 按文件名排序 std::sort(files.begin(), files.end()); } CEXMemFileQueue::CEXMemFileQueue(std::string strDataDir) { m_strBaseDir = strDataDir; m_pWriteFileItem = NULL; m_pReadFileItem = NULL; m_lReadIndex = 0; m_lItemCount = 0; InitializeCriticalSection(&m_cs); SYSTEMTIME stTime; GetLocalTime(&stTime); ChangeDataDate(stTime); } CEXMemFileQueue::~CEXMemFileQueue() { for (int i = 0; i < (int)m_vecFile.size(); i++) { delete m_vecFile[i]; } m_vecFile.clear(); } int CEXMemFileQueue::GetItemCount() { return m_lItemCount; } int CEXMemFileQueue::GetAutoSn() { ThreadLock stLock(&m_cs); m_lAutoSn++; return m_lAutoSn; } int CEXMemFileQueue::ReadItem(void* pData, int lIndex /* = -1 */) { ThreadLock stLock(&m_cs); if (lIndex > m_lItemCount) { return -1; } if (lIndex >= 0) { m_lReadIndex = lIndex; } else { m_lReadIndex++; } if (m_lReadIndex >= m_lItemCount) { return -1;//读完了 } if (NULL == m_pReadFileItem || m_lReadIndex < m_pReadFileItem->lBeindIndex || m_lReadIndex > m_pReadFileItem->lEndIndex) { m_pReadFileItem = NULL; //重新寻找 for (int i = 0; i < (int)m_vecFile.size(); i++) { if (m_lReadIndex >= m_vecFile[i]->lBeindIndex && m_lReadIndex <= m_vecFile[i]->lEndIndex) { m_pReadFileItem = m_vecFile[i]; break; } } } if (NULL == m_pReadFileItem) { return -1; } memcpy(pData, (char*)m_pReadFileItem->pBuf + DATA_FIRST_LINE_SIZE + DATA_LINE_SIZE *(m_lReadIndex- m_pReadFileItem->lBeindIndex), DATA_LINE_SIZE); return m_lReadIndex; } void CEXMemFileQueue::FlushWriteFile() { ThreadLock stLock(&m_cs); if (NULL != m_pWriteFileItem) { char acFirstLine[DATA_FIRST_LINE_SIZE+1] = { 0 }; sprintf_s(acFirstLine, "%-15d\n", m_pWriteFileItem->lFileSize); memcpy((char*)m_pWriteFileItem->pBuf, acFirstLine, DATA_FIRST_LINE_SIZE); FlushViewOfFile(m_pWriteFileItem->pBuf, m_pWriteFileItem->lFileSize); } } int CEXMemFileQueue::WriteItem(void* pData) { ThreadLock stLock(&m_cs); //判断日期是否更改 SYSTEMTIME stDate; GetLocalTime(&stDate); if (stDate.wYear != m_stDataDate.wYear || stDate.wMonth != m_stDataDate.wMonth || stDate.wDay != m_stDataDate.wDay) { AutoCleanData(5); ChangeDataDate(stDate); } if (m_pWriteFileItem == NULL || m_pWriteFileItem->lFileSize + DATA_LINE_SIZE > DATA_FILE_SIZE) { if (m_pWriteFileItem != NULL) { m_pWriteFileItem->lEndIndex = m_lItemCount-1; FlushWriteFile(); } m_pWriteFileItem = new CEX_QUEUE_FILE_ITEM; m_pWriteFileItem->lBeindIndex = m_lItemCount; m_pWriteFileItem->strFileName = m_strDataDir + "\\" + formatTimeAsString((int)m_vecFile.size()) + ".txt";; // 打开文件,用于读写和共享 m_pWriteFileItem->hFile = CreateFileA( m_pWriteFileItem->strFileName.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr ); if (m_pWriteFileItem->hFile == INVALID_HANDLE_VALUE) { TRACE("CreateFile failed\r\n"); delete m_pWriteFileItem; m_pWriteFileItem = NULL; return -1; } // 创建文件映射对象------第一行固定占用16字节记录文件实际大小 m_pWriteFileItem->hMapFile = CreateFileMappingA(m_pWriteFileItem->hFile,nullptr,PAGE_READWRITE, 0,DATA_FILE_SIZE+16,nullptr); if (m_pWriteFileItem->hMapFile == nullptr) { TRACE("CreateFileMapping failed\r\n"); delete m_pWriteFileItem; m_pWriteFileItem = NULL; return -2; } // 将文件内容映射到进程的地址空间 m_pWriteFileItem->pBuf = MapViewOfFile( m_pWriteFileItem->hMapFile,FILE_MAP_ALL_ACCESS,0, 0,DATA_FILE_SIZE+16); if (m_pWriteFileItem->pBuf == nullptr) { TRACE("MapViewOfFile failed\r\n"); delete m_pWriteFileItem; m_pWriteFileItem = NULL; return -3; } FlushWriteFile();//初始创建好文件,先保存一下,写入文件头 m_vecFile.push_back(m_pWriteFileItem); } //写入数据 memcpy((char*)m_pWriteFileItem->pBuf + m_pWriteFileItem->lFileSize, pData, DATA_LINE_SIZE); m_pWriteFileItem->lFileSize += DATA_LINE_SIZE; m_pWriteFileItem->lEndIndex = m_lItemCount; m_lItemCount++; //实时更新文件有效长度 char acFirstLine[DATA_FIRST_LINE_SIZE + 1] = { 0 }; sprintf_s(acFirstLine, "%-15d\n", m_pWriteFileItem->lFileSize); memcpy((char*)m_pWriteFileItem->pBuf, acFirstLine, DATA_FIRST_LINE_SIZE); return 0; } //切换当前日期 void CEXMemFileQueue::ChangeDataDate(SYSTEMTIME stDate) { ThreadLock stLock(&m_cs); //首先清除原数据 FlushWriteFile(); m_pReadFileItem = NULL; m_pWriteFileItem = NULL; for (int i = 0; i < (int)m_vecFile.size(); i++) { delete m_vecFile[i]; } m_vecFile.clear(); m_lItemCount = 0; //更新路径 m_stDataDate = stDate; char acDate[32] = { 0 }; sprintf_s(acDate, "%04d-%02d-%02d", m_stDataDate.wYear, m_stDataDate.wMonth, m_stDataDate.wDay); m_strDataDir = m_strBaseDir + "\\" + acDate; CreateDirectory(m_strDataDir.c_str(), NULL); //遍历文件夹,然后文件排序 std::vector files; traverse_directory_ansi(m_strDataDir.c_str(), files, 1); for (int i = 0; i < (int)files.size(); i++) { CEX_QUEUE_FILE_ITEM* pTempItem = new CEX_QUEUE_FILE_ITEM; pTempItem->lBeindIndex = m_lItemCount; pTempItem->strFileName = m_strDataDir + "\\" + files[i]; // 打开文件,用于读写和共享 pTempItem->hFile = CreateFileA( pTempItem->strFileName.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr ); if (pTempItem->hFile == INVALID_HANDLE_VALUE) { TRACE("CreateFile failed\r\n"); delete pTempItem; pTempItem = NULL; break; } // 创建文件映射对象------第一行固定占用16字节记录文件实际大小 pTempItem->hMapFile = CreateFileMappingA(pTempItem->hFile, nullptr, PAGE_READWRITE, 0, DATA_FILE_SIZE + 16, nullptr); if (pTempItem->hMapFile == nullptr) { TRACE("CreateFileMapping failed\r\n"); delete pTempItem; pTempItem = NULL; break; } // 将文件内容映射到进程的地址空间 pTempItem->pBuf = MapViewOfFile(pTempItem->hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, DATA_FILE_SIZE + 16); if (pTempItem->pBuf == nullptr) { TRACE("MapViewOfFile failed\r\n"); delete pTempItem; pTempItem = NULL; break; } pTempItem->lFileSize = atoi((char*)pTempItem->pBuf); m_lItemCount += (pTempItem->lFileSize - DATA_FIRST_LINE_SIZE) / DATA_LINE_SIZE; pTempItem->lEndIndex = m_lItemCount - 1; m_vecFile.push_back(pTempItem); } m_lAutoSn = m_lItemCount; if (m_vecFile.size() > 0) { m_pWriteFileItem = m_vecFile[m_vecFile.size() - 1]; } } static bool isValidDateFormat(const std::string& date) { // 正则表达式用于匹配日期格式 %04d-%02d-%02d std::regex datePattern(R"((\d{4})\-(\d{2})-(\d{2}))"); //std::regex datePattern(R"(\d{4}-\d{2}-\d{2})"); std::smatch match; // 尝试匹配日期字符串 if (std::regex_match(date, match, datePattern)) { // 提取年、月、日 int year = std::stoi(match[1].str()); int month = std::stoi(match[2].str()); int day = std::stoi(match[3].str()); // 检查日期的有效性(例如:月份在1到12之间,天数在1到31之间等) if (month < 1 || month > 12 || day < 1 || day > 31) { return false; } return true; } return false; } static bool DeleteDirectory(const std::string& dirPath) { WIN32_FIND_DATA findFileData; std::string searchPath = dirPath + _T("\\*"); HANDLE hFind = FindFirstFile(searchPath.c_str(), &findFileData); if (hFind == INVALID_HANDLE_VALUE) { return false; } do { std::string filePath; if (strcmp(findFileData.cFileName, _T(".")) == 0 || strcmp(findFileData.cFileName, _T("..")) == 0) { continue; } filePath = dirPath + _T("\\") + findFileData.cFileName; if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if (!DeleteDirectory(filePath)) { FindClose(hFind); return false; } } else { if (!DeleteFile(filePath.c_str())) { FindClose(hFind); return false; } } } while (FindNextFile(hFind, &findFileData) != 0); FindClose(hFind); return RemoveDirectory(dirPath.c_str()) != 0; } //获取有数据的日期列表 std::vector CEXMemFileQueue::GetDataList() { std::vector vecDir; traverse_directory_ansi(m_strBaseDir.c_str(), vecDir, 2); std::vector vecDate; //格式检查 for (int i = 0; i < (int)vecDir.size(); i++) { if (isValidDateFormat(vecDir[i].c_str())) { vecDate.push_back(vecDir[i].c_str()); } } return vecDate; } //自动清理历史数据,lRecvDay为保留的天数 void CEXMemFileQueue::AutoCleanData(int lRecvDay) { ThreadLock stLock(&m_cs); std::vector vecDate = GetDataList(); for (int i = 0; i < ((int)vecDate.size()) - 5; i++) { DeleteDirectory(m_strBaseDir + "\\" + vecDate[i]); } }