EPICS通道访问练习2--ca_array_get_callback的使用

news/2024/10/18 9:20:28/

编写以下程序的目的是测试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。


http://www.ppmy.cn/news/998185.html

相关文章

使用Qt中的QDir类进行目录操作

文章目录 概述QDir类的基本功能获取当前目录创建目录列出目录内容筛选目录内容筛选特定命名文件 复制文件和目录删除文件和目录 应用场景总结 概述 Qt是一个跨平台的C应用程序开发框架&#xff0c;其中提供了许多方便的类来处理文件和目录操作。其中&#xff0c;QDir类是用于处…

在使用Python爬虫时遇到解析错误解决办法汇总

在进行Python爬虫任务时&#xff0c;遇到解析错误是常见的问题之一。解析错误可能是由于网页结构变化、编码问题、XPath选择器错误等原因导致的。为了帮助您解决这个问题&#xff0c;本文将提供一些实用的解决办法&#xff0c;并给出相关的代码示例&#xff0c;希望对您的爬虫任…

Oracle也有回收站

在数据库管理中&#xff0c;数据的删除是一个常见的操作。然而&#xff0c;有时候我们可能会意外地删除了一些重要的数据。幸运的是&#xff0c;Oracle数据库提供了一个类似于回收站的功能&#xff0c;可以帮助我们恢复被删除的数据。本文将介绍Oracle数据库中的回收站功能以及…

快速转换PDF文件: Python和PyMuPDF教程

解决问题 有时候将文档上传Claude2做分析&#xff0c;有大小限制&#xff0c;所以需要切割pdf文档为几个小点的文档&#xff0c;故才有了本文章。 如何用Python和PyMuPDF制作你想要大小的PDF&#xff1f; PDF是一种广泛使用的文件格式&#xff0c;可以在任何设备上查看和打印…

【雕爷学编程】Arduino动手做(179)---超低成本,尝试五十元的麦克纳姆轮小车 2

【花雕动手做】超低成本&#xff0c;尝试做个五十元的麦克纳姆轮小车&#xff01; 实验接线方法&#xff1a; MX1508模块 Ardunio Uno GND---------GND接地线 VCC---------5V 接电源 IN1---------D6 IN2 ------- D7 IN3---------D8 IN4 ------- D9 IN1---------D10 IN2 -------…

第十三章 利用PCA简化数据

文章目录 第十三章 利用PCA简化数据13.1降维技术13.2PCA13.2.1移动坐标轴 13.2.2在NumPy中实现PCA13.3利用PCA对半导体制造数据降维 第十三章 利用PCA简化数据 PCA&#xff08;Principal Component Analysis&#xff0c;主成分分析&#xff09;是一种常用的降维技术&#xff0…

Elasticsearch更新文档

POST更新 查询当前ffbf索引信息 GET /ffbf/_search返回信息 {"took" : 1,"timed_out" : false,"_shards" : {"total" : 2,"successful"

小程序开发趋势:探索人工智能在小程序中的应用

第一章&#xff1a;引言 小程序开发近年来取得了快速的发展&#xff0c;成为了移动应用开发的重要一环。随着人工智能技术的飞速发展&#xff0c;越来越多的企业开始探索如何将人工智能应用于小程序开发中&#xff0c;为用户提供更智能、便捷的服务。本文将带您一起探索人工智能…