【问题标题】:Parsing the payload of the AT commands from the full response string从完整的响应字符串中解析 A​​T 命令的有效负载
【发布时间】:2021-12-27 23:21:22
【问题描述】:

我想从 AT 命令的输出中解析实际的负载。

例如:在下面的示例中,我只想阅读 "2021/11/16,11:12:14-32,0"

AT+QLTS=1                          // command
+QLTS: "2021/11/16,11:12:14-32,0"  // response

OK

在以下情况下,我只需要阅读12345678

AT+CIMI     // command
12345678   // example response

所以重点是:并非所有命令的输出格式都相同。我们可以假设响应存储在一个字符串数组中。

我已经实现了GetAtCmdRsp(),它将响应存储在一个字符数组中。

void GetPayload()
{
  char rsp[100] = {0};
  GetAtCmdRsp("AT+QLTS=1", rsp);
  // rsp now contains +QLTS: "2021/11/16,11:12:14-32,0"
  // now, I need to parse "2021/11/16,11:12:14-32,0" out of the response
  
  memset(rsp, 0, sizeof(rsp));

  GetAtCmdRsp("AT+CIMI", rsp);
  // rsp now contains 12345678   
  // no need to do additional parsing since the output already contains the value I need
}

我正在考虑使用char *start = strstr(rsp, ":") + 1; 来启动有效负载,但某些响应可能只包含有效负载,因为AT+CIMI 就是这种情况

也许正则表达式是确定字符串中+<COMMAND>: 模式的好主意?

【问题讨论】:

  • 我会建议进一步抽象 GetAtCmdRsp 以特定于命令。或者至少传入一个抽象的枚举/定义而不是一个固定的字符串,让实现生成字符串和每个命令特定的正确响应解析。
  • 如果您不想在您的实现中添加命令意识,那么另一种选择是使用启发式 - 例如如果响应包含:,则相应地解析,否则返回整个字符串。但这可能不太健壮,最终不得不处理许多不同的情况。
  • 摆弄标准库来做,通常很复杂,状态机是一件令人头疼的事,(我明白吗?)使用re2c,'@'解析器。
  • @kaylum 我不能仅根据: 进行解析,尽管如上所述。有效载荷还可以包含:
  • 是的,这就是为什么我说“最终不得不处理许多不同的情况”。启发式需要处理所有不同的情况。这只是作为第二种选择提供的——我的第一个建议是恕我直言。

标签: c string at-command string-parsing


【解决方案1】:

为了解析 AT 命令响应,一个很好的起点是了解它们可能具有的所有可能格式
因此,我不会实施特定于命令的例程,而是通过 区分命令em>“响应类型”:

  1. 回答中无负载的命令,例如

     AT
     OK
    
  2. 回答中没有标题的命令,例如

     AT+CIMI
     12345678
    
     OK
    
  3. 答案中只有一个标题的命令

     AT+QLTS=1
     +QLTS: "2021/11/16,11:12:14-32,0"
    
     OK
    
  4. 带有多行响应的命令。
    每一行都可以是 "single header" 类型,例如 +CGDCONT:

    AT+CDGCONT?
    +CGDCONT: 1,"IP","epc.tmobile.com","0.0.0.0",0,0
    +CGDCONT: 2,"IP","isp.cingular","0.0.0.0",0,0
    +CGDCONT: 3,"IP","","0.0.0.0",0,0
    
    OK
    

    或者我们甚至可以有混合类型,比如+CGML

     AT+CMGL="ALL"
    
     +CMGL: 1,"REC READ","+XXXXXXXXXX","","21/11/25,10:20:00+00"
     Good morning! How are you?
    
     +CMGL: 2,"REC READ","+XXXXXXXXXX","","21/11/25,10:33:33+00"
     I'll come a little late. See you. Bruce Wayne
    
     OK
    

    (请注意它怎么会有 "empty" 行,即\r\n)。

