fast/Plugin/Plc/CEXMemFileQueue.cpp
2025-01-20 10:30:01 +08:00

434 lines
10 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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]);
}
}