酒店点餐系统开发详解(三)

酒店点餐系统开发详解(三)

——通信模块设计

注:本系统通信实现采用CSocket

在上一节的数据流图中可以看到顾客(点餐)终端和厨师(任务分配)终端之间有数据的流动,这看起来好像二者是直接进行的通信,而本系统涉及的模块通信均是以系统管理端为“中转站”的,因为系统管理端将负责观察其他所有终端的运行状况,并记录各顾客(点餐)终端的点菜情况。网络结构如下:

 

在整个通信过程中,包括如下信息:

1、连接消息(OSM_CONNECT)——顾客终端和厨师终端首次连接系统管理端时发送该消息,提示系统管理端保留终端信息。

2、断开连接消息(OSM_DISCONNECT)——顾客终端退出时向系统管理端发送的消息,以提示系统管理端更新顾客终端连接状态信息。

3、断开连接确认消息(OSM_DIS_CONF)——系统管理端向提出断开连接的顾客终端发送的消息,以确认系统管理端已接收到退出消息,该顾客终端允许退出,从而保证系统管理端的信息的正确性。

4、网络测试消息(OSM_TEST)——顾客终端和厨师终端测试网络的消息,以确保网络连接的正常。

5、测试确认消息(OSM_TEST_CONFIRM)——系统管理端向顾客终端或厨师终端回复网络测试的消息,以告知终端网络状况良好。

6、完成点菜消息(OSM_DISHED)——顾客终端在完成点菜后,向系统管理端发送的消息,然后系统管理端转发该消息到厨师终端以通知厨师终端做菜。

7、等待菜品完成消息(OSM_WAIT)——系统管理端向完成点菜的终端回复的消息,已告知顾客收到点菜消息。

8、菜品完成消息(OSM_FINISH)——厨师终端在完成某菜品后向系统管理端该消息,然后系统管理端转发该消息到相应的顾客终端。

9、厨师终端不在线消息(OSM_COOK_OFF)——当厨师终端未连接系统管理端,而顾客终端却完成点菜时,由系统管理端向该完成点菜的顾客终端发送的提示消息,通知顾客不能点菜。

10、系统管理端退出消息(OSM_SERVER_EXIT)——当系统管理端退出时,向所有的顾客终端发送的消息,通知顾客终端不用等待系统管理端的确认就可以随时退出顾客终端。

构造的通信协议结构体如下:

//消息类

class CMessage  

{

public:

void CreateMsg(int type,DWORD sender,DWORD receiver,CString dishedtime = "",CString dishname = "");

DWORD m_SenderID; //发送方标识

DWORD m_ReceiverID; //接收方标识

UINT m_MsgType; //消息类型

char m_DishedTime[DISHED_TIME_LEN]; //点菜时间

char m_DishName[DISH_NAME_LEN]; //菜品名称

CMessage();

virtual ~CMessage();

};

//创建消息

void CMessage::CreateMsg(int type, DWORD sender, DWORD receiver, CString dishedtime, CString dishname)

{

m_MsgType = type;

m_SenderID = sender;

m_ReceiverID = receiver;

strncpy(m_DishedTime,dishedtime,DISHED_TIME_LEN);

strncpy(m_DishName,dishname,DISH_NAME_LEN);

}

在通信过程中,为防止顾客终端或厨师终端因为网络状况不好而导致与系统管理端连接的中断,故需每隔一定时间进行网络测试,在进行了一定时间的测试后,若仍未收到测试响应,则应重新与系统管理端建立连接。具体代码如下:

void CClientTerminalDlg::OnTimer(UINT nIDEvent) 

{

if(nIDEvent == /*网络测试事件*/){

//连接测试

CMessage Msg;

Msg.CreateMsg(OSM_TEST,OST_CLIENT | m_CTDeskID,OST_SERVER | 0x0001);

m_Socket.Send(&Msg,sizeof(CMessage));

if (m_ReconnectNum > 4){ //4*2秒种没有收到服务器发来的信息,表示已经断线

m_ReconnectNum = 0;

m_ConnectNormal = FALSE;

//重新连接服务器

ConnectInit();

if(m_Socket.Connect(m_ServerIP,m_ServerPort) == TRUE){

Msg.CreateMsg(OSM_CONNECT,OST_CLIENT | m_CTDeskID,OST_SERVER | 0x0001);

m_Socket.Send(&Msg,sizeof(CMessage));

m_ConnectNormal = TRUE;

}

}

else{

m_ReconnectNum++;

}

}/**/

CDialog::OnTimer(nIDEvent);

}

