完成pqdif文件匹配和获取
This commit is contained in:
@@ -2058,13 +2058,28 @@ static ST_RET Write_Named_Var(LD_info_t *LD_info,chnl_usr_t *chnl_usr,char* VarN
|
|||||||
* 2. 只下载最近一个完整周期内、且测点序号匹配的 .pqd 文件;
|
* 2. 只下载最近一个完整周期内、且测点序号匹配的 .pqd 文件;
|
||||||
* 3. 下载前检查 /FeProject/data/pqdif/ 下是否已有同名文件,避免重复下载。
|
* 3. 下载前检查 /FeProject/data/pqdif/ 下是否已有同名文件,避免重复下载。
|
||||||
*/
|
*/
|
||||||
|
/* Current rule:
|
||||||
|
* - read /PQDIF/ periodically per terminal;
|
||||||
|
* - parse the .pqd file's own yyyyMMdd_HHmm_period-hours tokens;
|
||||||
|
* - save valid files into /FeProject/data/pqdif/<ip>/;
|
||||||
|
* - prevent repeated downloads with /FeProject/data/pqdif/.state/<ip>.downloaded.
|
||||||
|
*/
|
||||||
#define PQDIF_REMOTE_DIR "/PQDIF/"
|
#define PQDIF_REMOTE_DIR "/PQDIF/"
|
||||||
#define PQDIF_LOCAL_DIR "/FeProject/data/pqdif"
|
#define PQDIF_LOCAL_DIR "/FeProject/data/pqdif"
|
||||||
|
#define PQDIF_TMP_DIR "/FeProject/data/pqdif/.tmp"
|
||||||
|
#define PQDIF_STATE_DIR "/FeProject/data/pqdif/.state"
|
||||||
#define PQDIF_AUTO_INTERVAL_HOURS 2
|
#define PQDIF_AUTO_INTERVAL_HOURS 2
|
||||||
#define PQDIF_SCAN_INTERVAL_MS (60 * 1000)
|
#define PQDIF_SCAN_INTERVAL_MS (60 * 1000)
|
||||||
#define PQDIF_MAX_DEVICE_INDEX 4096
|
#define PQDIF_MAX_DEVICE_INDEX 4096
|
||||||
#define PQDIF_MAX_FETCH_PER_SCAN 32
|
#define PQDIF_MAX_FETCH_PER_SCAN 32
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int cpu_digit;
|
||||||
|
int period_hours;
|
||||||
|
time_t start_time;
|
||||||
|
time_t end_time;
|
||||||
|
} pqdif_file_interval_t;
|
||||||
|
|
||||||
static const char* pqdif_basename(const char* filename)
|
static const char* pqdif_basename(const char* filename)
|
||||||
{
|
{
|
||||||
const char* base = filename;
|
const char* base = filename;
|
||||||
@@ -2120,6 +2135,147 @@ static int pqdif_use_interval_end_time(const char* dev_type)
|
|||||||
return (dev_type != NULL && strcmp(dev_type, "PS_NET_PQDIF") == 0);
|
return (dev_type != NULL && strcmp(dev_type, "PS_NET_PQDIF") == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Parse names such as xxx_LD1_20260720_0000_24.pqd.
|
||||||
|
* The token before yyyyMMdd supplies the CPU digit; the last numeric token is
|
||||||
|
* the file period in hours, so 1h/2h/24h files share the same matcher.
|
||||||
|
*/
|
||||||
|
static int pqdif_is_digit_token(const char* str, int expect_len)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (str == NULL)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
for (i = 0; str[i] != '\0'; ++i) {
|
||||||
|
if (!isdigit((unsigned char)str[i]))
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (i == expect_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pqdif_parse_datetime(const char* date_token, const char* time_token, time_t* out_time)
|
||||||
|
{
|
||||||
|
struct tm tm_value;
|
||||||
|
int year;
|
||||||
|
int month;
|
||||||
|
int day;
|
||||||
|
int hour;
|
||||||
|
int min;
|
||||||
|
|
||||||
|
if (!pqdif_is_digit_token(date_token, 8) || !pqdif_is_digit_token(time_token, 4) || out_time == NULL)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
year = (date_token[0] - '0') * 1000 + (date_token[1] - '0') * 100 +
|
||||||
|
(date_token[2] - '0') * 10 + (date_token[3] - '0');
|
||||||
|
month = (date_token[4] - '0') * 10 + (date_token[5] - '0');
|
||||||
|
day = (date_token[6] - '0') * 10 + (date_token[7] - '0');
|
||||||
|
hour = (time_token[0] - '0') * 10 + (time_token[1] - '0');
|
||||||
|
min = (time_token[2] - '0') * 10 + (time_token[3] - '0');
|
||||||
|
|
||||||
|
if (month < 1 || month > 12 || day < 1 || day > 31 || hour < 0 || hour > 23 || min < 0 || min > 59)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
memset(&tm_value, 0, sizeof(tm_value));
|
||||||
|
tm_value.tm_year = year - 1900;
|
||||||
|
tm_value.tm_mon = month - 1;
|
||||||
|
tm_value.tm_mday = day;
|
||||||
|
tm_value.tm_hour = hour;
|
||||||
|
tm_value.tm_min = min;
|
||||||
|
tm_value.tm_sec = 0;
|
||||||
|
tm_value.tm_isdst = -1;
|
||||||
|
|
||||||
|
*out_time = mktime(&tm_value);
|
||||||
|
return (*out_time != (time_t)-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pqdif_parse_file_interval(const char* filename, const char* dev_type, pqdif_file_interval_t* out_info)
|
||||||
|
{
|
||||||
|
char name_buf[256];
|
||||||
|
char* tokens[32];
|
||||||
|
char* token;
|
||||||
|
const char* base;
|
||||||
|
size_t len;
|
||||||
|
int token_count = 0;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (out_info == NULL)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
memset(out_info, 0, sizeof(*out_info));
|
||||||
|
|
||||||
|
if (!pqdif_has_pqd_suffix(filename))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
base = pqdif_basename(filename);
|
||||||
|
len = strlen(base);
|
||||||
|
if (len <= 4 || len >= sizeof(name_buf))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
snprintf(name_buf, sizeof(name_buf), "%s", base);
|
||||||
|
name_buf[len - 4] = '\0';
|
||||||
|
|
||||||
|
token = strtok(name_buf, "_");
|
||||||
|
while (token != NULL && token_count < (int)(sizeof(tokens) / sizeof(tokens[0]))) {
|
||||||
|
tokens[token_count++] = token;
|
||||||
|
token = strtok(NULL, "_");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 1; i + 2 < token_count; ++i) {
|
||||||
|
const char* cpu_token = tokens[i - 1];
|
||||||
|
const char* date_token = tokens[i];
|
||||||
|
const char* time_token = tokens[i + 1];
|
||||||
|
const char* period_token = tokens[i + 2];
|
||||||
|
size_t cpu_len = strlen(cpu_token);
|
||||||
|
time_t file_time;
|
||||||
|
int period_hours;
|
||||||
|
|
||||||
|
if (!pqdif_is_digit_token(date_token, 8) || !pqdif_is_digit_token(time_token, 4))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (cpu_len == 0 || !isdigit((unsigned char)cpu_token[cpu_len - 1]))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
for (len = 0; period_token[len] != '\0'; ++len) {
|
||||||
|
if (!isdigit((unsigned char)period_token[len]))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (period_token[len] != '\0')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
period_hours = atoi(period_token);
|
||||||
|
if (period_hours <= 0 || period_hours > 24 * 366)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!pqdif_parse_datetime(date_token, time_token, &file_time))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
out_info->cpu_digit = cpu_token[cpu_len - 1] - '0';
|
||||||
|
out_info->period_hours = period_hours;
|
||||||
|
|
||||||
|
if (pqdif_use_interval_end_time(dev_type)) {
|
||||||
|
out_info->end_time = file_time;
|
||||||
|
out_info->start_time = file_time - period_hours * 3600;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
out_info->start_time = file_time;
|
||||||
|
out_info->end_time = file_time + period_hours * 3600;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pqdif_cpu_matches(int file_cpu_digit, int cpu_no)
|
||||||
|
{
|
||||||
|
if (cpu_no <= 0)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
return (file_cpu_digit == (cpu_no % 10));
|
||||||
|
}
|
||||||
|
|
||||||
static time_t pqdif_align_interval_start(long long sec)
|
static time_t pqdif_align_interval_start(long long sec)
|
||||||
{
|
{
|
||||||
time_t hour_floor;
|
time_t hour_floor;
|
||||||
@@ -2139,8 +2295,16 @@ static time_t pqdif_align_interval_start(long long sec)
|
|||||||
|
|
||||||
static int pqdif_match_interval(const char* filename, int cpu_no, time_t interval_start, const char* dev_type)
|
static int pqdif_match_interval(const char* filename, int cpu_no, time_t interval_start, const char* dev_type)
|
||||||
{
|
{
|
||||||
time_t name_time;
|
pqdif_file_interval_t file_info;
|
||||||
char time_match[64];
|
|
||||||
|
if (!pqdif_parse_file_interval(filename, dev_type, &file_info))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (!pqdif_cpu_matches(file_info.cpu_digit, cpu_no))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
return (file_info.start_time <= interval_start && interval_start < file_info.end_time);
|
||||||
|
#if 0
|
||||||
|
|
||||||
if (!pqdif_has_pqd_suffix(filename))
|
if (!pqdif_has_pqd_suffix(filename))
|
||||||
return FALSE;
|
return FALSE;
|
||||||
@@ -2159,16 +2323,102 @@ static int pqdif_match_interval(const char* filename, int cpu_no, time_t interva
|
|||||||
|
|
||||||
pqdif_make_time_match(cpu_no, name_time, time_match, sizeof(time_match));
|
pqdif_make_time_match(cpu_no, name_time, time_match, sizeof(time_match));
|
||||||
return strstr(filename, time_match) != NULL;
|
return strstr(filename, time_match) != NULL;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pqdif_match_recent_interval(const char* filename, int cpu_no, const char* dev_type)
|
static int pqdif_match_recent_interval(const char* filename, int cpu_no, const char* dev_type)
|
||||||
{
|
{
|
||||||
time_t now_time;
|
time_t now_time;
|
||||||
time_t interval_start;
|
pqdif_file_interval_t file_info;
|
||||||
|
|
||||||
now_time = time(NULL);
|
now_time = time(NULL);
|
||||||
interval_start = pqdif_align_interval_start((long long)now_time) - PQDIF_AUTO_INTERVAL_HOURS * 3600;
|
if (!pqdif_parse_file_interval(filename, dev_type, &file_info))
|
||||||
return pqdif_match_interval(filename, cpu_no, interval_start, dev_type);
|
return FALSE;
|
||||||
|
|
||||||
|
if (!pqdif_cpu_matches(file_info.cpu_digit, cpu_no))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
return (file_info.end_time <= now_time);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pqdif_match_auto_candidate(const char* filename, int cpu_no, const char* dev_type,
|
||||||
|
time_t now_time, pqdif_file_interval_t* out_info)
|
||||||
|
{
|
||||||
|
pqdif_file_interval_t file_info;
|
||||||
|
|
||||||
|
if (!pqdif_parse_file_interval(filename, dev_type, &file_info))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (!pqdif_cpu_matches(file_info.cpu_digit, cpu_no))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (file_info.end_time > now_time)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (out_info != NULL)
|
||||||
|
*out_info = file_info;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pqdif_is_latest_auto_file(char **filenames, int filenum, const char* candidate_name,
|
||||||
|
int cpu_no, const char* dev_type, time_t now_time)
|
||||||
|
{
|
||||||
|
pqdif_file_interval_t best_info;
|
||||||
|
const char *best_name = NULL;
|
||||||
|
int have_best = FALSE;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!pqdif_match_auto_candidate(candidate_name, cpu_no, dev_type, now_time, NULL))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
memset(&best_info, 0, sizeof(best_info));
|
||||||
|
for (i = 0; i < filenum; i++) {
|
||||||
|
const char *base_name;
|
||||||
|
pqdif_file_interval_t file_info;
|
||||||
|
|
||||||
|
if (filenames[i] == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
base_name = pqdif_basename(filenames[i]);
|
||||||
|
if (base_name[0] == '\0')
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!pqdif_match_auto_candidate(base_name, cpu_no, dev_type, now_time, &file_info))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (!have_best ||
|
||||||
|
file_info.end_time > best_info.end_time ||
|
||||||
|
(file_info.end_time == best_info.end_time && file_info.start_time > best_info.start_time) ||
|
||||||
|
(file_info.end_time == best_info.end_time && file_info.start_time == best_info.start_time &&
|
||||||
|
best_name != NULL && strcmp(base_name, best_name) > 0)) {
|
||||||
|
have_best = TRUE;
|
||||||
|
best_info = file_info;
|
||||||
|
best_name = base_name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (have_best && best_name != NULL && strcmp(candidate_name, best_name) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pqdif_match_recall_range(const char* filename, int cpu_no, long long start_sec, long long end_sec,
|
||||||
|
const char* dev_type, pqdif_file_interval_t* out_info)
|
||||||
|
{
|
||||||
|
pqdif_file_interval_t file_info;
|
||||||
|
|
||||||
|
if (!pqdif_parse_file_interval(filename, dev_type, &file_info))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (!pqdif_cpu_matches(file_info.cpu_digit, cpu_no))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if ((long long)file_info.end_time <= start_sec || (long long)file_info.start_time >= end_sec)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
if (out_info != NULL)
|
||||||
|
*out_info = file_info;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pqdif_ensure_local_dir()
|
static int pqdif_ensure_local_dir()
|
||||||
@@ -2179,24 +2429,228 @@ static int pqdif_ensure_local_dir()
|
|||||||
|
|
||||||
if (mkdir(PQDIF_LOCAL_DIR, 0777) != 0 && errno != EEXIST)
|
if (mkdir(PQDIF_LOCAL_DIR, 0777) != 0 && errno != EEXIST)
|
||||||
return SD_FAILURE;
|
return SD_FAILURE;
|
||||||
|
|
||||||
|
if (mkdir(PQDIF_TMP_DIR, 0777) != 0 && errno != EEXIST)
|
||||||
|
return SD_FAILURE;
|
||||||
|
|
||||||
|
if (mkdir(PQDIF_STATE_DIR, 0777) != 0 && errno != EEXIST)
|
||||||
|
return SD_FAILURE;
|
||||||
#endif
|
#endif
|
||||||
return SD_SUCCESS;
|
return SD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pqdif_local_file_exists(const char* filename)
|
static void pqdif_make_ip_dir_name(const char* ip_str, char* out, size_t out_len)
|
||||||
{
|
{
|
||||||
char local_file[512];
|
size_t i;
|
||||||
|
size_t j = 0;
|
||||||
|
|
||||||
|
if (out == NULL || out_len == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ip_str == NULL || ip_str[0] == '\0') {
|
||||||
|
snprintf(out, out_len, "unknown");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; ip_str[i] != '\0' && j + 1 < out_len; ++i) {
|
||||||
|
unsigned char ch = (unsigned char)ip_str[i];
|
||||||
|
|
||||||
|
if (isalnum(ch) || ch == '.' || ch == '-' || ch == '_')
|
||||||
|
out[j++] = (char)ch;
|
||||||
|
else
|
||||||
|
out[j++] = '_';
|
||||||
|
}
|
||||||
|
out[j] = '\0';
|
||||||
|
|
||||||
|
if (out[0] == '\0')
|
||||||
|
snprintf(out, out_len, "unknown");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pqdif_build_device_dirs(chnl_usr_t *chnl_usr, char* local_dir, size_t local_dir_len, char* tmp_dir, size_t tmp_dir_len)
|
||||||
|
{
|
||||||
|
char ip_dir[128];
|
||||||
|
|
||||||
|
pqdif_make_ip_dir_name(chnl_usr ? chnl_usr->ip_str : NULL, ip_dir, sizeof(ip_dir));
|
||||||
|
|
||||||
|
if (local_dir != NULL && local_dir_len > 0)
|
||||||
|
snprintf(local_dir, local_dir_len, "%s/%s", PQDIF_LOCAL_DIR, ip_dir);
|
||||||
|
|
||||||
|
if (tmp_dir != NULL && tmp_dir_len > 0)
|
||||||
|
snprintf(tmp_dir, tmp_dir_len, "%s/%s", PQDIF_TMP_DIR, ip_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pqdif_ensure_device_dirs(chnl_usr_t *chnl_usr, char* local_dir, size_t local_dir_len, char* tmp_dir, size_t tmp_dir_len)
|
||||||
|
{
|
||||||
|
pqdif_build_device_dirs(chnl_usr, local_dir, local_dir_len, tmp_dir, tmp_dir_len);
|
||||||
|
|
||||||
|
if (pqdif_ensure_local_dir() != SD_SUCCESS)
|
||||||
|
return SD_FAILURE;
|
||||||
|
|
||||||
|
#ifdef _OS_UNIX_
|
||||||
|
if (local_dir == NULL || tmp_dir == NULL)
|
||||||
|
return SD_FAILURE;
|
||||||
|
|
||||||
|
if (mkdir(local_dir, 0777) != 0 && errno != EEXIST)
|
||||||
|
return SD_FAILURE;
|
||||||
|
|
||||||
|
if (mkdir(tmp_dir, 0777) != 0 && errno != EEXIST)
|
||||||
|
return SD_FAILURE;
|
||||||
|
#endif
|
||||||
|
return SD_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pqdif_copy_file(const char* src_file, const char* dst_file)
|
||||||
|
{
|
||||||
|
FILE *src;
|
||||||
|
FILE *dst;
|
||||||
|
char buf[8192];
|
||||||
|
size_t nread;
|
||||||
|
int ret = SD_SUCCESS;
|
||||||
|
|
||||||
|
if (src_file == NULL || dst_file == NULL || src_file[0] == '\0' || dst_file[0] == '\0')
|
||||||
|
return SD_FAILURE;
|
||||||
|
|
||||||
|
src = fopen(src_file, "rb");
|
||||||
|
if (src == NULL)
|
||||||
|
return SD_FAILURE;
|
||||||
|
|
||||||
|
dst = fopen(dst_file, "wb");
|
||||||
|
if (dst == NULL) {
|
||||||
|
fclose(src);
|
||||||
|
return SD_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ((nread = fread(buf, 1, sizeof(buf), src)) > 0) {
|
||||||
|
if (fwrite(buf, 1, nread, dst) != nread) {
|
||||||
|
ret = SD_FAILURE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ferror(src))
|
||||||
|
ret = SD_FAILURE;
|
||||||
|
|
||||||
|
if (fclose(dst) != 0)
|
||||||
|
ret = SD_FAILURE;
|
||||||
|
|
||||||
|
fclose(src);
|
||||||
|
|
||||||
|
if (ret != SD_SUCCESS)
|
||||||
|
remove(dst_file);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static long pqdif_get_local_file_size(const char* filename)
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
long size;
|
||||||
|
|
||||||
|
if (filename == NULL || filename[0] == '\0')
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
fp = fopen(filename, "rb");
|
||||||
|
if (fp == NULL)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (fseek(fp, 0, SEEK_END) != 0) {
|
||||||
|
fclose(fp);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
size = ftell(fp);
|
||||||
|
fclose(fp);
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Successful downloads are tracked outside the final data directory. The PQDIF
|
||||||
|
* reader may delete /FeProject/data/pqdif/<ip>/ files after processing, so the
|
||||||
|
* final directory must not be used as the duplicate-download ledger.
|
||||||
|
*/
|
||||||
|
static void pqdif_build_state_file(chnl_usr_t *chnl_usr, char* state_file, size_t state_file_len)
|
||||||
|
{
|
||||||
|
char ip_dir[128];
|
||||||
|
|
||||||
|
if (state_file == NULL || state_file_len == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
pqdif_make_ip_dir_name(chnl_usr ? chnl_usr->ip_str : NULL, ip_dir, sizeof(ip_dir));
|
||||||
|
snprintf(state_file, state_file_len, "%s/%s.downloaded", PQDIF_STATE_DIR, ip_dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pqdif_strip_line_end(char* line)
|
||||||
|
{
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
if (line == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
len = strlen(line);
|
||||||
|
while (len > 0 && (line[len - 1] == '\r' || line[len - 1] == '\n' ||
|
||||||
|
line[len - 1] == ' ' || line[len - 1] == '\t')) {
|
||||||
|
line[len - 1] = '\0';
|
||||||
|
len--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pqdif_download_record_exists(chnl_usr_t *chnl_usr, const char* filename)
|
||||||
|
{
|
||||||
|
char state_file[512];
|
||||||
|
char line[512];
|
||||||
|
FILE *fp;
|
||||||
const char* base = pqdif_basename(filename);
|
const char* base = pqdif_basename(filename);
|
||||||
|
|
||||||
if (base[0] == '\0')
|
if (base[0] == '\0')
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
snprintf(local_file, sizeof(local_file), "%s/%s", PQDIF_LOCAL_DIR, base);
|
pqdif_build_state_file(chnl_usr, state_file, sizeof(state_file));
|
||||||
#ifdef _OS_UNIX_
|
fp = fopen(state_file, "r");
|
||||||
return access(local_file, F_OK) == 0;
|
if (fp == NULL)
|
||||||
#else
|
|
||||||
return FALSE;
|
return FALSE;
|
||||||
#endif
|
|
||||||
|
while (fgets(line, sizeof(line), fp) != NULL) {
|
||||||
|
pqdif_strip_line_end(line);
|
||||||
|
if (strcmp(line, base) == 0) {
|
||||||
|
fclose(fp);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int pqdif_record_download_success(chnl_usr_t *chnl_usr, const char* filename)
|
||||||
|
{
|
||||||
|
char state_file[512];
|
||||||
|
FILE *fp;
|
||||||
|
const char* base = pqdif_basename(filename);
|
||||||
|
int ret = SD_SUCCESS;
|
||||||
|
|
||||||
|
if (base[0] == '\0')
|
||||||
|
return SD_FAILURE;
|
||||||
|
|
||||||
|
if (pqdif_download_record_exists(chnl_usr, base))
|
||||||
|
return SD_SUCCESS;
|
||||||
|
|
||||||
|
if (pqdif_ensure_local_dir() != SD_SUCCESS)
|
||||||
|
return SD_FAILURE;
|
||||||
|
|
||||||
|
pqdif_build_state_file(chnl_usr, state_file, sizeof(state_file));
|
||||||
|
fp = fopen(state_file, "a");
|
||||||
|
if (fp == NULL)
|
||||||
|
return SD_FAILURE;
|
||||||
|
|
||||||
|
if (fprintf(fp, "%s\n", base) < 0)
|
||||||
|
ret = SD_FAILURE;
|
||||||
|
|
||||||
|
if (fflush(fp) != 0)
|
||||||
|
ret = SD_FAILURE;
|
||||||
|
|
||||||
|
if (fclose(fp) != 0)
|
||||||
|
ret = SD_FAILURE;
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void pqdif_build_remote_filename(const char* dir_filename, char* out, size_t out_len)
|
static void pqdif_build_remote_filename(const char* dir_filename, char* out, size_t out_len)
|
||||||
@@ -2220,7 +2674,10 @@ static void pqdif_build_remote_filename(const char* dir_filename, char* out, siz
|
|||||||
static int pqdif_get_file_from_device(chnl_usr_t *chnl_usr, const char *rem_filename, char *only_filename_ret, size_t only_filename_ret_len)
|
static int pqdif_get_file_from_device(chnl_usr_t *chnl_usr, const char *rem_filename, char *only_filename_ret, size_t only_filename_ret_len)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
char loc_file_fullname[512];
|
char tmp_file_fullname[512];
|
||||||
|
char final_file_fullname[512];
|
||||||
|
char local_dir[512];
|
||||||
|
char tmp_dir[512];
|
||||||
char rem_file_fullname[512];
|
char rem_file_fullname[512];
|
||||||
const char *only_filename;
|
const char *only_filename;
|
||||||
|
|
||||||
@@ -2231,40 +2688,61 @@ static int pqdif_get_file_from_device(chnl_usr_t *chnl_usr, const char *rem_file
|
|||||||
if (only_filename[0] == '\0')
|
if (only_filename[0] == '\0')
|
||||||
return SD_FAILURE;
|
return SD_FAILURE;
|
||||||
|
|
||||||
if (pqdif_ensure_local_dir() != SD_SUCCESS) {
|
if (pqdif_ensure_device_dirs(chnl_usr, local_dir, sizeof(local_dir), tmp_dir, sizeof(tmp_dir)) != SD_SUCCESS) {
|
||||||
echo_warn1("[PQDIF] create local dir failed: %s\n", PQDIF_LOCAL_DIR);
|
echo_warn2("[PQDIF] create local dir failed: local=%s tmp=%s\n", local_dir, tmp_dir);
|
||||||
return SD_FAILURE;
|
return SD_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(loc_file_fullname,0,sizeof(loc_file_fullname));
|
memset(tmp_file_fullname,0,sizeof(tmp_file_fullname));
|
||||||
|
memset(final_file_fullname,0,sizeof(final_file_fullname));
|
||||||
memset(rem_file_fullname,0,sizeof(rem_file_fullname));
|
memset(rem_file_fullname,0,sizeof(rem_file_fullname));
|
||||||
apr_snprintf(loc_file_fullname,sizeof(loc_file_fullname),"%s/%s",PQDIF_LOCAL_DIR,only_filename);
|
apr_snprintf(tmp_file_fullname,sizeof(tmp_file_fullname),"%s/%s",tmp_dir,only_filename);
|
||||||
|
apr_snprintf(final_file_fullname,sizeof(final_file_fullname),"%s/%s",local_dir,only_filename);
|
||||||
apr_snprintf(rem_file_fullname,sizeof(rem_file_fullname),"%s",rem_filename);
|
apr_snprintf(rem_file_fullname,sizeof(rem_file_fullname),"%s",rem_filename);
|
||||||
|
|
||||||
if (only_filename_ret != NULL && only_filename_ret_len > 0)
|
if (only_filename_ret != NULL && only_filename_ret_len > 0)
|
||||||
apr_snprintf(only_filename_ret,only_filename_ret_len,"%s",only_filename);
|
apr_snprintf(only_filename_ret,only_filename_ret_len,"%s",only_filename);
|
||||||
|
|
||||||
printf("[PQDIF] mms_getFile local=%s remote=%s\n", loc_file_fullname, rem_file_fullname);
|
/* Keep the same safety flow as the C# reader:
|
||||||
|
* download to a temporary backup path first, remove failed/empty files,
|
||||||
|
* copy to the final PQDIF directory only after validation, then remove temp.
|
||||||
|
*/
|
||||||
|
remove(tmp_file_fullname);
|
||||||
|
|
||||||
ret = mms_getFile(chnl_usr->net_info,loc_file_fullname,rem_file_fullname,g_pt61850app->mmsOpTimeout);
|
printf("[PQDIF] mms_getFile tmp=%s final=%s remote=%s\n",
|
||||||
|
tmp_file_fullname, final_file_fullname, rem_file_fullname);
|
||||||
|
|
||||||
|
ret = mms_getFile(chnl_usr->net_info,tmp_file_fullname,rem_file_fullname,g_pt61850app->mmsOpTimeout);
|
||||||
if (ret != SD_SUCCESS) {
|
if (ret != SD_SUCCESS) {
|
||||||
echo_warn3("[PQDIF] mms_getFile failed IP=%s local=%s remote=%s\n",
|
remove(tmp_file_fullname);
|
||||||
chnl_usr->ip_str, loc_file_fullname, rem_file_fullname);
|
echo_warn3("[PQDIF] mms_getFile failed IP=%s tmp=%s remote=%s\n",
|
||||||
|
chnl_usr->ip_str, tmp_file_fullname, rem_file_fullname);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _OS_UNIX_
|
if (pqdif_get_local_file_size(tmp_file_fullname) <= 0) {
|
||||||
{
|
remove(tmp_file_fullname);
|
||||||
struct stat st;
|
|
||||||
|
|
||||||
if (stat(loc_file_fullname, &st) != 0 || st.st_size <= 0) {
|
|
||||||
unlink(loc_file_fullname);
|
|
||||||
echo_warn2("[PQDIF] file is empty or invalid: %s from %s\n",
|
echo_warn2("[PQDIF] file is empty or invalid: %s from %s\n",
|
||||||
loc_file_fullname, rem_file_fullname);
|
tmp_file_fullname, rem_file_fullname);
|
||||||
return SD_FAILURE;
|
return SD_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (pqdif_copy_file(tmp_file_fullname, final_file_fullname) != SD_SUCCESS) {
|
||||||
|
remove(tmp_file_fullname);
|
||||||
|
echo_warn2("[PQDIF] copy validated file failed: %s -> %s\n",
|
||||||
|
tmp_file_fullname, final_file_fullname);
|
||||||
|
return SD_FAILURE;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
if (pqdif_record_download_success(chnl_usr, only_filename) != SD_SUCCESS) {
|
||||||
|
remove(tmp_file_fullname);
|
||||||
|
remove(final_file_fullname);
|
||||||
|
echo_warn2("[PQDIF] write download record failed, remove copied file: IP=%s file=%s\n",
|
||||||
|
chnl_usr->ip_str, only_filename);
|
||||||
|
return SD_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
remove(tmp_file_fullname);
|
||||||
|
|
||||||
return SD_SUCCESS;
|
return SD_SUCCESS;
|
||||||
}
|
}
|
||||||
@@ -2281,8 +2759,7 @@ static apr_status_t ChannelRecallPQDIFFiles(chnl_usr_t *chnl_usr, LD_info_t *LD_
|
|||||||
int existed = 0;
|
int existed = 0;
|
||||||
int missing = 0;
|
int missing = 0;
|
||||||
int failed = 0;
|
int failed = 0;
|
||||||
time_t interval_start;
|
int matched = 0;
|
||||||
time_t interval_step;
|
|
||||||
|
|
||||||
if (chnl_usr == NULL || chnl_usr->chnl == NULL || chnl_usr->chnl->ied == NULL ||
|
if (chnl_usr == NULL || chnl_usr->chnl == NULL || chnl_usr->chnl->ied == NULL ||
|
||||||
chnl_usr->chnl->ied->usr_ext == NULL || chnl_usr->net_info == NULL ||
|
chnl_usr->chnl->ied->usr_ext == NULL || chnl_usr->net_info == NULL ||
|
||||||
@@ -2329,20 +2806,14 @@ static apr_status_t ChannelRecallPQDIFFiles(chnl_usr_t *chnl_usr, LD_info_t *LD_
|
|||||||
return APR_EGENERAL;
|
return APR_EGENERAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
interval_step = PQDIF_AUTO_INTERVAL_HOURS * 3600;
|
printf("[PQDIF][RECALL] begin mp_id=%s cpuno=%d range=%lld~%lld files=%d dev_type=%s\n",
|
||||||
interval_start = pqdif_align_interval_start(start_sec);
|
LD_info->mp_id, LD_info->cpuno, start_sec, end_sec, filenum, ied_usr->dev_type);
|
||||||
|
|
||||||
printf("[PQDIF][RECALL] begin mp_id=%s cpuno=%d range=%lld~%lld interval_start=%lld files=%d dev_type=%s\n",
|
|
||||||
LD_info->mp_id, LD_info->cpuno, start_sec, end_sec, (long long)interval_start,
|
|
||||||
filenum, ied_usr->dev_type);
|
|
||||||
|
|
||||||
while ((long long)interval_start < end_sec) {
|
|
||||||
int found_this_interval = FALSE;
|
|
||||||
|
|
||||||
for (file_idx = 0; file_idx < filenum; file_idx++) {
|
for (file_idx = 0; file_idx < filenum; file_idx++) {
|
||||||
const char *base_name;
|
const char *base_name;
|
||||||
char remote_filename[256];
|
char remote_filename[256];
|
||||||
char only_filename_ret[256];
|
char only_filename_ret[256];
|
||||||
|
pqdif_file_interval_t file_info;
|
||||||
int ret_get;
|
int ret_get;
|
||||||
|
|
||||||
if (filenames[file_idx] == NULL)
|
if (filenames[file_idx] == NULL)
|
||||||
@@ -2352,15 +2823,17 @@ static apr_status_t ChannelRecallPQDIFFiles(chnl_usr_t *chnl_usr, LD_info_t *LD_
|
|||||||
if (base_name[0] == '\0')
|
if (base_name[0] == '\0')
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!pqdif_match_interval(base_name, LD_info->cpuno, interval_start, ied_usr->dev_type))
|
if (!pqdif_match_recall_range(base_name, LD_info->cpuno, start_sec, end_sec,
|
||||||
|
ied_usr->dev_type, &file_info))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
found_this_interval = TRUE;
|
matched++;
|
||||||
if (pqdif_local_file_exists(base_name)) {
|
if (pqdif_download_record_exists(chnl_usr, base_name)) {
|
||||||
existed++;
|
existed++;
|
||||||
printf("[PQDIF][RECALL] local file exists, skip mp_id=%s interval=%lld file=%s\n",
|
printf("[PQDIF][RECALL] download record exists, skip mp_id=%s file=%s file_range=%lld~%lld period=%d\n",
|
||||||
LD_info->mp_id, (long long)interval_start, base_name);
|
LD_info->mp_id, base_name, (long long)file_info.start_time,
|
||||||
break;
|
(long long)file_info.end_time, file_info.period_hours);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(remote_filename,0,sizeof(remote_filename));
|
memset(remote_filename,0,sizeof(remote_filename));
|
||||||
@@ -2368,37 +2841,37 @@ static apr_status_t ChannelRecallPQDIFFiles(chnl_usr_t *chnl_usr, LD_info_t *LD_
|
|||||||
pqdif_build_remote_filename(filenames[file_idx], remote_filename, sizeof(remote_filename));
|
pqdif_build_remote_filename(filenames[file_idx], remote_filename, sizeof(remote_filename));
|
||||||
if (remote_filename[0] == '\0') {
|
if (remote_filename[0] == '\0') {
|
||||||
failed++;
|
failed++;
|
||||||
break;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret_get = pqdif_get_file_from_device(chnl_usr, remote_filename, only_filename_ret, sizeof(only_filename_ret));
|
ret_get = pqdif_get_file_from_device(chnl_usr, remote_filename, only_filename_ret, sizeof(only_filename_ret));
|
||||||
if (ret_get == SD_SUCCESS) {
|
if (ret_get == SD_SUCCESS) {
|
||||||
|
char local_dir[512];
|
||||||
|
|
||||||
fetched++;
|
fetched++;
|
||||||
chnl_usr->m_LastPosRespTime = sGetMsTime();
|
chnl_usr->m_LastPosRespTime = sGetMsTime();
|
||||||
printf("[PQDIF][RECALL] fetch success mp_id=%s interval=%lld remote=%s local=%s/%s\n",
|
pqdif_build_device_dirs(chnl_usr, local_dir, sizeof(local_dir), NULL, 0);
|
||||||
LD_info->mp_id, (long long)interval_start, remote_filename, PQDIF_LOCAL_DIR, only_filename_ret);
|
printf("[PQDIF][RECALL] fetch success mp_id=%s file_range=%lld~%lld period=%d remote=%s local=%s/%s\n",
|
||||||
|
LD_info->mp_id, (long long)file_info.start_time, (long long)file_info.end_time,
|
||||||
|
file_info.period_hours, remote_filename, local_dir, only_filename_ret);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
failed++;
|
failed++;
|
||||||
echo_warn3("[PQDIF][RECALL] fetch failed mp_id=%s remote=%s IP=%s\n",
|
echo_warn3("[PQDIF][RECALL] fetch failed mp_id=%s remote=%s IP=%s\n",
|
||||||
LD_info->mp_id, remote_filename, chnl_usr->ip_str);
|
LD_info->mp_id, remote_filename, chnl_usr->ip_str);
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!found_this_interval) {
|
if (matched == 0) {
|
||||||
missing++;
|
missing++;
|
||||||
printf("[PQDIF][RECALL] no matched file mp_id=%s interval=%lld dev_type=%s\n",
|
printf("[PQDIF][RECALL] no matched file mp_id=%s range=%lld~%lld dev_type=%s\n",
|
||||||
LD_info->mp_id, (long long)interval_start, ied_usr->dev_type);
|
LD_info->mp_id, start_sec, end_sec, ied_usr->dev_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
interval_start += interval_step;
|
printf("[PQDIF][RECALL] finish mp_id=%s matched=%d fetched=%d existed=%d missing=%d failed=%d\n",
|
||||||
}
|
LD_info->mp_id, matched, fetched, existed, missing, failed);
|
||||||
|
|
||||||
printf("[PQDIF][RECALL] finish mp_id=%s fetched=%d existed=%d missing=%d failed=%d\n",
|
if (matched == 0 || failed > 0)
|
||||||
LD_info->mp_id, fetched, existed, missing, failed);
|
|
||||||
|
|
||||||
if (missing > 0 || failed > 0)
|
|
||||||
return APR_EGENERAL;
|
return APR_EGENERAL;
|
||||||
|
|
||||||
return APR_SUCCESS;
|
return APR_SUCCESS;
|
||||||
@@ -2463,6 +2936,7 @@ static apr_status_t ChannelCheckPQDIFFiles(chnl_usr_t *chnl_usr)
|
|||||||
int matched = 0;
|
int matched = 0;
|
||||||
int scan_idx;
|
int scan_idx;
|
||||||
double now_ms;
|
double now_ms;
|
||||||
|
time_t now_time;
|
||||||
static double s_last_scan_ms[PQDIF_MAX_DEVICE_INDEX] = {0};
|
static double s_last_scan_ms[PQDIF_MAX_DEVICE_INDEX] = {0};
|
||||||
|
|
||||||
if (chnl_usr == NULL || chnl_usr->chnl == NULL || chnl_usr->chnl->ied == NULL ||
|
if (chnl_usr == NULL || chnl_usr->chnl == NULL || chnl_usr->chnl->ied == NULL ||
|
||||||
@@ -2489,6 +2963,7 @@ static apr_status_t ChannelCheckPQDIFFiles(chnl_usr_t *chnl_usr)
|
|||||||
return APR_SUCCESS;
|
return APR_SUCCESS;
|
||||||
}
|
}
|
||||||
s_last_scan_ms[scan_idx] = now_ms;
|
s_last_scan_ms[scan_idx] = now_ms;
|
||||||
|
now_time = time(NULL);
|
||||||
|
|
||||||
/* 本函数只做 PQDIF 文件获取:
|
/* 本函数只做 PQDIF 文件获取:
|
||||||
* 1. 从装置固定目录 /PQDIF/ 取文件列表;
|
* 1. 从装置固定目录 /PQDIF/ 取文件列表;
|
||||||
@@ -2543,13 +3018,16 @@ static apr_status_t ChannelCheckPQDIFFiles(chnl_usr_t *chnl_usr)
|
|||||||
* 这里保持同样的匹配方式,不依赖厂家前缀,也兼容普通设备用
|
* 这里保持同样的匹配方式,不依赖厂家前缀,也兼容普通设备用
|
||||||
* 周期开始时间命名、振兴类设备用周期结束时间命名的差异。
|
* 周期开始时间命名、振兴类设备用周期结束时间命名的差异。
|
||||||
*/
|
*/
|
||||||
if (!pqdif_match_recent_interval(base_name, LD_info->cpuno, ied_usr->dev_type))
|
if (!pqdif_is_latest_auto_file(filenames, filenum, base_name,
|
||||||
|
LD_info->cpuno, ied_usr->dev_type, now_time))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
matched++;
|
matched++;
|
||||||
if (pqdif_local_file_exists(base_name)) {
|
|
||||||
printf("[PQDIF] local file exists, skip mp_id=%s file=%s\n", LD_info->mp_id, base_name);
|
if (pqdif_download_record_exists(chnl_usr, base_name)) {
|
||||||
continue;
|
printf("[PQDIF] latest file already downloaded, skip mp_id=%s file=%s\n",
|
||||||
|
LD_info->mp_id, base_name);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(remote_filename,0,sizeof(remote_filename));
|
memset(remote_filename,0,sizeof(remote_filename));
|
||||||
@@ -2560,10 +3038,13 @@ static apr_status_t ChannelCheckPQDIFFiles(chnl_usr_t *chnl_usr)
|
|||||||
|
|
||||||
ret_get = pqdif_get_file_from_device(chnl_usr, remote_filename, only_filename_ret, sizeof(only_filename_ret));
|
ret_get = pqdif_get_file_from_device(chnl_usr, remote_filename, only_filename_ret, sizeof(only_filename_ret));
|
||||||
if (ret_get == SD_SUCCESS) {
|
if (ret_get == SD_SUCCESS) {
|
||||||
|
char local_dir[512];
|
||||||
|
|
||||||
fetched++;
|
fetched++;
|
||||||
chnl_usr->m_LastPosRespTime = sGetMsTime();
|
chnl_usr->m_LastPosRespTime = sGetMsTime();
|
||||||
|
pqdif_build_device_dirs(chnl_usr, local_dir, sizeof(local_dir), NULL, 0);
|
||||||
printf("[PQDIF] fetch success mp_id=%s name=%s remote=%s local=%s/%s\n",
|
printf("[PQDIF] fetch success mp_id=%s name=%s remote=%s local=%s/%s\n",
|
||||||
LD_info->mp_id, LD_info->name, remote_filename, PQDIF_LOCAL_DIR, only_filename_ret);
|
LD_info->mp_id, LD_info->name, remote_filename, local_dir, only_filename_ret);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
echo_warn3("[PQDIF] fetch failed mp_id=%s remote=%s IP=%s\n",
|
echo_warn3("[PQDIF] fetch failed mp_id=%s remote=%s IP=%s\n",
|
||||||
@@ -2575,6 +3056,7 @@ static apr_status_t ChannelCheckPQDIFFiles(chnl_usr_t *chnl_usr)
|
|||||||
PQDIF_MAX_FETCH_PER_SCAN, ied->id, chnl_usr->ip_str);
|
PQDIF_MAX_FETCH_PER_SCAN, ied->id, chnl_usr->ip_str);
|
||||||
return APR_SUCCESS;
|
return APR_SUCCESS;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user