434 lines
10 KiB
C++
434 lines
10 KiB
C++
#include "stdafx.h"
|
||
#include "CEXMemFileQueue.h"
|
||
#include <sys/timeb.h>
|
||
#include <iostream>
|
||
#include <iomanip>
|
||
#include <sstream>
|
||
#include <windows.h>
|
||
#include <algorithm>
|
||
#include <codecvt>
|
||
#include <regex>
|
||
#include <string>
|
||
|
||
|
||
// 获取精确到秒的时间字符串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<std::string>& 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<std::string> 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<std::string> CEXMemFileQueue::GetDataList()
|
||
{
|
||
std::vector<std::string> vecDir;
|
||
traverse_directory_ansi(m_strBaseDir.c_str(), vecDir, 2);
|
||
|
||
std::vector<std::string> 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<std::string> vecDate = GetDataList();
|
||
for (int i = 0; i < ((int)vecDate.size()) - 5; i++)
|
||
{
|
||
DeleteDirectory(m_strBaseDir + "\\" + vecDate[i]);
|
||
}
|
||
}
|