| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501 |
- /*
- * YM310 FTP ECO Log Upload Module - v1.1 (Chunked Upload)
- *
- * - 分块读取 ecode 记录,每块 ≤ 4KB,通过 QFTPPUT 续传
- * - 文件名 = IMEI_YYYYMMDD.txt,上传目录为 /scd_txt
- * - 上传结束后清除 easyflash 标志并重启
- */
- #define DBG_TAG "Y-FUL"
- #define DBG_LVL DBG_INFO
- #include <rtdbg.h>
- #include "ym310_mqtt.h"
- #include <string.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <easyflash.h>
- #include <rthw.h>
- #include <rtthread.h>
- #include "ecode.h"
- #include "ym310_protecl.h"
- /*-----------------------------------------------------------------------------*/
- typedef struct
- {
- char username[32];
- char password[32];
- char host[64];
- uint16_t port;
- char filename[128];
- } ftp_url_info_t;
- /*-----------------------------------------------------------------------------*/
- extern void ym310_clear_rx_buffer(void);
- extern at_resp_type_t ym310_at_exec_cmd(const char *cmd, char *resp,
- rt_uint16_t resp_size, rt_int32_t timeout_ms);
- extern at_resp_type_t ym310_at_exec_cmd_with_retry(const char *cmd, char *resp,
- rt_uint16_t resp_size, rt_int32_t timeout_ms,
- rt_uint8_t retry_max);
- extern int ym310_at_send_line(const char *line);
- extern int ym310_at_send_raw(const char *data, rt_uint16_t len);
- extern int ym310_at_wait_for(const char *expected, char *buf, rt_uint16_t buf_size,
- rt_int32_t timeout_ms);
- extern rt_err_t ym310_at_mutex_lock(rt_int32_t timeout_ms);
- extern void ym310_at_mutex_unlock(void);
- extern int ym310_at_core_init(void);
- extern uint8_t ef_cfged;
- extern rt_device_t g_ym310_uart;
- extern char start_ftp;
- extern int ym310_ftp_parse_url(const char *url, ftp_url_info_t *info);
- extern int ym310_ftp_wait_sim_ready(void);
- extern int ym310_ftp_wait_network_ready(void);
- extern int ym310_ftp_activate_pdp(void);
- extern int ym310_ftp_configure(void);
- /*-----------------------------------------------------------------------------*/
- #define UPLOAD_FLAG_VALUE 0x9D
- #define EF_UPLOAD_KEY "upload_ecode"
- #define FTP_DEFAULT_UPLOAD_URL "ftp://updateuser:123456@www.yfzhwlw.com:21/scd_log"
- // #define FTP_DEFAULT_UPLOAD_URL "ftp://ruofeng:MhRx3NJsiaAx@47.92.123.150:21/"
- #define FTP_CMD_TIMEOUT_MS 20000
- #define FTP_OPEN_TIMEOUT_MS 20000
- #define FTP_CHUNK_BUF_SIZE 4096 /* 每块最大字节数 */
- /*-----------------------------------------------------------------------------*/
- /**
- * @brief 发送一个数据块(通过 QFTPPUT 续传)
- * @param filename 上传文件名
- * @param offset 文件的起始写入位置(字节)
- * @param data 数据缓冲区
- * @param len 数据长度
- * @param is_last 是否为最后一包
- * @return 成功返回 RT_EOK
- */
- static int ftp_put_chunk(const char *filename, uint32_t offset,
- const uint8_t *data, uint16_t len, rt_bool_t is_last)
- {
- char cmd[128];
- int retry_count = 0;
- const int MAX_RETRY = 3;
- while (retry_count < MAX_RETRY)
- {
- rt_snprintf(cmd, sizeof(cmd), "AT+QFTPPUT=\"%s\",\"COM:\",%lu,%d,%d",
- filename, offset, len, is_last ? 1 : 0);
- LOG_D("FTP chunk: %s offset=%lu len=%d last=%d (retry %d/%d)",
- cmd, offset, len, is_last, retry_count + 1, MAX_RETRY);
- if (ym310_at_mutex_lock(60000) != RT_EOK)
- {
- LOG_E("FTP chunk: mutex lock failed");
- return -RT_ERROR;
- }
- ym310_clear_rx_buffer();
- rt_thread_mdelay(10);
- if (ym310_at_send_line(cmd) != RT_EOK)
- {
- LOG_E("FTP chunk: send command failed");
- ym310_at_mutex_unlock();
- retry_count++;
- rt_thread_mdelay(500);
- continue;
- }
- /* 等待 CONNECT */
- if (ym310_at_wait_for("CONNECT", NULL, 0, 30000) != RT_EOK)
- {
- LOG_E("FTP chunk: no CONNECT (retry %d)", retry_count + 1);
- ym310_at_mutex_unlock();
- retry_count++;
- rt_thread_mdelay(500);
- continue;
- }
- /* 发送数据 */
- if (ym310_at_send_raw((const char *)data, len) < 0)
- {
- LOG_E("FTP chunk: send data failed");
- ym310_at_mutex_unlock();
- retry_count++;
- rt_thread_mdelay(500);
- continue;
- }
- /* 等待 +QFTPPUT: 结果 */
- char urc[64] = {0};
- if (ym310_at_wait_for("+QFTPPUT:", urc, sizeof(urc), 30000) != RT_EOK)
- {
- LOG_E("FTP chunk: no +QFTPPUT URC (retry %d)", retry_count + 1);
- ym310_at_mutex_unlock();
- retry_count++;
- rt_thread_mdelay(500);
- continue;
- }
- int err = ym310_str_extract_nth_int(urc, 0);
- int transferred = ym310_str_extract_nth_int(urc, 1);
- if (err != 0 || transferred != len)
- {
- LOG_E("FTP chunk: transfer err=%d, got=%d, expected=%d (retry %d)",
- err, transferred, len, retry_count + 1);
- ym310_at_mutex_unlock();
- retry_count++;
- rt_thread_mdelay(500);
- continue;
- }
- /* 消耗掉可能存在的 OK */
- ym310_at_wait_for("OK", NULL, 0, 2000);
- ym310_at_mutex_unlock();
- LOG_I("FTP chunk: success offset=%lu len=%d", offset, len);
- return RT_EOK;
- }
- LOG_E("FTP chunk: failed after %d retries at offset=%lu", MAX_RETRY, offset);
- return -RT_ERROR;
- }
- /*-----------------------------------------------------------------------------*/
- /**
- * @brief 分块上传全部 ecode 记录到指定文件
- * @param filename 上传的文件名
- * @return RT_EOK 成功
- */
- static int ftp_upload_ecode_file(const char *filename)
- {
- uint32_t total_records = ecode_get_count();
- if (total_records == 0)
- {
- LOG_W("FTP Upload: no ecode records");
- return -RT_ERROR;
- }
- LOG_I("FTP Upload: %u records to upload", total_records);
- uint32_t sent_records = 0;
- uint32_t sent_bytes = 0; /* 文件已写入的字节数 (用于 startpos) */
- uint8_t *chunk_buf = rt_malloc(FTP_CHUNK_BUF_SIZE);
- if (!chunk_buf)
- {
- LOG_E("FTP Upload: malloc chunk buffer failed");
- return -RT_ERROR;
- }
- int ret = -RT_ERROR;
- while (sent_records < total_records)
- {
- uint16_t chunk_len = 0;
- uint16_t added = 0;
- /* 填充当前块,尽量填满 4KB */
- while (sent_records + added < total_records &&
- chunk_len < FTP_CHUNK_BUF_SIZE - 200) /* 留出余量 */
- {
- ecode_record_t rec;
- if (ecode_read(sent_records + added, &rec) != 0)
- break; /* 读取失败终止 */
- int wrote = rt_snprintf((char *)chunk_buf + chunk_len,
- FTP_CHUNK_BUF_SIZE - chunk_len,
- "%u - %02u/%02u/%02u %02u:%02u:%02u: (%5u-%-5u) %s\n",
- rec.num,
- rec.time_stamp.tm_year + 2000, rec.time_stamp.tm_mon,
- rec.time_stamp.tm_mday,
- rec.time_stamp.tm_hour, rec.time_stamp.tm_min,
- rec.time_stamp.tm_sec,
- rec.code, rec.para, rec.detail);
- if (wrote < 0 || (uint16_t)wrote >= (FTP_CHUNK_BUF_SIZE - chunk_len))
- break;
- chunk_len += wrote;
- added++;
- }
- if (added == 0)
- {
- LOG_E("FTP Upload: failed to read any record at offset %u", sent_records);
- goto cleanup;
- }
- /* 判断是否是最后一包 */
- rt_bool_t is_last = (sent_records + added >= total_records) ? RT_TRUE : RT_FALSE;
- LOG_D("FTP Upload: sending chunk records %u-%u, %u bytes",
- sent_records, sent_records + added - 1, chunk_len);
- if (ftp_put_chunk(filename, sent_bytes, chunk_buf, chunk_len, is_last) != RT_EOK)
- {
- LOG_E("FTP Upload: chunk send failed at record %u", sent_records);
- goto cleanup;
- }
- sent_records += added;
- sent_bytes += chunk_len;
- /* 防止过于频繁,给模块一点喘息 */
- rt_thread_mdelay(210);
- }
- ret = RT_EOK;
- LOG_I("FTP Upload: all chunks sent, total bytes=%u", sent_bytes);
- cleanup:
- rt_free(chunk_buf);
- return ret;
- }
- /*-----------------------------------------------------------------------------*/
- /**
- * @brief 上传主流程:解析URL -> 打开FTP -> CWD -> 上传文件 -> 关闭
- */
- static int ftp_upload_ecode_report(const char *upload_url)
- {
- ftp_url_info_t url_info;
- int ret;
- LOG_I("========================================");
- LOG_I("FTP ECO Upload: Starting...");
- LOG_I("Upload URL: %s", upload_url);
- /* 1. 解析 URL */
- if (ym310_ftp_parse_url(upload_url, &url_info) != RT_EOK)
- {
- LOG_E("FTP Upload: URL parse failed");
- return -RT_ERROR;
- }
- /* 2. 基本 AT 就绪 */
- if (ym310_at_exec_cmd_with_retry("AT", NULL, 0, 3000, 3) != AT_RESP_OK)
- {
- LOG_E("FTP Upload: module unresponsive");
- return -RT_ERROR;
- }
- /* === 新增:网络准备与 FTP 配置 === */
- if (ym310_ftp_wait_sim_ready() != RT_EOK)
- {
- LOG_E("FTP Upload: SIM not ready");
- return -RT_ERROR;
- }
- if (ym310_ftp_wait_network_ready() != RT_EOK)
- {
- LOG_E("FTP Upload: Network not ready");
- return -RT_ERROR;
- }
- if (ym310_ftp_activate_pdp() != RT_EOK)
- {
- LOG_E("FTP Upload: PDP activation failed");
- return -RT_ERROR;
- }
- if (ym310_ftp_configure() != RT_EOK)
- {
- LOG_E("FTP Upload: FTP configure failed");
- return -RT_ERROR;
- }
- ym310_at_exec_cmd_with_retry("ATE0", NULL, 0, 3000, 2);
- ym310_at_exec_cmd_with_retry("AT+CMEE=2", NULL, 0, 3000, 2);
- /* 3. 设置 FTP 账户 */
- char cmd[128];
- rt_snprintf(cmd, sizeof(cmd), "AT+QFTPCFG=\"account\",\"%s\",\"%s\"",
- url_info.username, url_info.password);
- if (ym310_at_exec_cmd_with_retry(cmd, NULL, 0, FTP_CMD_TIMEOUT_MS, 3) != AT_RESP_OK)
- {
- LOG_E("FTP Upload: set account failed");
- return -RT_ERROR;
- }
- /* 4. 打开 FTP 连接 */
- rt_memset(cmd, 0, sizeof(cmd));
- rt_snprintf(cmd, sizeof(cmd), "AT+QFTPOPEN=\"%s\",%d", url_info.host, url_info.port);
- LOG_I("FTP Upload: opening %s:%d", url_info.host, url_info.port);
- ym310_clear_rx_buffer();
- rt_thread_mdelay(10);
- if (ym310_at_send_line(cmd) != RT_EOK)
- {
- LOG_E("FTP Upload: send OPEN failed");
- return -RT_ERROR;
- }
- if (ym310_at_wait_for("OK", NULL, 0, 10000) != RT_EOK)
- {
- LOG_E("FTP Upload: no OK after OPEN");
- return -RT_ERROR;
- }
- char open_urc[64] = {0};
- if (ym310_at_wait_for("+QFTPOPEN:", open_urc, sizeof(open_urc), FTP_OPEN_TIMEOUT_MS) != RT_EOK)
- {
- LOG_E("FTP Upload: no +QFTPOPEN URC");
- ym310_at_exec_cmd("AT+QFTPCLOSE", NULL, 0, 5000);
- return -RT_ERROR;
- }
- if (ym310_str_extract_nth_int(open_urc, 0) != 0)
- {
- LOG_E("FTP Upload: OPEN failed, err=%d", ym310_str_extract_nth_int(open_urc, 0));
- ym310_at_exec_cmd("AT+QFTPCLOSE", NULL, 0, 5000);
- return -RT_ERROR;
- }
- LOG_I("FTP Upload: connection opened");
- /* 5. 切换目录 */
- const char *target_dir = url_info.filename;
- if (target_dir[0] == '\0')
- target_dir = "/";
- rt_snprintf(cmd, sizeof(cmd), "AT+QFTPCWD=\"%s\"", target_dir);
- LOG_I("FTP Upload: CWD to %s", target_dir);
- ym310_clear_rx_buffer();
- rt_thread_mdelay(10);
- if (ym310_at_send_line(cmd) != RT_EOK)
- {
- LOG_E("FTP Upload: send CWD failed");
- ym310_at_exec_cmd("AT+QFTPCLOSE", NULL, 0, 5000);
- return -RT_ERROR;
- }
- if (ym310_at_wait_for("OK", NULL, 0, 10000) != RT_EOK)
- {
- LOG_E("FTP Upload: no OK after CWD");
- ym310_at_exec_cmd("AT+QFTPCLOSE", NULL, 0, 5000);
- return -RT_ERROR;
- }
- char cwd_urc[64] = {0};
- if (ym310_at_wait_for("+QFTPCWD:", cwd_urc, sizeof(cwd_urc), FTP_CMD_TIMEOUT_MS) != RT_EOK)
- {
- LOG_E("FTP Upload: no +QFTPCWD URC");
- ym310_at_exec_cmd("AT+QFTPCLOSE", NULL, 0, 5000);
- return -RT_ERROR;
- }
- if (ym310_str_extract_nth_int(cwd_urc, 0) != 0)
- {
- LOG_E("FTP Upload: CWD failed");
- ym310_at_exec_cmd("AT+QFTPCLOSE", NULL, 0, 5000);
- return -RT_ERROR;
- }
- LOG_I("FTP Upload: CWD success");
- /* 6. 生成文件名 */
- char filename[64];
- rt_snprintf(filename, sizeof(filename), "%s_%04d%02d%02d.txt",
- sys_info.imei, time_now.year, time_now.month, time_now.date);
- /* 7. 分块上传 */
- ret = ftp_upload_ecode_file(filename);
- /* 8. 关闭 FTP 连接 */
- ym310_at_exec_cmd("AT+QFTPCLOSE", NULL, 0, 5000);
- LOG_I("FTP Upload: connection closed");
- if (ret == RT_EOK)
- {
- LOG_I("========================================");
- LOG_I("FTP ECO Upload completed successfully!");
- LOG_I("========================================");
- }
- return ret;
- }
- extern void_lte_power_on(void);
- extern void_lte_power_off(void);
- /*-----------------------------------------------------------------------------*/
- /* 上传任务线程 */
- static void ftp_upload_thread_entry(void *param)
- {
- uint8_t upload_flag = 0;
- rt_thread_mdelay(3000);
- int timeout = 0;
- while (!ef_cfged && timeout < 300)
- {
- rt_thread_mdelay(100);
- timeout++;
- }
- if (!ef_cfged)
- {
- LOG_E("FTP Upload: EasyFlash not ready");
- return;
- }
- ef_get_env_blob(EF_UPLOAD_KEY, &upload_flag, sizeof(upload_flag), NULL);
- if (upload_flag != UPLOAD_FLAG_VALUE)
- {
- LOG_I("FTP Upload: no upload task");
- return;
- }
- /* 清除标志 */
- upload_flag = 0;
- ef_set_env_blob(EF_UPLOAD_KEY, &upload_flag, sizeof(upload_flag));
- LOG_I("FTP Upload: task detected");
- start_ftp = VALID;
- void_lte_power_off();
- rt_thread_mdelay(1000);
- void_lte_power_on();
- if (g_ym310_uart == RT_NULL)
- {
- if (ym310_at_core_init() != RT_EOK)
- {
- LOG_E("FTP Upload: AT core init failed");
- return;
- }
- }
- rt_thread_mdelay(1000);
- int ret = ftp_upload_ecode_report(FTP_DEFAULT_UPLOAD_URL);
- if (ret == RT_EOK)
- {
- Recode_e_code(e_upload_ecode, 1, "EC日志上传成功");
- LOG_I("FTP Upload: success, rebooting...");
- char upload_res = VALID;
- ef_set_env_blob("upload_res", &upload_res, sizeof(upload_res));
- }
- else
- {
- Recode_e_code(e_upload_ecode, 1, "EC日志上传失败");
- LOG_E("FTP Upload: failed, rebooting...");
- char upload_res = 0x9A;
- ef_set_env_blob("upload_res", &upload_res, sizeof(upload_res));
- }
- void_lte_power_off();
- rt_thread_mdelay(1000);
- void_lte_power_on();
- rt_hw_cpu_reset();
- }
- /*-----------------------------------------------------------------------------*/
- static rt_bool_t upload_thread_started = RT_FALSE;
- int ym310_ftp_upload_start(void)
- {
- if (upload_thread_started)
- return RT_EOK;
- rt_thread_t tid = rt_thread_create("ftp_upload",
- ftp_upload_thread_entry,
- RT_NULL,
- 4096,
- 21,
- 10);
- if (tid == RT_NULL)
- {
- LOG_E("FTP Upload: thread create failed");
- return -RT_ERROR;
- }
- rt_thread_startup(tid);
- upload_thread_started = RT_TRUE;
- return RT_EOK;
- }
- INIT_APP_EXPORT(ym310_ftp_upload_start);
|