本程序展示了如何使用ca_create_subscription通道访问库函数订阅指定过程变量的值变化的用法。在这个程序中,使用了多线程,创建了抢占式回调上下文,因此为了防止不同线程同时修改某个变量,需要互斥锁来保护这样的变量,用信号量来实现线程之间的同步。
在本程序中,定义了一个类型epicsMutexId互斥锁accessMutex, 并且定义了一个类型为epicsEventId的信号量monitorEvent。
// 创建一个互斥锁
accessMutex = epicsMutexCreate();
// 创建一个信号量
monitorEvent = epicsEventCreate(epicsEventEmpty);
使用ca_create_subscription通道访问库函数订阅过程变量的某种类型的变化。
#include <cadef.h>
typedef void ( caEventCallBackFunc ) (struct event_handler_args);
int ca_create_subscription ( chtype TYPE, unsigned long COUNT,
chid CHID, unsigned long MASK,
caEventCallBackFunc USERFUNC, void *USERARG,
evid *PEVID);
- TYPE:提交给回调函数的值类型。
- COUNT:要从指定通道读取的元素数目。
- CHID:通道标识符。
- MASK:用于请求事件触发类型的位集合掩码。事件触发掩码必须是一个以下一个或多个常量的位与。
- USERFUNC:指向一个用户提供的回调函数的指针,用每个回调更新调用这个函数。
- USERARG:保留的指针大小的变量,并且回给用户回调函数。
- PEVID:这是一个指向用户提供的事件id的指针,如果成功,将被重写。这个事件id可以在之后用于清除一个特定事件。通过传递一个null指针,这个选项可以被省略。
- DBE_VALUE - 当通道值超过监视死区时,触发事件。
- DBE_ARCHIVE (或 DBE_LOG) - 当通道值超过存档死区时,触发事件。
- DBE_ALARM - 当通道警报状态变化时,触发事件。
- DBE_PROPERTY - 当一个通道属性变化时,触发事件。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* EPICS header */
#include "cadef.h"
#define epicsAlarmGLOBAL
#include "epicsEvent.h"
#include "epicsMutex.h"
#define TIMEOUT 1.0
epicsEventId monitorEvent = NULL;
epicsMutexId accessMutex = NULL;
static void monitor(struct event_handler_args args)
{
printf("In Monitor:\n");
epicsMutexLock(accessMutex);
if (args.status != ECA_NORMAL)
{
SEVCHK(args.status, "monitor");
epicsMutexUnlock(accessMutex);
return ;
}
switch (args.type){
case DBR_LONG:
int * dest = (int *)args.usr;
int * src = (int *)args.dbr;
memcpy(dest, src, sizeof(int) * args.count);
break;
case DBR_STRING:
strcpy((char*)args.usr, (char*)args.dbr);
break;
default:
{
printf("%s unsupported data byte %ld\n", ca_name(args.chid),args.type);
goto EMT;
}
}
EMT:
epicsMutexUnlock(accessMutex);
epicsEventSignal(monitorEvent);
printf("Go outside of Monitor:\n");
}
int main(int argc, char ** argv)
{
int stat;
chid pCh;
char pvname[20] = {0};
char svalue[100];
int ivalue[20];
void * pvalue;
if (argc != 2){
printf("Usage: %s channelname\n", argv[0]);
exit(1);
}
strcpy(pvname, argv[1]);
printf("PVNAME: %s\n", pvname);
/* Initialize channel access */
stat = ca_context_create(ca_enable_preemptive_callback);
if (stat != ECA_NORMAL){
printf("ca_context_create failed:\n%s\n",ca_message(stat));
exit(1);
}
else{
printf("channel accesss initialized successfully\n");
}
/* create the pv */
stat = ca_create_channel(pvname, NULL, NULL, CA_PRIORITY_DEFAULT, &pCh);
if (stat != ECA_NORMAL){
printf("ca_create_channel failed:\n%s\n", ca_message(stat));
goto EXIT;
}
else{
printf("PV for channel name %s created successfully\n", pvname);
}
/* call ca_pend_io to process the search */
stat = ca_pend_io(TIMEOUT);
if (stat != ECA_NORMAL)
{
printf("search for PV:[%s] timed out after %g sec", pvname, TIMEOUT);
goto DESTROY;
}
else{
printf("search pv[%s] successfully\n",pvname);
}
int request_type = ca_field_type(pCh);
long request_count = ca_element_count(pCh);
void * usrarg;
if (request_type == DBR_STRING){
usrarg = svalue;
}
else if (request_type == DBR_LONG){
usrarg = ivalue;
}
else{
goto DESTROY;
}
monitorEvent = epicsEventCreate(epicsEventEmpty);
accessMutex = epicsMutexCreate();
stat = ca_create_subscription(request_type, request_count,
pCh, DBE_VALUE, monitor, usrarg, NULL);
SEVCHK(stat, "monitor");
while (1){
ca_pend_event(1.0);
epicsEventWait(monitorEvent);
epicsMutexLock(accessMutex);
if (request_type == DBR_STRING){
printf("PV[%s] ===> Value[%s]\n", pvname, svalue);
}
else{
int i;
int len = 0;
for (i = 0; i < request_count; i++){
len += sprintf(svalue+len, "%d ", ivalue[i]);
}
printf("PV[%s] ===> Value[%s]\n", pvname, svalue);
}
epicsMutexUnlock(accessMutex);
}
DESTROY:
stat = ca_clear_channel(pCh);
if (stat != ECA_NORMAL){
printf("ca_clear_channel failed [%s] \n%s\n",pvname,ca_message(stat));
goto EXIT;
}
/* clean up channel */
EXIT: ca_context_destroy();
printf("channel access context destroyed and exits the program\n");
return 0;
}
编译以上代码并且进行测试,测试所用数据库实例文件同EPICS通道访问介绍以及练习_yuyuyuliang00的博客-CSDN博客中使用的实例。
通过通道访问修改TEST:StrIn:
orangepi@orangepi5:~$ caput TEST:StrIn Morning
Old : TEST:StrIn Good
New : TEST:StrIn Morning
修改前后的变化:
orangepi@orangepi4-lts:~/host_program/host/bin/linux-aarch64$ ./simpleget_subscription TEST:StrIn
PVNAME: TEST:StrIn
channel accesss initialized successfully
PV for channel name TEST:StrIn created successfully
search pv[TEST:StrIn] successfully
In Monitor:
Go outside of Monitor:
PV[TEST:StrIn] ===> Value[Good]
In Monitor: # 在别处通过通道访问修改这个PV后,这里会显示变化后的值
Go outside of Monitor:
PV[TEST:StrIn] ===> Value[Morning]
通过通道访问修改TEST:wfin:
orangepi@orangepi5:~$ caput -a TEST:wfin 10 10 9 8 7 6 5 4 3 2 1
Old : TEST:wfin 10 6 6 6 8 8 6 6 6 6 6
New : TEST:wfin 10 10 9 8 7 6 5 4 3 2 1
修改前后的变化:
orangepi@orangepi4-lts:~/host_program/host/bin/linux-aarch64$ ./simpleget_subscription TEST:wfin
PVNAME: TEST:wfin
channel accesss initialized successfully
PV for channel name TEST:wfin created successfully
search pv[TEST:wfin] successfully
In Monitor:
Go outside of Monitor:
PV[TEST:wfin] ===> Value[6 6 6 8 8 6 6 6 6 6 ]
In Monitor: # # 在别处通过通道访问修改这个PV后,这里会显示变化后的值
Go outside of Monitor:
PV[TEST:wfin] ===> Value[10 9 8 7 6 5 4 3 2 1 ]