12384|3

965

帖子

0

TA的资源

纯净的硅(高级)

楼主
 

linux上VXI-11协仪服务器的实现 [复制链接]

本帖最后由 石玉 于 2017-4-1 14:15 编辑

此内容由EEWORLD论坛网友石玉原创,如需转载或用于商业用途需征得作者同意并注明出处



这几天在看VXI-11协仪,这个协仪应用比较少,在网上基本找不到资料,经过老大的点拨,终于成功运行了VXI-11协仪服务器,也就是模拟出了一个LXI设备


首先解释几个名词:
LXI设备:符合LXI规范的仪器设备,我的理解是用网口通信的仪器设备就是LXI设备
VXI-11:控制器和设备通过TCP/IP网络通信的网络仪器协仪

RPC:远程调用,基于C/S模型,一台计算机上的程序调用另一台计算机上的程序
XDR:外部数据表示,就是在不同的计算机系统中转换数据的格式,避免出现类似大小端的问题



使用linux做开发的好处是首先TCP/IP协仪栈已经有了,portmap和RPC也有了(如果没有就安装一个),我们只用关心RPC编程了


在网上找了一个VXI-11协仪客户端的例子,我们要做的是服务器,所我只需要他的种子文件vxi11.x(种子文件后面会给出),在种子文件中定义了VXI-11协仪的三个通道DEVICE_ASYNC,DEVICE_CORE,DEVICE_INTR,三个通道总共有17个消息,还定义了协仪交互时的一些数据结构,这些定义都会体现到生成的.c文件中


//中止通道有1个消息
program DEVICE_ASYNC
{
        version DEVICE_ASYNC_VERSION
        {
                Device_Error                device_abort                (Device_Link)                = 1;
        } = 1;
} = 0x0607B0;
//核心通道有15个消息
program DEVICE_CORE
{
        version DEVICE_CORE_VERSION
        {
                Create_LinkResp                create_link                (Create_LinkParms)        = 10;
                Device_WriteResp        device_write                (Device_WriteParms)        = 11;
                Device_ReadResp                device_read                (Device_ReadParms)        = 12;
                Device_ReadStbResp        device_readstb                (Device_GenericParms)        = 13;
                Device_Error                device_trigger                (Device_GenericParms)        = 14;
                Device_Error                device_clear                (Device_GenericParms)        = 15;
                Device_Error                device_remote                (Device_GenericParms)        = 16;
                Device_Error                device_local                (Device_GenericParms)        = 17;
                Device_Error                device_lock                (Device_LockParms)        = 18;
                Device_Error                device_unlock                (Device_Link)                = 19;
                Device_Error                device_enable_srq        (Device_EnableSrqParms)        = 20;
                Device_DocmdResp        device_docmd                (Device_DocmdParms)        = 22;
                Device_Error                destroy_link                (Device_Link)                = 23;
                Device_Error                create_intr_chan        (Device_RemoteFunc)        = 25;
                Device_Error                destroy_intr_chan        (void)                        = 26;
        } = 1;
} = 0x0607AF;

//中断通道有1个消息
program DEVICE_INTR
{
        version DEVICE_INTR_VERSION
        {
                void                        device_intr_srq                (Device_SrqParms)        = 30;
        } = 1;
} = 0x0607B1;

接下来我们利用种子文件生成程序模版
运行:rpcgen vxi11.x
生成4个文件:vxi11.h vxi11_clnt.c vxi11_svc.c vxi11_xdr.c
运行:rpcgen -Sc -o vxi11_clnt_func.c vxi11.x
生成客户端应用接口文件:vxi11_clnt_func.c 我们不用客户端,这个文件实际没有用,但为了后面编译不报错,还是生成一下
运行:rpcgen -Ss -o vxi11_srv_func.c vxi11.x
生成服务器应用接口文件:vxi11_srv_func.c

上面生成了6个文件,实际上需要我们修改的只有vxi11_clnt_func.c和vxi11_srv_func.c,因为我们只做服客器,所以只用修改vxi11_srv_func.c

打开vxi11_srv_func.c,里面有17个函数,这17个函数就是VXI-11协仪的17个消息,在vxi11_svc.c中,PRC分别注册了UDP和TCP的DEVICE_ASYNC,DEVICE_CORE,DEVICE_INTR三个通道,当客户端发送请求后,根据请求的程序号调用device_async_1,device_core_1或device_intr_1,在device_async_1,device_core_1或device_intr_1中又根据请求的过程号调用vxi11_srv_func.c中17个函数中的一个,所以我们只要填充vxi11_srv_func.c中的17个函数,根据请求返回相应的数据就能够完成通信

接下来编译程序,生成服务器可执行文件
输入:g++ -g -o vxi11_server vxi11_clnt.c vxi11_srv_func.c vxi11_svc.c vxi11_xdr.c
生成:vxi11_serve
运行程序:./vxi11_serve

打开NI max (到NI网站一下载NI visa安装,也可以用其他的客户端工具)


点击添加网络设备


选择自动发现设备,点下一步

怎么发现了这么多设备?抓包看一下

在发现设备的过程中客户端会从inst0到inst9查询有没有这个设备,从抓包的数据可以看出,每次查询的时候服务器都回复NO ERROR,所以发现了10个设备,我们要让他只发现一个设备怎么办,看一下服务器回复的消息

