luat_lib_ercoap.c 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. /*
  2. @module ercoap
  3. @summary 新的Coap协议解析库
  4. @version 1.0
  5. @date 2023.11.14
  6. @auther wendal
  7. @demo ercoap
  8. */
  9. #include "luat_base.h"
  10. #include "luat_timer.h"
  11. #include "luat_mem.h"
  12. #include "luat_msgbus.h"
  13. #include "er-coap-13.h"
  14. #define LUAT_LOG_TAG "ercoap"
  15. #include "luat_log.h"
  16. /*
  17. 解析coap数据包
  18. @ercoap.parse(data)
  19. @string coap数据包
  20. @return table 成功返回table,否则返回nil
  21. @usage
  22. -- 本函数是解析coap数据包
  23. local rcoap = ercoap.parse(data)
  24. if rcoap then
  25. log.info("coap", rcoap.type, rcoap.code, rcoap.payload)
  26. -- rcoap的属性
  27. -- type 消息类型, 0 - CON 需要答复, 1 - NON 无需答复, 2 - ACK 已收到, 3 - RST 出错了
  28. -- msgid 消息id
  29. -- payload 携带的数据
  30. -- code 类似于http的statue code, 通过有 2xx 正常, 4xx 出错了
  31. else
  32. log.info("ercoap", "数据包解析失败")
  33. end
  34. */
  35. static int l_ercoap_parse(lua_State *L)
  36. {
  37. size_t len = 0;
  38. coap_packet_t coap_packet[1] = { 0 };
  39. coap_status_t coap_error_code = COAP_STATUS_NO_ERROR;
  40. const char* data = luaL_checklstring(L, 1, &len);
  41. int ret = coap_parse_message(coap_packet, data, len);
  42. if (ret) {
  43. LLOGD("coap_parse_message %d", ret);
  44. return 0;
  45. }
  46. lua_newtable(L);
  47. lua_pushinteger(L, coap_packet->type);
  48. lua_setfield(L, -2, "type");
  49. lua_pushlstring(L, coap_packet->token, coap_packet->token_len);
  50. lua_setfield(L, -2, "token");
  51. lua_pushinteger(L, (coap_packet->code >> 5) * 100 + (coap_packet->code & 0x1F));
  52. lua_setfield(L, -2, "code");
  53. lua_pushinteger(L, coap_packet->mid);
  54. lua_setfield(L, -2, "msgid");
  55. lua_pushlstring(L, coap_packet->payload, coap_packet->payload_len);
  56. lua_setfield(L, -2, "payload");
  57. return 1;
  58. }
  59. /*
  60. 打印coap数据包
  61. @ercoap.print(data)
  62. @string coap数据包
  63. @return boolean 解析成功返回true
  64. @usage
  65. -- 本函数单纯就打印一下coap数据包
  66. */
  67. static int l_ercoap_print(lua_State *L)
  68. {
  69. size_t len = 0;
  70. coap_packet_t coap_packet[1] = { 0 };
  71. coap_status_t coap_error_code = COAP_STATUS_NO_ERROR;
  72. const char* data = luaL_checklstring(L, 1, &len);
  73. int ret = coap_parse_message(coap_packet, data, len);
  74. if (ret) {
  75. LLOGD("coap_parse_message %d", ret);
  76. return 0;
  77. }
  78. LLOGD(
  79. "Parsed: type %u, tkl %u, token %02x%02x%02x%02x%02x%02x%02x%02x ,code %u.%.2u, mid %u",
  80. coap_packet->type,
  81. coap_packet->token_len,
  82. coap_packet->token[0],
  83. coap_packet->token[1],
  84. coap_packet->token[2],
  85. coap_packet->token[3],
  86. coap_packet->token[4],
  87. coap_packet->token[5],
  88. coap_packet->token[6],
  89. coap_packet->token[7],
  90. coap_packet->code >> 5,
  91. coap_packet->code & 0x1F,
  92. coap_packet->mid);
  93. if (coap_packet->payload_len > 0) {
  94. //LLOGD("!Payload! %.*s", coap_packet->payload_len, coap_packet->payload);
  95. }
  96. lua_pushboolean(L, 1);
  97. return 1;
  98. }
  99. //--------------------------------------------------
  100. //--------- 针对OneNet的数据封装
  101. //--------------------------------------------------
  102. static uint16_t onenet_mid = 1;
  103. static int32_t uri_add_path_tm_prefix(coap_packet_t* pkt, const char* suffix, const uint8_t* product_id, const uint8_t* dev_name)
  104. {
  105. //$sys/{pid}/{device-name}/
  106. uint16_t header_uri_temp_length
  107. = 8 + strlen((const uint8_t*)suffix) + strlen((const uint8_t*)product_id) + strlen((const uint8_t*)dev_name); // 7+1
  108. uint8_t* header_uri_temp = luat_heap_calloc(1, header_uri_temp_length);
  109. if (header_uri_temp == NULL) {
  110. return -1;
  111. }
  112. sprintf_(header_uri_temp, (const uint8_t*)"$sys/%s/%s/%s", product_id, dev_name, suffix);
  113. // LLOGD("URI %s", header_uri_temp);
  114. coap_set_header_uri_path(pkt, (const char*)header_uri_temp);
  115. luat_heap_free(header_uri_temp);
  116. return 0;
  117. }
  118. static int32_t payload_add_lifetime_and_saastoken(coap_packet_t* pkt, uint32_t lifetime, const uint8_t* saastoken)
  119. {
  120. uint8_t* payload_temp = NULL;
  121. uint16_t payload_temp_length = 4; //{,}
  122. payload_temp_length += (7 + strlen(saastoken)); //"st":""
  123. if (lifetime != 0) {
  124. payload_temp_length += 14; // 4+10,10是2的32次方 "lt":
  125. }
  126. if (NULL == (payload_temp = luat_heap_calloc(1, payload_temp_length))) {
  127. return -1;
  128. }
  129. sprintf_(payload_temp, (const uint8_t*)"{\"st\":\"%s\"", saastoken);
  130. if (lifetime != 0) {
  131. sprintf_(payload_temp + strlen((const uint8_t*)payload_temp), (const uint8_t*)",\"lt\":%d", lifetime);
  132. }
  133. strcat(payload_temp, (const uint8_t*)"}");
  134. coap_set_header_content_type(pkt, APPLICATION_JSON);
  135. coap_set_payload(pkt, (const void*)payload_temp, strlen(payload_temp));
  136. return 0;
  137. }
  138. static void add_random_token(coap_packet_t* pkt, uint16_t mid)
  139. {
  140. // generate a token
  141. uint8_t temp_token[8];
  142. uint64_t tv_sec = luat_mcu_tick64();
  143. temp_token[0] = (uint8_t)(mid | (tv_sec >> 2));
  144. temp_token[1] = (uint8_t)(mid | (tv_sec >> 4));
  145. temp_token[2] = (uint8_t)(tv_sec);
  146. temp_token[3] = (uint8_t)(tv_sec >> 6);
  147. temp_token[4] = (uint8_t)(tv_sec >> 8);
  148. temp_token[5] = (uint8_t)(tv_sec >> 10);
  149. temp_token[6] = (uint8_t)(tv_sec >> 12);
  150. temp_token[7] = (uint8_t)(tv_sec >> 14);
  151. coap_set_header_token(pkt, temp_token, 8);
  152. }
  153. /*
  154. 快速生成onenet数据包
  155. @ercoap.onenet(tp, product_id, device_name, token, payload)
  156. @string 请求类型,作为reply时可选,其他情况必选
  157. @string 项目id,必须填写
  158. @string 设备名称,必须填写
  159. @string token,必须填写
  160. @string 物模型json字符串,可选
  161. @return string 合成好的数据包,可通过UDP上行
  162. @usage
  163. -- 参考文档: coap接入 https://open.iot.10086.cn/doc/v5/fuse/detail/924
  164. -- 参考文档: 物模型 https://open.iot.10086.cn/doc/v5/fuse/detail/902
  165. -- 类型 tp值 token来源 payload
  166. -- 登陆 login iotauth.onenet函数生成 无
  167. -- 心跳 keep_live iotauth.onenet函数生成 无
  168. -- 登出 logout iotauth.onenet函数生成 无
  169. -- 属性上报 thing/property/post login时获取 必须有
  170. -- 属性回复 thing/property/reply login时获取 必须有
  171. -- 事件上报 thing/event/post login时获取 必须有
  172. -- 远程调用答复 无 login时获取 必须有
  173. */
  174. static int l_ercoap_onenet(lua_State *L)
  175. {
  176. int32_t ret = 0;
  177. uint16_t mid = 0;
  178. size_t len = 0;
  179. size_t payload_len = 0;
  180. size_t token_len = 0;
  181. coap_packet_t message[1] = { 0 };
  182. const char* type = luaL_checklstring(L, 1, &len);
  183. const char* product_id = luaL_checklstring(L, 2, &len);
  184. const char* dev_name = luaL_checklstring(L, 3, &len);
  185. const char* saastoken = luaL_checklstring(L, 4, &token_len);
  186. const char* payload = luaL_optlstring(L, 5, "", &payload_len);
  187. int lifetime = 3600;
  188. mid = onenet_mid ++;
  189. coap_init_message(message, COAP_TYPE_CON, COAP_POST, mid);
  190. // LLOGD("coap %s %s %s %s", type, product_id, dev_name, saastoken);
  191. if (type != NULL && strlen(type) > 0) {
  192. ret = uri_add_path_tm_prefix(message, type, product_id, dev_name);
  193. if (ret != 0) {
  194. LLOGD("uri_add_path_tm_prefix %d", ret);
  195. goto exit1;
  196. }
  197. }
  198. if (payload_len > 0){
  199. // LLOGD("自定义payload %s", payload);
  200. coap_set_header_accept(message, APPLICATION_JSON);
  201. coap_set_header_content_type(message, APPLICATION_JSON);
  202. coap_set_payload(message, (const void*)payload, payload_len);
  203. coap_set_header_token(message, saastoken, token_len);
  204. }
  205. else {
  206. ret = payload_add_lifetime_and_saastoken(message, lifetime, saastoken);
  207. add_random_token(message, mid);
  208. if (ret != 0) {
  209. LLOGD("payload_add_lifetime_and_saastoken %d", ret);
  210. goto exit2;
  211. }
  212. }
  213. //LLOGD("uplink payload %s", message->payload);
  214. ret = coap_serialize_get_size(message);
  215. if (ret < 1) {
  216. LLOGD("coap_serialize_get_size %d", ret);
  217. goto exit2;
  218. }
  219. char* ptr = luat_heap_malloc(ret);
  220. if (ptr == NULL) {
  221. LLOGD("out of memory when malloc message buff");
  222. goto exit2;
  223. }
  224. ret = coap_serialize_message(message, ptr);
  225. lua_pushlstring(L, ptr, ret);
  226. luat_heap_free(ptr);
  227. coap_free_header(message);
  228. return 1;
  229. exit2:
  230. coap_free_header(message);
  231. exit1:
  232. return 0;
  233. }
  234. #include "rotable2.h"
  235. static const rotable_Reg_t reg_ercoap[] =
  236. {
  237. { "onenet", ROREG_FUNC(l_ercoap_onenet)},
  238. { "parse", ROREG_FUNC(l_ercoap_parse)},
  239. { "print", ROREG_FUNC(l_ercoap_print)},
  240. { NULL, ROREG_INT(0) }
  241. };
  242. LUAMOD_API int luaopen_ercoap( lua_State *L ) {
  243. luat_newlib2(L, reg_ercoap);
  244. return 1;
  245. }