Files
front_linux/LFtid1056/pqdif_thread_processor.cpp

726 lines
23 KiB
C++
Raw Normal View History

#include "pqdif_thread_processor.h"
#include <algorithm>
#include <chrono>
#include <thread>
#include <cctype>
#include <cmath>
#include <ctime>
#include <deque>
#include <iostream>
#include <mutex>
#include <sstream>
#include <string>
#include <vector>
#include <experimental/filesystem>
#include <fstream>
#include <iomanip>
#include <cstdio>
// PQDIF <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
#include "pqdif/PQDIF.h"
#include "pqdif/include/pqdif_ph.h"
#include "pqdif/include/pqdif_id.h"
namespace fs = std::experimental::filesystem;
namespace {
// ============================
// <20><><EFBFBD><EFBFBD><EFBFBD>ò<EFBFBD><C3B2><EFBFBD>
// ============================
constexpr int kScanIntervalSec = 60; // ɨ<><C9A8>Ŀ¼<C4BF><C2BC><EFBFBD><EFBFBD>
constexpr int kBackupLimit = 4800; // <20>ɹ<EFBFBD><><CAA7>Ŀ¼<C4BF><C2BC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD>
constexpr size_t kParsedCacheLimit = 128; // <20>ڴ滺<DAB4><E6BBBA><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
const char* kPqdRootDir = "download"; // <20><><EFBFBD><EFBFBD>Ŀ¼<C4BF><C2BC>download/<mac>/*.pqd
const char* kDoneRootDir = "download_done";
const char* kFailRootDir = "download_fail";
// ============================
// <20>ڴ滺<DAB4><E6BBBA>
// ============================
std::deque<ParsedPqdifFile> g_parsed_cache;
std::mutex g_parsed_cache_mutex;
// ============================
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ߺ<EFBFBD><DFBA><EFBFBD>
// ============================
// ============================
// <20><><EFBFBD>Դ<EFBFBD>ӡ<EFBFBD><D3A1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
// <20><><EFBFBD>ã<EFBFBD><C3A3><EFBFBD><EFBFBD>ݴ浽<DDB4>ڴ<EFBFBD><DAB4><EFBFBD><EFBFBD><EFBFBD> PQDIF <20><><EFBFBD>ݴ<EFBFBD>ӡ<EFBFBD><D3A1><EFBFBD><EFBFBD>
// ============================
// GUID ת<>ַ<EFBFBD><D6B7><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڲ鿴<DAB2><E9BFB4>ǩֵ
std::string guid_to_string(const GUID& g)
{
char buf[64] = { 0 };
std::snprintf(buf, sizeof(buf),
"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
static_cast<unsigned int>(g.Data1),
static_cast<unsigned int>(g.Data2),
static_cast<unsigned int>(g.Data3),
static_cast<unsigned int>(g.Data4[0]),
static_cast<unsigned int>(g.Data4[1]),
static_cast<unsigned int>(g.Data4[2]),
static_cast<unsigned int>(g.Data4[3]),
static_cast<unsigned int>(g.Data4[4]),
static_cast<unsigned int>(g.Data4[5]),
static_cast<unsigned int>(g.Data4[6]),
static_cast<unsigned int>(g.Data4[7]));
return std::string(buf);
}
// <20><>ӡ<EFBFBD><D3A1>ֵ<EFBFBD><D6B5><EFBFBD><EFBFBD>ǰ<EFBFBD><C7B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>־<EFBFBD><D6BE><EFBFBD><EFBFBD>
void print_value_preview(const std::vector<double>& values, const char* title, size_t preview_count = 5)
{
if (values.empty())
return;
std::cout << " " << title << " count=" << values.size() << " preview=[";
const size_t n = std::min(values.size(), preview_count);
for (size_t i = 0; i < n; ++i)
{
if (i > 0)
std::cout << ", ";
std::cout << values[i];
}
if (values.size() > preview_count)
std::cout << ", ...";
std::cout << "]" << std::endl;
}
// <20><>ӡʱ<D3A1><CAB1><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǰ<EFBFBD><C7B0><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
void print_time_preview(const std::vector<time_t>& values, const char* title, size_t preview_count = 5)
{
if (values.empty())
return;
std::cout << " " << title << " count=" << values.size() << " preview=[";
const size_t n = std::min(values.size(), preview_count);
for (size_t i = 0; i < n; ++i)
{
if (i > 0)
std::cout << ", ";
std::cout << static_cast<long long>(values[i]);
}
if (values.size() > preview_count)
std::cout << ", ...";
std::cout << "]" << std::endl;
}
// <20><>ӡijһ<C4B3><D2BB> series <20>ı<EFBFBD>ǩ
void print_series_meta(const RawSeriesTagMeta& meta, const char* name)
{
std::cout << " [" << name << "]"
<< " units_id=" << meta.quantity_units_id
<< " characteristic_id=" << guid_to_string(meta.quantity_characteristic_id)
<< " value_type_id=" << guid_to_string(meta.value_type_id)
<< " base_type=" << meta.series_base_type
<< " scale=" << meta.series_scale
<< " offset=" << meta.series_offset
<< std::endl;
}
// <20><>ӡ<EFBFBD><D3A1><EFBFBD><EFBFBD>ͨ<EFBFBD><CDA8><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
void dump_channel_detail(const std::string& key, const RawChannelSeries& ch)
{
std::cout << " [CHANNEL]"
<< " key=" << key
<< " raw_name=" << ch.channel_tag.raw_channel_name
<< " phase_id=" << ch.channel_tag.phase_id
<< " quantity_type_id=" << guid_to_string(ch.channel_tag.quantity_type_id)
<< " quantity_measured_id=" << ch.channel_tag.quantity_measured_id
<< " freq=" << ch.channel_tag.channel_frequency
<< " group_id=" << ch.channel_tag.group_id
<< std::endl;
if (!ch.times.empty())
{
print_series_meta(ch.time_meta, "TIME");
print_time_preview(ch.times, "TIME");
}
if (!ch.max_values.empty())
{
print_series_meta(ch.max_meta, "MAX");
print_value_preview(ch.max_values, "MAX");
}
if (!ch.min_values.empty())
{
print_series_meta(ch.min_meta, "MIN");
print_value_preview(ch.min_values, "MIN");
}
if (!ch.avg_values.empty())
{
print_series_meta(ch.avg_meta, "AVG");
print_value_preview(ch.avg_values, "AVG");
}
if (!ch.cp95_values.empty())
{
print_series_meta(ch.cp95_meta, "CP95");
print_value_preview(ch.cp95_values, "CP95");
}
if (!ch.val_values.empty())
{
print_series_meta(ch.val_meta, "VAL");
print_value_preview(ch.val_values, "VAL");
}
}
// <20><>ӡ<EFBFBD><D3A1><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD>ݴ<EFBFBD><DDB4><EFBFBD><EFBFBD><EFBFBD>ժҪ
void dump_parsed_map_summary(const std::string& file_path, const RawChannelMap& raw_map)
{
std::cout << "========== PQDIF PARSED SUMMARY ==========" << std::endl;
std::cout << "file=" << file_path
<< ", channel_count=" << raw_map.size()
<< std::endl;
for (const auto& kv : raw_map)
{
dump_channel_detail(kv.first, kv.second);
}
std::cout << "==========================================" << std::endl;
}
bool dump_file_basic_info(const std::string& file_path, std::string& err)
{
std::error_code ec;
fs::path p(file_path);
if (!fs::exists(p, ec))
{
err = "file not exists";
return false;
}
if (!fs::is_regular_file(p, ec))
{
err = "not a regular file";
return false;
}
auto file_size = fs::file_size(p, ec);
if (ec)
{
err = "cannot get file size";
return false;
}
std::ifstream ifs(file_path, std::ios::binary);
if (!ifs.is_open())
{
err = "ifstream open failed";
return false;
}
std::cout << "[PQDIF] file basic info: path=" << file_path
<< ", size=" << file_size << std::endl;
// <20><>ӡǰ 32 <20>ֽ<EFBFBD>ʮ<EFBFBD><CAAE><EFBFBD><EFBFBD><EFBFBD>ƣ<EFBFBD><C6A3><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ж<EFBFBD><D0B6>Ƿ<EFBFBD><C7B7><EFBFBD><EFBFBD><EFBFBD>׼<EFBFBD>ļ<EFBFBD>ͷ
unsigned char buf[32] = { 0 };
ifs.read(reinterpret_cast<char*>(buf), sizeof(buf));
std::streamsize n = ifs.gcount();
std::ostringstream oss;
oss << "[PQDIF] first " << n << " bytes: ";
for (std::streamsize i = 0; i < n; ++i)
{
oss << std::hex << std::setw(2) << std::setfill('0')
<< static_cast<int>(buf[i]) << " ";
}
std::cout << oss.str() << std::endl;
return true;
}
std::string to_upper_copy(std::string s)
{
std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) {
return static_cast<char>(std::toupper(c));
});
return s;
}
std::string trim_copy(const std::string& s)
{
size_t beg = 0;
while (beg < s.size() && std::isspace(static_cast<unsigned char>(s[beg])))
++beg;
size_t end = s.size();
while (end > beg && std::isspace(static_cast<unsigned char>(s[end - 1])))
--end;
return s.substr(beg, end - beg);
}
// <20><EFBFBD><E6B7B6>ͨ<EFBFBD><CDA8><EFBFBD><EFBFBD>
// ע<><EFBFBD><E2A3BA>ǰ<EFBFBD>׶<EFBFBD>ֻ<EFBFBD><D6BB><EFBFBD><EFBFBD> map key<65><79><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϊʶ<CEAA><CAB6><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
std::string normalize_key(const std::string& src)
{
std::string out;
out.reserve(src.size());
for (char c : src)
{
unsigned char uc = static_cast<unsigned char>(c);
if (std::isalnum(uc) || c == '[' || c == ']')
out.push_back(static_cast<char>(std::toupper(uc)));
}
return out;
}
bool is_pqdif_file(const fs::path& path)
{
if (!fs::is_regular_file(path))
return false;
const std::string ext = to_upper_copy(path.extension().string());
return ext == ".PQD" || ext == ".PQDIF";
}
bool ensure_dir(const fs::path& dir)
{
std::error_code ec;
if (fs::exists(dir, ec))
return true;
return fs::create_directories(dir, ec) || fs::exists(dir, ec);
}
bool move_file_with_fallback(const fs::path& src, const fs::path& dst)
{
std::error_code ec;
ensure_dir(dst.parent_path());
fs::rename(src, dst, ec);
if (!ec)
return true;
ec.clear();
fs::copy_file(src, dst, fs::copy_options::overwrite_existing, ec);
if (ec)
return false;
ec.clear();
fs::remove(src, ec);
return !ec;
}
void cleanup_backup_dir(const fs::path& dir, int limit)
{
if (limit < 0)
return;
std::error_code ec;
if (!fs::exists(dir, ec) || !fs::is_directory(dir, ec))
return;
std::vector<fs::path> files;
for (fs::directory_iterator it(dir, ec), end; !ec && it != end; it.increment(ec))
{
if (!ec && is_pqdif_file(it->path()))
files.push_back(it->path());
}
if (files.size() <= static_cast<size_t>(limit))
return;
std::sort(files.begin(), files.end(), [](const fs::path& a, const fs::path& b) {
std::error_code ea, eb;
return fs::last_write_time(a, ea) < fs::last_write_time(b, eb);
});
const size_t remove_count = files.size() - static_cast<size_t>(limit);
for (size_t i = 0; i < remove_count; ++i)
{
std::error_code remove_ec;
fs::remove(files[i], remove_ec);
}
}
// <20><><EFBFBD><EFBFBD><EFBFBD>ȶ<EFBFBD> key<65><79>ֻ<EFBFBD><D6BB><EFBFBD>ڴ洢
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ָ<EFBFBD><D6B8>ʶ<EFBFBD><CAB6><EFBFBD><EFBFBD>Ҫ<EFBFBD><D2AA><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
std::string build_channel_key(const std::string& channel_name, double channel_freq, int group_id)
{
std::ostringstream oss;
oss << trim_copy(channel_name);
if (channel_freq > 0.0)
{
const int harmonic_no = static_cast<int>(std::llround(channel_freq / 50.0));
if (harmonic_no > 0)
oss << "[" << harmonic_no << "]";
}
else if (group_id > 0)
{
oss << "[" << group_id << "]";
}
return normalize_key(oss.str());
}
// ============================
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
// ============================
bool push_parsed_result_to_cache(ParsedPqdifFile&& parsed)
{
std::lock_guard<std::mutex> guard(g_parsed_cache_mutex);
if (g_parsed_cache.size() >= kParsedCacheLimit)
{
std::cout << "[PQDIF] cache full, drop oldest: "
<< g_parsed_cache.front().source_file << std::endl;
g_parsed_cache.pop_front();
}
g_parsed_cache.emplace_back(std::move(parsed));
return true;
}
// ============================
// PQDIF ԭʼ<D4AD><CABC><EFBFBD><EFBFBD>
// <20><>ǰ<EFBFBD>׶Σ<D7B6>ֻ<EFBFBD><D6BB><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǩ + ԭʼֵ
// ============================
bool parse_pqdif_file_raw(const std::string& file_path, RawChannelMap& out_map, std::string& err)
{
CPQDIF file_convert;
file_convert.put_FlatFileName(file_path);
std::string basic_err;
if (!dump_file_basic_info(file_path, basic_err))
{
err = "precheck failed: " + basic_err;
return false;
}
if (!file_convert.Read())
{
err = "CPQDIF::Read() failed";
return false;
}
const int record_count = static_cast<int>(file_convert.RecordGetCount());
for (int i_record = 0; i_record < record_count; ++i_record)
{
GUID record_guid{};
std::string record_name;
if (!file_convert.RecordGetInfo(i_record, &record_guid, record_name))
continue;
// <20><>ǰ<EFBFBD>׶<EFBFBD>ֻ<EFBFBD><D6BB><EFBFBD><EFBFBD> Observation Record
if (!PQDIF_IsEqualGUID(record_guid, tagRecObservation))
continue;
long observation_handle = 0;
if (!file_convert.RecordRequestObservation(i_record, &observation_handle))
continue;
DATE time_start = 0;
std::string observation_name;
long channel_count = 0;
if (!file_convert.ObservationGetInfo(observation_handle, time_start, observation_name, channel_count))
{
file_convert.RecordReleaseObservation(observation_handle);
continue;
}
// <20><>ǰ<EFBFBD>߳<EFBFBD>ֻ<EFBFBD><D6BB><EFBFBD><EFBFBD>ͳ<EFBFBD><CDB3><EFBFBD><EFBFBD> Observation<6F><6E><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>¼<EFBFBD><C2BC><EFBFBD><EFBFBD> Observation
long trigger_method_id = 0;
DATE trigger_time = 0;
file_convert.ObservationGetTriggerInfo(observation_handle, &trigger_method_id, &trigger_time);
if (trigger_method_id == ID_TRIGGER_METH_CHANNEL || trigger_method_id == -1)
{
file_convert.RecordReleaseObservation(observation_handle);
continue;
}
time_t observation_start_ts = 0;
file_convert.GetTime(time_start, &observation_start_ts);
// <20><><EFBFBD><EFBFBD>ͨ<EFBFBD><CDA8>
for (int i_channel = 0; i_channel < channel_count; ++i_channel)
{
PqdifChannelInfoEx ch_info;
if (!file_convert.ObservationGetChannelInfoEx(observation_handle, i_channel, &ch_info))
continue;
if (ch_info.name.empty())
continue;
double channel_freq = 0.0;
int group_id = 0;
file_convert.ObservationGetChannelFreq(observation_handle, i_channel, &channel_freq);
file_convert.ObservationGetChannelGroupID(observation_handle, i_channel, &group_id);
const std::string key = build_channel_key(ch_info.name, channel_freq, group_id);
RawChannelSeries& raw_series = out_map[key];
// <20><><EFBFBD><EFBFBD>ͨ<EFBFBD><CDA8><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ǩ
raw_series.channel_tag.raw_channel_name = ch_info.name;
raw_series.channel_tag.normalized_channel_name = key;
raw_series.channel_tag.phase_id = ch_info.phaseId;
raw_series.channel_tag.quantity_type_id = ch_info.quantityTypeId;
raw_series.channel_tag.quantity_measured_id = ch_info.quantityMeasuredId;
raw_series.channel_tag.channel_frequency = channel_freq;
raw_series.channel_tag.group_id = group_id;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ͨ<EFBFBD><CDA8><EFBFBD>µ<EFBFBD><C2B5><EFBFBD><EFBFBD><EFBFBD> series
for (int i_series = 0; i_series < ch_info.countSeries; ++i_series)
{
PqdifSeriesInfoEx sr_info;
if (!file_convert.ObservationGetSeriesInfoEx(observation_handle, i_channel, i_series, &sr_info))
continue;
double* values = nullptr;
long value_count = 0;
if (!file_convert.ObservationGetSeriesData(observation_handle, i_channel, i_series, &values, &value_count) ||
values == nullptr || value_count <= 0)
{
delete[] values;
continue;
}
RawSeriesTagMeta series_meta;
series_meta.quantity_units_id = sr_info.quantityUnitsId;
series_meta.quantity_characteristic_id = sr_info.quantityCharacteristicId;
series_meta.value_type_id = sr_info.valueTypeId;
series_meta.series_base_type = sr_info.seriesBaseType;
series_meta.series_scale = sr_info.scale;
series_meta.series_offset = sr_info.offset;
// <20><><EFBFBD><EFBFBD>ͬ valueType <20><><EFBFBD><EFBFBD><E6B5BD>ͬͰ
if (PQDIF_IsEqualGUID(sr_info.valueTypeId, ID_SERIES_VALUE_TYPE_TIME))
{
raw_series.time_meta = series_meta;
for (long i = 0; i < value_count; ++i)
{
raw_series.times.push_back(
observation_start_ts + static_cast<time_t>(std::llround(values[i]))
);
}
}
else if (PQDIF_IsEqualGUID(sr_info.valueTypeId, ID_SERIES_VALUE_TYPE_MAX))
{
raw_series.max_meta = series_meta;
raw_series.max_values.insert(raw_series.max_values.end(), values, values + value_count);
}
else if (PQDIF_IsEqualGUID(sr_info.valueTypeId, ID_SERIES_VALUE_TYPE_MIN))
{
raw_series.min_meta = series_meta;
raw_series.min_values.insert(raw_series.min_values.end(), values, values + value_count);
}
else if (PQDIF_IsEqualGUID(sr_info.valueTypeId, ID_SERIES_VALUE_TYPE_AVG))
{
raw_series.avg_meta = series_meta;
raw_series.avg_values.insert(raw_series.avg_values.end(), values, values + value_count);
}
else if (PQDIF_IsEqualGUID(sr_info.valueTypeId, ID_SERIES_VALUE_TYPE_P95))
{
raw_series.cp95_meta = series_meta;
raw_series.cp95_values.insert(raw_series.cp95_values.end(), values, values + value_count);
}
else if (PQDIF_IsEqualGUID(sr_info.valueTypeId, ID_SERIES_VALUE_TYPE_VAL))
{
raw_series.val_meta = series_meta;
raw_series.val_values.insert(raw_series.val_values.end(), values, values + value_count);
}
delete[] values;
}
}
file_convert.RecordReleaseObservation(observation_handle);
}
file_convert.Close();
return true;
}
// ============================
// <20><><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD><EFBFBD>
// ============================
bool process_single_pqdif_file(const fs::path& file_path, const std::string& mac)
{
RawChannelMap raw_map;
std::string err;
if (!parse_pqdif_file_raw(file_path.string(), raw_map, err))
{
std::cout << "[PQDIF] parse failed: " << file_path.string()
<< " reason=" << err << std::endl;
return false;
}
// <20><><EFBFBD>ԣ<EFBFBD><D4A3><EFBFBD>ӡ<EFBFBD><D3A1><EFBFBD>ν<EFBFBD><CEBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ݴ<EFBFBD><DDB4><EFBFBD><EFBFBD><EFBFBD>
dump_parsed_map_summary(file_path.string(), raw_map);
ParsedPqdifFile parsed_file;
parsed_file.mac = mac;
parsed_file.source_file = file_path.string();
parsed_file.parsed_at = std::time(nullptr);
parsed_file.channels = std::move(raw_map);
if (!push_parsed_result_to_cache(std::move(parsed_file)))
{
std::cout << "[PQDIF] push cache failed: " << file_path.string() << std::endl;
return false;
}
std::cout << "[PQDIF] processed ok: " << file_path.string()
<< " channels=" << GetParsedPqdifCacheSize()
<< std::endl;
//<2F>ڴ˴<DAB4><CBB4><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD><C4BC><EFBFBD><EFBFBD><EFBFBD>
return true;
}
// ============================
// ɨ<><C9A8>Ŀ¼
// ============================
void scan_once()
{
ensure_dir(kPqdRootDir);
ensure_dir(kDoneRootDir);
ensure_dir(kFailRootDir);
std::error_code ec;
if (!fs::exists(kPqdRootDir, ec) || !fs::is_directory(kPqdRootDir, ec))
return;
// <20><>һ<EFBFBD><D2BB>Ŀ¼<C4BF><C2BC><EFBFBD><EFBFBD> mac <20><><EFBFBD><EFBFBD>
for (fs::directory_iterator mac_it(kPqdRootDir, ec), mac_end; !ec && mac_it != mac_end; mac_it.increment(ec))
{
if (ec || !fs::is_directory(mac_it->path()))
continue;
const std::string mac = mac_it->path().filename().string();
std::vector<fs::path> pqdif_files;
std::error_code file_ec;
for (fs::directory_iterator file_it(mac_it->path(), file_ec), file_end; !file_ec && file_it != file_end; file_it.increment(file_ec))
{
if (!file_ec && is_pqdif_file(file_it->path()))
pqdif_files.push_back(file_it->path());
}
if (pqdif_files.empty())
continue;
// <20><><EFBFBD>ȴ<EFBFBD><C8B4><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ļ<EFBFBD>
std::sort(pqdif_files.begin(), pqdif_files.end(), [](const fs::path& a, const fs::path& b) {
std::error_code ea, eb;
return fs::last_write_time(a, ea) > fs::last_write_time(b, eb);
});
for (const auto& file_path : pqdif_files)
{
const bool ok = process_single_pqdif_file(file_path, mac);
const fs::path target_root = ok ? fs::path(kDoneRootDir) : fs::path(kFailRootDir);
const fs::path dst = target_root / mac / file_path.filename();
//<2F><><EFBFBD><EFBFBD>ʱ<EFBFBD><CAB1>ʱ<EFBFBD>ر<EFBFBD><D8B1>ļ<EFBFBD>ת<EFBFBD><D7AA>
/*if (!move_file_with_fallback(file_path, dst))
{
std::cout << "[PQDIF] move failed: "
<< file_path.string() << " -> " << dst.string()
<< std::endl;
}*/
}
cleanup_backup_dir(fs::path(kDoneRootDir) / mac, kBackupLimit);
cleanup_backup_dir(fs::path(kFailRootDir) / mac, kBackupLimit);
}
}
} // namespace
// ============================
// <20><><EFBFBD><EFBFBD><E2BBBA><EFBFBD>ӿ<EFBFBD>
// ============================
bool PopOldestParsedPqdifFile(ParsedPqdifFile& out)
{
std::lock_guard<std::mutex> guard(g_parsed_cache_mutex);
if (g_parsed_cache.empty())
return false;
out = std::move(g_parsed_cache.front());
g_parsed_cache.pop_front();
return true;
}
bool PeekOldestParsedPqdifFile(ParsedPqdifFile& out)
{
std::lock_guard<std::mutex> guard(g_parsed_cache_mutex);
if (g_parsed_cache.empty())
return false;
out = g_parsed_cache.front();
return true;
}
size_t GetParsedPqdifCacheSize()
{
std::lock_guard<std::mutex> guard(g_parsed_cache_mutex);
return g_parsed_cache.size();
}
void ClearParsedPqdifCache()
{
std::lock_guard<std::mutex> guard(g_parsed_cache_mutex);
g_parsed_cache.clear();
}
// ============================
// <20>߳<EFBFBD><DFB3><EFBFBD>ѭ<EFBFBD><D1AD>
// ============================
void RunPqdifScanLoop()
{
std::cout << "[PQDIF] scan loop started, root=" << kPqdRootDir
<< ", interval=" << kScanIntervalSec << "s"
<< std::endl;
while (true)
{
try
{
scan_once();
}
catch (const std::exception& ex)
{
std::cout << "[PQDIF] scan exception: " << ex.what() << std::endl;
}
catch (...)
{
std::cout << "[PQDIF] scan exception: unknown" << std::endl;
}
std::this_thread::sleep_for(std::chrono::seconds(kScanIntervalSec));
}
}