状态机与状态模式

最近在学习GUI框架,发现GUI本质上就是一个大状态机。以EW为例,在每次loop的update之前,都会判断这次是否有input/signal/device/timer这四类会改变系统状态的外部变量(这些就是各种各样的condition)。如果有,再去执行对应的slot(也就是action),执行完再把各个对应的component的状态更改掉(或者是gui框架observer一个variable或者property, 当这个状态变化的时候,执行对用的function); 关于是先改变状态再执行action, 还是先执行action再改变状态,我个人认为,取决于这个具体的状态是否是一个持续性的动作。你比方说,通过GUI设置一个开关的打开,假如这个打开过程需要一段时间才能完成,这种情况就是先执行完action, 判断返回值成功后再修改状态。否则假如有另外一个线程在监控状态,它就会得到一个错误的信息。也就是有可能,monitor得到的是open, 结果实际上还没有打开,甚至过了一会,打开动作失败也是有可能的。那么有没有需要先改变状态,然后再执行action的情况呢,也是有的。

比如我前面说的对property的observer. 这个是一个什么机制呢?就是说,gui的loop在判断input/signal/device/timer, 比如有一个消息推送,machine内部的某个状态发生了变化,比如说电量,或者信号强度,这种变化需要反应到UI上绘制出来,那么就是在对应的控件上会有一个value记录当前值,一旦发现某个loop中,这个value发生变化,那么就执行对应的slot,发出提示或者重绘控件。这两种情况都是存在的,需要根据具体场景去具体分析。

说了不少,下面看一些具体的案例来学习其中的一些技巧。比如,汽车上面有一个安全带控制器,其工作场景是:如果有人坐在座椅上,但是固定时间内没有系好安全带,那么将打开蜂鸣器进行提示。该系统具有三个传感器输入,分别是座椅传感器,安全带传感器,定时器,以及一个输出蜂鸣器。

下面我们简单画一下这个场景的状态图:

 

 个人认为,这里选取状态是有一定方法和技巧的:状态是输入传感器组合的子集

 状态图有了,下面就要开始编码了。这里面最关键的部分是对State的抽象,以及将各个状态子类化:

class CState {
public:
    virtual void checkPara(CInput* input) {
        if(!ptr) {return;}
    }
    virtual doProcessInput(CInput* input, CMachine *machine) {
        checkPara(input);
        processInput(input);
    }
    virtual void processInput(CInput* input) = 0;
    virtual const char* type(void) = 0;
};

class CStateIdle : public CState {
    void processInput(CInput* input, CMachine *machine) override {
        switch(input->getType()) {
        case SIG_SEATED: machine->startTimer(1000);
                         machine->setState(Seated_NoBelt_NoTimeout);
                         break;
        default:
            printf("do nothing in current state: %s\n", type());
        }
    }
    const char* type(void) override {
        return STR(StateIdle);
    }
};

class CStateSeated_NoBelt_NoTimeout : public CState {
    const char* type(void) override {
        return STR(Seated_NoBelt_NoTimeout);
    }
    void processInput(CInput* input, CMachine *machine) override {
        switch(input->getType()) {
        case SIG_TIME_OUT:    machine->openBeeper(1000);
                            machine->setState(Seated_NoBelt_Timeout);
                            break;
        case SIG_SEATED_LEAVE:
                            machine->resetTimer();
                            machine->stopTimer();
        default:
            printf("do nothing in current state: %s\n", type());
            break;
        }
    }
};

class CStateSeated_NoBelt_Timeout : public CState {
    const char* type(void) override {
        return STR(Seated_NoBelt_Timeout);
    }
    void processInput(CInput* input, CMachine *machine) override {
        switch(input->getType()) {
        case SIG_BELTED:    machine->closeBeeper();
                            machine->resetTimer();
                            machine->setState(Seated_Belted);
                            break;
        case SIG_SEATED_LEAVE:
                            machine->closeBeeper();
                            machine->resetTimer();
                            machine->stopTimer();
                            machine->setState(Idle);
        default:
            printf("do nothing in current state: %s\n", type());
            break;
        }
    }
};

class CStateSeated_Belted : public CState {
    const char* type(void) override {
        return STR(Seated_Belted);
    }
    void processInput(CInput* input, CMachine *machine) override {
        switch(input->getType()) {
        case SIG_BELTED_LEAVE:
                            machine->openBeeper();
                            machine->setState(Seated_NoBelt_Timeout);
        default:
            printf("do nothing in current state: %s\n", type());
            break;
        }
    }
};

class Machine {
public:
    typedef enum {
        StateIdle,
        Seated_NoBelt_NoTimeout
        Seated_NoBelt_Timeout,
        Seated_Belted
    } MachineState;

    Machine() {
        mIdle = new CStateIdle;
        mNoBeltNoTimeout = new CStateSeated_NoBelt_NoTimeout;
        mNoBeltTimeout = new CStateSeated_NoBelt_Timeout;
        mSeatedBelted = new CStateSeated_Belted;
    }
    void openBeeper();
    void closeBeeper();
    void startTimer();
    void stopTimer();
    void resetTimer();
    CInput* getInput(void);
    void setState(MachineState state) {
        switch(sig) {
        case Idle: mState = mIdleState; break;
        case Seated_NoBelt_NoTimeout: mState = mSeatedNoBeltNoTimeout ; break;
        case Seated_NoBelt_Timeout: mState = mSeatedNoBeltTimeout ; break;
        case Seated_Belted: mState = mSeatedBelted; break;
        default: break;
        }
    }
private:
    CState* mState;
    CStateIdle *mIdle;
    CStateSeated_NoBelt_NoTimeout* mNoBeltNoTimeout;
    CStateSeated_NoBelt_Timeout *mNoBeltTimeout;
    CStateSeated_Belted* mSeatedBelted;
};

在主循环中,不断收集事件,然后驱动这个状态机运行:

void machine_logic(Machine *machine)
{
    while(1) {
        CInput* input = machine->getInput();
        machine_state->processInput(input);
    }
}

input事件的收集一般放到另外一个单独的线程,这里简单示意一下

void machine_input(Machine *machine)
{
    while(1) {
        if(machine->isBelted() != machine->isBelted()) {
            input->sendToQue();
        }
    }
}

以上

 

posted on 2021-11-21 11:40  疾速瓜牛  阅读(369)  评论(0编辑  收藏  举报

导航