目前我无法考虑任何其他情况。
通过这种方式,您将能够定义一个类似的枚举

typedef enum
{
    AT_RESPONSE_TYPE_NO_RESPONSE,
    AT_RESPONSE_TYPE_NO_HEADER,
    AT_RESPONSE_TYPE_SINGLE_HEADER,
    AT_RESPONSE_TYPE_MULTILINE,
    AT_RESPONSE_TYPE_MAX
}

并将其传递给您的GetAtCmdRsp( ) 函数,以便相应地解析响应。如果在那个函数中实现微分,或者在它之后(或者在一个外部函数中是你的选择。


没有显式分类的解决方案

一旦你清楚了所有可能发生的场景,你就可以考虑一个适用于所有场景的通用算法:

  1. 在命令 echo 之后和关闭 OKERROR 之前获取完整的响应 resp确保删除结尾的\r\n\r\nOK(或\r\nERROR。或\r\nNO CARRIER。或响应的终止消息可能是什么)。
    还要确保删除命令回声

  2. 如果strlen( resp ) == 0 我们属于NO_RESPONSE 类别,那么工作就完成了

  3. 如果响应中包含\r\ns,我们就有MULTILINE 答案。因此,对其进行标记并将每一行放入数组元素resp_arr[i]。确保删除尾随 \r\n

  4. 对于响应中的每一行(对于每个resp_arr[i] 元素),搜索<CMD> : 模式(不仅是:,它可能包含在有效载荷也是如此!)。类似的东西:

     size_t len = strlen( resp_cur_line );
     char *payload;
    
     if( strstr( "+YOURCMD: ", resp_cur_line) == NULL )
     {
         // We are in "NO_HEADER" case
         payload = resp_cur_line;
     }
     else
     {
         // We are in "HEADER" case
         payload = resp_cur_line + strlen( "+YOURCMD: " );
     }
    

    现在payload 指针指向实际的有效负载。

    请注意,在MULTILINE 回答的情况下,在将行拆分为数组元素后,每个循环也将正确处理像+CMGL 中的混合场景,因为您将能够区分包含的行包含数据的标题(当然还有空行)。有关+CMGL 响应解析的更深入分析,请查看this answer

【讨论】:

  • 关于At the moment I cannot think about any other scenario - 还有一个:多个响应,每个响应跨越多行,例如+CMGL: <index>,<stat>,<oa/da>,[<alpha>],[<scts>][,<tooa/toda>,<length>]<CR><LF><data>[<CR><LF>。因此,名称 AT_RESPONSE_TYPE_HEADERAT_RESPONSE_TYPE_MULTILINE 可能会更好,因为 AT_RESPONSE_TYPE_HEADER_SINGLEAT_RESPONSE_TYPE_HEADER_MULTIPLE 加上额外的 AT_RESPONSE_TYPE_HEADER_MULTIPLE_MULTILINE 用于 CMGL 用例。
  • 虽然可以使用更细粒度的除法,但请参阅this answer 的示例,其中我只使用了 CMGL_NONE、CMGL_PREFIX 和 CMGL_DATA(尽管专门针对 AT+CMGL)。
  • @hlovdal 感谢您的建议。我实际上忘记了+CMGL :)。正如您建议的那样,我将“HEADER”案例重命名为“SINGLE_HEADER”,但我将“MULTILINE”保留为单个场景。事实上,为了删除标题,幸运的是我的伪代码涵盖了“多行混合大小写”,因为每一行都会被相应地解析。 ;) 您链接的答案对+CMGL 更具体,我将其链接。
  • @hlovdal 顺便说一句:我们所有明智的建议只会在某个讨厌的人发送以+CMGL: 开头的短信之前有效。 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-07
  • 2023-03-27
  • 1970-01-01
  • 2012-08-06
  • 2016-07-30
  • 1970-01-01
相关资源
最近更新 更多