#pragma once // 下列 ifdef 块是创建使从 DLL 导出更简单的 // 宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的 DIOSSMACHINE_EXPORTS // 符号编译的。在使用此 DLL 的 // 任何其他项目上不应定义此符号。这样,源文件中包含此文件的任何其他项目都会将 // DIOSSMACHINE_API 函数视为是从 DLL 导入的,而此 DLL 则将用此宏定义的 // 符号视为是被导出的。 #ifndef DIOSSMACHINE_EXPORTS #ifdef _WIN64 #ifdef _DEBUG #pragma comment(lib, "DiosSMachineV3X64D.lib") #else #pragma comment(lib, "DiosSMachineV3X64.lib") #endif #else #ifdef _DEBUG #pragma comment(lib, "DiosSMachineV3D.lib") #else #pragma comment(lib, "DiosSMachineV3.lib") #endif #endif #endif #ifdef DIOSSMACHINE_EXPORTS #define DIOSSMACHINE_API __declspec(dllexport) #define DIOSSMACHINE_C_API extern "C" __declspec(dllexport) #else #define DIOSSMACHINE_API __declspec(dllimport) #define DIOSSMACHINE_C_API extern "C" __declspec(dllimport) #endif #include "ResDataObject.h" #include "DiosThread.h" #include "MsgCircle.h" #include "DiosLock.h" #include "MsgQueue.h" #include "LogicClient.h" #include using namespace std; #define TIMEOUT_TEMP (3000) //#define DiosErrorType "Error" //行为,失败,超时等正常执行时错误 //#define DiosFrameError "Frame" //人为的错误发送 //#define DiosSeqError "Seq" //状态机不关心状态是不是Error,定义者自己来定义错误状态 //#define DiosStmEntryPosName "StateMachineEntry" //#define DiosStmExitPosName "StateMachineExit" //状态机进出状态名称,用户自定义 //#define DiosTopStateMachineName ("SMOuter") class StateMachineDevicePool; typedef enum _DiosStMachineRet { DIOSSMRET_NG, DIOSSMRET_OK, DIOSSMRET_TIMEOUT, DIOSSMRET_EXIT, DIOSSMRET_MAX }DIOSSTMRET; //assert,通用的事件和錯誤的事件處理上有所不同 //最终有个==的判断,到时候错误和事件的处理方案不一样 #define DiosStmEvtInfo "DiosStmEvtInfo" class ConditionEvent; //JSON格式,EVT事件相关的必须有,而INFO相关的是附加内容,可有可无 /* { "evtkey" : "evt内容", "DiosStmEvtInfo" : "错误内容 or 事件INFO or 其他附加信息" } */ typedef enum _SMEVTTYPE { SMEVT_NOTIFY,//有硬件事件,只读通知 SMEVT_READATTR,//有硬件事件,step1:读通知,step2:没有通知情况下读属性,step3:没有属性置位情况下等通知. SMEVT_PASS,//无匹配,无条件穿过. SMEVT_BLOCK,//无匹配,保持阻挡状态 SMEVT_MAX }SMEVTTYPE; class DIOSSMACHINE_API DiosStMEvt { ResDataObject *m_pEvt; ResDataObject *m_pInfo; SMEVTTYPE m_type; bool m_bTriggered; HANDLE m_hTriggered; ConditionEvent* m_pConditions; bool m_bActived; public: DiosStMEvt(); DiosStMEvt(const DiosStMEvt &tValue); ~DiosStMEvt(); bool SetEvt(const char* pKey, INT Val, const char *pInfo = NULL); bool SetEvt(const char* pKey, const char *pVal, const char *pInfo = NULL); DiosStMEvt& operator = (const DiosStMEvt &tValue); bool operator == (const DiosStMEvt &Obj); bool operator == (const char* pszEventName); const char *encode_s(); bool decode(const char *pdata); //设置事件上下文 bool parse(ResDataObject *evtContext); bool IsEmpty(); ResDataObject& GetEvtContext(); bool IsTriggered(); //事件是激发态 //过滤激活事件,包括Action/Notfiy/External bool Active(const char* pszType, ResDataObject* resContext, const char* pszDevice); const char* GetEventName() { return m_pEvt->GetKey(0); } HANDLE GetHandle() { return m_hTriggered; } void Reset(); void Enable(bool state) { m_bActived = state; } int GetTimeout(); }; //state route pos class DIOSSMACHINE_API DiosStMRouteLine { bool m_ActiveState; bool m_InRoute; DWORD m_Timeout; DiosStMEvt *m_pEvt; //一个out对应一个 事件 string *m_pSrcRoutePos; string *m_pGuard; string *m_pDesRoutePos; public: DiosStMRouteLine(); DiosStMRouteLine(const DiosStMRouteLine &tValue); virtual ~DiosStMRouteLine(); void SetRoute(DiosStMEvt &evt, const char* pGuard, const char* pDes); void SetRoute(DiosStMEvt &evt, const char* pSrc,const char* pGuard, const char* pDes); const char* GetDesName(); const char* GetSrcName(); const char* GetGuardName(); bool GetRouteType();//true:in,false:out void Active(bool state, DWORD timeout); bool GetActiveState(); operator DiosStMEvt *(); DiosStMRouteLine& operator = (const DiosStMRouteLine &tValue); DWORD GetTimeout(); }; class DiosSMachineIF; //一个状态有多个RouteLine, 每个RouteLine有一个单独的事件 class DIOSSMACHINE_API DiosStMRoutePos { bool m_ActiveState; DWORD m_Timeout; string *m_pRoutePosName; vector *m_pOutRouteLineVec; vector< DiosStMEvt*>* m_pRouteLineEvents; //出状态的事件列表,和RouteLine一一对应 StateMachineDevicePool* m_pDevicePool; ResDataObject m_resStateActions; ResDataObject m_resDisable; //禁用全局事件列表 ResDataObject m_resEvent2DestPos; bool m_bBreakIn; bool m_bBreakAction; bool m_bBreakOut; public: DiosStMRoutePos(); DiosStMRoutePos(const char *pName, StateMachineDevicePool* pDevicePool); virtual ~DiosStMRoutePos(); virtual bool IsSMachine();//no void SetName(const char *pName); const char* GetName(); bool PosAddOutRouteLine(DiosStMEvt *Evt, const char *pSrcPosName, const char *pGuardName, const char *pDesPosName); void SetAction(ResDataObject actions) { m_resStateActions = actions; } DWORD GetRouteLineCount(); DiosStMRouteLine **GetOutRouteLineVec(); DiosStMRouteLine *operator [](DiosStMEvt &Evt); void Active(bool state, DWORD timeout); bool GetActiveState(); DWORD GetTimeout(); vector< DiosStMEvt*>* GetPosEvent() { return m_pRouteLineEvents; } //执行进入当前状态的Action DIOSSTMRET DoAction(DiosSMachineIF* pSM); DIOSSTMRET DoIn(DiosSMachineIF* pSM); DIOSSTMRET DoOut(DiosSMachineIF* pSM); //定时检查是否超时,最小间隔为1s,有可能不执行 DIOSSTMRET CheckTimeout(); //状态到了的检查,是否激活事件 DIOSSTMRET CheckNotify(const char* pszDevice, ResDataObject* res); // bool TriggerEvent(const char* pszEvent); DiosStMRouteLine* WaitFroNextState(int timeout); //等待进入下一个状态 //禁用全局事件 void SetDisable(ResDataObject disable); void SetBreak(bool bBreakIn, bool bBrakAction, bool bBreakOut); // void EnableEvent(const char* pszEventName, bool state); }; using StateChangedCallback = std::function; //通用状态机类 class DIOSSMACHINE_API DiosSMachineIF : public DiosLock { protected: //DiosSMachineIF *m_pParent; string *m_pStateMachineName; StateMachineDevicePool* m_pDevicePool; string m_strLastPos; //上一个状态点 string m_strCurrentPos; //当前状态点 string m_StmEntryPosName; //状态机入口状态名称 string m_StmExitPosName; //状态机出口状态名称 ResDataObject m_resUserVariable; //用户变量 ResDataObject m_resSystemVariable; //系统变量 HANDLE m_StateCangeEvt; HANDLE m_RunningState; DiosStMRouteLine* StateMachineWaitForEvents(/*int &ExtEvtIndex*/);//[< 0]:Local or outline, [>= 0]: externalEvtIndex StateChangedCallback m_StateNotify; bool m_bDebugEnable; //是否启用Debug模式 HANDLE m_hBreaked; //断点等待事件,用户发送Continue后触发,状态机继续执行,直至下一个断点 protected: DiosStMRoutePos *m_pCurrentRoutePos; MsgCircle *m_pArrivedEvts; //状态与路由映射表 map *m_pRoutePosMap; //inline,全局路由表 //vector *m_pRouteInLineMap; DiosStMRoutePos* m_pGlobalRoute; ////external //size_t m_RouteExternalEvtCount; //DiosStMRouteLine** m_pRouteExternalMap; void ClearState();//reset DIOSSTMRET TransToPos(const char *pPosName); void PostError(const char *pErrorVal,const char *pErrorInfo = NULL); DiosStMRouteLine *GetEntryRouteLine(); INT ExecStateMachine(HANDLE ThreadExitEvt/*,DiosStMRouteLine *pExternalWaitEvts[], size_t WaitCount*/); //INT EnterSubStateMachine(HANDLE ThreadExitEvt); bool PrePareStateMachine(); bool GetRunningState(DWORD waittime = 0); void SetRunningState(bool Running); virtual void PushStateChange(ResDataObject &ChangedPos);//statemachinename:statemachinepos public: void SetMachineName(const char* pszEntryName, const char * pszExitName); //设置状态机名称及进出状态名称 void SetUserVariable(const char* pKey, ResDataObject& resValue); //设置用户变量 void GetUserVariable(const char* pKey, ResDataObject& resValue); //读取用户变量值 void GetSystemVariable(const char* pKey, ResDataObject& resValue); //读取系统变量 void EnableEvent(const char* pszGlobalEvent, bool state); DIOSSTMRET ProcessUserVaiable(const char* vName, ResDataObject& resVaiable); void SetNotifyCallback(StateChangedCallback callback) { m_StateNotify = callback; } public: DiosSMachineIF(void); virtual ~DiosSMachineIF(void); void SetStateMachineName(const char *pszName); const char *GetStateMachineName(); //void SetParentSMachine(DiosSMachineIF *pParent); const char* GetCurrentState() { return m_strCurrentPos.c_str(); } //for this IF //virtual bool IsSMachine();//yes //void CopyEvtTo(DiosSMachineIF *pDes); //init state machine //第一个节点的接入比较特殊,所以做了个函数 bool AddEntryRoutePos(DiosStMRoutePos *pPos); bool AddRoutePos(DiosStMRoutePos *pPos); bool AddInRouteLine(DiosStMEvt &Evt, const char *pGuardName, const char *pDesPosName); bool AddOutRouteLine(DiosStMEvt *Evt,const char *pSrcPosName,const char *pGuardName,const char *pDesPosName); //active state machine bool ActiveRoutePos(const char *pPosName, DWORD timeout = TIMEOUT_TEMP); bool ActiveRouteLine(DiosStMEvt &Evt, DWORD timeout = TIMEOUT_TEMP); bool DeActiveAll(); DiosStMRoutePos *GetCurrentRoutePos(); //evt bool PushEvent(DiosStMEvt &Evt); bool PopEvent(DiosStMEvt &Evt); bool PeekEvent(DiosStMEvt &Evt); HANDLE GetEvtNotifyHandle(); //for Debug //设置Debug模式,启用或者禁用 void SetDebugMode(bool bEnable); bool InDebugMode() { return m_bDebugEnable; } //设置状态断点,IN/ACTION/OUT 三个断点位置可以设置 void EnableBreakPoint(const char* pState, bool bBreakInEable, bool bActionInEnable, bool bBreakOutEnable); //继续运行,当处于中断状态时 void Continue(); //获取状态的所有事件的激活值 void GetEventStatus(const char* pState, const char* pszEvent); //获取当前断点位置 void GetCurrentBreak(); //等待继续事件触发 void WaitContinue(); HANDLE GetStateChangeEvtHandle(); //for state machine thread virtual DIOSSTMRET StateMachineEntry(DWORD timeout);//Begin状态点 virtual DIOSSTMRET StateMachineExit(DWORD timeout);//End状态点 //状态点Action执行动作 virtual DIOSSTMRET StateMachineAction(const char *pState, DWORD timeout); //状态点Guard virtual DIOSSTMRET StateMachineGuard(const char * pState, DWORD timeout); //返回值的定义 //因为全局事件,本状态机事件,本状态点事件都需要监控 //所以 //返回值为负:退出状态机 //返回值为正: 0 <= EvtIdx < CountOfLocal + CountOfExternal + CountOfOutpath // 至于那个数组的事件,可以自行推算 // 例: Outpath第二个事件 => EvtIdx = CountOfLocal + CountOfExternal + 1 //关于TIMEOUT:函数中wait的是OutpathEvts,LocalEvts和ExternalEvts是没有必要等待,有就有,没有就没有.... virtual DiosStMRouteLine* WaitNotfiyProcess(); //子类需要从设备上获取通知,然后根据优先级找到目标状态路由 //virtual int StateMachineWaitForEvents( // DiosStMRouteLine *pLocalEvts[], DWORD CountOfLocal, // //DiosStMRouteLine *pExternalEvts[], DWORD CountOfExternal, // DiosStMRouteLine *pOutpathEvts[], DWORD CountOfOutpath, // DWORD timeout // ); }; class StateMachineDevicePool; //标准状态机 class DIOSSMACHINE_API DiosSMachine : public Dios_Thread,public DiosSMachineIF { protected: virtual bool OnStartThread(); virtual bool Exec(); HANDLE m_StateQuedEvent; ResDataObject m_LastHitStatePos; MsgQueue *m_pStatePosList; virtual void PushStateChange(ResDataObject &ChangedPos);//statemachinename:statemachinepos public: DiosSMachine(void); virtual ~DiosSMachine(void); void SetStateMachineLog(const char *pszLogTitle); bool PopStateChange(ResDataObject &LastPos); HANDLE GetStateQuedEvent(); bool RunStateMachine(const char* pszLogTitle); //for other threads to call bool StartStateMachine(/*DiosStMRouteLine *pExternalWaitEvts[], DWORD WaitCount*/); void StopStateMachine(DWORD timeout); //清除所有的状态和设备. virtual bool Reset(DWORD Timeout); /* 在状态机停止情况下是可以的. 单元设备列表添加 */ virtual bool AddDevices(ResDataObject& DevList) ;//. //单元设备列表清理. virtual bool ClearDevices(); /* 触发状态机事件 */ bool TriggerEvent(const char* pEventName);// //添加状态点映射,各个状态点的Actions with Params. /* List of below format... "ParentStateName":"StateName":"Devpath":ActionName:Params */ bool AddStateActions(ResDataObject& ActionMap);//状态机激活中,应该是在SeqReady点位 //添加事件映射,所有的抽象事件的物理映射. /* List of EventName:{ Need : 1 or 0, Devpath : Key:Value } */ bool AddStateEvents(ResDataObject& EventMap);//状态机激活中,应该是在SeqReady点位 //从状态机定义中加载当前状态机定义 bool LoadMachine(ResDataObject& Machine); //状态机自行跳入FrameError的时候,错误信息读取.(空的情况反馈false) bool GetStateMachinesErrorInfo(ResDataObject& ErrInfo); void ClearStateActionMaps(); void ClearStateEventMaps(); //Actions---------------------------------------------------------------------- //Entry&Exit Action virtual DIOSSTMRET StateMachineEntry(DWORD timeout);//Begin状态点 virtual DIOSSTMRET StateMachineExit(DWORD timeout);//End状态点 //RoutePos Action //virtual DIOSSTMRET StateMachineAction(const char* pAction, DWORD timeout) = 0; //virtual DIOSSTMRET StateMachineGuard(const char* pGuard, DWORD timeout) = 0; DiosStMRouteLine* WaitNotfiyProcess() override; //int StateMachineWaitForEvents( // DiosStMRouteLine* pLocalEvts[], DWORD CountOfLocal, // //DiosStMRouteLine *pExternalEvts[], DWORD CountOfExternal, // DiosStMRouteLine* pOutpathEvts[], DWORD CountOfOutpath, // DWORD timeout //) override; }; /* //子状态机 class DIOSSMACHINE_API DiosSubSMachine : public DiosStMRoutePos, public DiosSMachineIF { protected: public: DiosSubSMachine(void); DiosSubSMachine(const char *pName); virtual ~DiosSubSMachine(void); virtual bool IsSMachine();//yes }; */ typedef enum _DrStateMachineErrorType { DSM_ERROR_NORMAL, DSM_ERROR_ACTION_FAILED, DSM_ERROR_ACTION_TIMEOUT, DSM_ERROR_WAITEVENT_TIMEOUT, DSM_ERROR_MAX }DSMERRORTYPE; #define INVALID_DEVIDX (-1) #define STATEMACHINE_ACTION_TIMEOUT (2500) class DIOSSMACHINE_API StateMachineDevicePool : public DiosLock { MsgQueue* m_pErrorInfoQue; HANDLE m_ThreadExitHandle; //map* m_pDevpath;//devidx:devpath 设备序号->设备路径 LogicClient* m_pDevNotifyClient;//devidx:client 用于获取通知的连接 LogicClient* m_pDevActionClient; // 用于执行Action的连接 DWORD GetDeviceIdx(const char* pszDevpath); const char* GetStateMachineEventName(DWORD DevIdx, const char* pKey, const char* pVal); void PushErrInfo(const char* pszContext, DSMERRORTYPE ErrType = DSM_ERROR_NORMAL); DWORD WINAPI ActionThread(LPVOID pPara); public: StateMachineDevicePool(); ~StateMachineDevicePool(); void SetThreadExitHandle(HANDLE tHand); //list of "devicepath":"" bool AddDevices(ResDataObject& Devices); LogicClient* FindClient(const char* pszDevShortPath); /* List of below format... "ParentStateName":"StateName":"Devpath":ActionName:Params */ bool AddStateActions(ResDataObject& ActionMaps); /* List of EventName:{ {Need : 1 or 0}, {ReadFromAttribute : 1 or 0},{Devpath : Key:Value}} */ bool AddStateEvents(ResDataObject& EventMaps); void ClearStateActionMaps(); void ClearStateEventMaps(); //device init bool ClearDevices(); bool OpenDevices(); bool CloseDevices(); //通用的Action和Event处理 //DIOSSTMRET StateMachineAction(const char* pParentStatePos, const char* pStatePos, DWORD timeout); DIOSSTMRET StateMachineAction(ResDataObject* resActions, ResDataObject& resResult, ResDataObject* pVariable, DWORD Timeout); //DIOSSTMRET StateMachineAction(ResDataObject* resActions, ResDataObject& resResult); int ReadForDeviceEvents(ResDataObject& resNotfiy, DWORD timeout, int nMaxNotifyNum); //error info bool PopErrInfo(ResDataObject& info); };