服务器回复的消息过程号是10,错误代码是0,在vxi11.h中对过程号进行了定义,过程号10对应用服务器端函数是create_link_1_svc(),
我们在vxi11_srv_func.c中找到create_link_1_svc()
Create_LinkResp *
create_link_1_svc(Create_LinkParms *argp, struct svc_req *rqstp)
{
        static Create_LinkResp  result;

        /*
         * insert server code here
         */

        return &result;
}

这个函数写义了一个结构体变量,这个变量就是服务器返回的数据,因为什么都没做,所以返回的数据全是0,错误号也是0,客户端认为没有错误就是找到了设备
我们把他修改一下:
create_link_1_svc(Create_LinkParms *argp, struct svc_req *rqstp)
{
        static Create_LinkResp  result;

        /*
         * insert server code here
         */

        if( strcmp(argp->device, "inst0") == 0 )
        {
                result.error = 0;
                result.lid = 1;
                result.abortPort = 1025;
                result.maxRecvSize = 0;
        }
        else
        {
                result.error = 21;
                result.lid = 522133279;
                result.abortPort = 0;
                result.maxRecvSize = 0;
        }

        return &result;
}


在函数中做一个判断,如何查询的是inst0才返回NO ERROR,否则返回错误号21,表示没有这个设备
再运行vxi11_serve,打开vi max添加网络设备

现在就只找到一个设备了,再点完成,发现vi max卡住了,服务器在不停的执行device_read_1_svc()函数,抓包看下是什么问题

抓包发现,当NI max按下完成时,客户端发送了“*IDN?”,这是一个SCPI标准指令,用来查询设备的信息,接着客户端开始读,但是读不到设备信息,VXI-11协仪中如果查询没有正确回复,会不停发送读信号直到超时,通过查看抓包数据,我们知道服务器端接收“*IDN?”是11号消息,发送数据是12号消息,看看服务器的11号消息
Device_WriteResp *
device_write_1_svc(Device_WriteParms *argp, struct svc_req *rqstp)
{
        static Device_WriteResp  result;
        /*
         * insert server code here
         */
        return &result;
}

和10号消息一样,就返回了一个结构体变量,我们修改一下这个函数
Device_WriteResp *
device_write_1_svc(Device_WriteParms *argp, struct svc_req *rqstp)
{
        static Device_WriteResp  result;
        /*
         * insert server code here
         */

        if(argp->lid == 1)
        {
                if( strcmp(argp->data.data_val, "*IDN?") == 0 )
                {
                        result.error = 0;
                        result.size = sizeof(argp->data.data_val);
                }
        }

        return &result;
}

argp->lid是上面我们create_link时告诉客户端设备的ID号,和inst0绑定,如果客户端是向inst0发送的消息那么
Device_WriteParms结构体中的lid就是create_link时告诉客户端设备的ID号,判断一下这个消息是不是发送给inst0的,再判断消息的内容,回复一个NO ERROR
再看device_read_1_svc(),把这个函数修改一下
char device_info[] = {"Iphone,5s,A1053,10.3"};
Device_ReadResp *
device_read_1_svc(Device_ReadParms *argp, struct svc_req *rqstp)
{
        static Device_ReadResp  result;
        /*
         * insert server code here
         */

        if( argp->lid == 1 )
        {
                result.error = 0;
                result.reason = 0x04;
                result.data.data_len = sizeof(device_info);
                result.data.data_val = &device_info;
//                printf("result.data.data_val = %s \n" , result.data.data_val);
        }

        return &result;
}

判断了是不是inst0发送的消息,如果是就把device_info字符串返回
再次运行,添加设备,已经正确发现设备了


打开测试面板,点击write发送“*IDN?”,再点击读,就可以读到设备信息了


这个程序还没写完,因为我只实现了“*IDN?”这一个指令,所以在device_write_1_svc()和device_read_1_svc()函数中没有作任何SCPI指令的判断,如何有多条指命,可以定义一个枚举类型变量,然后在device_write_1_svc()中接收到相应的SCPI指令就改变变量的值,然后在device_read_1_svc()中根据枚举变量的值返回相应的数据

这只是实现了LXI设备的发现过程,终止通道和中断通道还没用到,然后就是对SCPI指令的解析了,还有一些东西没搞明白,
留下我的QQ:971586331,欢迎一起交流

vxi11.x

11.27 KB, 下载次数: 142

最新回复

直到2024 年还是感觉大佬牛逼,,也是做这个的。emmp   详情 回复 发表于 2024-6-5 10:32

赞赏

2

查看全部赞赏

点赞 关注

回复
举报

1

帖子

0

TA的资源

一粒金砂(初级)

沙发
 
你好,我准备做一个lxi设备,本来毫无头绪,看了你的帖子帮助很大,但是对终止通道那部分还是不太明白该如何使用,不清楚要如何终止core进程,能否提供些思路或者资源,谢谢了
 
 

回复

8

帖子

0

TA的资源

一粒金砂(初级)

板凳
 

感谢作者分享解惑

 
 
 

回复

1

帖子

0

TA的资源

一粒金砂(初级)

4
 

直到2024 年还是感觉大佬牛逼,,也是做这个的。emmp

 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/9 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表