五. SECS/GEM封装库RapidSecs开发手记-基础库DEMO开发-2

4.4 状态机

状态机的数据结构如下:

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 process
	int ret_code = RAPID_SECS_SUCCESS;
	switch (stream) {
	case 1: // ========= S1
		switch (function) {
		case 14: ret_code = on_s1_f14(eq_ptr, ptr_item);			break;	// S1,F14
		default: 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消息结构如下:

解释一下就是,该结构是一个具有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);
	// 将此成员追加到创建的List
	rapid_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);
	// 追加到成员List
	rapid_secs_list_item_append(item_sublist, item_mdln);
	rapid_secs_list_item_append(item_sublist, item_softrev);
	// 追加到根List
	rapid_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 processed
	if ((eq_ptr->m_comm_fsm.state == gem_impl::NOT_COMMUNICATING) &&
		(stream != 1 || ((function != 13) && (function != 14)))) {
			// notify 'equipment-initiated connect' substate
			comm_fsm_send_event(&(eq_ptr->m_comm_fsm), gem_impl::RECV_NOT_S1_F13);
			return RAPID_SECS_SUCCESS;
		}

	// normal process
	int ret_code = RAPID_SECS_SUCCESS;
	switch (stream) {
	case 1: // ========= S1
		switch (function) {
		case 1:  ret_code = on_s1_f1(eq_ptr, ptr_item, msgid);	break;	// S1,F1
		case 3:  ret_code = on_s1_f3(eq_ptr, ptr_item, msgid);	break;	// S1,F3
		case 13: ret_code = on_s1_f13(eq_ptr, ptr_item, msgid);	break;	// S1,F13
		case 14: ret_code = on_s1_f14(eq_ptr, ptr_item);		break;	// S1,F14
		default: ret_code = RAPID_SECS_ERROR_UFN;					break;
		}
		break;

	case 2: // ========= S2
		ret_code = RAPID_SECS_ERROR_USN;
		break;

	case 5: // ========= S5
		switch (function) {
		case 1:  ret_code = on_s5_f1(eq_ptr, ptr_item, msgid);	break;	// S5,F1
		case 2:													break;
		case 5:  ret_code = on_s5_f5(eq_ptr, ptr_item, msgid);	break;	// S5,F5
		default: ret_code = RAPID_SECS_ERROR_UFN;					break;
		}
		break;

	case 6: // ========= S6
		switch (function) {
		case 11:												break;  // S6,F11
		case 12:												break;	// S6,F12
		default: 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消息是由一个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
	// 创建成员ALCD
	RAPID_SECS_ITEM* ptr_item_alcd = rapid_secs_create_item_binary(&alcd, 1);
// 创建成员ALID
	RAPID_SECS_ITEM* ptr_item_alid = rapid_secs_create_item_u2(1000);
	// 创建成员ALTX
	RAPID_SECS_ITEM* ptr_item_altx = rapid_secs_create_item_ascii("a sample alarm text...");
// 追加到List
	rapid_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消息到主机端,结果如下:

其它的主动上报命令也可参照此种方式实现,比如S2F23、S6F11等等。

综上,通过此种方式逐步的完善对各种命令的响应就可以实现GEM的全部功能。

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值