EPICS通道访问练习3--ca_create_subscription订阅方式

 本程序展示了如何使用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指针,这个选项可以被省略。
  1. DBE_VALUE    - 当通道值超过监视死区时,触发事件。
  2. DBE_ARCHIVE (或 DBE_LOG) - 当通道值超过存档死区时,触发事件。
  3. DBE_ALARM    - 当通道警报状态变化时,触发事件。
  4. 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 ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值