学习open62541 --- [24] 定时执行任务

open62541提供定时执行任务功能,这个功能很实用,在前面的文章中,有时我们想定时的去修改一些变量节点的值,都是开一个线程去做的,相对来说有点小麻烦。使用open62541自带的定时器就会简单很多。

下面来看下如何使用open62541提供定时功能。


一 相关api

首先要知道一些关于定时任务的简单知识,

  1. 定时任务分为定时执行一次(也叫one shot)和定时循环执行(有个定时周期)2种
  2. 当定时任务被添加到定时器的执行列表里后,就开始计时

也可以看下linux系统提供的api,促进理解,点击这里。

api分为Server端和Client端,如下,

Server端
/**
 * Timed Callbacks
 * --------------- */
typedef void (*UA_ServerCallback)(UA_Server *server, void *data);

/* Add a callback for execution at a specified time. If the indicated time lies
 * in the past, then the callback is executed at the next iteration of the
 * server's main loop.
 *
 * @param server The server object.
 * @param callback The callback that shall be added.
 * @param data Data that is forwarded to the callback.
 * @param date The timestamp for the execution time.
 * @param callbackId Set to the identifier of the repeated callback . This can
 *        be used to cancel the callback later on. If the pointer is null, the
 *        identifier is not set.
 * @return Upon success, UA_STATUSCODE_GOOD is returned. An error code
 *         otherwise. */
UA_StatusCode UA_EXPORT
UA_Server_addTimedCallback(UA_Server *server, UA_ServerCallback callback,
                           void *data, UA_DateTime date, UA_UInt64 *callbackId);

/* Add a callback for cyclic repetition to the server.
 *
 * @param server The server object.
 * @param callback The callback that shall be added.
 * @param data Data that is forwarded to the callback.
 * @param interval_ms The callback shall be repeatedly executed with the given
 *        interval (in ms). The interval must be positive. The first execution
 *        occurs at now() + interval at the latest.
 * @param callbackId Set to the identifier of the repeated callback . This can
 *        be used to cancel the callback later on. If the pointer is null, the
 *        identifier is not set.
 * @return Upon success, UA_STATUSCODE_GOOD is returned. An error code
 *         otherwise. */
UA_StatusCode UA_EXPORT
UA_Server_addRepeatedCallback(UA_Server *server, UA_ServerCallback callback,
                              void *data, UA_Double interval_ms, UA_UInt64 *callbackId);

UA_StatusCode UA_EXPORT
UA_Server_changeRepeatedCallbackInterval(UA_Server *server, UA_UInt64 callbackId,
                                         UA_Double interval_ms);

/* Remove a repeated callback. Does nothing if the callback is not found.
 *
 1. @param server The server object.
 2. @param callbackId The id of the callback */
void UA_EXPORT
UA_Server_removeCallback(UA_Server *server, UA_UInt64 callbackId);

简述:

  1. UA_ServerCallback是定时任务的类型
  2. UA_Server_addTimedCallback()用来添加定时执行一次的任务
  3. UA_Server_addRepeatedCallback用来添加定时循环执行的任务
  4. UA_Server_changeRepeatedCallbackInterval()用来修改循环周期
  5. UA_Server_removeCallback()用来从定时器列表里删除指定的定时任务
Client端
/**
 * Timed Callbacks
 * ---------------
 * Repeated callbacks can be attached to a client and will be executed in the
 * defined interval. */

typedef void (*UA_ClientCallback)(UA_Client *client, void *data);

/* Add a callback for execution at a specified time. If the indicated time lies
 * in the past, then the callback is executed at the next iteration of the
 * server's main loop.
 *
 * @param client The client object.
 * @param callback The callback that shall be added.
 * @param data Data that is forwarded to the callback.
 * @param date The timestamp for the execution time.
 * @param callbackId Set to the identifier of the repeated callback . This can
 *        be used to cancel the callback later on. If the pointer is null, the
 *        identifier is not set.
 * @return Upon success, UA_STATUSCODE_GOOD is returned. An error code
 *         otherwise. */
UA_StatusCode UA_EXPORT
UA_Client_addTimedCallback(UA_Client *client, UA_ClientCallback callback,
                           void *data, UA_DateTime date, UA_UInt64 *callbackId);

/* Add a callback for cyclic repetition to the client.
 *
 * @param client The client object.
 * @param callback The callback that shall be added.
 * @param data Data that is forwarded to the callback.
 * @param interval_ms The callback shall be repeatedly executed with the given
 *        interval (in ms). The interval must be positive. The first execution
 *        occurs at now() + interval at the latest.
 * @param callbackId Set to the identifier of the repeated callback . This can
 *        be used to cancel the callback later on. If the pointer is null, the
 *        identifier is not set.
 * @return Upon success, UA_STATUSCODE_GOOD is returned. An error code
 *         otherwise. */
