epicsTimer.h描述了一个C++和C timer功能。
1 C++接口
1.1 epicsTimerNotify和epicsTimer
/* 使用一个timer的代码必须实现epicsTimerNotify */
class LIBCOM_API epicsTimerNotify {
public:
enum restart_t { noRestart, restart };
class expireStatus {
public:
LIBCOM_API expireStatus ( restart_t );
LIBCOM_API expireStatus ( restart_t, const double & expireDelaySec );
LIBCOM_API bool restart () const;
LIBCOM_API double expirationDelay () const;
private:
double delay;
};
virtual ~epicsTimerNotify () = 0;
/* 返回"noRestart"或者"expireStatus ( restart, 30.0 )" */
virtual expireStatus expire ( const epicsTime & currentTime ) = 0;
virtual void show ( unsigned int level ) const;
};
class LIBCOM_API epicsTimer {
public:
/* 调用cancel并且接着销毁这个timer */
virtual void destroy () = 0;
virtual void start ( epicsTimerNotify &, const epicsTime & ) = 0;
virtual void start ( epicsTimerNotify &, double delaySeconds ) = 0;
/* WARNING: A deadlock will occur if you hold a lock while
* calling this function that you also take within the timer
* expiration callback.
*/
virtual void cancel () = 0;
struct expireInfo {
expireInfo ( bool active, const epicsTime & expireTime );
bool active;
epicsTime expireTime;
};
virtual expireInfo getExpireInfo () const = 0;
double getExpireDelay ();
virtual void show ( unsigned int level ) const = 0;
protected:
virtual ~epicsTimer () = 0; /* protected => delete() must not be called */
};
方法 | 含义 |
epicsTimerNotify ::expire() | 使用一个epicsTimer的代码必须保护一个从epicsTimerNotify继承的类。派生类必须实现方法expire(),其在相关联timer超时时被epicsTimer调用。epicsTimerNotify定义了一个类expireStatus,它使得实现一次性和周期timers变得简单。一次性expire()以语句return(noRestart)返回;周期性timer用像return(restart,10.0)的语句返回;此处第二个参数是下次回调前的延时。 |
epicsTimer | epicsTimer是一个抽象基类。只能通过调用createTimer创建一个epics timer,createTimer是epicsTimeQueue的一个方法。 |
destroy | 提供这个替代析构函数。在释放由这个timer使用的所有资源前,这将自动调用cancel。 |
start() | 启动这个timer,到以指定时间或者在之后指定的秒数发生超时。如果在调用start前,timer已经活动了,它首先被取消。 |
cancel() | 如果计时器被调度了,取消它。如果它未被调度,什么也不做。注意:如果expire()方法已经运行了,这个调用延时到expire()结束。 |
getExpireInfo | 获取expireInfo, 其表明timer是否活动以及如果这样,它何时结束。 |
getExpireDelay | 返回秒数,直到timer超时。如果timer没有活动,它返回DBL_MAX |
show() | 显示有关对象的信息 |
1.2 epicsTimerQueue
class epicsTimerQueue {
public:
virtual epicsTimer & createTimer () = 0;
virtual void show ( unsigned int level ) const = 0;
protected:
LIBCOM_API virtual ~epicsTimerQueue () = 0;
};
方法 | 含义 |
createTimer() | 这是一个创建使用这个队列的timers的工厂方法。 |
show() | 显示有关对象的信息 |
1.3 epicsTimerQueueActive
class epicsTimerQueueActive
: public epicsTimerQueue {
public:
static LIBCOM_API epicsTimerQueueActive & allocate (
bool okToShare, unsigned threadPriority = epicsThreadPriorityMin + 10 );
virtual void release () = 0;
protected:
LIBCOM_API virtual ~epicsTimerQueueActive () = 0;
};
方法 | 含义 |
allocate() | 这是创建一个timer队列的工厂方法。如果okToShare是(true, false),则一个(共享的,单独的)线程将管理这个timer请求。如果okToShare构造器参数是true并且一个timer队列已经运行在指定的优先级,则它将被引用用于有程序共享使用,并且将不创建一个独立的timer队列。不应该从一个C++静态构造函数内调用这个方法,由于队列线程要求一个当前的时间提供者可用并且在所有构造器完成运行前不能确保已经注册了最后手段的时间提供者。编者注释:如果有多个处理器,这对两个独立timer队列运行在相同优先级是有用的。 |
release() | 释放这个队列,即:进行调用的功能将不再使用这个队列。调用者必须确保它不再拥有任何活动的timers。当使用队列的最后功能调用release时,由这个队列使用的所有资源被释放。 |
1.4 epicsTimerQueueNotify和epicsTimerQueuePassive
class epicsTimerQueueNotify {
public:
/* 当一个新的timer被插入到这个队列时以及到下次超时的延时修改时被调用 */
virtual void reschedule () = 0;
/* if there is a quantum in the scheduling of timer intervals */
/* return this quantum in seconds. If unknown then return zero.
*/
virtual double quantum () = 0;
protected:
LIBCOM_API virtual ~epicsTimerQueueNotify () = 0;
};
class epicsTimerQueuePassive
: public epicsTimerQueue {
public:
static LIBCOM_API epicsTimerQueuePassive & create ( epicsTimerQueueNotify & );
LIBCOM_API virtual ~epicsTimerQueuePassive () = 0; /* ok to call delete */
virtual double process ( const epicsTime & currentTime ) = 0; /* returns delay to next expire */
};
方法 | 含义 |
epicsTimerQueueNotify ::reschedule() | 当在timer队列上到下次timer超时时的延时更高时,虚拟函数epicsTimerQueueNotify::reschedule()被调用。 |
epicsTimerQueueNotify ::quantum | 虚函数epicsQueueNotifyQueue::quantum()返回这个计时器以秒为单位的expire间隔调度量。这使得不同类型的计时器队列能够使用程序特定的计时器expire延时调度策略。epicsTimerQueueActive的实现为这个目的使用了epicsThreadSleep(),并且因而epicsTimerQueueActive::quantum()返回从epicsThreadSleepQuantum()返回的值。其它类型的计时器队列可能选择使用了专用硬件中断的计时器失效。在这种情况中,epicsTimerQueueNotify::quantum()可能返回一个反映一个硬件计时器精度的值。如果未知,则epicsTimerQueueNotify::quantum()返回0。 |
epicsTimerQueuePassive | epicsTimerQueuePassive是一个抽象基类,因而不能直接被实例化,但包含一个创建一个具体被动计时器队列的派生类对象的静态成员函数。 |
create() | 一个创建一个非线程化计时器队列的工厂类。进行调用的软件也传递一个从epicsTimerQueueNotify派生的对象来接收reschedule()回调。 |
~epicsTimerQueuePassive() | 析构器。调用者必须确保它不拥有任何活动的计时器,即:在删除这个epicsTimerQueuePassive对象前,它必须删除任何活动的计时器。 |
process() | 这为所有已经过期的计时器调用expire()。创建这个队列的工具必须调用这个。在下次计时器失效前,它返回这个延时。 |
2 C接口
typedef struct epicsTimerForC * epicsTimerId;
typedef void ( *epicsTimerCallback ) ( void *pPrivate );
/* 线程管理的计时器队列 */
typedef struct epicsTimerQueueActiveForC * epicsTimerQueueId;
// 分配
LIBCOM_API epicsTimerQueueId epicsStdCall
epicsTimerQueueAllocate ( int okToShare, unsigned int threadPriority );
// 释放
LIBCOM_API void epicsStdCall
epicsTimerQueueRelease ( epicsTimerQueueId );
LIBCOM_API epicsTimerId epicsStdCall
epicsTimerQueueCreateTimer ( epicsTimerQueueId queueid,
epicsTimerCallback callback, void *arg );
LIBCOM_API void epicsStdCall
epicsTimerQueueDestroyTimer ( epicsTimerQueueId queueid, epicsTimerId id );
LIBCOM_API void epicsStdCall
epicsTimerQueueShow ( epicsTimerQueueId id, unsigned int level );
/* 被动计时器队列 */
typedef struct epicsTimerQueuePassiveForC * epicsTimerQueuePassiveId;
typedef void ( * epicsTimerQueueNotifyReschedule ) ( void * pPrivate );
typedef double ( * epicsTimerQueueNotifyQuantum ) ( void * pPrivate );
LIBCOM_API epicsTimerQueuePassiveId epicsStdCall
epicsTimerQueuePassiveCreate ( epicsTimerQueueNotifyReschedule,
epicsTimerQueueNotifyQuantum, void *pPrivate );
LIBCOM_API void epicsStdCall
epicsTimerQueuePassiveDestroy ( epicsTimerQueuePassiveId );
LIBCOM_API epicsTimerId epicsStdCall
epicsTimerQueuePassiveCreateTimer (
epicsTimerQueuePassiveId queueid, epicsTimerCallback pCallback, void *pArg );
LIBCOM_API void epicsStdCall
epicsTimerQueuePassiveDestroyTimer ( epicsTimerQueuePassiveId queueid, epicsTimerId id );
LIBCOM_API double epicsStdCall
epicsTimerQueuePassiveProcess ( epicsTimerQueuePassiveId );
LIBCOM_API void epicsStdCall
epicsTimerQueuePassiveShow ( epicsTimerQueuePassiveId id, unsigned int level );
/* 计时器 */
LIBCOM_API void epicsStdCall
epicsTimerStartTime ( epicsTimerId id, const epicsTimeStamp *pTime );
LIBCOM_API void epicsStdCall
epicsTimerStartDelay ( epicsTimerId id, double delaySeconds );
LIBCOM_API void epicsStdCall
epicsTimerCancel ( epicsTimerId id );
LIBCOM_API double epicsStdCall
epicsTimerGetExpireDelay ( epicsTimerId id );
LIBCOM_API void epicsStdCall
epicsTimerShow ( epicsTimerId id, unsigned int level );
C接口提供了大部分C++接口的功能。它没有提供周期计时器特性。typedefs epicsTimerQueueNotifyReschedule和epicsTimerQueueNotifyQuantumn是等价于epicsTimerQueueNotify::reschedule()和epicsTimerQueueNotify::quantumn()的C接口。
3 示例
本例分配一个计时器队列和两个对象,它们有一个使用这个队列的计时器。每个对象被请求调度它自己。expire()回调仅打印这个对象的名称。在调度每个对象后,主线程为每个要发生超期的对象仅睡眠足够长时间并且接着在释放这个队列后就返回。
#include <stdio.h>
#include "epicsThread.h"
#include "epicsTimer.h"
#include "epicsTime.h"
class Something : public epicsTimerNotify {
public:
Something(const char * nm, epicsTimerQueueActive &queue)
:name(nm), timer(queue.createTimer()){}
virtual ~Something(){ timer.destroy(); }
void start(double delay) {timer.start(*this, delay);}
virtual expireStatus expire(const epicsTime ¤tTime){
printf("%s\n", name);
currentTime.show(1);
return noRestart;
}
private:
const char * name;
epicsTimer &timer;
};
int main()
{
epicsTimerQueueActive &queue = epicsTimerQueueActive::allocate(true);
{
Something first("first", queue);
Something second("second", queue);
first.start(1.0);
second.start(1.5);
epicsThreadSleep(2.0);
}
queue.release();
return 0;
}
编译以上程序,并且执行:
orangepi@orangepi800:~/host_program/hostpgrmApp$ O.linux-aarch64/epicstimer
first
epicsTime: Thu Aug 17 2023 20:31:17.573360337
second
epicsTime: Thu Aug 17 2023 20:31:18.074096491
派生于epicsTimerNotify的Something类的实例化对象first和second之间调度时间相差0.5秒。
4 C示例
此示例展示了C程序如何使用EPICS计时器。本例中构造一个结构体,其中包含3个整型遍历,一个函数,在计时器回调函数中用传入结构体中的函数处理两个成员变量op1和op2,并且把结果存储到成员变量ret中。在主线程中,按指定格式输出在计时器回调函数中计算的结果。
#include <stdio.h>
#include "epicsTimer.h"
#include "epicsThread.h"
typedef int (* ADDFUNC)(int, int);
int add(int i, int j)
{
return i + j;
}
int mul(int i, int j)
{
return i * j;
}
typedef struct Data
{
int op1;
int op2;
char op;
ADDFUNC func;
int ret;
}Data;
static void handler(void * arg)
{
Data * data = (Data *)arg;
printf("Timer tripped:%c\n", data->op);
data->ret = data->func(data->op1, data->op2);
printf("Go out of the timer:%c\n", data->op);
}
int main(int argc, int argv)
{
epicsTimerQueueId timerQueue;
epicsTimerId first, second;
// create the queue of timer requests
timerQueue = epicsTimerQueueAllocate(1,epicsThreadPriorityScanHigh);
// create the timers
Data data1, data2;
data1.op1 = 6;
data1.op2 = 6;
data1.op = '+';
data1.func = add;
data2.op1 = 6;
data2.op2 = 6;
data2.op = '*';
data2.func = mul;
first = epicsTimerQueueCreateTimer(timerQueue, handler, (void *)&data1);
second = epicsTimerQueueCreateTimer(timerQueue, handler, (void *)&data2);
/* start a timer */
printf("First timer should trip in 3 seconds.\n");
epicsTimerStartDelay(first, 3.0);
epicsThreadSleep(5.0);
printf("Frist timer should have triped by now.\n");
printf("%d %c %d = %d\n", data1.op1, data1.op,data1.op2, data1.ret);
// try starting and then cancelling a request
printf("Second timer should trip in 3 seconds\n");
epicsTimerStartDelay(first, 3.0);
epicsTimerStartDelay(second, 3.0);
epicsThreadSleep(1.0);
epicsTimerCancel(first);
epicsThreadSleep(5.0);
printf("Second timer should have tripped, first timer should not havetripped\n");
printf("%d %c %d = %d\n", data2.op1, data2.op,data2.op2, data2.ret);
/* cleanup a single timer */
epicsTimerQueueDestroyTimer(timerQueue, first);
// cleanup an entire of timers
epicsTimerQueueRelease(timerQueue);
return 0;
}
编译以上程序并且运行,结果如下:
orangepi@orangepi800:~/host_program/hostpgrmApp$ O.linux-aarch64/epicstimerdemo
First timer should trip in 3 seconds.
Timer tripped:+
Go out of the timer:+
Frist timer should have triped by now.
6 + 6 = 12
Second timer should trip in 3 seconds
Timer tripped:*
Go out of the timer:*
Second timer should have tripped, first timer should not havetripped
6 * 6 = 36