插件开发是Yate开发的主要内容,本文将以一个简单的插件为例,说明Yate插件开发的基础方式。
插件的源码地址为:https://gitee.com/iseelgy/demo00
插件源码
#include "Aux_cmd_define.h"
#include "Aux_yate_hal.h"
//=====================================================================================================================
//=====================================================================================================================
//=====================================================================================================================
//=====================================================================================================================
using namespace TelEngine;
// 注意: 插件名称和动态库名称需要保持一致,否则会导致部分插件管理功能缺失
#define MY_PLUGIN_NAME "demo00"
namespace { // 使用匿名空间
//===============================
// 类定义
class MyPlugin : public Plugin
{
public:
MyPlugin() : Plugin(MY_PLUGIN_NAME)
, _debug_hdl(0), m_initialized(false)
{
}
~MyPlugin() {
}
// 模块初始化函数,会被多次调用
void initialize(void) override;
// 引擎启动处理
void onEngineStart();
// 引擎退出处理
void onEngineStop();
// debug消息
bool onYateDebug(Message& msg);
// 消息处理函数
bool onYateMessage(Message& msg);
private:
// 插件对象初始化标记
bool m_initialized;
YateMsgHandler* _debug_hdl;
};
//===============================
// 变量定义
// 实现模块对象
INIT_PLUGIN(MyPlugin);
// 配置文件
static Configuration s_cfg;
//===============================
// 功能实现
void MyPlugin::initialize()
{
Output( "Initializing module " MY_PLUGIN_NAME);
// 配置文件,配置文件名可任意指定,
s_cfg = Engine::configFile(MY_PLUGIN_NAME);
s_cfg.load();
int level = s_cfg.getIntValue("debug", "level", TelEngine::debugLevel());
withDebugEnabler()->debugLevel(level);
if (!m_initialized) {
// 设置初始化标记,避免多次调用情况下出现问题
m_initialized = true;
// 订阅消息处理
YateMsgHandler* handler;
// 引擎启动消息
handler = new YateMsgHandler(MSG_ENGINE_START);
handler->_handler = [this](Message& msg) {
this->onEngineStart();
return false;
};
Engine::install(handler);
// 引擎退出消息
handler = new YateMsgHandler(MSG_ENGINE_STOP);
handler->_handler = [this](Message& msg) {
this->onEngineStop();
return false;
};
Engine::install(handler);
int priority = s_cfg.getIntValue("general", "priority", 100);
handler = new YateMsgHandler(MSG_YATE, priority);
handler->_handler = [this](Message& msg) {
return this->onYateMessage(msg);
};
Engine::install(handler);
}
// 调试消息处理
int on = s_cfg.getBoolValue("debug", "on", false);
if (on) {
if (!_debug_hdl) {
int priority = s_cfg.getIntValue("debug", "priority", 100);
_debug_hdl = new YateMsgHandler("Yateshop.debug", priority);
_debug_hdl->_handler = [this](Message & msg) {
return this->onYateDebug(msg);
};
Engine::install(_debug_hdl);
}
}
else {
if (_debug_hdl) {
Engine::uninstall(_debug_hdl);
delete _debug_hdl;
_debug_hdl = nullptr;
}
}
//String demo = s_cfg.getValue("general", "demo", "default");
//S_INFO("config -> " << demo.safe());
}
void MyPlugin::onEngineStart()
{
//S_INFO("onEngineStart, ***************************");
//Y_WARN("onEngineStart, ***************************");
//const String& shared = Engine::sharedPath();
// 用户信息配置路径, 与操作系统的登录用户关联
//const String& user = Engine::configPath(true);
// Yate全局配置路径, 与yate的安装路径相关
//const String& config = Engine::configPath(false);
}
void MyPlugin::onEngineStop()
{
//S_INFO( "onEngineStop ") ;
}
bool MyPlugin::onYateMessage(Message& msg)
{
//String Op = msg.getParam("Op");
return false;
}
bool MyPlugin::onYateDebug(Message& msg)
{
return false;
}
}
/*
* 支持模块内日志宏
*/
TelEngine::DebugEnabler * withDebugEnabler()
{
return &__plugin;
}
TE::Configuration * get_configuration()
{
return &s_cfg;
}
const char* get_app_name()
{
return s_cfg.getValue("general", "app.name", __plugin.name());
}
类定义
插件可以通过Plugin派生实现MyPlugin,插件初始化的时候,需要确定插件的名称。Yate引擎通过插件名称对插件进行管理。相同名称的插件,在系统中只能注册成功一个,如果有多个同名插件,后续的插件将注册失败。
initialize函数
插件MyPlugin必须实现纯虚函数initialize(),该函数实现插件的初始化处理。系统启动后,Yate引擎在初始化过程中,在加载完插件后,会调用该函数。通常在该函数中,读取插件的配置文件,并完成插件的初始化处理等操作的。 需要注意的是,该函数在系统初始化的时候会被调用,在后续的处理中,如果系统动态加载了插件,也会调用该函数; 同时也可使用系统提供的API函数,static bool Engine::init(const String& name); 单独对某个插件进行初始化,并触发调用该插件的initialize函数。
消息订阅
插件通过API函数bool Engine::install(MessageHandler* handler); 订阅消息;也可以通过接口 bool Engine::uninstall(MessageHandler* handler);取消消息订阅。
订阅消息的处理流程如下:
YateMsgHandler* handler;
// 引擎启动消息
handler = new YateMsgHandler( “消息名称” );
handler->_handler = [this](Message& msg) {
// ..........
// 实现消息处理逻辑
// 返回true 或者 false,控制后续处理流程
return true; // 返回true,将结束非广播消息的处理流程
return false; // 返回false,消息继续处理
};
Engine::install(handler);
或者实现一个消息处理类,XXXHandler 的类,并从MessageHandler 基类继承。
1. class XXXHandler : public MessageHandler
2. {
3. public:
4. XXXHandler(int prio)
5. : MessageHandler("call.route",prio) { }
6. virtual bool received(Message &msg);
7. };
在 received 方法中实现消息处理逻辑。
插件对象实例
插件对象实例化通过定义一个静态对象即可实现,可通过宏NIT_PLUGIN即可完成。
INIT_PLUGIN(MyPlugin);
配置
Yate提供了配置管理类Configuration,可以提供配置管理功能,在插件开发中,通常在initialize函数中实现配置的读取,完成配置逻辑。
日志:
全局控制日志
S_TRACE(message)
S_DEBUG(message)
S_INFO(message)
S_WARN(message)
S_ERROR(message)
S_FATAL(message)
插件控制日志
Y_TRACE(message)
Y_DEBUG(message)
Y_INFO(message)
Y_WARN(message)
Y_ERROR(message)
Y_FATAL(message)
其中全局控制日志,其显示级别与Yate系统的显示级别是一致的,而插件控制日志,是在每个的插件中,独立控制日志显示级别;插件控制日志可以通过配置文件,控制当前模块的日志显示。
为了支持插件控制日志,当前插件需要实现接口函数,TelEngine::DebugEnabler * withDebugEnabler()
该文档用于实现一个最基础的Yate插件,在后续的文档中,将介绍Yate插件开发的更多方面的内容。