——系统管理端

在系统管理端负责维护全部的顾客终端和厨师终端的套接字,系统管理端退出时,应向所有的已连接的顾客终端发送系统管理端退出消息(OSM_SERVER_EXIT),然后关闭其维护的所有套接字。其中顾客终端的套接字放在一链表中。以下是系统管理端退出时关闭连接的代码:

//断开连接

void CSystemManagementApp::CloseConnect()

{

POSITION pos = m_CTList.GetHeadPosition();

CMessage Msg;

int count = m_CTList.GetCount();

if(pos){

CClientInfo *m_pClientInfo;

for(int i=0; i < count; i++){

if (pos != NULL){

m_pClientInfo = static_cast <CClientInfo *>(m_CTList.GetNext(pos));

if(m_pClientInfo != NULL){

if(m_pClientInfo->m_ClientSock != NULL){

//发送系统管理端退出消息OSM_SERVER_EXIT

Msg.CreateMsg(OSM_SERVER_EXIT,OST_SERVER | 0x0001,OST_CLIENT|m_pClientInfo->m_DeskID);

m_pClientInfo->m_ClientSock->Send(&Msg,sizeof(CMessage));

}

//注意m_pClientInfo中已自动关闭套接字

delete m_pClientInfo;

}

}

}

}

if(m_CookTerminal != NULL){

m_CookTerminal->ShutDown();

m_CookTerminal->Close();

delete m_CookTerminal;

}

}

以下是系统管理端处理所有经过的消息的过程:

void CClientSocket::OnReceive(int nErrorCode) 

