编写以下程序的目的是测试ca_array_get_callback通道访问库函数的用法以及如何传递用户参数给与之设置的回调函数。
1) struct ca_connection_handler结构体在连接状态变化时作为参数传递给设定的回调函数;caCh是回调函数的原型,即在定义连接回调函数时,需要定义为这种类型。
struct ca_connection_handler args{chanId chid;long op; // CA_OP_CONN_UP/CA_OP_CONN_DOWN};typedef void caCh(struct connection_handler_args ARGS);
2)定义的UserData结构体是用来在调用ca_create_channel创建通道时向其中的回调函数传递用户数据。在回调函数中,可以通过使用ca_puser宏获取指向这个结构体的指针。
3)ca_array_get_call_back需要指定以下几个参数:
- TYPE:要返回这个值给用户变量的外部类型。
- COUNT:要从指定通道读取的元素数目。必须匹配由PVALUE指向的数组。
- CHID:通道标识符。
- PVALUE:指向程序提供缓存的指针,要写这个通道的当前值到此处。
- USERFUNC:指向用户提供的回调函数的指针,当请求的操作结束时,运行这个函数。
- USERARG:保留的指针大小的变量,并且回传给以上用户提供的回调函数。
int ca_array_get_callback(chtype TYPE,unsigned long COUNT,chid CHID,pCallback USERFUNC,void * USERARG //调用时ca_array_get_callback设置USERARG传递用户其它参数,在回调函数中通过ARGS.usr获取
);
4)struct event_handler_args结构体是通道访问库传递给回调函数的结构体,并且回调函数需要指定pCallback指定的类型。
struct event_handler_args{void * usr; // 提供给请求的用户参数chanId chid; //通道IDlong type; //返回项的类型long count; //返回项的元素数目const void * dbr; //指向返回项的指针int status; //请求op的ECA_XXX状态
};//调用ca_array_get_callback时,设置的事件回调函数
// 回调函数原型:
typedef void (*pCallback)(struct event_handler_args ARGS);
5) 程序源代码:
/* 标准C的头文件 */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>/* EPICS 头文件 */
#include "cadef.h"
#define epicsAlarmGLOBAL
#include "epicsEvent.h"
#include "epicsMutex.h"#define TIMEOUT 1.0/* 自己定义一个结构体,此结构体用来在回调函数与主程序之间传递数据 */
typedef struct _userdata{char name[20];int ivalue;float fvalue;
}UserData;/* 当通道访问连接状态发生变化时,将调用这个回调函数 */
void connect_handler(struct connection_handler_args ARGS)
{if (ARGS.op == CA_OP_CONN_UP)// 通道状态变成连接{printf("PV[%s] connected\n", ca_name(ARGS.chid));}else if (ARGS.op == CA_OP_CONN_DOWN) // 通道状态变成断开{printf("PV[%s] disconnected\n", ca_name(ARGS.chid));}else{printf("ERROR: NO this op code:%ld\n", ARGS.op);return;}/* 调用ca_create_channel创建通道时,向回调函数提供的其它用户参数, 用ca_puser从通道标识符变量中获取创建时传递的参数。*/UserData * pud = ca_puser(ARGS.chid);/* 打印传递来的参数,与在主程序中设置的值进行比较,测试参数传递功能 */printf("Call connect handler name%s---ivalue:%d---fvalue:%3.2f\n",pud->name, pud->ivalue++, pud->fvalue);}void eventhandler(struct event_handler_args ARGS)
{// 在回调函数中测试请求操作的状态ECA_XXXXif (ARGS.status == ECA_NORMAL && ARGS.dbr){//请求类型为DBR_STRING,则从通道访问返回结果所在地址中复制字符串到用户提供的地址中if (ARGS.type == DBR_STRING){char * str = (char *)ARGS.usr;strcpy(str, (char*)ARGS.dbr);}else//请求类型为DBR_LONG,则从通道访问返回结果所在地址中复制(整数字节*整数数目)个字节到用户提供的地址中{int * src = (int *)ARGS.dbr;int * dest = (int *)ARGS.usr;memcpy(dest, src , ARGS.count * sizeof(int));}}
}int main(int argc, char **argv)
{int stat;chid pCh;char pvname[20] = {0};/*定义一个用户定义的结构体类型数据,并且向其中填充一些内容 */UserData ud;strcpy(ud.name, "helloworld");ud.ivalue = 100;ud.fvalue = 3.14f;char svalue[100];int ivalue[20];void * pvalue;if (argc != 2){printf("Usage: %s channelname\n", argv[0]);exit(1);}// 从命令行上复制字符串到pvname,作为通道访问PV名称strcpy(pvname, argv[1]);printf("The example will use PV: %s\n", pvname);/* 初始化通道访问 */stat = ca_context_create(ca_disable_preemptive_callback);if (stat != ECA_NORMAL){printf("ca_context_create failed:\n%s\n",ca_message(stat));exit(1);}else{printf("Channel Access Context initialized successfully!\n");}/* 用指定字符串名称创建一个通道访问PV,其连接一个连接状态变化回调函数,并且用&ud传递了其它用户数据, 通道标识符存入变量pCh**/stat = ca_create_channel(pvname, connect_handler, (void *)&ud, CA_PRIORITY_DEFAULT,&pCh);if (stat != ECA_NORMAL){printf("ca_create_channel failed:\n%s\n", ca_message(stat));goto EXIT;}else{printf("create PV[%s] successfully\n", pvname);}/* 搜索通道 */ca_pend_event(1.0);/* 获取通道访问PV的本地数据类型 */int request_type = ca_field_type(pCh);/* 获取通道访问PV的数组元素个数 */long request_count = ca_element_count(pCh);while (1){/* 根据请求的数据类型,传递不同的用户数据,此数据为地址,svalue存储字符串,ivalue存储整数数组 */if (request_type == DBR_STRING){stat = ca_array_get_callback(DBR_STRING, request_count, pCh,eventhandler, (void *)svalue);}else if (request_type == DBR_LONG){stat = ca_array_get_callback(DBR_LONG, request_count, pCh,eventhandler, (void *)ivalue);}else{printf("NOT SUPPORT TYPE:%d\n", request_type);goto DESTROY;}if (stat != ECA_NORMAL){printf("ca_array_get_callback failed:\n%s\n",ca_message(stat));}/* 进行get通道访问的请求 */ca_pend_event(2.0);/* 根据请求类型, 如果是DBR_STRING, 打印字符串 */if (request_type == DBR_STRING){printf("pvname[%s]===>value[%s]\n", pvname, svalue);}// 如果是DBR_LONG,将获取的整数数组,格式化成一个字符串,并且输出else if (request_type == DBR_LONG){int i; int len = 0;for (i = 0; i < request_count; i++){len += sprintf(svalue+len, "%d ", ivalue[i]);}printf("pvname[%s]===>value[%s]\n", pvname, svalue);}else{break;} }DESTROY:stat = ca_clear_channel(pCh);if (stat != ECA_NORMAL){printf("ca_clear_channel failed [%s]\n%s\n", pvname, ca_message(stat));}else{printf("Clear Channel PV [%s]\n", pvname); }EXIT:ca_context_destroy();return 0;
}
6) 编译以上代码并且进行测试:
注意:本程序中使用数据库实例同EPICS通道访问介绍以及练习_yuyuyuliang00的博客-CSDN博客
中使用的数据库。
orangepi@orangepi4-lts:~/host_program/host/bin/linux-aarch64$ ./simpleget_callback TEST:StrIn
The example will use PV: TEST:StrIn
Channel Access Context initialized successfully!
create PV[TEST:StrIn] successfully
PV[TEST:StrIn] connected
Call connect handler namehelloworld---ivalue:100---fvalue:3.14
pvname[TEST:StrIn]===>value[Good]
pvname[TEST:StrIn]===>value[Good]
^C
orangepi@orangepi4-lts:~/host_program/host/bin/linux-aarch64$ ./simpleget_callback TEST:wfin
The example will use PV: TEST:wfin
Channel Access Context initialized successfully!
create PV[TEST:wfin] successfully
PV[TEST:wfin] connected
Call connect handler namehelloworld---ivalue:100---fvalue:3.14
pvname[TEST:wfin]===>value[6 6 6 8 8 6 6 6 6 6 ]
pvname[TEST:wfin]===>value[6 6 6 8 8 6 6 6 6 6 ]
7) 退出运行的IOC以及再次运行IOC,会调用以上定义的回调函数connect_handler。