diff --git a/mms/mms_process.c b/mms/mms_process.c index d46d51f..ee7d0b6 100644 --- a/mms/mms_process.c +++ b/mms/mms_process.c @@ -2058,13 +2058,28 @@ static ST_RET Write_Named_Var(LD_info_t *LD_info,chnl_usr_t *chnl_usr,char* VarN * 2. 只下载最近一个完整周期内、且测点序号匹配的 .pqd 文件; * 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//; + * - prevent repeated downloads with /FeProject/data/pqdif/.state/.downloaded. + */ #define PQDIF_REMOTE_DIR "/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_SCAN_INTERVAL_MS (60 * 1000) #define PQDIF_MAX_DEVICE_INDEX 4096 #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) { 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); } +/* 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) { 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) { - time_t name_time; - char time_match[64]; + 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; + + return (file_info.start_time <= interval_start && interval_start < file_info.end_time); +#if 0 if (!pqdif_has_pqd_suffix(filename)) 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)); return strstr(filename, time_match) != NULL; +#endif } static int pqdif_match_recent_interval(const char* filename, int cpu_no, const char* dev_type) { time_t now_time; - time_t interval_start; + pqdif_file_interval_t file_info; now_time = time(NULL); - interval_start = pqdif_align_interval_start((long long)now_time) - PQDIF_AUTO_INTERVAL_HOURS * 3600; - return pqdif_match_interval(filename, cpu_no, interval_start, dev_type); + 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.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() @@ -2179,24 +2429,228 @@ static int pqdif_ensure_local_dir() if (mkdir(PQDIF_LOCAL_DIR, 0777) != 0 && errno != EEXIST) 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 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// 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); if (base[0] == '\0') return FALSE; - snprintf(local_file, sizeof(local_file), "%s/%s", PQDIF_LOCAL_DIR, base); -#ifdef _OS_UNIX_ - return access(local_file, F_OK) == 0; -#else + pqdif_build_state_file(chnl_usr, state_file, sizeof(state_file)); + fp = fopen(state_file, "r"); + if (fp == NULL) + return FALSE; + + 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; -#endif +} + +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) @@ -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) { 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]; 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') return SD_FAILURE; - if (pqdif_ensure_local_dir() != SD_SUCCESS) { - echo_warn1("[PQDIF] create local dir failed: %s\n", PQDIF_LOCAL_DIR); + if (pqdif_ensure_device_dirs(chnl_usr, local_dir, sizeof(local_dir), tmp_dir, sizeof(tmp_dir)) != SD_SUCCESS) { + echo_warn2("[PQDIF] create local dir failed: local=%s tmp=%s\n", local_dir, tmp_dir); 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)); - 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); if (only_filename_ret != NULL && only_filename_ret_len > 0) 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) { - echo_warn3("[PQDIF] mms_getFile failed IP=%s local=%s remote=%s\n", - chnl_usr->ip_str, loc_file_fullname, rem_file_fullname); + remove(tmp_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; } -#ifdef _OS_UNIX_ - { - 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", - loc_file_fullname, rem_file_fullname); - return SD_FAILURE; - } + if (pqdif_get_local_file_size(tmp_file_fullname) <= 0) { + remove(tmp_file_fullname); + echo_warn2("[PQDIF] file is empty or invalid: %s from %s\n", + tmp_file_fullname, rem_file_fullname); + return SD_FAILURE; } -#endif + + 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; + } + + 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; } @@ -2281,8 +2759,7 @@ static apr_status_t ChannelRecallPQDIFFiles(chnl_usr_t *chnl_usr, LD_info_t *LD_ int existed = 0; int missing = 0; int failed = 0; - time_t interval_start; - time_t interval_step; + int matched = 0; 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 || @@ -2329,76 +2806,72 @@ static apr_status_t ChannelRecallPQDIFFiles(chnl_usr_t *chnl_usr, LD_info_t *LD_ return APR_EGENERAL; } - interval_step = PQDIF_AUTO_INTERVAL_HOURS * 3600; - interval_start = pqdif_align_interval_start(start_sec); + printf("[PQDIF][RECALL] begin mp_id=%s cpuno=%d range=%lld~%lld files=%d dev_type=%s\n", + 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); + for (file_idx = 0; file_idx < filenum; file_idx++) { + const char *base_name; + char remote_filename[256]; + char only_filename_ret[256]; + pqdif_file_interval_t file_info; + int ret_get; - while ((long long)interval_start < end_sec) { - int found_this_interval = FALSE; + if (filenames[file_idx] == NULL) + continue; - for (file_idx = 0; file_idx < filenum; file_idx++) { - const char *base_name; - char remote_filename[256]; - char only_filename_ret[256]; - int ret_get; + base_name = pqdif_basename(filenames[file_idx]); + if (base_name[0] == '\0') + continue; - if (filenames[file_idx] == NULL) - continue; + if (!pqdif_match_recall_range(base_name, LD_info->cpuno, start_sec, end_sec, + ied_usr->dev_type, &file_info)) + continue; - base_name = pqdif_basename(filenames[file_idx]); - if (base_name[0] == '\0') - continue; - - if (!pqdif_match_interval(base_name, LD_info->cpuno, interval_start, ied_usr->dev_type)) - continue; - - found_this_interval = TRUE; - if (pqdif_local_file_exists(base_name)) { - existed++; - printf("[PQDIF][RECALL] local file exists, skip mp_id=%s interval=%lld file=%s\n", - LD_info->mp_id, (long long)interval_start, base_name); - break; - } - - memset(remote_filename,0,sizeof(remote_filename)); - memset(only_filename_ret,0,sizeof(only_filename_ret)); - pqdif_build_remote_filename(filenames[file_idx], remote_filename, sizeof(remote_filename)); - if (remote_filename[0] == '\0') { - failed++; - break; - } - - ret_get = pqdif_get_file_from_device(chnl_usr, remote_filename, only_filename_ret, sizeof(only_filename_ret)); - if (ret_get == SD_SUCCESS) { - fetched++; - chnl_usr->m_LastPosRespTime = sGetMsTime(); - printf("[PQDIF][RECALL] fetch success mp_id=%s interval=%lld remote=%s local=%s/%s\n", - LD_info->mp_id, (long long)interval_start, remote_filename, PQDIF_LOCAL_DIR, only_filename_ret); - } - else { - failed++; - echo_warn3("[PQDIF][RECALL] fetch failed mp_id=%s remote=%s IP=%s\n", - LD_info->mp_id, remote_filename, chnl_usr->ip_str); - } - break; + matched++; + if (pqdif_download_record_exists(chnl_usr, base_name)) { + existed++; + printf("[PQDIF][RECALL] download record exists, skip mp_id=%s file=%s file_range=%lld~%lld period=%d\n", + LD_info->mp_id, base_name, (long long)file_info.start_time, + (long long)file_info.end_time, file_info.period_hours); + continue; } - if (!found_this_interval) { - missing++; - printf("[PQDIF][RECALL] no matched file mp_id=%s interval=%lld dev_type=%s\n", - LD_info->mp_id, (long long)interval_start, ied_usr->dev_type); + memset(remote_filename,0,sizeof(remote_filename)); + memset(only_filename_ret,0,sizeof(only_filename_ret)); + pqdif_build_remote_filename(filenames[file_idx], remote_filename, sizeof(remote_filename)); + if (remote_filename[0] == '\0') { + failed++; + continue; } - interval_start += interval_step; + ret_get = pqdif_get_file_from_device(chnl_usr, remote_filename, only_filename_ret, sizeof(only_filename_ret)); + if (ret_get == SD_SUCCESS) { + char local_dir[512]; + + fetched++; + chnl_usr->m_LastPosRespTime = sGetMsTime(); + pqdif_build_device_dirs(chnl_usr, local_dir, sizeof(local_dir), NULL, 0); + 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 { + failed++; + echo_warn3("[PQDIF][RECALL] fetch failed mp_id=%s remote=%s IP=%s\n", + LD_info->mp_id, remote_filename, chnl_usr->ip_str); + } } - printf("[PQDIF][RECALL] finish mp_id=%s fetched=%d existed=%d missing=%d failed=%d\n", - LD_info->mp_id, fetched, existed, missing, failed); + if (matched == 0) { + missing++; + printf("[PQDIF][RECALL] no matched file mp_id=%s range=%lld~%lld dev_type=%s\n", + LD_info->mp_id, start_sec, end_sec, ied_usr->dev_type); + } - if (missing > 0 || failed > 0) + 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); + + if (matched == 0 || failed > 0) return APR_EGENERAL; return APR_SUCCESS; @@ -2463,6 +2936,7 @@ static apr_status_t ChannelCheckPQDIFFiles(chnl_usr_t *chnl_usr) int matched = 0; int scan_idx; double now_ms; + time_t now_time; static double s_last_scan_ms[PQDIF_MAX_DEVICE_INDEX] = {0}; 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; } s_last_scan_ms[scan_idx] = now_ms; + now_time = time(NULL); /* 本函数只做 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; 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); - continue; + + if (pqdif_download_record_exists(chnl_usr, base_name)) { + 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)); @@ -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)); if (ret_get == SD_SUCCESS) { + char local_dir[512]; + fetched++; 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", - 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 { 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); return APR_SUCCESS; } + break; } }