UA_StatusCode UA_EXPORT
UA_Client_addRepeatedCallback(UA_Client *client, UA_ClientCallback callback,
                              void *data, UA_Double interval_ms,
                              UA_UInt64 *callbackId);

UA_StatusCode UA_EXPORT
UA_Client_changeRepeatedCallbackInterval(UA_Client *client,
                                         UA_UInt64 callbackId,
                                         UA_Double interval_ms);

void UA_EXPORT
UA_Client_removeCallback(UA_Client *client, UA_UInt64 callbackId);

简述:

  1. UA_ClientCallback是定时任务的类型
  2. UA_Client_addTimedCallback()用来添加定时执行一次的任务
  3. UA_Client_addRepeatedCallback用来添加定时循环执行的任务
  4. UA_Client_changeRepeatedCallbackInterval()用来修改循环周期
  5. UA_Client_removeCallback()用来从定时器列表里删除指定的定时任务

二 使用

有了上节的知识,就可以开始写代码了。这里只以Server端为例,Client端的使用是一样的,不再赘述。

#include 
#include 

#include "open62541.h"

UA_Boolean running = true;
static void stopHandler(int sign) 
{
	UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_SERVER, "received ctrl-c");
	running = false;
}


static void ctrlCallback(UA_Server *server, void *data)
{
	UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "ctrl callback");

	UA_UInt64 callbackId = *(UA_UInt64*)data;
	UA_Server_changeRepeatedCallbackInterval(server, callbackId, 5000); // change to every 5s
}

static void oneshotCallback(UA_Server *server, void *data)
{
	UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "oneshot callback");
}


static void cycleCallback(UA_Server *server, void *data) 
{
	UA_LOG_INFO(UA_Log_Stdout, UA_LOGCATEGORY_USERLAND, "cycle callback");
}

// expectedTime, 其单位是ms
static UA_DateTime convertToDateTime(UA_UInt64 expectedTime)
{
	UA_UInt64 interval = (UA_UInt64)(expectedTime * UA_DATETIME_MSEC);
	UA_DateTime nextTime = UA_DateTime_nowMonotonic() + (UA_DateTime)interval;

	return nextTime;
}

int main(void)
{
	signal(SIGINT, stopHandler);
	signal(SIGTERM, stopHandler);

	UA_StatusCode retval;

	UA_Server *server = UA_Server_new();
	UA_ServerConfig *config = UA_Server_getConfig(server);
	UA_ServerConfig_setDefault(config);

	UA_UInt64 callbackId = 0;
	UA_Server_addRepeatedCallback(server, cycleCallback, NULL, 2000, &callbackId); // call every 2s

	UA_DateTime nextTime = convertToDateTime(10000); // after 10s
	UA_Server_addTimedCallback(server, ctrlCallback, &callbackId, nextTime, NULL);

	nextTime = convertToDateTime(3000); // after 3s
	UA_Server_addTimedCallback(server, oneshotCallback, NULL, nextTime, NULL);

	retval = UA_Server_run(server, &running);

cleanup:
	UA_Server_delete(server);
	return retval == UA_STATUSCODE_GOOD ? EXIT_SUCCESS : EXIT_FAILURE;
}

代码解释

  1. 特别要注意UA_Server_addRepeatedCallback()里的时间参数类型是UA_Double,是个相对时间;而UA_Server_addTimedCallback()里的时间参数类型是UA_DateTime,是个绝对时间
  2. 使用UA_Server_addRepeatedCallback()添加循环定时任务cycleCallback,并获取其callbackId
  3. 使用UA_Server_addTimedCallback()添加oneshot定时任务ctrlCallback,在10s后执行,并把cycleCallback的callbackId传给这个任务
  4. ctrlCallback任务里把cycleCallback的循环周期改为5s
  5. 使用UA_Server_addTimedCallback()添加oneshot定时任务oneshotCallback,3s后执行

打印如下,
学习open62541 --- [24] 定时执行任务_第1张图片
第一行是程序执行后打印的,可以看做是系统运行的起始时间点,以此为起点,后面的打印就是我们添加的定时任务,可以通过时间差观察其定时周期,还是比较准确的。


三 总结

本文讲述如何使用open62541提供的定时功能去执行定时任务,还是比较容易理解的。需要注意的是UA_Server_addRepeatedCallback()和UA_Server_addTimedCallback()里的时间参数类型不一样,前者用的相对时间,后者用的是绝对时间。

如果有写的不对的地方,希望能留言指正,谢谢阅读。

你可能感兴趣的:(open62541,C/C++)