程序名称:多线程电梯调度仿真程序 程序说明:利用多线程同步技术模拟电梯调度算法。主要用到了事件内核对象、临界区来让电梯服务线程、楼层请求正确地对请求队列进行访问、以及修改。 作者:Seed-L 完成日期:2012-3-4 特别鸣谢:ICE-Light!s 童鞋(阿池)
主要文件:
/* ------------------------------------------------------------------------------------- * Copyright(c) 2012-3-6 Seed-L * All rights reserved. * 文件名称: LiftDemo.h * 简要描述: 定义各类数据结构、常量 * 作者:Seed-L * 完成日期:2012-3-4 ------------------------------------------------------------------------------------- */ #ifndef LIFTDEMO_H_ #define LIFTDEMO_H_ #include"help.h" #include<windows.h> #include<bitset> #include<string> using std::bitset ; using std::string ; //楼层请求的类型:电梯外、电梯内、没有请求 //本来定义的名称是OUT、IN的简单名称,结果编译不通过,找了也挺久的错误 //后来发现原来OUT 和 IN 是库里面已经定义好的。但是错误提示又没有提示说重定义 enum eReqStatus { LIFTOUT = 0 ,LIFTIN = 1 , NOTREQ = 2} ; //电梯方向: //貌似发现LIFTSTOP 没有用上 enum eLiftDir { LIFTUP , LIFTDOWN ,LIFTSTOP} ; const int DEFAULT_FLOOR_NUM = 5 ; //楼层总楼 const int TOPFLOOR = 5 ; //顶层 const int BOTTOMFLOOR = 1 ; //底层 //动画电梯结构 typedef struct LiftStruct { HWND hwnd ; //电梯轨道 int speed ; RECT rect ; //相对自己的窗口区 RECT realrect ; //相对窗口客户区 int pos ; //电梯的位置 } LiftStruct , *PLiftStruct ; //动画楼层结构 typedef struct FloorStruct { HWND hwndDisplay ; //上下楼请求绘制窗口句柄 HWND hwndLog ; //显示请求的窗口,文字描述 RECT rect ; RECT realrect ; //相对窗口窗户区的坐标 POINT pt[3] ; //绘制三角形的三个点的坐标 eLiftDir direction ; } FlooStruct ,PFloorStruct; class LiftDemoS { public : //本来是私有函数 inline void EnterLiftCs() { EnterCriticalSection(&m_Cs) ; } inline void LeaveLiftCs() { LeaveCriticalSection(&m_Cs) ; } inline eLiftDir GetLiftDirection() { return m_eLiftCurDir ; } inline int GetLiftCurFloor() { return m_iLiftCurFloor ;} inline int GetLiftCurDesNum() { return m_bsReqQueue.count() ; } inline int GetLiftNextDes() { return m_iLiftNextDes ;} inline bitset<DEFAULT_FLOOR_NUM+1> GetLiftDesFloor() { return m_bsReqQueue ;} inline bitset<DEFAULT_FLOOR_NUM+1> GetUpDirQueue() { return m_bsUpDirQueue ;} inline bitset<DEFAULT_FLOOR_NUM+1> GetDownDirQueue() { return m_bsDownDirQueue ;} void LiftReachAndWait(int iNum) ; int LiftChangeDirection( int* iOldDir) ; LiftDemoS() ; virtual ~LiftDemoS() ; private : HANDLE m_hReqHappenEvents[DEFAULT_FLOOR_NUM] ; //某楼层发出请求事件对象 HANDLE m_hLiftReachEvents[DEFAULT_FLOOR_NUM] ; //电梯到达楼层时,通知楼层事件 HANDLE m_LiftGoEvent ; //用来告知电梯可以起动 CRITICAL_SECTION m_Cs ; //互斥段,用来修改队列时使用 CRITICAL_SECTION m_ResAllEventCs ; CRITICAL_SECTION m_InsertReqCs ; private : inline void InitLiftCs() { InitializeCriticalSection(&m_Cs) ; InitializeCriticalSection(&m_ResAllEventCs) ; InitializeCriticalSection(&m_InsertReqCs) ; } inline void DeleteLiftCs() { DeleteCriticalSection(&m_Cs) ; DeleteCriticalSection(&m_ResAllEventCs) ; DeleteCriticalSection(&m_InsertReqCs) ; } void CreateLiftEvent() ; public : //分两种 //第一种是电梯外请求,第一个参数代表请求的状态,第二个请求代表发生请求的楼层 //第二个是电梯内请求,第一个参数代表请求的状态是电梯内的,第二个请求代表里面发生的楼层 bool SetFloorReq(eReqStatus eStatus , bitset<DEFAULT_FLOOR_NUM+1> &bsFloor, int iFloorNum , eLiftDir eWantDir) ; bool ReSetFloorReq(eReqStatus &eStatus , int &biFloor) ; void LiftSetGoOnEvent() ; void LiftResetGoOnEvent() ; void ResetAllEvents() ; inline void EnterInsertReqCs() { EnterCriticalSection(&m_InsertReqCs) ; } inline void LeaveInsertReqCs() { LeaveCriticalSection(&m_InsertReqCs) ; } private : eLiftDir m_eLiftCurDir ; //电梯此时的方向,转向有用 int m_iLiftCurFloor ; //电梯此时所在楼层,请求发生时,用来和这一个比较, //以便决定放入到上升队列,还是下降队列 int m_iLiftNextDes ; //下一站 bitset<DEFAULT_FLOOR_NUM+1> m_bsReqQueue ; bitset<DEFAULT_FLOOR_NUM+1> m_bsUpDirQueue ; //1为向上,0为向下 //将这一个原来的方向队列,变为上升队列 //某位的1,表示某楼层要向上,0表示没有请求,或者向下 bitset<DEFAULT_FLOOR_NUM+1> m_bsDownDirQueue ; //新增的一个向下请求队列 //某位的1,表示某楼层要向下,0表示没有请求,或者向上 bool m_fTurnToFindFloor ; bool m_fAcceptOppReq ; } ; #endif
/* ------------------------------------------------------------------------------------- * Copyright(c) 2012-3-6 Seed-L * All rights reserved. * 文件名称: Lift.cpp * 简要描述: 程序主要框架 * 作者: Seed-L * 完成日期: 2012-3-4 ------------------------------------------------------------------------------------- */ #include"help.h" #include"LiftDemo.h" #include<tchar.h> #include<windowsx.h> #include<windows.h> #include<process.h> #include<Commctrl.h> #include<time.h> #include"resource.h" /*核心对象区*/ LiftDemoS Lift ; HINSTANCE g_hInst ; //实例句柄 /*线程相关区*/ HANDLE g_hThreads[DEFAULT_FLOOR_NUM + 1] ; int g_nNumThreads = 0 ; /*测试统计转向次数*/ int iTurnDirCount = 0 ; /*一些全局变量*/ int iFinishCount = 0 ; //用于统计完成请求数窗口 int iLiftSize = 0 ; //用于保存电梯的大小,画图时所用 int iListViewIndex = 0 ; //将日志记录插入到列表视图控件的索引 int iLiftSpeed = 25 ; //设置电梯的速度,默认值为25 int iReqSpeed = 7000 ; //楼层产生请求的速度,默认值为7000 /*布尔变量区*/ volatile long g_fShutDown = FALSE ; //volatile用于防止编译器优化这,产生未知错误 bool g_fInsertReq = false ; //用于判断电梯在上升途中是否产生了优先级更高的请求 bool g_fSuspend = false ; //用于判断暂停按钮是否按下 bitset<DEFAULT_FLOOR_NUM+1> g_bsDrawDir ; //用于判断决定是否重绘三角形(上升或者下降请求) /*全局句柄变量区*/ HWND g_HwndMain ; //程序主窗口 HWND g_HwndListView ; //列表视图控件窗口 HWND g_HwndDesFloor ; //电梯内控件窗口 HWND g_HwndDir ; //电梯方向窗口 HWND g_HwndNextDesFloor ; //下一站窗口 HWND g_HwndFinishCount ; //统计完成请求窗口 HWND g_HwndLiftSpeed ; //电梯速度窗口 HWND g_HwndReqSpeed ; //楼层请求窗口 HWND g_HwndMainStop ; //停止按钮 //RECT g_DlgClientRect ; //RECT g_rcDlgRealRect ; /*电梯动画显示区*/ FloorStruct g_fsFloor[DEFAULT_FLOOR_NUM] ; //楼层显示结构 RECT g_rcFloor ; //某楼层的坐标,用来计算电梯的大小,画图所用 LiftStruct g_lsLift ; //电梯显示结构 int g_FloorPos[DEFAULT_FLOOR_NUM] ; //每一层楼的坐标 /*主要函数声明区*/ DWORD LiftSeverThread(PVOID pvParam) ; //电梯服务函数 DWORD LiftClientThread(PVOID pvParam) ; //楼层请求函数 INT WINAPI Dlg_Proc(HWND hwnd ,UINT uMsg, WPARAM wParam , LPARAM lParam) ; //主窗口消息处理函数 BOOL CALLBACK AboutDlgProc(HWND,UINT,WPARAM,LPARAM) ; /*消息处理宏对应消息处理函数区*/ BOOL Dlg_OnInitDialog(HWND hwnd, HWND hwndFocus,LPARAM lParam) ; void Dlg_OnPaint(HWND hwnd) ; void Dlg_OnCommand(HWND hwnd, int id, HWND hwndCtl , UINT codeNotify) ; /*有关按钮消息处理函数*/ void OnStart() ; void OnStop() ; /*画三角形的函数,也就是上下楼请求*/ void Triangle(HDC hdc,POINT pt[]) ; /* ------------------------------------------------------------------------------------- * 函数名称: _tWinMain * 功能描述: 作为程序的入口点函数,生成主窗口,让各线程安全退出,以及关闭内核对象 程序的主框架取自《Windows核心编程 5th》一书 * 参数列表: 这一个略 * 返回结果: 这一个略,呵呵 ------------------------------------------------------------------------------------- */ int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstanec , LPTSTR lpCmdLind,int nCmdShow) { g_hInst = hInstance ; //方便后面使用 DialogBox(hInstance,MAKEINTRESOURCE(IDD_LIFTDEMO),NULL, Dlg_Proc) ; InterlockedExchange(&g_fShutDown,TRUE) ; //VC6下要转型(long *) WaitForMultipleObjects(g_nNumThreads,g_hThreads,TRUE,INFINITE) ; while(g_nNumThreads--) //关闭句柄 { CloseHandle(g_hThreads[g_nNumThreads]) ; } MessageBox(NULL,"程序结束","测试程序是否死锁",MB_OK) ; //测试死锁 return (0) ; } /* ------------------------------------------------------------------------------------- * 函数名称: LiftSeverThread * 功能描述: 作为电梯线程的入口函数,处理楼层的请求,更新相应窗口的信息 * 参数列表: 这一个略,呵呵 * 返回结果: 0 ------------------------------------------------------------------------------------- */ DWORD LiftSeverThread(PVOID pvParam) { Sleep(2000) ; eLiftDir LiftCurDir = Lift.GetLiftDirection() ; TCHAR szDesFloor[MAX_PATH] ; TCHAR szDir[10] ; TCHAR szCount[10] ; bitset<DEFAULT_FLOOR_NUM+1> btLiftInReq ; bitset<DEFAULT_FLOOR_NUM+1> btUpDirQueuue ; bitset<DEFAULT_FLOOR_NUM+1> btDownDirQueue ; string strLiftInReq ; //保存电梯内所有的请求 int i = 0 ; int iLiftCurFloor = 1 ; int iLiftDesFloor = 0 ; int iSleepTime = 0 ; eReqStatus eStatus = LIFTOUT ; int bsFloor ; eLiftDir iWantDir = LIFTDOWN ; eLiftDir eLiftCurDir = LIFTUP ; while((PVOID)1 != InterlockedCompareExchangePointer((PVOID *)&g_fShutDown,(PVOID)0,(PVOID)0)) { if(!Lift.ReSetFloorReq(eStatus,bsFloor)) { continue ; //显示错误 } else { iLiftCurFloor = Lift.GetLiftCurFloor() ; iLiftDesFloor = Lift.GetLiftNextDes() ; eLiftCurDir = Lift.GetLiftDirection() ; iSleepTime = ( iLiftDesFloor > iLiftCurFloor ) ? (iLiftDesFloor - iLiftCurFloor ) :(iLiftCurFloor - iLiftDesFloor) ; //Sleep( iSleepTime * 2000) ; //这里挂了,因为下楼的时候,传了一个负数,卡死了 //必须停止两秒以上,不然的话,就会出现系统BUG wsprintf(szDesFloor,"%d",iLiftDesFloor) ; SetWindowText(g_HwndNextDesFloor, szDesFloor) ; btLiftInReq = Lift.GetLiftDesFloor() ; Lift.EnterLiftCs() ; //本来是私有的 btUpDirQueuue = Lift.GetUpDirQueue() ; btDownDirQueue = Lift.GetDownDirQueue() ; for(i = 1 ; i < DEFAULT_FLOOR_NUM + 1 ; i++) { if(btUpDirQueuue.test(i) && eLiftCurDir == LIFTUP && (iLiftCurFloor <= i)) { wsprintf(szDesFloor," %d 楼",i) ; strLiftInReq += szDesFloor ; } else if(btDownDirQueue.test(i) && eLiftCurDir == LIFTDOWN && (iLiftCurFloor >= i)) { wsprintf(szDesFloor," %d 楼",i) ; strLiftInReq += szDesFloor ; } } SetWindowText(g_HwndDesFloor,strLiftInReq.c_str()) ; Lift.LeaveLiftCs() ; strLiftInReq.clear() ; //插入事件日志 LVITEM lvitem; lvitem.mask=LVIF_TEXT; lvitem.cchTextMax=MAX_PATH; lvitem.iSubItem=0; lvitem.pszText = TEXT("电梯"); lvitem.iItem = iListViewIndex ;//插入第几行,从0开始 ListView_InsertItem(g_HwndListView,&lvitem); wsprintf(szDesFloor,"下一站目的地为 %d 楼",iLiftDesFloor) ; ListView_SetItemText(g_HwndListView,lvitem.iItem,1,szDesFloor); iListViewIndex++ ; if(LIFTUP == Lift.GetLiftDirection()) { wsprintf(szDir,"%s","上升") ; SetWindowText(g_HwndDir,szDir) ; while(g_lsLift.pos >= g_FloorPos[iLiftDesFloor-1] && !g_fInsertReq)//while(!g_fInsertReq)上升过程如果有中途插入的请求,则跳到下一次循环 { Lift.EnterInsertReqCs() ; //互斥 g_lsLift.pos -= 1 ; Lift.LeaveInsertReqCs() ; InvalidateRect(g_HwndMain,&g_lsLift.realrect,TRUE); //少了这一句,电梯则不会上升 Sleep(g_lsLift.speed) ; } if(g_fInsertReq) //有插入请求,跳到下一次循环 { g_fInsertReq = false ; continue ; } } else if(LIFTDOWN == Lift.GetLiftDirection() ) { wsprintf(szDir,"%s","下降") ; SetWindowText(g_HwndDir,szDir) ; while(g_lsLift.pos <= g_FloorPos[iLiftDesFloor-1] && !g_fInsertReq ) { Lift.EnterInsertReqCs() ; //互斥 g_lsLift.pos += 1 ; Lift.LeaveInsertReqCs() ; InvalidateRect(g_HwndMain,&g_lsLift.realrect,TRUE); Sleep(g_lsLift.speed) ; } if(g_fInsertReq) { g_fInsertReq = false ; continue ; } } //这里用iLiftCurFloor代替 Lift.LiftReachAndWait(iLiftDesFloor) ; //电梯到达目标楼层,触发电梯到达事件 /*统计一下电梯运行了多少次*/ iFinishCount++ ; wsprintf(szCount,"%d",iFinishCount) ; SetWindowText(g_HwndFinishCount,szCount) ; continue ; } } Lift.ResetAllEvents() ; return (0) ; } /* ------------------------------------------------------------------------------------ * 函数名称: LiftClientThread * 功能描述: 产生请求,请求有两种,一种是电梯外的请求,也就是上楼,下楼请求。另外 一种是电梯内的请求,也就是电梯应该前行的目的地。对于楼层的这两种请求, 我都做简化,呵呵,有点懒。对于电梯外的请求,我做的简化就是,每一层产 生请求时,有且只有一种请求,也就是要么上楼,要么下楼。不过,如果要实 现两个方向的请求也不难。对于电梯内的请求,我做的简化就是,电梯内产生 的目的地一定是电梯此时方向可以到达的,比如电梯在2楼,方向向上2楼的人 只能产生到3、4、5楼的请求,而不会产生去1楼的请求,因为这不符合逻辑, 呵呵,虽然现实生活中真的有人这样做。 * 参数列表: 略 * 返回结果: 0 ------------------------------------------------------------------------------------ */ DWORD LiftClientThread(PVOID pvParam) //添加一个对g_fInsertReq处理 { int iFloorNum = PtrToUlong(pvParam) ; TCHAR *szpUpDirReq = TEXT("上楼请求"); TCHAR *szpDownDirReq = TEXT("下楼请求"); Sleep(1000 * (5 - iFloorNum + 1 )) ; int iWantFloorNum = 0 ; ; HWND hwndLV = GetDlgItem(g_HwndMain,IDC_LIST_LIFTLOG) ; //列表控件的句柄 eReqStatus eStatus = LIFTOUT ; bool IsTopOrBottom = false ; eLiftDir iWantDir = LIFTDOWN ; // bitset<DEFAULT_FLOOR_NUM+1> bsFloor ; srand(time(0)) ; //产生随机数 if(TOPFLOOR == iFloorNum) { iWantDir = LIFTDOWN ; IsTopOrBottom = true ; } else if(BOTTOMFLOOR == iFloorNum) { iWantDir = LIFTUP ; IsTopOrBottom = true ; } while((PVOID)1 != InterlockedCompareExchangePointer((PVOID *)&g_fShutDown,(PVOID)0,(PVOID)0)) { if(!IsTopOrBottom) { iWantDir = (eLiftDir)(rand() % 3 ); //产生3个状态,0 1 2 分别代表停上下 } if(NOTREQ == iWantDir) //这里应该是判断是否等于LIFTSTOP,而不是NOTREQ { Sleep(5000) ; continue ; } else if(LIFTOUT == eStatus) { bsFloor.set(iFloorNum) ; if(iWantDir == LIFTUP) { SetWindowText(g_fsFloor[iFloorNum-1].hwndLog,szpUpDirReq) ; g_fsFloor[iFloorNum-1].direction = LIFTUP ; g_bsDrawDir.set(iFloorNum) ; // InvalidateRect(g_HwndMain,&g_fsFloor[iFloorNum-1].realrect,TRUE); } else { SetWindowText(g_fsFloor[iFloorNum-1].hwndLog,szpDownDirReq) ; g_fsFloor[iFloorNum-1].direction = LIFTDOWN ; g_bsDrawDir.set(iFloorNum) ; // InvalidateRect(g_HwndMain,&g_fsFloor[iFloorNum-1].realrect,TRUE); } InvalidateRect(g_HwndMain,&g_fsFloor[iFloorNum-1].realrect,TRUE); Lift.SetFloorReq(eStatus,bsFloor,iFloorNum,iWantDir) ; //这里会阻塞// g_bsDrawDir.reset(iFloorNum) ; //进入电梯之后,不用画 eStatus = LIFTIN ; //表明已经进入电梯 bsFloor.reset() ; if(LIFTUP == iWantDir) { int iDesNum = (rand()% (DEFAULT_FLOOR_NUM - iFloorNum)) + 1 ; //如果是上升,总共可以产生这么多个请求 for(int i = 0 ; i < iDesNum ; i++) { iWantFloorNum = (rand() % (TOPFLOOR- iFloorNum) )+ iFloorNum ; if(iFloorNum == iWantFloorNum && iFloorNum < 5) { iWantFloorNum++ ; } bsFloor.set(iWantFloorNum) ; //原来里面这个(rand() % (TOPFLOOR- iFloorNum) )+ 1 }//for g_bsDrawDir.reset(iFloorNum) ; //进入电梯之后,不用画 // InvalidateRect(g_HwndMain,&g_fsFloor[iFloorNum-1].realrect,TRUE); InvalidateRect(g_HwndMain,NULL,TRUE) ; //重画整个窗口 Lift.SetFloorReq(eStatus,bsFloor,iFloorNum,iWantDir) ; //触发电梯继续事件 SetWindowText(g_fsFloor[iFloorNum-1].hwndLog,TEXT("")) ; eStatus = LIFTOUT ; //重置人的状态,表明请求是电梯外的 }//if else if(LIFTDOWN == iWantDir) //下楼 { int iDesNum = rand()% (iFloorNum - BOTTOMFLOOR) + 1 ; for(int i = 0 ; i < iDesNum ; i++) { iWantFloorNum = (rand() % (iFloorNum - BOTTOMFLOOR) ) + 1 ; if(iFloorNum == iWantFloorNum && iFloorNum > 1) { iWantFloorNum-- ; }//if bsFloor.set(iWantFloorNum) ; //原来里面这个(rand() % (iFloorNum - BOTTOMFLOOR) )+ 1 }//for Lift.SetFloorReq(eStatus,bsFloor,iFloorNum,iWantDir) ; g_bsDrawDir.reset(iFloorNum) ; //进入电梯之后,不用画 InvalidateRect(g_HwndMain,&g_fsFloor[iFloorNum-1].realrect,TRUE); // InvalidateRect(g_HwndMain,NULL,TRUE) ; SetWindowText(g_fsFloor[iFloorNum-1].hwndLog,TEXT("")) ; eStatus = LIFTOUT ; }//else if }//else if bsFloor.reset() ; Sleep(iReqSpeed * ( ( rand() % iFloorNum) + 1)) ; } //while Lift.ResetAllEvents() ; return 0 ; } /* ------------------------------------------------------------------------------------- * 函数名称: Dlg_Proc * 功能描述: 主窗口的消息处理处理,分发消息,实现对各消息的处理。这一消息处理函数框 架取自《Windows核心编程 5th》一书 这一部分使用了消息处理宏,简化工作量。其中的chHANDLE_DLGMSG是取自核心 编程一书,源码可以在那本书看到。定义相关消息的处理函数时,只需对那个消 息点击右击,转到其定义,便可以看到其函数的原型。 例如 void Cls_OnPaint(HWND hwnd) 对应HANDLE_WM_PAINT * 参数列表: 这一个略,呵呵 * 返回结果: 略 ------------------------------------------------------------------------------------- */ INT WINAPI Dlg_Proc(HWND hwnd ,UINT uMsg, WPARAM wParam , LPARAM lParam) { switch(uMsg) { chHANDLE_DLGMSG(hwnd,WM_INITDIALOG,Dlg_OnInitDialog) ; chHANDLE_DLGMSG(hwnd,WM_COMMAND, Dlg_OnCommand ) ; chHANDLE_DLGMSG(hwnd,WM_PAINT , Dlg_OnPaint) ; //不要乱用,会占用过多的CPU } return (FALSE) ; } /* ------------------------------------------------------------------------------------- * 函数名称: Dlg_OnInitDialog * 功能描述: 初始化各窗口句柄、电梯速度、电梯大小、电梯轨道、楼层相对窗口客户区的坐 标,设置窗口属性等。 * 参数列表: 略 * 返回结果: 略 ------------------------------------------------------------------------------------- */ BOOL Dlg_OnInitDialog(HWND hwnd, HWND hwndFocus,LPARAM lParam) { /*初始化对话框,得到对话框窗口句柄*/ HMENU hMenu ; g_HwndMain = hwnd ; // GetClientRect(g_HwndMain,&g_DlgClientRect) ; g_HwndMainStop = GetDlgItem(g_HwndMain,IDC_BUTTON_STOP) ; EnableWindow(g_HwndMainStop,false) ; chSETDLGICONS(g_HwndMain,IDI_ICONLIFT) ; //图标 hMenu = LoadMenu(g_hInst,MAKEINTRESOURCE(IDR_MENULEFT)) ; SetMenu(hwnd,hMenu) ; SetWindowLong(g_HwndMain,GWL_STYLE,WS_OVERLAPPEDWINDOW &~ WS_MAXIMIZEBOX ) ; //除去窗口最大化效果 /*视图控件*/ LVCOLUMN LvC ; TCHAR szTextLift[] = "电梯" ; TCHAR szTextEvent[] = "事件" ; /*全局电梯状态窗口初始化*/ g_HwndListView = GetDlgItem(hwnd,IDC_LIST_LIFTLOG) ; g_HwndDesFloor = GetDlgItem(hwnd,IDC_EDIT_DES) ; g_HwndDir = GetDlgItem(hwnd,IDC_EDIT_DIR) ; g_HwndNextDesFloor = GetDlgItem(hwnd,IDC_EDIT_NEXTDES) ; g_HwndFinishCount = GetDlgItem(hwnd ,IDC_FINISHCOUNT) ; g_HwndLiftSpeed = GetDlgItem(hwnd,IDC_EDIT_LIFTSPEED) ; g_HwndReqSpeed = GetDlgItem(hwnd,IDC_EDIT_REQSPEED) ;; /*每一层的窗口初始化*/ g_fsFloor[0].hwndDisplay = GetDlgItem(hwnd,IDC_PICTURE_FRISTFLOOR) ; g_fsFloor[0].hwndLog = GetDlgItem(hwnd,IDC_EDIT_FIRST) ; g_fsFloor[1].hwndDisplay = GetDlgItem(hwnd,IDC_PICTURE_SECONDFLOOR) ; g_fsFloor[1].hwndLog = GetDlgItem(hwnd,IDC_EDIT_SECOND) ; g_fsFloor[2].hwndDisplay = GetDlgItem(hwnd,IDC_PICTURE_THRIDFLOOR) ; g_fsFloor[2].hwndLog = GetDlgItem(hwnd,IDC_EDIT_THRID) ; g_fsFloor[3].hwndDisplay = GetDlgItem(hwnd,IDC_PICTURE_FORTHFLOOR) ; g_fsFloor[3].hwndLog = GetDlgItem(hwnd,IDC_EDIT_FORTH) ; g_fsFloor[4].hwndDisplay = GetDlgItem(hwnd,IDC_PICTURE_FIFTHFLOOR) ; g_fsFloor[4].hwndLog = GetDlgItem(hwnd,IDC_EDIT_FIFTH) ; /*初始化电梯动画显示窗口*/ g_lsLift.hwnd = GetDlgItem(hwnd,IDC_PICTURE_LIFT) ; /*初始化视图控件*/ ZeroMemory( &LvC, sizeof(LVCOLUMN)) ; LvC.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM ; LvC.iSubItem = 0 ; LvC.cx = 200 ; LvC.fmt = LVCFMT_LEFT ; LvC.pszText = szTextLift ; if(-1 == ListView_InsertColumn(g_HwndListView,0,&LvC) ) { MessageBox(hwnd,"测试","多测试",MB_OK) ; } LvC.pszText = szTextEvent ; LvC.cx = 250 ; if(-1 == ListView_InsertColumn(g_HwndListView,1,&LvC)) { MessageBox(hwnd,"测试","多测试",MB_OK) ; } /*初始化动画,坐标转换*/ POINT pt ; RECT rcDlgRect ; RECT rcDlgRealRect ; GetClientRect(g_HwndMain,&rcDlgRect) ; //获取对话框的大小 pt.x = rcDlgRect.left ; pt.y = rcDlgRect.top ; ClientToScreen(g_HwndMain,&pt) ; rcDlgRealRect.top = pt.y ; rcDlgRealRect.left = pt.x ; pt.x = rcDlgRect.right ; pt.y = rcDlgRect.bottom ; ClientToScreen(g_HwndMain,&pt) ; rcDlgRealRect.bottom = pt.y ; //客户区相对于屏幕的绝对坐标 rcDlgRealRect.right = pt.x ; /////电梯客户区转换成屏幕坐标 RECT rect ; RECT rcRealRect ; RECT tmpRect ; GetClientRect(g_lsLift.hwnd ,&rect) ; GetClientRect(g_lsLift.hwnd ,&tmpRect) ; pt.x = tmpRect.left ; pt.y = tmpRect.top ; ClientToScreen(g_lsLift.hwnd ,&pt) ; rcRealRect.top = pt.y ; rcRealRect.left = pt.x ; pt.x = tmpRect.right ; pt.y = tmpRect.bottom ; ClientToScreen(g_lsLift.hwnd ,&pt) ; rcRealRect.bottom = pt.y ; //客户区相对于屏幕的绝对坐标 rcRealRect.right = pt.x ; rcRealRect.top -= rcDlgRealRect.top ; //电梯轨道相对于客户区的坐标 22 rcRealRect.left -= rcDlgRealRect.left ; //3 rcRealRect.bottom -= rcDlgRealRect.top ; rcRealRect.right -= rcDlgRealRect.left ; g_lsLift.rect = rect ; g_lsLift.pos = rcRealRect.bottom ; g_lsLift.realrect = rcRealRect ; g_lsLift.speed = 25 ; //电梯速度 // GetWindowRect(g_lsLift.hwnd ,&tmpRect) ; //测试是否成功转换绝对坐标 /*初始化楼层*/ int i = 0 ; int j = 0 ; for( i = 0 ; i < DEFAULT_FLOOR_NUM ; i++) { GetClientRect(g_fsFloor[i].hwndDisplay,&g_fsFloor[i].rect) ; GetWindowRect(g_fsFloor[i].hwndDisplay,&g_fsFloor[i].realrect) ; g_FloorPos[i] = g_fsFloor[i].realrect.bottom - rcDlgRealRect.top ; //电梯的相对位置 g_fsFloor[i].realrect.top -= rcDlgRealRect.top ; //得到每一层相对于窗口客户区的位置 g_fsFloor[i].realrect.left -= rcDlgRealRect.left ; g_fsFloor[i].realrect.right -= rcDlgRealRect.left ; g_fsFloor[i].realrect.bottom -= rcDlgRealRect.top ; } g_rcFloor = g_fsFloor[0].rect ; //电梯大小,也就是那个矩形的大小 iLiftSize = g_rcFloor.bottom - g_rcFloor.top ; //固定电梯的size return TRUE ; } /* ------------------------------------------------------------------------------------- * 函数名称: Dlg_OnCommand * 功能描述: 处理WM_COMMAND消息,响应各按钮,响应菜单 * 参数列表: 略 * 返回结果: 无 ------------------------------------------------------------------------------------- */ void Dlg_OnCommand(HWND hwnd, int id, HWND hwndCtl , UINT codeNotify) { switch(id) { case IDC_BUTTON_START : OnStart() ; break ; case IDC_BUTTON_STOP : OnStop() ; break ; case ID_MENUITEM_ABOUT : DialogBox(g_hInst,MAKEINTRESOURCE(IDD_ABOUT),g_HwndMain, AboutDlgProc) ; break ; case IDCANCEL : EndDialog(hwnd,id) ; break ; } } /* ------------------------------------------------------------------------------------- * 函数名称: AboutDlgProc * 功能描述: “关于”对话框的消息处理函数,什么也没有用,只是简单的显示一下。 * 参数列表: 略 * 返回结果: 略 ------------------------------------------------------------------------------------- */ BOOL CALLBACK AboutDlgProc(HWND hDlg,UINT message ,WPARAM wParam,LPARAM lParam) { switch(message) { case WM_INITDIALOG : return TRUE ; case WM_COMMAND : switch(LOWORD(wParam)) { case IDOK: case IDCANCEL : EndDialog(hDlg,0) ; return TRUE ; } break ; } return FALSE ; } /* ------------------------------------------------------------------------------------- * 函数名称: OnStart * 功能描述: 响应开始按钮,设置电梯、楼层速度,生成电梯服务线程以及楼层请求线程 * 参数列表: 略 * 返回结果: 无 ------------------------------------------------------------------------------------- */ void OnStart() { //生成线程 HWND ButtonStartHwnd = GetDlgItem(g_HwndMain ,IDC_BUTTON_START) ; DWORD dwThreadID ; int i = 0 ; /*初始化速度*/ int iLiftSpeedValue = 0 ; int iReqSpeedValue = 0 ; TCHAR szLiftSpeed[10] ; TCHAR szReqSpeed[10] ; iLiftSpeedValue = GetWindowText(GetDlgItem(g_HwndMain,IDC_EDIT_LIFTSPEED),szLiftSpeed,10) ; iReqSpeedValue = GetWindowText(GetDlgItem(g_HwndMain,IDC_EDIT_REQSPEED),szReqSpeed,10) ; if(iLiftSpeedValue != 0 && iReqSpeedValue != 0) { iLiftSpeedValue = atoi(szLiftSpeed) ; iReqSpeedValue = atoi(szReqSpeed) ; if(iLiftSpeed != 0 && iReqSpeed != 0 && iLiftSpeed >= 10 && iLiftSpeed <= 35 && iReqSpeed >= 3000 && iReqSpeed <= 9000) { iLiftSpeed = iLiftSpeedValue ; iReqSpeed = iReqSpeedValue ; g_lsLift.speed = iLiftSpeedValue ; //电梯速度 } } EnableWindow(GetDlgItem(g_HwndMain,IDC_EDIT_LIFTSPEED),false) ; EnableWindow(GetDlgItem(g_HwndMain,IDC_EDIT_REQSPEED),false) ; EnableWindow(ButtonStartHwnd,false) ; EnableWindow(g_HwndMainStop,true) ; for(i = 0 ; i < DEFAULT_FLOOR_NUM ; i++) { g_hThreads[g_nNumThreads++] = chBEGINTHREADEX(NULL,0,LiftClientThread,(PVOID)(i+1), 0,&dwThreadID) ; } g_hThreads[g_nNumThreads++] = chBEGINTHREADEX(NULL,0,LiftSeverThread,(PVOID)(i+1), 0,&dwThreadID) ; } /* ------------------------------------------------------------------------------------- * 函数名称: OnStop * 功能描述: 响应停止按钮,挂起各线程 * 参数列表: 略 * 返回结果: 无 ------------------------------------------------------------------------------------- */ void OnStop() { int i = 0 ; HWND Canclehwnd = GetDlgItem(g_HwndMain,IDCANCEL) ; if(!g_fSuspend) { g_fSuspend = true ; //这里改变停止按钮的文字 SetWindowText(g_HwndMainStop,"继续动画显示") ; EnableWindow(Canclehwnd,false) ; for(i = 0 ; i < g_nNumThreads ; i++) { SuspendThread(g_hThreads[i]) ; } } else { g_fSuspend = false ; //改变停止按钮的文件 SetWindowText(g_HwndMainStop,"暂停") ; EnableWindow(Canclehwnd,true) ; for(i = 0 ; i < g_nNumThreads ; i++) { ResumeThread(g_hThreads[i]) ; } } } /* ------------------------------------------------------------------------------------- * 函数名称: Dlg_OnPaint * 功能描述: 处理WM_PAINT消息,绘制电梯位置、绘制各楼层的上下楼请求 * 参数列表: 略 * 返回结果: 无 ------------------------------------------------------------------------------------- */ void Dlg_OnPaint(HWND hwnd) { PAINTSTRUCT ps ; HDC hdc ; hdc = BeginPaint(hwnd,&ps) ; /******************************开始画图********************************/ HBRUSH hBrush = (HBRUSH)GetStockObject(BLACK_BRUSH) ; HGDIOBJ hOldBrush ; hOldBrush = SelectObject(hdc,hBrush) ; Rectangle(hdc,g_lsLift.realrect.left ,g_lsLift.pos - iLiftSize , g_lsLift.realrect.right,g_lsLift.pos) ; //全部填满 SelectObject(hdc,hOldBrush) ; /*画三角形*/ for(int i = 0 ; i < DEFAULT_FLOOR_NUM ; i++) { if(g_bsDrawDir.test(i+1)) { if(g_fsFloor[i].direction == LIFTUP) { g_fsFloor[i].pt[0].x = g_fsFloor[i].realrect.left + g_fsFloor[i].rect.right / 2 ; g_fsFloor[i].pt[1].x = g_fsFloor[i].realrect.left + g_fsFloor[i].rect.right / 3 ; g_fsFloor[i].pt[2].x = g_fsFloor[i].realrect.right - g_fsFloor[i].rect.right / 3 ; g_fsFloor[i].pt[0].y = g_fsFloor[i].realrect.top ; g_fsFloor[i].pt[1].y = g_fsFloor[i].realrect.bottom ; g_fsFloor[i].pt[2].y = g_fsFloor[i].realrect.bottom ; Triangle(hdc,g_fsFloor[i].pt) ; } else if(g_fsFloor[i].direction == LIFTDOWN) { g_fsFloor[i].pt[0].x = g_fsFloor[i].realrect.left + g_fsFloor[i].rect.right / 2 ; g_fsFloor[i].pt[1].x = g_fsFloor[i].realrect.left + g_fsFloor[i].rect.right / 3 ; g_fsFloor[i].pt[2].x = g_fsFloor[i].realrect.right - g_fsFloor[i].rect.right / 3 ; g_fsFloor[i].pt[0].y = g_fsFloor[i].realrect.bottom ; g_fsFloor[i].pt[1].y = g_fsFloor[i].realrect.top ; g_fsFloor[i].pt[2].y = g_fsFloor[i].realrect.top ; Triangle(hdc,g_fsFloor[i].pt) ; } } } EndPaint(hwnd,&ps) ; } /* ------------------------------------------------------------------------------------- * 函数名称: Triangle * 功能描述: 此函数取自《Windows程序设计 5th》。此函数用来绘制上下楼的三角形 * 参数列表: 略 * 返回结果: 无 ------------------------------------------------------------------------------------- */ void Triangle(HDC hdc,POINT pt[]) { HBRUSH hBrush = CreateSolidBrush(RGB(255,216,0)) ; HGDIOBJ hOldBrush = SelectObject(hdc,hBrush) ; Polygon(hdc,pt,3) ; SelectObject(hdc,hOldBrush) ; DeleteObject((HGDIOBJ)hBrush) ; //不用的话,记得要删除,否则内存泄漏,造成界面问题严重 }
/* ------------------------------------------------------------------------------------- * Copyright(c) 2012-3-6 Seed-L * All rights reserved. * 文件名称: LiftDemo.h * 简要描述: 定义各类数据结构、常量 * 作者:Seed-L * 完成日期:2012-3-4 ------------------------------------------------------------------------------------- */ #ifndef LIFTDEMO_H_ #define LIFTDEMO_H_ #include"help.h" #include<windows.h> #include<bitset> #include<string> using std::bitset ; using std::string ; //楼层请求的类型:电梯外、电梯内、没有请求 //本来定义的名称是OUT、IN的简单名称,结果编译不通过,找了也挺久的错误 //后来发现原来OUT 和 IN 是库里面已经定义好的。但是错误提示又没有提示说重定义 enum eReqStatus { LIFTOUT = 0 ,LIFTIN = 1 , NOTREQ = 2} ; //电梯方向: //貌似发现LIFTSTOP 没有用上 enum eLiftDir { LIFTUP , LIFTDOWN ,LIFTSTOP} ; const int DEFAULT_FLOOR_NUM = 5 ; //楼层总楼 const int TOPFLOOR = 5 ; //顶层 const int BOTTOMFLOOR = 1 ; //底层 //动画电梯结构 typedef struct LiftStruct { HWND hwnd ; //电梯轨道 int speed ; RECT rect ; //相对自己的窗口区 RECT realrect ; //相对窗口客户区 int pos ; //电梯的位置 } LiftStruct , *PLiftStruct ; //动画楼层结构 typedef struct FloorStruct { HWND hwndDisplay ; //上下楼请求绘制窗口句柄 HWND hwndLog ; //显示请求的窗口,文字描述 RECT rect ; RECT realrect ; //相对窗口窗户区的坐标 POINT pt[3] ; //绘制三角形的三个点的坐标 eLiftDir direction ; } FlooStruct ,PFloorStruct; class LiftDemoS { public : //本来是私有函数 inline void EnterLiftCs() { EnterCriticalSection(&m_Cs) ; } inline void LeaveLiftCs() { LeaveCriticalSection(&m_Cs) ; } inline eLiftDir GetLiftDirection() { return m_eLiftCurDir ; } inline int GetLiftCurFloor() { return m_iLiftCurFloor ;} inline int GetLiftCurDesNum() { return m_bsReqQueue.count() ; } inline int GetLiftNextDes() { return m_iLiftNextDes ;} inline bitset<DEFAULT_FLOOR_NUM+1> GetLiftDesFloor() { return m_bsReqQueue ;} inline bitset<DEFAULT_FLOOR_NUM+1> GetUpDirQueue() { return m_bsUpDirQueue ;} inline bitset<DEFAULT_FLOOR_NUM+1> GetDownDirQueue() { return m_bsDownDirQueue ;} void LiftReachAndWait(int iNum) ; int LiftChangeDirection( int* iOldDir) ; LiftDemoS() ; virtual ~LiftDemoS() ; private : HANDLE m_hReqHappenEvents[DEFAULT_FLOOR_NUM] ; //某楼层发出请求事件对象 HANDLE m_hLiftReachEvents[DEFAULT_FLOOR_NUM] ; //电梯到达楼层时,通知楼层事件 HANDLE m_LiftGoEvent ; //用来告知电梯可以起动 CRITICAL_SECTION m_Cs ; //互斥段,用来修改队列时使用 CRITICAL_SECTION m_ResAllEventCs ; CRITICAL_SECTION m_InsertReqCs ; private : inline void InitLiftCs() { InitializeCriticalSection(&m_Cs) ; InitializeCriticalSection(&m_ResAllEventCs) ; InitializeCriticalSection(&m_InsertReqCs) ; } inline void DeleteLiftCs() { DeleteCriticalSection(&m_Cs) ; DeleteCriticalSection(&m_ResAllEventCs) ; DeleteCriticalSection(&m_InsertReqCs) ; } void CreateLiftEvent() ; public : //分两种 //第一种是电梯外请求,第一个参数代表请求的状态,第二个请求代表发生请求的楼层 //第二个是电梯内请求,第一个参数代表请求的状态是电梯内的,第二个请求代表里面发生的楼层 bool SetFloorReq(eReqStatus eStatus , bitset<DEFAULT_FLOOR_NUM+1> &bsFloor, int iFloorNum , eLiftDir eWantDir) ; bool ReSetFloorReq(eReqStatus &eStatus , int &biFloor) ; void LiftSetGoOnEvent() ; void LiftResetGoOnEvent() ; void ResetAllEvents() ; inline void EnterInsertReqCs() { EnterCriticalSection(&m_InsertReqCs) ; } inline void LeaveInsertReqCs() { LeaveCriticalSection(&m_InsertReqCs) ; } private : eLiftDir m_eLiftCurDir ; //电梯此时的方向,转向有用 int m_iLiftCurFloor ; //电梯此时所在楼层,请求发生时,用来和这一个比较, //以便决定放入到上升队列,还是下降队列 int m_iLiftNextDes ; //下一站 bitset<DEFAULT_FLOOR_NUM+1> m_bsReqQueue ; bitset<DEFAULT_FLOOR_NUM+1> m_bsUpDirQueue ; //1为向上,0为向下 //将这一个原来的方向队列,变为上升队列 //某位的1,表示某楼层要向上,0表示没有请求,或者向下 bitset<DEFAULT_FLOOR_NUM+1> m_bsDownDirQueue ; //新增的一个向下请求队列 //某位的1,表示某楼层要向下,0表示没有请求,或者向上 bool m_fTurnToFindFloor ; bool m_fAcceptOppReq ; } ; #endif
/* ------------------------------------------------------------------------- * Copyright(c) 来自《Windows核心编程》 * All rights reserved. * 文件名称: help.h * 简要描述: 此文件的内容来自核心编程一书,主要是一些辅助宏,用来 简化自己的工作量 * 作者 : 《Windows核心编程》一书的作者 --------------------------------------------------------------------------*/ #ifndef HELP_H_ #define HELP_H_ #define _WIN32_WINNT 0x0500 //#define WINVER 0x0500 #include<tchar.h> #include<windowsx.h> #include<windows.h> ///////////////////////////// chBEGINTHREADEX Macro /////////////////////////// // This macro function calls the C runtime's _beginthreadex function. // The C runtime library doesn't want to have any reliance on Windows' data // types such as HANDLE. This means that a Windows programmer needs to cast // values when using _beginthreadex. Since this is terribly inconvenient, // I created this macro to perform the casting. typedef unsigned (__stdcall *PTHREAD_START) (void *); #define chBEGINTHREADEX(psa, cbStack, pfnStartAddr, \ pvParam, fdwCreate, pdwThreadId) \ ((HANDLE)_beginthreadex( \ (void *) (psa), \ (unsigned) (cbStack), \ (PTHREAD_START) (pfnStartAddr), \ (void *) (pvParam), \ (unsigned) (fdwCreate), \ (unsigned *) (pdwThreadId))) //////////////////////////////// chDIMOF Macro //////////////////////////////// // This macro evaluates to the number of elements in an array. #define chDIMOF(Array) (sizeof(Array) / sizeof(Array[0])) /////////////////////////// Quick MessageBox Macro //////////////////////////// inline void chMB(PCSTR s) { char szTMP[128]; GetModuleFileNameA(NULL, szTMP, chDIMOF(szTMP)); MessageBoxA(GetActiveWindow(), s, szTMP, MB_OK); } ////////////////////////// chHANDLE_DLGMSG Macro ///////////////////////////// // The normal HANDLE_MSG macro in WindowsX.h does not work properly for dialog // boxes because DlgProc return a BOOL instead of an LRESULT (like // WndProcs). This chHANDLE_DLGMSG macro corrects the problem: #define chHANDLE_DLGMSG(hwnd, message, fn) \ case (message): return (SetDlgMsgResult(hwnd, uMsg, \ HANDLE_##message((hwnd), (wParam), (lParam), (fn)))) // Sets the dialog box icons inline void chSETDLGICONS(HWND hwnd, int idi) { SendMessage(hwnd, WM_SETICON, TRUE, (LPARAM) LoadIcon((HINSTANCE) GetWindowLong(hwnd, GWL_HINSTANCE), MAKEINTRESOURCE(idi))); SendMessage(hwnd, WM_SETICON, FALSE, (LPARAM) LoadIcon((HINSTANCE) GetWindowLong(hwnd, GWL_HINSTANCE), MAKEINTRESOURCE(idi))); } #endif //////////////////////////////////////以上的来自核心编程///////////////////////////////////////
本来注释已经写好的,但是VS出了点Bug关闭后发现刚才的保存完全没有作用。。。