新增了PQDIF补招线程,导入了新的lib库
This commit is contained in:
726
LFtid1056/pqdif_thread_processor.cpp
Normal file
726
LFtid1056/pqdif_thread_processor.cpp
Normal file
@@ -0,0 +1,726 @@
|
||||
#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));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user