Perl状态机(FSM)如何解析比特流(字节序列)?
How to do Perl state machine (FSM) to parse bitstream (byte sequence)?
我目前正在使用 Perl 来解析来自 RS232 串行端口的传入命令序列。
我尝试使用状态机,它的预期行为是:
(1) 从串口接收一系列字节;
(2) 状态机以字节为输入,跳转到合适的状态。
我想出了一个简化的演示 Perl 代码(贴在下面),但是遇到了一个问题:
代码进入“while(1){}”时,卡在这里,出不来
因此,$din 字节序列分配被“while(1){}”阻塞,并且对状态机不可见。
因此,FSM 卡在“INIT”状态,根本不跳。
我认为这应该是 Perl 编码中的一个非常简单或入门级的练习,
但是搜索 Google 对我帮助不大。
谁能帮我这个?
提前致谢~
...
my %next_state = (
"INIT" => sub{
$din eq "AA" and return "HEADER0" ;
return "INIT" ;
},
"HEADER0" => sub{
$din eq "99" and return "HEADER1" ;
return "INIT" ;
},
...
);
# Set state machine's initial state.
my $cur_state = "INIT" ;
# Integer for debugging purpose.
my $itgi = 0;
# Run the state machine.
while(1){
$cur_state = $next_state{$cur_state}();
print "$itgi, will jump to: $cur_state\n\n";
$itgi++;
}
# Send in input byte sequence, which simulates
# incoming bytes from RS-232 COM port:
$din = "AA" ;
sleep(1) ;
...
========== 2020.10.09 22:10更新==========
感谢@ikegami 的帮助和努力
调试工作,现在我可以得到我可爱的 Perl 状态
启动机器并运行,代码如下。
但是,还是有一个问题,那就是:
输入字节序列(即@seq)必须是非0x00值;
如果我将 0x00 放入命令序列,则 FSM
遇到0x00.
会退出
这是为什么?该代码使用“$cur_byte >= 0”,这在我看来
应该能够像处理非零值一样处理 0x00。
为什么0x00把状态机拉出运行?
use strict ;
use warnings ;
# input to the state machine
my $din ;
#---------------------------------------------------------------------
# FSM's state table.
#---------------------------------------------------------------------
# Expected input sequence is:
# AA 99 00 01 ....
# In which:
# (1) Fixed pattern "AA" and "99" are two bytes of header,
# (2) Following bytes are uart ID, etc.
my %next_state = (
"INIT" => sub{
# If receives "AA" from input,
# then jumpt to "HEADER0" state:
$din eq "AA" and return "HEADER0" ;
# Otherwise just stay here:
return "INIT" ;
},
"HEADER0" => sub{
# If receives "99" from input,
# then proceed to "HEADER1" state:
$din eq "99" and return "HEADER1" ;
# Otherwise, return to initial state:
return "INIT" ;
},
"HEADER1" => sub{
# Capture first byte of uart ID:
return "UARTID0";
},
"UARTID0" => sub{
# Capture second byte of uart ID:
return "UARTID1";
},
"UARTID1" => sub{
# Capture second byte of uart ID:
return "FINISHED";
},
"FINISHED" => sub{
return "INIT";
},
);
#---------------------------------------------------------------------
# Set state machine's initial state.
#---------------------------------------------------------------------
my $cur_state = "INIT" ;
#---------------------------------------------------------------------
# Send in command sequence
#---------------------------------------------------------------------
my @seq = (-1, 0xAA, -1, 0x99, -1, 0x06, -1, 0x07,
-1, 0x08, -1, 0x09, -1, 0x0a, -1, 0x0b,
-1, 0x0c, -1, 0x0d
);
sub get_next_byte {
while (@seq) { #(A)
my $cur_byte = shift(@seq);
return $cur_byte if $cur_byte >= 0;
#
sleep(-$cur_byte);
}
return (); #(B)
}
#---------------------------------------------------------------------
# Run the state machine.
#---------------------------------------------------------------------
# Integer for debugging purpose.
my $itgi = 0;
while( $din = get_next_byte() ){ #(C)
$din = sprintf("%02X",$din);
$cur_state = $next_state{$cur_state}();
print "-- Iteration $itgi, will jump to: $cur_state\n";
$itgi++;
}
print "-- Program finish.\n";
您无需更改就进入循环 $din
。你需要像
这样的东西
# Run the state machine.
while ( my ($din) = get_next_byte() ) {
$din = sprintf("%02X", $din);
$cur_state = $next_state{$cur_state}();
print "$itgi, will jump to: $cur_state\n\n";
$itgi++;
}
出于测试目的,您可以使用
my @seq = (-1, 0xAA, -1, 0x99);
sub get_next_byte {
while (@seq) {
my $next = shift(@seq);
return $next if $next >= 0;
sleep(-$next);
}
return ();
}
感谢@zdim 和@ikegami 的帮助,终于把这个程序搞定了。我将post我的工作代码如下,以防有人有同样的问题。
以下代码受 zdim 启发:
use strict ;
use warnings ;
# input to the state machine
my $din ;
# FSM's state table.
# Expected input sequence is:
# AA 99 00 01 ....
# In which:
# "AA" and "99" are two bytes of header,
# "00" and "01" are two bytes of uart ID.
my %next_state = (
"INIT" => sub{
# If receives "AA" from input,
# then jumpt to "HEADER0" state:
$din eq "AA" and return "HEADER0" ;
# Otherwise just stay here:
return "INIT" ;
},
"HEADER0" => sub{
# If receives "99" from input,
# then proceed to "HEADER1" state:
$din eq "99" and return "HEADER1" ;
# Otherwise, return to initial state:
return "INIT" ;
},
"HEADER1" => sub{
# Capture first byte of uart ID:
return "UARTID0";
},
"UARTID0" => sub{
# Capture second byte of uart ID:
return "UARTID1";
},
# "UARTID1" => sub{
# return "FINISHED";
# },
"FINISHED" => sub{
return "INIT";
},
);
# Set state machine's initial state.
my $cur_state = "INIT" ;
# Integer for debugging purpose.
my $itgi = 0;
# Run the state machine.
while($din = <>){
chomp $din ;
$cur_state = $next_state{$cur_state}();
print "$itgi, will jump to: $cur_state\n\n";
$itgi++;
}
# Send in input bytes:
$din = "AA" ;
sleep(1) ;
$din = "99" ;
sleep(1) ;
下面的 dode 灵感来自 ikegami,注意 ($din)
和不带括号的简单 $din
之间的区别:使用括号,我们得到 TRUE 或 FALSE 结果;没有括号,我们得到 @seq
的实际元素值,如果这个值是 0x00
那么 while
将变成 while(0)
并退出 .
use strict ;
use warnings ;
# input to the state machine
my $din ;
#---------------------------------------------------------------------
# FSM's state table.
#---------------------------------------------------------------------
# Expected input sequence is:
# AA 99 00 01 ....
# In which:
# (1) Fixed pattern "AA" and "99" are two bytes of header,
# (2) Following bytes are uart ID, etc.
my %next_state = (
"INIT" => sub{
# If receives "AA" from input,
# then jumpt to "HEADER0" state:
$din eq "AA" and return "HEADER0" ; #(D)
# Otherwise just stay here:
return "INIT" ;
},
"HEADER0" => sub{
# If receives "99" from input,
# then proceed to "HEADER1" state:
$din eq "99" and return "HEADER1" ;
# Otherwise, return to initial state:
return "INIT" ;
},
"HEADER1" => sub{
# Capture first byte of uart ID:
return "UARTID0";
},
"UARTID0" => sub{
# Capture second byte of uart ID:
return "UARTID1";
},
"UARTID1" => sub{
# Capture second byte of uart ID:
return "FINISHED";
},
"FINISHED" => sub{
return "INIT";
},
);
#---------------------------------------------------------------------
# Set state machine's initial state.
#---------------------------------------------------------------------
my $cur_state = "INIT" ;
#---------------------------------------------------------------------
# Send in command sequence
#---------------------------------------------------------------------
my @seq = (-1, 0xAA, -1, 0x99, -1, 0x00, -1, 0x00,
-1, 0x00, -1, 0x00, -1, 0x0a, -1, 0x0b,
-1, 0x0c, -1, 0x0d
);
sub get_next_byte {
while (@seq) { #(A)
my $cur_byte = shift(@seq);
return $cur_byte if $cur_byte >= 0;
#
sleep(-$cur_byte);
}
return (); #(B)
}
#---------------------------------------------------------------------
# Run the state machine.
#---------------------------------------------------------------------
# Integer for debugging purpose.
my $itgi = 0;
##--while( my ($din) = get_next_byte() ){ #(C)
while( ($din) = get_next_byte() ){ #(C)
$din = sprintf("%02X",$din);
$cur_state = $next_state{$cur_state}();
print "-- Iteration $itgi, will jump to: $cur_state\n";
$itgi++;
}
print "-- Program finish.\n";
我目前正在使用 Perl 来解析来自 RS232 串行端口的传入命令序列。 我尝试使用状态机,它的预期行为是: (1) 从串口接收一系列字节; (2) 状态机以字节为输入,跳转到合适的状态。
我想出了一个简化的演示 Perl 代码(贴在下面),但是遇到了一个问题: 代码进入“while(1){}”时,卡在这里,出不来 因此,$din 字节序列分配被“while(1){}”阻塞,并且对状态机不可见。 因此,FSM 卡在“INIT”状态,根本不跳。
我认为这应该是 Perl 编码中的一个非常简单或入门级的练习, 但是搜索 Google 对我帮助不大。 谁能帮我这个? 提前致谢~
...
my %next_state = (
"INIT" => sub{
$din eq "AA" and return "HEADER0" ;
return "INIT" ;
},
"HEADER0" => sub{
$din eq "99" and return "HEADER1" ;
return "INIT" ;
},
...
);
# Set state machine's initial state.
my $cur_state = "INIT" ;
# Integer for debugging purpose.
my $itgi = 0;
# Run the state machine.
while(1){
$cur_state = $next_state{$cur_state}();
print "$itgi, will jump to: $cur_state\n\n";
$itgi++;
}
# Send in input byte sequence, which simulates
# incoming bytes from RS-232 COM port:
$din = "AA" ;
sleep(1) ;
...
========== 2020.10.09 22:10更新==========
感谢@ikegami 的帮助和努力 调试工作,现在我可以得到我可爱的 Perl 状态 启动机器并运行,代码如下。
但是,还是有一个问题,那就是:
输入字节序列(即@seq)必须是非0x00值; 如果我将 0x00 放入命令序列,则 FSM 遇到0x00.
会退出这是为什么?该代码使用“$cur_byte >= 0”,这在我看来 应该能够像处理非零值一样处理 0x00。
为什么0x00把状态机拉出运行?
use strict ;
use warnings ;
# input to the state machine
my $din ;
#---------------------------------------------------------------------
# FSM's state table.
#---------------------------------------------------------------------
# Expected input sequence is:
# AA 99 00 01 ....
# In which:
# (1) Fixed pattern "AA" and "99" are two bytes of header,
# (2) Following bytes are uart ID, etc.
my %next_state = (
"INIT" => sub{
# If receives "AA" from input,
# then jumpt to "HEADER0" state:
$din eq "AA" and return "HEADER0" ;
# Otherwise just stay here:
return "INIT" ;
},
"HEADER0" => sub{
# If receives "99" from input,
# then proceed to "HEADER1" state:
$din eq "99" and return "HEADER1" ;
# Otherwise, return to initial state:
return "INIT" ;
},
"HEADER1" => sub{
# Capture first byte of uart ID:
return "UARTID0";
},
"UARTID0" => sub{
# Capture second byte of uart ID:
return "UARTID1";
},
"UARTID1" => sub{
# Capture second byte of uart ID:
return "FINISHED";
},
"FINISHED" => sub{
return "INIT";
},
);
#---------------------------------------------------------------------
# Set state machine's initial state.
#---------------------------------------------------------------------
my $cur_state = "INIT" ;
#---------------------------------------------------------------------
# Send in command sequence
#---------------------------------------------------------------------
my @seq = (-1, 0xAA, -1, 0x99, -1, 0x06, -1, 0x07,
-1, 0x08, -1, 0x09, -1, 0x0a, -1, 0x0b,
-1, 0x0c, -1, 0x0d
);
sub get_next_byte {
while (@seq) { #(A)
my $cur_byte = shift(@seq);
return $cur_byte if $cur_byte >= 0;
#
sleep(-$cur_byte);
}
return (); #(B)
}
#---------------------------------------------------------------------
# Run the state machine.
#---------------------------------------------------------------------
# Integer for debugging purpose.
my $itgi = 0;
while( $din = get_next_byte() ){ #(C)
$din = sprintf("%02X",$din);
$cur_state = $next_state{$cur_state}();
print "-- Iteration $itgi, will jump to: $cur_state\n";
$itgi++;
}
print "-- Program finish.\n";
您无需更改就进入循环 $din
。你需要像
# Run the state machine.
while ( my ($din) = get_next_byte() ) {
$din = sprintf("%02X", $din);
$cur_state = $next_state{$cur_state}();
print "$itgi, will jump to: $cur_state\n\n";
$itgi++;
}
出于测试目的,您可以使用
my @seq = (-1, 0xAA, -1, 0x99);
sub get_next_byte {
while (@seq) {
my $next = shift(@seq);
return $next if $next >= 0;
sleep(-$next);
}
return ();
}
感谢@zdim 和@ikegami 的帮助,终于把这个程序搞定了。我将post我的工作代码如下,以防有人有同样的问题。
以下代码受 zdim 启发:
use strict ;
use warnings ;
# input to the state machine
my $din ;
# FSM's state table.
# Expected input sequence is:
# AA 99 00 01 ....
# In which:
# "AA" and "99" are two bytes of header,
# "00" and "01" are two bytes of uart ID.
my %next_state = (
"INIT" => sub{
# If receives "AA" from input,
# then jumpt to "HEADER0" state:
$din eq "AA" and return "HEADER0" ;
# Otherwise just stay here:
return "INIT" ;
},
"HEADER0" => sub{
# If receives "99" from input,
# then proceed to "HEADER1" state:
$din eq "99" and return "HEADER1" ;
# Otherwise, return to initial state:
return "INIT" ;
},
"HEADER1" => sub{
# Capture first byte of uart ID:
return "UARTID0";
},
"UARTID0" => sub{
# Capture second byte of uart ID:
return "UARTID1";
},
# "UARTID1" => sub{
# return "FINISHED";
# },
"FINISHED" => sub{
return "INIT";
},
);
# Set state machine's initial state.
my $cur_state = "INIT" ;
# Integer for debugging purpose.
my $itgi = 0;
# Run the state machine.
while($din = <>){
chomp $din ;
$cur_state = $next_state{$cur_state}();
print "$itgi, will jump to: $cur_state\n\n";
$itgi++;
}
# Send in input bytes:
$din = "AA" ;
sleep(1) ;
$din = "99" ;
sleep(1) ;
下面的 dode 灵感来自 ikegami,注意 ($din)
和不带括号的简单 $din
之间的区别:使用括号,我们得到 TRUE 或 FALSE 结果;没有括号,我们得到 @seq
的实际元素值,如果这个值是 0x00
那么 while
将变成 while(0)
并退出 .
use strict ;
use warnings ;
# input to the state machine
my $din ;
#---------------------------------------------------------------------
# FSM's state table.
#---------------------------------------------------------------------
# Expected input sequence is:
# AA 99 00 01 ....
# In which:
# (1) Fixed pattern "AA" and "99" are two bytes of header,
# (2) Following bytes are uart ID, etc.
my %next_state = (
"INIT" => sub{
# If receives "AA" from input,
# then jumpt to "HEADER0" state:
$din eq "AA" and return "HEADER0" ; #(D)
# Otherwise just stay here:
return "INIT" ;
},
"HEADER0" => sub{
# If receives "99" from input,
# then proceed to "HEADER1" state:
$din eq "99" and return "HEADER1" ;
# Otherwise, return to initial state:
return "INIT" ;
},
"HEADER1" => sub{
# Capture first byte of uart ID:
return "UARTID0";
},
"UARTID0" => sub{
# Capture second byte of uart ID:
return "UARTID1";
},
"UARTID1" => sub{
# Capture second byte of uart ID:
return "FINISHED";
},
"FINISHED" => sub{
return "INIT";
},
);
#---------------------------------------------------------------------
# Set state machine's initial state.
#---------------------------------------------------------------------
my $cur_state = "INIT" ;
#---------------------------------------------------------------------
# Send in command sequence
#---------------------------------------------------------------------
my @seq = (-1, 0xAA, -1, 0x99, -1, 0x00, -1, 0x00,
-1, 0x00, -1, 0x00, -1, 0x0a, -1, 0x0b,
-1, 0x0c, -1, 0x0d
);
sub get_next_byte {
while (@seq) { #(A)
my $cur_byte = shift(@seq);
return $cur_byte if $cur_byte >= 0;
#
sleep(-$cur_byte);
}
return (); #(B)
}
#---------------------------------------------------------------------
# Run the state machine.
#---------------------------------------------------------------------
# Integer for debugging purpose.
my $itgi = 0;
##--while( my ($din) = get_next_byte() ){ #(C)
while( ($din) = get_next_byte() ){ #(C)
$din = sprintf("%02X",$din);
$cur_state = $next_state{$cur_state}();
print "-- Iteration $itgi, will jump to: $cur_state\n";
$itgi++;
}
print "-- Program finish.\n";