第10回:关于计算机模拟状态机在工程实践编程中的实际应用(5)

在base工程中抽象了状态机的基本概念,如果在实际的计算机上模拟状态机运行和多个状态机的相互交互,还需要与特定操作系统与架构相联系,windows运行在x86架构之中,因此,本文描述的工程中状态机运行在windows应用程序的线程中,由x86架构的CPU调度运行,其状态机和状态的定义如下:

logic中状态机的定义

#ifndef VSM_THREAD_H
#define VSM_THREAD_H

#include "logic.h"
#include "v_statemachine.h"
class VSM_Thread : public V_StateMachine
{
public:
    VSM_Thread();
    VSM_Thread(int iID,const QString& sName);
    ~VSM_Thread();
public:
    HANDLE CreateThread();
    HANDLE m_ThreadHandle;
    void pushBack_Msg(int iStateID,const V_Msg& msg);
    void pushFront_Msg(int iStateID,const V_Msg& msg);
    bool m_bExit;
protected:
    void Init();
};

#endif // VARSM_THREAD_H

其中CreateThread函数创建了一个由windows操作系统使用中断时间片调度的状态机执行线程,在线程中无限执行状态机run函数(直至m_bExit变量为true),在执行过程中,为防止线程空转造成了CPU资源和电脑电源浪费,在状态机没有任何消息执行时,使用::SuspendThread函数将线程挂起,而当外界向状态机消息队列输入消息时(调用pushBack_Msg/pushFront_Msg),循环使用::ResumeThread将线程激活。

线程状态机充分利用了线程挂起与激活函数,与CPU的hlt和中断激活机制遥相呼应,且非常切合CPU+中断的设计初衷,非常完美。

在Logic中也实现了本工程所需要的具备消息和消息执行函数的状态机和状态类(严格上说,在不考虑跨平台移植性(作者的base工程也没有充分考虑linux的移植性,毕竟作者的所有项目都运行在windows之下)的情况下,可以将Logic中的线程状态机类挪到base中),这些类定义如下:

VSMT_Ball状态机类定义:

#ifndef VSMT_BALL_H
#define VSMT_BALL_H
#include "logic.h"
#include "vsm_thread.h"
#include "variant.h"
#include "variants.h"
class VSMT_Ball : public VSM_Thread
{
public:
    VSMT_Ball(int iID, const QString &sName);
protected:
    void Init();
    void do_Msg(const V_Msg& msg);
};

#endif // VSMT_BALL_H

该类仅定义了两个函数,其中Init函数定义了一些变量参数并重新定义了work状态(VSW_Ball)(因此,在调用基类初始化虚函数时就不会定义该状态),作者使用重写了do_Msg虚函数(派生自状态类),这是因为在停止消息处理中需要重新读取XML配置文件。

VSW_Ball状态类定义:

#ifndef VSW_BALL_H
#define VSW_BALL_H
#include "logic.h"
#include "vs_work.h"
class VSW_Ball : public VS_Work
{
public:
    VSW_Ball(int iID,V_StateMachine* pParent);
public:
    void do_Msg(const V_Msg& msg);
};

#endif // VSW_BALL_H

在该类中定义了小球位置计算消息,根据小球的XY位置和速度计算下一位置,如果计算坐标大于最大值或者小于最小值,则将速度值正负号取反,并且在界面中使用定时器持续向该work状态发送消息,计算过程如下:。

case VarMsg_Ball_FreshPOS:
    {
        VSMT_Ball* pSM=(VSMT_Ball*)m_pParent;

        double dPosX=pSM->m_Variants.get_Variant(Var_Ball_PosX_ID)->m_vVar.toDouble();
        double dPosY=pSM->m_Variants.get_Variant(Var_Ball_PosY_ID)->m_vVar.toDouble();

        double dSpdX=pSM->m_Variants.get_Variant(Var_Ball_SpeedX_ID)->m_vVar.toDouble();
        double dSpdY=pSM->m_Variants.get_Variant(Var_Ball_SpeedY_ID)->m_vVar.toDouble();

        double dRangeX=pSM->m_Variants.get_Variant(Var_Ball_RangeX_ID)->m_vVar.toDouble();
        double dRangeY=pSM->m_Variants.get_Variant(Var_Ball_RangeY_ID)->m_vVar.toDouble();

        double dRadius=pSM->m_Variants.get_Variant(Var_Ball_Radius_ID)->m_vVar.toDouble();

        dPosX+=dSpdX*m_gdProcessTime;
        dPosY+=dSpdY*m_gdProcessTime;

        pSM->m_Variants.get_Variant(Var_Ball_PosX_ID)->m_vVar=dPosX;
        pSM->m_Variants.get_Variant(Var_Ball_PosY_ID)->m_vVar=dPosY;



        if(dPosX+dRadius>dRangeX)
        {
            dSpdX=-fabs(dSpdX);
        }
        if(dPosY+dRadius>dRangeY)
        {
            dSpdY=-fabs(dSpdY);
        }

        if(dPosX-dRadius<0)
        {
            dSpdX=fabs(dSpdX);
        }
        if(dPosY-dRadius<0)
        {
            dSpdY=fabs(dSpdY);
        }


        pSM->m_Variants.get_Variant(Var_Ball_SpeedX_ID)->m_vVar=dSpdX;
        pSM->m_Variants.get_Variant(Var_Ball_SpeedY_ID)->m_vVar=dSpdY;
        break;

    }

下一篇描述如何读写XML配置文件。


         不喜欢 0

D7FECB19D148C1AD1D829F1CD0E23DB0

发表回复

站内搜索

最新评论

归档日历

2024 年 9 月
 1
2345678
9101112131415
16171819202122
23242526272829
30  

标签云