状态机在指纹通讯编程中的应用

状态机变量

在使用QT进行串口通讯时,一直用的是qextserialport(http://code.google.com/p/qextserialport)。

m_com = new QextSerialPort();
#ifdef Q_OS_WIN
    m_com->setPortName(QLatin1String("COM3"));
#elif defined(Q_OS_LINUX)
    m_com->setPortName(QLatin1String("/dev/ttyUSB0"));
#endif

connect(m_com, SIGNAL(readyRead()), this, SLOT(OnDataAvailable()));

所有收到的数据,都将在OnDataAvailable()中接收。在这个方法中肯定少不了if,else if或switch case了, 因为要知道收到的数据是发送什么数据后的回复。如果串口通信协议是我们自己设计的,当然可以在回复的数据 中加入若干个字节用来表示指令(和发送的数据一样或对应),但如果是别人的协议,没有按照这样的方式做, 那我们就只能通过增加一个类的成员变量来表示对应的这条回复的发送的指令是什么了。这个就变量就可以是 一个状态机变量(当然也可以只是命令的值)。

class MyFinger{
 enum STATUS_TYPE{
    STATUS_REGIST,
    STATUS_FIND,
    ……
 }

}

如果不存在丢包或者一问一答不存在错误的情况下,上面的一条状态对应一条指令好像是够用了。但是现实是残酷的, 各种意外情况必然是会出现的。受电磁干扰或串口设备中的软处理有问题,那么我们可能没有收到数据或者收到的是脏数据。 我们就需要在发送处理中重发直到收到回复或者收到成功处理的回复。所以我们对应的为每个状态增加一个结束的状态。 如

 enum STATUS_TYPE{
    STATUS_REGIST,
    STATUS_REGIST_OVER,
    STATUS_FIND,
    STATUS_FIND_OVER
    ……
 }

在接收处理中收到状态的回复后,改变状态机变量为*_OVER, 在发送处理中,就可以通过判断状态进行重发数据包了:

    _FingerStatus = Finger::SATAUS_FIND;

    FingerPacket data;
    data.cmd = Finger::CMD_ONE_VS_N;
    while(_FingerStatus != Finger::SATAUS_FIND_OVER)
    {        
        _FingerStatus = Finger::SATAUS_FIND;
        SendPacket(&data);
        //大约5秒后没收到回复,重发
        int times = 0;
        while(_FingerStatus == Finger::SATAUS_FIND)
        {
            HertzUtils::Sleep(500);
            if(++times > 10) break;
        }
    }

但是上面的重发可能陷入无限循环,所以我们不必要引入一个成员变量,以便可以在希望结束时优雅的结束。所以

while(_FingerStatus != Finger::SATAUS_FIND_OVER)变成了:
while(_FingerWorking && _FingerStatus != Finger::SATAUS_FIND_OVER)

如果只需要有限次的重发或重试次数,引入个局部变量就可以了。