ym310_ftp_upload.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501
  1. /*
  2. * YM310 FTP ECO Log Upload Module - v1.1 (Chunked Upload)
  3. *
  4. * - 分块读取 ecode 记录,每块 ≤ 4KB,通过 QFTPPUT 续传
  5. * - 文件名 = IMEI_YYYYMMDD.txt,上传目录为 /scd_txt
  6. * - 上传结束后清除 easyflash 标志并重启
  7. */
  8. #define DBG_TAG "Y-FUL"
  9. #define DBG_LVL DBG_INFO
  10. #include <rtdbg.h>
  11. #include "ym310_mqtt.h"
  12. #include <string.h>
  13. #include <stdio.h>
  14. #include <stdlib.h>
  15. #include <easyflash.h>
  16. #include <rthw.h>
  17. #include <rtthread.h>
  18. #include "ecode.h"
  19. #include "ym310_protecl.h"
  20. /*-----------------------------------------------------------------------------*/
  21. typedef struct
  22. {
  23. char username[32];
  24. char password[32];
  25. char host[64];
  26. uint16_t port;
  27. char filename[128];
  28. } ftp_url_info_t;
  29. /*-----------------------------------------------------------------------------*/
  30. extern void ym310_clear_rx_buffer(void);
  31. extern at_resp_type_t ym310_at_exec_cmd(const char *cmd, char *resp,
  32. rt_uint16_t resp_size, rt_int32_t timeout_ms);
  33. extern at_resp_type_t ym310_at_exec_cmd_with_retry(const char *cmd, char *resp,
  34. rt_uint16_t resp_size, rt_int32_t timeout_ms,
  35. rt_uint8_t retry_max);
  36. extern int ym310_at_send_line(const char *line);
  37. extern int ym310_at_send_raw(const char *data, rt_uint16_t len);
  38. extern int ym310_at_wait_for(const char *expected, char *buf, rt_uint16_t buf_size,
  39. rt_int32_t timeout_ms);
  40. extern rt_err_t ym310_at_mutex_lock(rt_int32_t timeout_ms);
  41. extern void ym310_at_mutex_unlock(void);
  42. extern int ym310_at_core_init(void);
  43. extern uint8_t ef_cfged;
  44. extern rt_device_t g_ym310_uart;
  45. extern char start_ftp;
  46. extern int ym310_ftp_parse_url(const char *url, ftp_url_info_t *info);
  47. extern int ym310_ftp_wait_sim_ready(void);
  48. extern int ym310_ftp_wait_network_ready(void);
  49. extern int ym310_ftp_activate_pdp(void);
  50. extern int ym310_ftp_configure(void);
  51. /*-----------------------------------------------------------------------------*/
  52. #define UPLOAD_FLAG_VALUE 0x9D
  53. #define EF_UPLOAD_KEY "upload_ecode"
  54. #define FTP_DEFAULT_UPLOAD_URL "ftp://updateuser:123456@www.yfzhwlw.com:21/scd_log"
  55. // #define FTP_DEFAULT_UPLOAD_URL "ftp://ruofeng:MhRx3NJsiaAx@47.92.123.150:21/"
  56. #define FTP_CMD_TIMEOUT_MS 20000
  57. #define FTP_OPEN_TIMEOUT_MS 20000
  58. #define FTP_CHUNK_BUF_SIZE 4096 /* 每块最大字节数 */
  59. /*-----------------------------------------------------------------------------*/
  60. /**
  61. * @brief 发送一个数据块(通过 QFTPPUT 续传)
  62. * @param filename 上传文件名
  63. * @param offset 文件的起始写入位置(字节)
  64. * @param data 数据缓冲区
  65. * @param len 数据长度
  66. * @param is_last 是否为最后一包
  67. * @return 成功返回 RT_EOK
  68. */
  69. static int ftp_put_chunk(const char *filename, uint32_t offset,
  70. const uint8_t *data, uint16_t len, rt_bool_t is_last)
  71. {
  72. char cmd[128];
  73. int retry_count = 0;
  74. const int MAX_RETRY = 3;
  75. while (retry_count < MAX_RETRY)
  76. {
  77. rt_snprintf(cmd, sizeof(cmd), "AT+QFTPPUT=\"%s\",\"COM:\",%lu,%d,%d",
  78. filename, offset, len, is_last ? 1 : 0);
  79. LOG_D("FTP chunk: %s offset=%lu len=%d last=%d (retry %d/%d)",
  80. cmd, offset, len, is_last, retry_count + 1, MAX_RETRY);
  81. if (ym310_at_mutex_lock(60000) != RT_EOK)
  82. {
  83. LOG_E("FTP chunk: mutex lock failed");
  84. return -RT_ERROR;
  85. }
  86. ym310_clear_rx_buffer();
  87. rt_thread_mdelay(10);
  88. if (ym310_at_send_line(cmd) != RT_EOK)
  89. {
  90. LOG_E("FTP chunk: send command failed");
  91. ym310_at_mutex_unlock();
  92. retry_count++;
  93. rt_thread_mdelay(500);
  94. continue;
  95. }
  96. /* 等待 CONNECT */
  97. if (ym310_at_wait_for("CONNECT", NULL, 0, 30000) != RT_EOK)
  98. {
  99. LOG_E("FTP chunk: no CONNECT (retry %d)", retry_count + 1);
  100. ym310_at_mutex_unlock();
  101. retry_count++;
  102. rt_thread_mdelay(500);
  103. continue;
  104. }
  105. /* 发送数据 */
  106. if (ym310_at_send_raw((const char *)data, len) < 0)
  107. {
  108. LOG_E("FTP chunk: send data failed");
  109. ym310_at_mutex_unlock();
  110. retry_count++;
  111. rt_thread_mdelay(500);
  112. continue;
  113. }
  114. /* 等待 +QFTPPUT: 结果 */
  115. char urc[64] = {0};
  116. if (ym310_at_wait_for("+QFTPPUT:", urc, sizeof(urc), 30000) != RT_EOK)
  117. {
  118. LOG_E("FTP chunk: no +QFTPPUT URC (retry %d)", retry_count + 1);
  119. ym310_at_mutex_unlock();
  120. retry_count++;
  121. rt_thread_mdelay(500);
  122. continue;
  123. }
  124. int err = ym310_str_extract_nth_int(urc, 0);
  125. int transferred = ym310_str_extract_nth_int(urc, 1);
  126. if (err != 0 || transferred != len)
  127. {
  128. LOG_E("FTP chunk: transfer err=%d, got=%d, expected=%d (retry %d)",
  129. err, transferred, len, retry_count + 1);
  130. ym310_at_mutex_unlock();
  131. retry_count++;
  132. rt_thread_mdelay(500);
  133. continue;
  134. }
  135. /* 消耗掉可能存在的 OK */
  136. ym310_at_wait_for("OK", NULL, 0, 2000);
  137. ym310_at_mutex_unlock();
  138. LOG_I("FTP chunk: success offset=%lu len=%d", offset, len);
  139. return RT_EOK;
  140. }
  141. LOG_E("FTP chunk: failed after %d retries at offset=%lu", MAX_RETRY, offset);
  142. return -RT_ERROR;
  143. }
  144. /*-----------------------------------------------------------------------------*/
  145. /**
  146. * @brief 分块上传全部 ecode 记录到指定文件
  147. * @param filename 上传的文件名
  148. * @return RT_EOK 成功
  149. */
  150. static int ftp_upload_ecode_file(const char *filename)
  151. {
  152. uint32_t total_records = ecode_get_count();
  153. if (total_records == 0)
  154. {
  155. LOG_W("FTP Upload: no ecode records");
  156. return -RT_ERROR;
  157. }
  158. LOG_I("FTP Upload: %u records to upload", total_records);
  159. uint32_t sent_records = 0;
  160. uint32_t sent_bytes = 0; /* 文件已写入的字节数 (用于 startpos) */
  161. uint8_t *chunk_buf = rt_malloc(FTP_CHUNK_BUF_SIZE);
  162. if (!chunk_buf)
  163. {
  164. LOG_E("FTP Upload: malloc chunk buffer failed");
  165. return -RT_ERROR;
  166. }
  167. int ret = -RT_ERROR;
  168. while (sent_records < total_records)
  169. {
  170. uint16_t chunk_len = 0;
  171. uint16_t added = 0;
  172. /* 填充当前块,尽量填满 4KB */
  173. while (sent_records + added < total_records &&
  174. chunk_len < FTP_CHUNK_BUF_SIZE - 200) /* 留出余量 */
  175. {
  176. ecode_record_t rec;
  177. if (ecode_read(sent_records + added, &rec) != 0)
  178. break; /* 读取失败终止 */
  179. int wrote = rt_snprintf((char *)chunk_buf + chunk_len,
  180. FTP_CHUNK_BUF_SIZE - chunk_len,
  181. "%u - %02u/%02u/%02u %02u:%02u:%02u: (%5u-%-5u) %s\n",
  182. rec.num,
  183. rec.time_stamp.tm_year + 2000, rec.time_stamp.tm_mon,
  184. rec.time_stamp.tm_mday,
  185. rec.time_stamp.tm_hour, rec.time_stamp.tm_min,
  186. rec.time_stamp.tm_sec,
  187. rec.code, rec.para, rec.detail);
  188. if (wrote < 0 || (uint16_t)wrote >= (FTP_CHUNK_BUF_SIZE - chunk_len))
  189. break;
  190. chunk_len += wrote;
  191. added++;
  192. }
  193. if (added == 0)
  194. {
  195. LOG_E("FTP Upload: failed to read any record at offset %u", sent_records);
  196. goto cleanup;
  197. }
  198. /* 判断是否是最后一包 */
  199. rt_bool_t is_last = (sent_records + added >= total_records) ? RT_TRUE : RT_FALSE;
  200. LOG_D("FTP Upload: sending chunk records %u-%u, %u bytes",
  201. sent_records, sent_records + added - 1, chunk_len);
  202. if (ftp_put_chunk(filename, sent_bytes, chunk_buf, chunk_len, is_last) != RT_EOK)
  203. {
  204. LOG_E("FTP Upload: chunk send failed at record %u", sent_records);
  205. goto cleanup;
  206. }
  207. sent_records += added;
  208. sent_bytes += chunk_len;
  209. /* 防止过于频繁,给模块一点喘息 */
  210. rt_thread_mdelay(210);
  211. }
  212. ret = RT_EOK;
  213. LOG_I("FTP Upload: all chunks sent, total bytes=%u", sent_bytes);
  214. cleanup:
  215. rt_free(chunk_buf);
  216. return ret;
  217. }
  218. /*-----------------------------------------------------------------------------*/
  219. /**
  220. * @brief 上传主流程:解析URL -> 打开FTP -> CWD -> 上传文件 -> 关闭
  221. */
  222. static int ftp_upload_ecode_report(const char *upload_url)
  223. {
  224. ftp_url_info_t url_info;
  225. int ret;
  226. LOG_I("========================================");
  227. LOG_I("FTP ECO Upload: Starting...");
  228. LOG_I("Upload URL: %s", upload_url);
  229. /* 1. 解析 URL */
  230. if (ym310_ftp_parse_url(upload_url, &url_info) != RT_EOK)
  231. {
  232. LOG_E("FTP Upload: URL parse failed");
  233. return -RT_ERROR;
  234. }
  235. /* 2. 基本 AT 就绪 */
  236. if (ym310_at_exec_cmd_with_retry("AT", NULL, 0, 3000, 3) != AT_RESP_OK)
  237. {
  238. LOG_E("FTP Upload: module unresponsive");
  239. return -RT_ERROR;
  240. }
  241. /* === 新增:网络准备与 FTP 配置 === */
  242. if (ym310_ftp_wait_sim_ready() != RT_EOK)
  243. {
  244. LOG_E("FTP Upload: SIM not ready");
  245. return -RT_ERROR;
  246. }
  247. if (ym310_ftp_wait_network_ready() != RT_EOK)
  248. {
  249. LOG_E("FTP Upload: Network not ready");
  250. return -RT_ERROR;
  251. }
  252. if (ym310_ftp_activate_pdp() != RT_EOK)
  253. {
  254. LOG_E("FTP Upload: PDP activation failed");
  255. return -RT_ERROR;
  256. }
  257. if (ym310_ftp_configure() != RT_EOK)
  258. {
  259. LOG_E("FTP Upload: FTP configure failed");
  260. return -RT_ERROR;
  261. }
  262. ym310_at_exec_cmd_with_retry("ATE0", NULL, 0, 3000, 2);
  263. ym310_at_exec_cmd_with_retry("AT+CMEE=2", NULL, 0, 3000, 2);
  264. /* 3. 设置 FTP 账户 */
  265. char cmd[128];
  266. rt_snprintf(cmd, sizeof(cmd), "AT+QFTPCFG=\"account\",\"%s\",\"%s\"",
  267. url_info.username, url_info.password);
  268. if (ym310_at_exec_cmd_with_retry(cmd, NULL, 0, FTP_CMD_TIMEOUT_MS, 3) != AT_RESP_OK)
  269. {
  270. LOG_E("FTP Upload: set account failed");
  271. return -RT_ERROR;
  272. }
  273. /* 4. 打开 FTP 连接 */
  274. rt_memset(cmd, 0, sizeof(cmd));
  275. rt_snprintf(cmd, sizeof(cmd), "AT+QFTPOPEN=\"%s\",%d", url_info.host, url_info.port);
  276. LOG_I("FTP Upload: opening %s:%d", url_info.host, url_info.port);
  277. ym310_clear_rx_buffer();
  278. rt_thread_mdelay(10);
  279. if (ym310_at_send_line(cmd) != RT_EOK)
  280. {
  281. LOG_E("FTP Upload: send OPEN failed");
  282. return -RT_ERROR;
  283. }
  284. if (ym310_at_wait_for("OK", NULL, 0, 10000) != RT_EOK)
  285. {
  286. LOG_E("FTP Upload: no OK after OPEN");
  287. return -RT_ERROR;
  288. }
  289. char open_urc[64] = {0};
  290. if (ym310_at_wait_for("+QFTPOPEN:", open_urc, sizeof(open_urc), FTP_OPEN_TIMEOUT_MS) != RT_EOK)
  291. {
  292. LOG_E("FTP Upload: no +QFTPOPEN URC");
  293. ym310_at_exec_cmd("AT+QFTPCLOSE", NULL, 0, 5000);
  294. return -RT_ERROR;
  295. }
  296. if (ym310_str_extract_nth_int(open_urc, 0) != 0)
  297. {
  298. LOG_E("FTP Upload: OPEN failed, err=%d", ym310_str_extract_nth_int(open_urc, 0));
  299. ym310_at_exec_cmd("AT+QFTPCLOSE", NULL, 0, 5000);
  300. return -RT_ERROR;
  301. }
  302. LOG_I("FTP Upload: connection opened");
  303. /* 5. 切换目录 */
  304. const char *target_dir = url_info.filename;
  305. if (target_dir[0] == '\0')
  306. target_dir = "/";
  307. rt_snprintf(cmd, sizeof(cmd), "AT+QFTPCWD=\"%s\"", target_dir);
  308. LOG_I("FTP Upload: CWD to %s", target_dir);
  309. ym310_clear_rx_buffer();
  310. rt_thread_mdelay(10);
  311. if (ym310_at_send_line(cmd) != RT_EOK)
  312. {
  313. LOG_E("FTP Upload: send CWD failed");
  314. ym310_at_exec_cmd("AT+QFTPCLOSE", NULL, 0, 5000);
  315. return -RT_ERROR;
  316. }
  317. if (ym310_at_wait_for("OK", NULL, 0, 10000) != RT_EOK)
  318. {
  319. LOG_E("FTP Upload: no OK after CWD");
  320. ym310_at_exec_cmd("AT+QFTPCLOSE", NULL, 0, 5000);
  321. return -RT_ERROR;
  322. }
  323. char cwd_urc[64] = {0};
  324. if (ym310_at_wait_for("+QFTPCWD:", cwd_urc, sizeof(cwd_urc), FTP_CMD_TIMEOUT_MS) != RT_EOK)
  325. {
  326. LOG_E("FTP Upload: no +QFTPCWD URC");
  327. ym310_at_exec_cmd("AT+QFTPCLOSE", NULL, 0, 5000);
  328. return -RT_ERROR;
  329. }
  330. if (ym310_str_extract_nth_int(cwd_urc, 0) != 0)
  331. {
  332. LOG_E("FTP Upload: CWD failed");
  333. ym310_at_exec_cmd("AT+QFTPCLOSE", NULL, 0, 5000);
  334. return -RT_ERROR;
  335. }
  336. LOG_I("FTP Upload: CWD success");
  337. /* 6. 生成文件名 */
  338. char filename[64];
  339. rt_snprintf(filename, sizeof(filename), "%s_%04d%02d%02d.txt",
  340. sys_info.imei, time_now.year, time_now.month, time_now.date);
  341. /* 7. 分块上传 */
  342. ret = ftp_upload_ecode_file(filename);
  343. /* 8. 关闭 FTP 连接 */
  344. ym310_at_exec_cmd("AT+QFTPCLOSE", NULL, 0, 5000);
  345. LOG_I("FTP Upload: connection closed");
  346. if (ret == RT_EOK)
  347. {
  348. LOG_I("========================================");
  349. LOG_I("FTP ECO Upload completed successfully!");
  350. LOG_I("========================================");
  351. }
  352. return ret;
  353. }
  354. extern void_lte_power_on(void);
  355. extern void_lte_power_off(void);
  356. /*-----------------------------------------------------------------------------*/
  357. /* 上传任务线程 */
  358. static void ftp_upload_thread_entry(void *param)
  359. {
  360. uint8_t upload_flag = 0;
  361. rt_thread_mdelay(3000);
  362. int timeout = 0;
  363. while (!ef_cfged && timeout < 300)
  364. {
  365. rt_thread_mdelay(100);
  366. timeout++;
  367. }
  368. if (!ef_cfged)
  369. {
  370. LOG_E("FTP Upload: EasyFlash not ready");
  371. return;
  372. }
  373. ef_get_env_blob(EF_UPLOAD_KEY, &upload_flag, sizeof(upload_flag), NULL);
  374. if (upload_flag != UPLOAD_FLAG_VALUE)
  375. {
  376. LOG_I("FTP Upload: no upload task");
  377. return;
  378. }
  379. /* 清除标志 */
  380. upload_flag = 0;
  381. ef_set_env_blob(EF_UPLOAD_KEY, &upload_flag, sizeof(upload_flag));
  382. LOG_I("FTP Upload: task detected");
  383. start_ftp = VALID;
  384. void_lte_power_off();
  385. rt_thread_mdelay(1000);
  386. void_lte_power_on();
  387. if (g_ym310_uart == RT_NULL)
  388. {
  389. if (ym310_at_core_init() != RT_EOK)
  390. {
  391. LOG_E("FTP Upload: AT core init failed");
  392. return;
  393. }
  394. }
  395. rt_thread_mdelay(1000);
  396. int ret = ftp_upload_ecode_report(FTP_DEFAULT_UPLOAD_URL);
  397. if (ret == RT_EOK)
  398. {
  399. Recode_e_code(e_upload_ecode, 1, "EC日志上传成功");
  400. LOG_I("FTP Upload: success, rebooting...");
  401. char upload_res = VALID;
  402. ef_set_env_blob("upload_res", &upload_res, sizeof(upload_res));
  403. }
  404. else
  405. {
  406. Recode_e_code(e_upload_ecode, 1, "EC日志上传失败");
  407. LOG_E("FTP Upload: failed, rebooting...");
  408. char upload_res = 0x9A;
  409. ef_set_env_blob("upload_res", &upload_res, sizeof(upload_res));
  410. }
  411. void_lte_power_off();
  412. rt_thread_mdelay(1000);
  413. void_lte_power_on();
  414. rt_hw_cpu_reset();
  415. }
  416. /*-----------------------------------------------------------------------------*/
  417. static rt_bool_t upload_thread_started = RT_FALSE;
  418. int ym310_ftp_upload_start(void)
  419. {
  420. if (upload_thread_started)
  421. return RT_EOK;
  422. rt_thread_t tid = rt_thread_create("ftp_upload",
  423. ftp_upload_thread_entry,
  424. RT_NULL,
  425. 4096,
  426. 21,
  427. 10);
  428. if (tid == RT_NULL)
  429. {
  430. LOG_E("FTP Upload: thread create failed");
  431. return -RT_ERROR;
  432. }
  433. rt_thread_startup(tid);
  434. upload_thread_started = RT_TRUE;
  435. return RT_EOK;
  436. }
  437. INIT_APP_EXPORT(ym310_ftp_upload_start);