{

CMessage Msg,tempMsg;

int deskid = 0;

CString str,desk;

CSocket *pSocket = NULL;

CClientInfo *pInfo = NULL;

CSystemManagementDlg *m_Parent = NULL;

m_Parent = (CSystemManagementDlg*)AfxGetMainWnd();

Receive(&Msg,sizeof(CMessage));

switch(Msg.m_MsgType){

case OSM_CONNECT:

//首次连接,先检查是否是重连,若为重连应将原套接字关闭,然后

//记录顾客终端或厨师终端信息,调用主窗口函数更新信息

switch(Msg.m_SenderID & 0xf000){

case OST_CLIENT:

SetClientSocket(Msg.m_SenderID & 0x0fff);

//显示终端连接状态

m_Parent->SetDeskState(Msg.m_SenderID & 0x0fff,_T("已连接"));

//消息框显示信息

desk.Format(_T("%d号桌"),Msg.m_SenderID & 0x0fff);

m_Parent->ShowAllMsg(desk,_T("连接成功!"));

break;

case OST_COOK:

pSocket = ((CSystemManagementApp*)AfxGetApp())->m_CookTerminal;

if(pSocket != NULL){

pSocket->ShutDown(); //重新连接,销毁原套接字

pSocket->Close();

delete pSocket;

}

((CSystemManagementApp*)AfxGetApp())->m_CookTerminal = (CSocket *)this;

//消息框显示信息

m_Parent->ShowAllMsg(_T("厨师终端"),"后厨已准备好!");

break;

}

break;

case OSM_DISHED:

//向顾客终端发送等待消息OSM_WAIT

//转发消息到厨师终端,调用主窗口函数更新信息

//获取厨师终端套接字,若pSocket为空,则向顾客终端发送

//厨师终端掉线消息禁止点菜

pSocket = ((CSystemManagementApp*)AfxGetApp())->m_CookTerminal;

if(pSocket == NULL){

//发送厨师终端掉线消息OSM_COOK_OFF

tempMsg.CreateMsg(OSM_COOK_OFF,OST_SERVER | 0x0001,Msg.m_SenderID);

Send(&tempMsg,sizeof(CMessage));

break;

}

//保存点菜信息

pInfo = m_Parent->GetClientInfo(Msg.m_SenderID & 0x0fff);

if(pInfo == NULL)

break;

pInfo->m_DishedTime.Format(_T("%s"),Msg.m_DishedTime);

//发送等待消息OSM_WAIT

tempMsg.CreateMsg(OSM_WAIT,OST_SERVER | 0x0001,Msg.m_SenderID);

Send(&tempMsg,sizeof(CMessage));

//向厨师终端转发消息

pSocket->Send(&Msg,sizeof(CMessage));

//显示终端完成点菜状态

m_Parent->SetDeskState(Msg.m_SenderID & 0x0fff,_T("完成点菜"));

//消息框显示信息

desk.Format(_T("%d号桌"),Msg.m_SenderID & 0x0fff);

m_Parent->ShowAllMsg(desk,_T("完成点菜!"));

break;

case OSM_TEST:

//网络状态测试,返回测试确认消息OSM_TEST_CONFIRM

tempMsg.CreateMsg(OSM_TEST_CONFIRM,OST_SERVER | 0x0001,Msg.m_SenderID);

Send(&tempMsg,sizeof(CMessage));

break;

case OSM_FINISH:

//菜品完成,转发消息到顾客终端,调用主窗口函数更新信息

//获取接收信息的顾客终端的信息

pInfo = m_Parent->GetClientInfo(Msg.m_ReceiverID & 0x0fff);

//转发消息

if(pInfo != NULL){

pInfo->m_ClientSock->Send(&Msg,sizeof(CMessage));

//消息框显示终端某菜品已完成信息

str.Format(_T("%d号桌所点菜品< %s >已完成!"),Msg.m_ReceiverID & 0x0fff,Msg.m_DishName);

m_Parent->ShowAllMsg(_T("厨师终端"),str);

}

break;

case OSM_DISCONNECT:

//断开确认

tempMsg.CreateMsg(OSM_DIS_CONF,OST_SERVER | 0x0001,Msg.m_SenderID);

Send(&tempMsg,sizeof(CMessage));

//连接断开,获取终端信息,关闭套接字,调用主窗口函数更新信息

switch(Msg.m_SenderID & 0xf000){

case OST_CLIENT:

//获取所要断开的顾客终端的信息

pInfo = m_Parent->GetClientInfo(Msg.m_SenderID & 0x0fff);

//从列表中删除该终端信息

if(pInfo != NULL)

DelClientInfo(pInfo);

//显示终端断线状态

m_Parent->SetDeskState(Msg.m_SenderID & 0x0fff,_T("已中断连接"));

//显示在线终端情况

m_Parent->ShowOnlineDesk();

//消息框显示终端断线信息

desk.Format(_T("%d号桌"),Msg.m_SenderID & 0x0fff);

m_Parent->ShowAllMsg(desk,_T("已中断连接!"));

break;

case OST_COOK:

pSocket = ((CSystemManagementApp*)AfxGetApp())->m_CookTerminal;

if(pSocket != NULL){

pSocket->ShutDown(); //销毁套接字

pSocket->Close();

delete pSocket;

}

((CSystemManagementApp*)AfxGetApp())->m_CookTerminal = NULL;

//消息框显示终端断线信息

m_Parent->ShowAllMsg(_T("厨师终端"),_T("断开连接!"));

break;

}

break;

}

CSocket::OnReceive(nErrorCode);

}

系统管理端在完成收银后将向相应的顾客终端发送结账消息:

//发送结账消息

CSystemManagementDlg *m_Parent = (CSystemManagementDlg *)AfxGetMainWnd();

CMessage Msg;

CString str;

Msg.m_MsgType = OSM_CHECK_OUT;

Msg.m_SenderID = OST_SERVER | 0x0001;

Msg.m_ReceiverID = OST_CLIENT | m_DeskID;

m_Socket->Send(&Msg,sizeof(CMessage));

//设置终端状态

m_Parent->SetDeskState(m_DeskID,_T("已结账"));

//在消息框中显示信息

str.Format(_T("%d号桌"),m_DeskID);

m_Parent->ShowAllMsg(str,_T("结账完毕!"));

——顾客(点餐)终端

顾客终端处理消息的过程如下:

void CCTSocket::OnReceive(int nErrorCode) 

