上一篇文章中我创建了BusObject: MyFirstBusObject以及BUsAttachment。 为AllJoyn应用提供了与其他设备进行交互的基本信息以及API。
但是仅仅完成了API调用是远远不够的,我们还需要清晰的在AllJoyn框架,申明应用所感兴趣的无会话信号或广播信号
/* 创建已使用接口列表,从而使我们能够跟踪到使用这些接口的应用 */
int len = interfacesUsed.size();
const char* interfaces[len];
for( int i = 0; i < len; i++) {
interfaces[i] = interfacesUsed[i].c_str();
}
/*
* Register this class to receive AllJoyn About feature annoucements from the services we care about.
* 注册类,使之能够接受AllJoyn的About特性
* This performs service level discovery
*/
AnnouncementRegistrar::RegisterAnnounceHandler(*mBusAttachment, *this, interfaces, len);
接口列表被显示出来,从而使我们能够筛选并选择应用所需要的支持接口并能够与之进行交互。
最后我们需要向世界提供我们的存在信息。 我们需要对About特性进行调用从而使应用能够广播信息。
/* With the objects registered and everything setup we Annouce to tell the world that we exist */
status = AboutServiceApi::getInstance()->Announce();
if (ER_OK != status) {
printf("Failed to addMatch for sessionless signals: %s\n", QCC_StatusText(status));
}
AllJoyn的API已经被用于设置所有的交互方式,但是我们还需要定义交互的方法。 在这一部分,我们将要实现我们的AllJoyn接口并将其暴露给总线对象
AllJoyn接口可以有很多属性,本文使用一个简单的案例来向各位解释AllJoyn接口的一般形式:
<node name="/my/first/busobject/communicate">
<interface name="org.example.my.first.alljoyn.interface.communicate">
<method name="Tell">
<arg name="thought" type="s" direction="in"/>
<arg name="reply" type="s" direction="out"/>
</method>
<signal name="Share">
<arg name="thought" type="s"/>
</signal>
<signal name="Broadcast">
<arg name="thought" type="s"/>
</signal>
</interface>
</node>
在上图的案例中,分享与广播功能是相似的。 这么做的原因在于让应用能够表达并展示信号如何在单一会话或无会话信号中发送信息。 分享的信号可以被设置为无会话信号从而减少定义信号时的代码量。 同样的,广播功能因为其简单的实现方法与较少的代码量,可以让开发者更好的理解。
那么我们现在就定义了我们的AllJoyn接口,然后我们就可以在软件中进行实现了
static const char * MY_FIRST_OBJECT_PATH = "/my/first/busobject/communicate";
static const char * MY_FIRST_INTERFACE_NAMES[] = {"org.example.my.first.alljoyn.interface.communicate"};
static const uint32_t MY_NUMBER_OF_INTERFACES = 1;
...
...
QStatus status;
InterfaceDescription* myFirstBusObjectIntf = NULL;
if (!mBusAttachment.GetInterface(MY_FIRST_INTERFACE_NAMES[0])) {
status = mBusAttachment.CreateInterface(MY_FIRST_INTERFACE_NAMES[0], myFirstBusObjectIntf);
//Add BusMethods
myFirstBusObjectIntf->AddMethod("Tell", "s", "s", "thought,reply", 0);
myFirstBusObjectIntf->AddSignal("Share", "s", "thought", 0);// on a session
myFirstBusObjectIntf->AddSignal("Broadcast", "s", "thought", 0);
myFirstBusObjectIntf->Activate();
}
经过上面的代码,现在接口就已经触发而且可以使用了。现在可以将其添加到我们的总线对象中,从而使得AllJoyn框架能够感知到我们的对象以及接口。
/* 将服务接口添加到对象中 */
const InterfaceDescription* myFirstBusObjectTestIntf = mBusAttachment.GetInterface(MY_FIRST_INTERFACE_NAMES[0]);
assert(myFirstBusObjectTestIntf);
AddInterface(*myFirstBusObjectTestIntf);
/* Set the local methods to which BusMethod linkage */
const MethodEntry methodEntries[] = {
{ myFirstBusObjectTestIntf->GetMember("Tell"), static_cast<MessageReceiver::MethodHandler>(&MyFirstBusObject::handleTell) },
};
status = AddMethodHandlers(methodEntries, sizeof(methodEntries)/sizeof(methodEntries[0]));
/* Register the signal handlers */
shareMember = myFirstBusObjectTestIntf->GetMember("Share");
broadcastMember = myFirstBusObjectTestIntf->GetMember("Broadcast");
if (shareMember) {
status = mBusAttachment.RegisterSignalHandler(this,
static_cast<MessageReceiver::SignalHandler>(&MyFirstBusObject::shareHandler),
shareMember,
NULL);
}
if (broadcastMember) {
status = mBusAttachment.RegisterSignalHandler(this,
static_cast<MessageReceiver::SignalHandler>(&MyFirstBusObject::broadcastHandler),
broadcastMember,
NULL);
}
static const char * MY_FIRST_ADD_MATCH_RULE = "type='signal',interface='org.example.my.first.alljoyn.interface.communicate'";
...
/* Make addMatch calls to complete the registration with the AllJoyn router */
mBusAttachment.AddMatch(MY_FIRST_ADD_MATCH_RULE);
...
一旦AllJoyn信息到达,并且信息与我们的应用有联系,那么上述的方法就会马上被触发。 需要注意的是,在框架中我们可能碰到信号、总线方法调用以及无会话广播信号同时到达的情况。
void MyFirstBusObject::handleTell(const InterfaceDescription::Member* member, Message& msg)
{
const char* receivedThought = msg->GetArg(0)->v_string.str;
printf("Someone(%s) told you (%s)\n", msg->GetSender(), receivedThought);
MsgArg reply;
reply.Set("s", "You're so funny!");
QStatus status = MethodReply(msg, &reply, 1);
if (status == ER_OK) {
printf("You let them know they are funny!\n");
} else {
printf("An error occured and they do not know that they are funny.\n");
}
}
void MyFirstBusObject::shareHandler(const InterfaceDescription::Member* member, const char* srcPath, Message& msg)
{
const char* receivedThought = msg->GetArg(0)->v_string.str;
const char* fromUser = msg->GetSender();
printf("Received shared thought (%s) from %s on sessionId %d\n", receivedThought, fromUser, msg->GetSessionId());
}
void MyFirstBusObject::broadcastHandler(const InterfaceDescription::Member* member, const char* srcPath, Message& msg)
{
const char* receivedThought = msg->GetArg(0)->v_string.str;
const char* fromUser = msg->GetSender();
printf("Received a broudcast thought (%s) from %s\n", receivedThought, fromUser);
}
最后,应用所需要的就是将总线方法放置到代码中。 如果我们需要在其他应用中执行一个总线方法, 我们需要创建一个ProxyBusObject来表达另一个应用的方法,而且我们还要为其他应用单独建立一个独特的名字:也就是SessionId,以及对象的路径。 在本教程中,我们是将路径固定为/my/first/busobject/comunicate中。
qcc::String MyFirstBusObject::doTell(qcc::String uniqueName, qcc::String thought, int sessionId)
{
ProxyBusObject remoteObj = ProxyBusObject(*bus, uniqueName.c_str(), MY_FIRST_OBJECT_PATH, (SessionId)sessionId);
remoteObj.AddInterface(MY_FIRST_INTERFACE_NAMES[0]);
Message reply(*bus);
MsgArg arg("s", thought.c_str());
QStatus status = remoteObj.MethodCall(MY_FIRST_INTERFACE_NAMES[0], "Tell", &arg, 1, reply);
if (ER_OK == status) {
return reply->GetArg(0)->v_string.str;
}
return "ERROR";
}
信号的发送我们通过总线对象的信号方法进行了实现。 并通过传递一个SessionId绑定到了一个会话中。 无会话信号的实现则是通过设置标识符来实现。
void MyFirstBusObject::doShare(qcc::String thought, int sessionId)
{
MsgArg payload("s", thought.c_str());
uint8_t flags = 0;
Signal(NULL, sessionId, *shareMember, &payload, 1, 0, flags);
}
void MyFirstBusObject::doBroadcast(qcc::String thought)
{
MsgArg payload("s", thought.c_str());
uint8_t flags = ALLJOYN_FLAG_SESSIONLESS;
Signal(NULL, 0, *broadcastMember, &payload, 1, 0, flags);
}
这些功能几乎是相同的,但其实在语义上差别很大。 这些差别主要在于接收信息的受众以及权限。 打比方来说,doShare方法的信号主要发送给那些已经加入到会话中的应用,而doBroadcast方法主要发送给那些感兴趣以及正在监听的应用。
随着代码创建,我们现在以及为AllJoyn应用设置好了框架。 接下来的教程将会介绍如何跟踪加入会话的成员,并通过SessionListener来处理命令韩输入。