typedef struct _comm_fsm{COMM_STATE state;COMM_STATE state_host_init_connect;COMM_STATE state_eq_init_connect;deque<COMM_EVENT> m_comm_ev_queue;HANDLE m_comm_ev_handle;HANDLE m_comm_estab_comm_timer;unsigned int m_msgid_s1f13;} COMM_FSM;
还要定义状态切换函数:
void comm_fsm_switch_state(COMM_FSM fsm, COMM_STATE new_state, COMM_FSM_ACTION pfn_action, void action_param)
在”start_gem_service”中启动GEM服务之前启动"start_comm_fsm"状态机维护线程。
4.5 数据接收处理接收数据的处理函数是” OnRecvMessage”,添加一个过程函数来处理接收到的消息:

unsigned long do_process_msg(gem_impl::GEM_EQUIPMENT eq_ptr,DWORD msgid,UCHAR stream,UCHAR function,RAPID_SECS_ITEM ptr_item){// normal processint ret_code = RAPID_SECS_SUCCESS;switch (stream) {case 1: // ========= S1switch (function) {case 14: ret_code = on_s1_f14(eq_ptr, ptr_item);break;// S1,F14default: ret_code = RAPID_SECS_ERROR_UFN;break;}break;default:ret_code = RAPID_SECS_ERROR_USN;break;}return ret_code;}
4.6 数据组装
发送的数据需要按照SECS协议中规定的标准格式发送,这部分内容RapidSecs基础库已经封装好了,只需要调用相应的接口即可。下面来实现上面的”on_s1_f13”函数,来看看如何调用接口组装数据。
首先,SECS E5中规定的S1F14消息结构如下:
S1F14消息结构
解释一下就是,该结构是一个具有2个成员的List,第一个成员表示对建立通信进行确认,而第二个成员也是一个包含2个成员的List,其中成员1是设备型号,成员2是软件版本。于是,函数实现如下:
static int on_s1_f13(gem_impl::GEM_EQUIPMENT ptr_eq, RAPID_SECS_ITEM ptr_item, DWORD msgid){assert(ptr_item->format == E5_FC_LIST);assert(ptr_item->list_size == 0); // Host should send zero-length list.comm_fsm_send_event(&(ptr_eq->m_comm_fsm), gem_impl::RECV_S1_F13);// reply S1,F14// 首先,创建一个具有2个成员的根List,注意调用的接口,参数表示2个成员。RAPID_SECS_ITEM item_s1f14 = rapid_secs_create_item_list(2);char bin[1]; bin[0] = 0; // accepted// 创建此List的第一个成员COMMACK,要注意数据类型和长度。RAPID_SECS_ITEM item_commack = rapid_secs_create_item_binary(bin, 1);// 将此成员追加到创建的Listrapid_secs_list_item_append(item_s1f14, item_commack);// 创建此List的第二个成员List RAPID_SECS_ITEM item_sublist = rapid_secs_create_item_list(2);// 创建成员MDLN,是ASCII类型。RAPID_SECS_ITEM item_mdln = rapid_secs_create_item_ascii(EQ_MDLN);// 创建成员SOFTREV,是ASCII类型。RAPID_SECS_ITEM item_softrev = rapid_secs_create_item_ascii(EQ_SOFTREV);// 追加到成员Listrapid_secs_list_item_append(item_sublist, item_mdln);rapid_secs_list_item_append(item_sublist, item_softrev);// 追加到根Listrapid_secs_list_item_append(item_s1f14, item_sublist);// 发送响应数据rapid_secs_reply_msg_item(g_eq_ptr->hsms_session, 1, 14, item_s1f14, 0, msgid);// 清除创建的数据rapid_secs_del_item(item_s1f14);return RAPID_SECS_SUCCESS;}
到此,设备端Demo程序就可以与主机端测试工具建立正式的通讯连接了。响应消息如下图所示:
响应消息
4.7接收数据处理完善接口” do_process_msg”是用来处理数据响应的,也就是对收到的命令进行回复,比如收到S1F13,需要回复S1F14,收到S1F1,需要回复S1F2等等,那么本例中完整的接口内容如下:
unsigned long do_process_msg(gem_impl::GEM_EQUIPMENT eq_ptr,DWORD msgid,UCHAR stream,UCHAR function,RAPID_SECS_ITEM ptr_item){if (eq_ptr->m_comm_fsm.state == gem_impl::DISABLED) {assert(FALSE);return RAPID_SECS_SUCCESS;} // in NOT_COMMUNICATING state, only S1,F13/14 should be processedif ((eq_ptr->m_comm_fsm.state == gem_impl::NOT_COMMUNICATING) &&(stream != 1 || ((function != 13) && (function != 14)))) {// notify 'equipment-initiated connect' substatecomm_fsm_send_event(&(eq_ptr->m_comm_fsm), gem_impl::RECV_NOT_S1_F13);return RAPID_SECS_SUCCESS;} // normal processint ret_code = RAPID_SECS_SUCCESS;switch (stream) {case 1: // ========= S1switch (function) {case 1: ret_code = on_s1_f1(eq_ptr, ptr_item, msgid);break;// S1,F1case 3: ret_code = on_s1_f3(eq_ptr, ptr_item, msgid);break;// S1,F3case 13: ret_code = on_s1_f13(eq_ptr, ptr_item, msgid);break;// S1,F13case 14: ret_code = on_s1_f14(eq_ptr, ptr_item);break;// S1,F14default: ret_code = RAPID_SECS_ERROR_UFN;break;}break; case 2: // ========= S2ret_code = RAPID_SECS_ERROR_USN;break; case 5: // ========= S5switch (function) {case 1: ret_code = on_s5_f1(eq_ptr, ptr_item, msgid);break;// S5,F1case 2:break;case 5: ret_code = on_s5_f5(eq_ptr, ptr_item, msgid);break;// S5,F5default: ret_code = RAPID_SECS_ERROR_UFN;break;}break; case 6: // ========= S6switch (function) {case 11:break; // S6,F11case 12:break;// S6,F12default: ret_code = RAPID_SECS_ERROR_UFN;break;}break; case 14:break; default:ret_code = RAPID_SECS_ERROR_USN;break;} return ret_code;}
4.8 主动上报消息的添加
设备端有一些消息需要主动上报,比如报警,当然前提也是需要主机端进行了使能。当设备产生报警信息时,需要主动的向主机发送S5F1消息进行上报,而主机在收到后需要回复S5F2消息进行确认。下面就来实现这个接口。
主动发送的消息采用在命令行中输入命令索引的方式,如下所示:
命令索引
同样先来看一下S5F1消息的结构:
S5F1消息结构
从结构上来看S5F1消息是由一个List构成,其带有3个成员ALCD、ALID和ALTX,结构很简单。具体的函数实现如下:
// 参数session是会话的句柄unsigned long test_send_alarm(RSECSH session){// 首先,创建一个具有3个成员的根List,注意调用的接口,参数表示3个成员。RAPID_SECS_ITEM ptr_item_s5f1 = rapid_secs_create_item_list(3);char alcd = (char)0x80; // Alarm Set// 创建成员ALCDRAPID_SECS_ITEM ptr_item_alcd = rapid_secs_create_item_binary(&alcd, 1);// 创建成员ALIDRAPID_SECS_ITEM ptr_item_alid = rapid_secs_create_item_u2(1000);// 创建成员ALTXRAPID_SECS_ITEM ptr_item_altx = rapid_secs_create_item_ascii("a sample alarm text...");// 追加到Listrapid_secs_list_item_append(ptr_item_s5f1, ptr_item_alcd);rapid_secs_list_item_append(ptr_item_s5f1, ptr_item_alid);rapid_secs_list_item_append(ptr_item_s5f1, ptr_item_altx);// 发送消息unsigned long msg_id = rapid_secs_send_msg_item(session, 5, 1 , ptr_item_s5f1, RAPID_SECS_FLAG_NEED_REPLY);// 清除rapid_secs_del_item(ptr_item_s5f1);return msg_id;}
这样当在dos界面输入命令”1”后将发送S5F1消息到主机端,结果如下:
S5F1消息响应
其它的主动上报命令也可参照此种方式实现,比如S2F23、S6F11等等。
综上,通过此种方式逐步的完善对各种命令的响应就可以实现GEM的全部功能。