0

I tried to write a parser to a simple binary protocol

  • 0 or more out of frame bytes
  • Start of frame with STX (0x02)
  • Two messaje length bytes for messaje length.
  • One command byte .
  • 0 or more data bytes.
  • One checksun byte.

The ragel script

static int len;
static unsigned char command;
static int indexx;
static unsigned char data[0x10000];
static unsigned char checksum;
%%{

  machine themachine;

  action out_of_frame_action  { }
  action start_of_frame_action {}
  action first_byte_len_action { len = fc *256; }
  action second_byte_len_action { len += fc; }
  action command_action { command = fc; indexx=0; len--;}
  action data_action { 
            if(!len) 
                fgoto check; 
            else
            {
                data[indexx++];
                len --;
            }
  }
  action checksum_action { checksum = fc; }

  stx = 0x02;
  first_byte_len = any;
  second_byte_len = any;
  command = any;
  data = any*;
  checksum = any;

  main :=  (^stx* $ out_of_frame_action) 
           (stx > start_of_frame_action) 
       (first_byte_len > first_byte_len_action) 
       (second_byte_len > second_byte_len_action) 
       (command > command_action) 
       (data $ data_action) ;

 check :=   (checksum % checksum_action)  ;

}%%

machine state

But the result machine don't jump to check (5) status so doesn't execute checksum_action and don't return to the first state to continue parsing.

What is wrong?

Follow @Roman recomendations I post a full example. This example doesn't work either because get a wrong checksum.

#include <stdint.h>
#include <stdio.h>

static int len;
static unsigned char command;

static int indexx;
static unsigned char data[0x10000];
static unsigned char checksum;

%%{

  machine themachine;

  stx = 0x02;

  action out_of_frame_action
  {
      printf("OOF %02X\n",fc);
  }

  action start_of_frame_action
  {
      printf("STX ");
  }

  action first_byte_len_action
  {
      printf("fbl[%d] ",(int)(fc));
      len = 256*((unsigned char)fc);
  }

  action second_byte_len_action
  {
      len += (unsigned char )fc ;
      printf("sbl[%d] Len=%d ",(int)(fc),len);
      indexx=0;
      len-=2; // Checksum and command are included on message len
  }

  action command_action
  {
      command = fc;
      printf("CMM=%02X ", command);
  }

  action check_len
  {
      len > 0
  }

  action data_action
  {
      data[indexx++]=fc;
      printf("[%02X]",(unsigned char) fc);
      len--;
  }

  action checksum_action
  {
      checksum = fc;
      printf(" Chk=%02X \n",checksum);
  }


  first_byte_len = any ;
  second_byte_len = any;
  command = any;
  data = any*;
  checksum = any;


  check = (checksum % checksum_action);

  main := ((^stx* $ out_of_frame_action)
           (stx > start_of_frame_action)
           (first_byte_len > first_byte_len_action)
           (second_byte_len > second_byte_len_action)
           (command > command_action)
           (data when check_len $ data_action)
           check
          )**;

}%%

%% write data;

int main(void)
{
    uint8_t buf[] = {
        0x00,                                    // OOF 00
        0x00,                                    // OOF 00
        0x02,0x00,0x03,0x20,0x01,0x21,           // STX fbl[0] sbl[3] Len = 3 CMM=20 [01] Chk=21
        0x00,                                    // OOF 00
        0x00,                                    // OOF 00
        0x02,0x00,0x05,0x81,0x01,0x02,0x03,0x87, // STX fbl[0] sbl[5] Len = 5 CMM=81 [01][02][03] Chk=87
        0x02,0x00,0x03,0x03,0x01,0x04,           // STX fbl[0] sbl[3] Len = 3 CMM=03 [01] Chk=04
        0x02,0x00,0x05,0x07,0x01,0x02,0x03,0x0D  // STX fbl[0] sbl[5] Len = 5 CMM=07 [01][02][03] Chk=0D
    };

    int cs;
    uint8_t *p, *pe, *eof;


    p = buf;
    eof = pe = buf + sizeof(buf)/sizeof(uint8_t);

    %% write init;
    %% write exec;

}

I change

check = (checksum % checksum_action);

by

check = (checksum > checksum_action);

but is not a solution.

4

2 回答 2

2
  1. 如果您向我们提供了示例输入和完整的源代码来验证整个程序,那就太好了
  2. 我不确定为什么你的goto不起作用,但我可能会通过根本不使用来简化事情fgoto。这里不需要它,你可以使用语义条件 ( when)
  3. 您的机器不会返回到初始状态,因为没有任何东西告诉它。

所以我对你的原始代码做了这些小改动:

--- main.c.orig 2017-12-17 11:48:49.369200291 +0300
+++ main.c      2017-12-20 22:51:11.096283379 +0300
@@ -12,14 +12,12 @@
        action first_byte_len_action { len = fc *256; }
        action second_byte_len_action { len += fc; }
        action command_action { command = fc; indexx=0; len--;}
+       action check_len {
+               len
+       }
        action data_action { 
-               if(!len) 
-                       fgoto check; 
-               else
-               {
-                       data[indexx++];
-                       len --;
-               }
+               data[indexx++];
+               len--;
        }
        action checksum_action { checksum = fc; }

@@ -30,13 +28,13 @@
        data = any*;
        checksum = any;

-main :=  (^stx* $ out_of_frame_action) 
+       check = (checksum % checksum_action);
+main :=  ((^stx* $ out_of_frame_action) 
                (stx > start_of_frame_action) 
                (first_byte_len > first_byte_len_action) 
                (second_byte_len > second_byte_len_action) 
                (command > command_action) 
-               (data $ data_action) ;
+         (data when check_len $ data_action) check)**;

-check :=   (checksum % checksum_action)  ;

 }%%

请注意如何when使用它来data简化其操作,并注意**main 中最长匹配的 kleene 星号运算符,该运算符指示它在成功帧解析后循环回到起点。这让我们得到了这个漂亮的图表,它可能更接近你想要的:

固定状态机

于 2017-12-20T20:19:58.340 回答
0

在您的第一个示例中,fgoto使用校验和数据,因此稍后在您需要校验和时不可用。您可以使用fhold它来防止它被消耗。checksum_action也应该回到 main (并使用转换而$不是 '%',只有在它是 . 时才会发生EOF)。

action data_action { 
          if(!len) { 
              fhold;
              fgoto check; 
          } else {
              data[indexx++] = fc;
              len --;
          }
}

action checksum_action { checksum = fc; fnext main; }

(或者您可以将校验和逻辑放入data_action代码中)。

于 2018-01-02T19:08:34.037 回答