{

CMessage Msg;

CString  tips;

CClientTerminalDlg *m_Parent = (CClientTerminalDlg *)AfxGetMainWnd();

if(m_Parent == NULL){

AfxMessageBox(_T("系统出错!"));

}

Receive(&Msg,sizeof(CMessage));

switch(Msg.m_MsgType){

case OSM_WAIT:

//提示用户等待菜品的完成

m_Parent->DisplayMsgInfo("已经做菜任务发到后厨,请稍等...");

break;

case OSM_CHECK_OUT:

//完成收银后的终端清理工作

m_Parent->ClearTerminal();

break;

case OSM_FINISH:

//播放提示音

PlaySound(MAKEINTRESOURCE(IDR_COMPLETE_SOUND),AfxGetResourceHandle(), SND_SYNC|SND_RESOURCE);

//显示完成的菜品信息

tips.Format(_T("< %s >菜品已完成,稍后将由服务员送到您的餐桌!"),Msg.m_DishName);

m_Parent->DisplayMsgInfo(tips);

break;

case OSM_TEST_CONFIRM:

//测试确认,修改测试条件

m_Parent->SetConnectState(OSM_ONLINE);

break;

case OSM_DIS_CONF:

//断开确认,关闭并清理对话框

m_Parent->CloseTerminal();

::PostQuitMessage(0);

break;

case OSM_COOK_OFF:

//厨师终端不在线,不能点菜

AfxMessageBox(_T("后厨系统出现问题,请联系服务人员!"),MB_OK);

m_Parent->m_HasOrdered = FALSE;

m_Parent->m_btn_Mark.EnableWindow(FALSE); //禁用评分按钮

break;

case OSM_SERVER_EXIT:

//系统管理端退出,允许顾客终端退出系统

m_Parent->SetConnectState(OSM_OFFLINE);

break;

}

CSocket::OnReceive(nErrorCode);

}

顾客终端发送点菜消息:

void CClientTerminalDlg::OnOK() 

{

……

//向系统管理发送“点菜消息”

CMessage Msg;

Msg.CreateMsg(OSM_DISHED,OST_CLIENT | m_CTDeskID,OST_COOK | 0x0001,m_DishTime);

m_Socket.Send(&Msg,sizeof(CMessage));

//评分按钮可用,m_HasOrdered设为TRUE

m_HasOrdered = TRUE;

m_btn_Mark.EnableWindow(m_HasOrdered);

}

顾客终端发送退出消息:

void CClientTerminalDlg::OnClose() 

{

if(m_ConnectNormal == FALSE){

CloseTerminal();

//当系统管理端退出或网络连接异常时可直接退出系统

CDialog::OnCancel();

return;

}

//终止连接

CMessage Msg;

Msg.CreateMsg(OSM_DISCONNECT,OST_CLIENT | m_CTDeskID,OST_SERVER | 0x0001);

m_Socket.Send(&Msg,sizeof(CMessage));/**/

}

——厨师(任务分配)终端

厨师终端处理消息的过程:

void CCookClientSocket::OnReceive(int nErrorCode) 

{

CMessage Msg;

Receive(&Msg,sizeof(CMessage));

CCookTerminalDlg *m_Parent = (CCookTerminalDlg*)AfxGetMainWnd();

if(m_Parent == NULL){

AfxMessageBox(_T("系统出错!"));

return;

}

//分析消息

switch(Msg.m_MsgType){

case OSM_TEST_CONFIRM: //确认连接

//连接确认到达,修改重连标识

m_Parent->SetConnectState(OSM_ONLINE);

break;

case OSM_DISHED: //点菜任务到达

//开始自动分配任务

m_Parent->AutoTaskAssign(Msg.m_SenderID & 0x0fff,Msg.m_DishedTime);

break;

}

CSocket::OnReceive(nErrorCode);

}

厨师终端发送菜品完成消息:

//任务完成处理

BOOL CCookTerminalDlg::TaskFinish(struct TASK *task)

{

CString cookid,dishid;

CString cookname,dishname,dishtime;

int deskid,amount;

cookid.Format("%s",task->cookid);

dishid.Format("%s",task->dishid);

dishtime.Format("%s",task->dishtime);

deskid = task->deskid;

//检索并更新数据库

amount = UpdateAndGetAmount(dishtime,deskid,dishid,cookid);

//获取菜品名

dishname = GetDishName(dishid);

//构造菜品完成消息结构

CMessage Msg;

Msg.CreateMsg(OSM_FINISH,OST_COOK | 0x0001,OST_CLIENT | deskid,dishtime,dishname);

m_Socket.Send(&Msg,sizeof(CMessage));

if(task != NULL)

delete task;

return TRUE;

}

源代码下载地址:http://download.csdn.net/source/2406335 标题有误,请见谅...

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值