简介:本人对EPICS IOC的开发需求主要是针对device support。作为新手,在大量阅读EPICS的开发手册后,准备做几个小练习来熟悉IOC的开发过程。
练习1:编写device support处理ai和ao记录对外部文件中数据的读写
1 开发过程
1)创建IOC应用程序
-
mkdir RW_test cd RW_test makeBaseApp.pl -t ioc RW_test makeBaseApp.pl -i -t ioc RW_test
2)编写device support文件
- 进入src目录
cd RW_testApp/src
- 编写devai.c用于读取文件数据
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <epicsExport.h> #include <devSup.h> #include <aiRecord.h> #include <recGbl.h> #include <dbAccess.h> #include <epicsTypes.h> #include <alarm.h> // 定义设备支持函数原型 static long init_record(aiRecord *pai); static long read_ai(aiRecord *pai); // 定义设备支持结构 struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN read_ai; DEVSUPFUN special_linconv; } devAiFile = { 6, NULL, NULL, init_record, NULL, read_ai, NULL }; // 初始化记录 static long init_record(aiRecord *pai) { // 检查链接类型是否为INST_IO if (pai->inp.type != INST_IO) { recGblRecordError(S_db_badField, (void *)pai, "devAiFile (init_record) Illegal INP field"); return S_db_badField; } // 从INP字段中读取文件路径 char *filename = pai->inp.value.instio.string; pai->dpvt = calloc(1, strlen(filename) + 1); if (pai->dpvt == NULL) { recGblSetSevr(pai, COMM_ALARM, INVALID_ALARM); return -1; } strcpy(pai->dpvt, filename); return 0; } // 读取数据 static long read_ai(aiRecord *pai) { char *filename = (char *)pai->dpvt; FILE *file = fopen(filename, "r"); if (!file) { recGblSetSevr(pai, READ_ALARM, INVALID_ALARM); return -1; } double value; if (fscanf(file, "%lf", &value) != 1) { fclose(file); recGblSetSevr(pai, READ_ALARM, INVALID_ALARM); return -1; } pai->val = value; pai->udf = FALSE; fclose(file); return 2; // 新值有效 } // 注册设备支持 epicsExportAddress(dset, devAiFile);
- 编写devao.c用于写入数据
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <epicsExport.h> #include <devSup.h> #include <aoRecord.h> #include <recGbl.h> #include <dbAccess.h> #include <epicsTypes.h> #include <alarm.h> // 定义设备支持函数原型 static long init_record(aoRecord *pao); static long write_ao(aoRecord *pao); // 定义设备支持结构 struct { long number; DEVSUPFUN report; DEVSUPFUN init; DEVSUPFUN init_record; DEVSUPFUN get_ioint_info; DEVSUPFUN write_ao; DEVSUPFUN special_linconv; } devAoFile = { 6, NULL, NULL, init_record, NULL, write_ao, NULL }; // 初始化记录 static long init_record(aoRecord *pao) { // 检查链接类型是否为INST_IO if (pao->out.type != INST_IO) { recGblRecordError(S_db_badField, (void *)pao, "devAoFile (init_record) Illegal INP field"); return S_db_badField; } // 从INP字段中读取文件路径 char *filename = pao->out.value.instio.string; pao->dpvt = calloc(1, strlen(filename) + 1); if (pao->dpvt == NULL) { recGblSetSevr(pao, COMM_ALARM, INVALID_ALARM); return -1; } strcpy(pao->dpvt, filename); return 0; } // 读取数据 static long write_ao(aoRecord *pao) { char *filename = (char *)pao->dpvt; FILE *file = fopen(filename, "w"); if (!file) { recGblSetSevr(pao, WRITE_ALARM, INVALID_ALARM); return -1; } fprintf(file, "%f\n", pao->val); fclose(file); return 0; } // 注册设备支持 epicsExportAddress(dset, devAoFile);
- 编写devai.dbd
device(ai,INST_IO,devAiFile,"devai")
- 编写devao.dbd
device(ao, INST_IO, devAoFile, "devao")
-
编写外部数据文件data.txt
230
- 修改Makefile文件
# Include dbd files from all support applications: RW_test_DBD += devai.dbd RW_test_DBD += devao.dbd RW_test_SRCS += devai.c RW_test_SRCS += devao.c
3)编写数据库文件
- 进入Db目录
cd .. cd Db
- 编写RW_test.db
record(ai, "devai") { field(DESC, "Read File") field(DTYP, "devai") field(INP, "@/.../RW_test/RW_testApp/src/data.txt") } record(ao, "devao") { field(DESC, "Write File") field(DTYP, "devao") field(OUT, "@/.../RW_test/RW_testApp/src/data.txt") }
- 修改Makefile文件
DB += RW_test.db
4)退回RW_test目录,执行make
5)编辑和执行启动文件
- 进入启动文件目录并修改st.cmd的权限
cd iocBoot/iocRW_test chmod +x st.cmd
- 编辑st.cmd文件
#!../../bin/linux-x86_64/RW_test #- You may have to change RW_test to something else #- everywhere it appears in this file < envPaths cd "${TOP}" ## Register all support components dbLoadDatabase "dbd/RW_test.dbd" RW_test_registerRecordDeviceDriver pdbbase ## Load record instances dbLoadRecords("db/RW_test.db","user=user") cd "${TOP}/iocBoot/${IOC}" iocInit ## Start any sequence programs #seq sncxxx,"user=user"
- 执行st.cmd文件
./st.cmd #!../../bin/linux-x86_64/RW_test < envPaths epicsEnvSet("IOC","iocRW_test") epicsEnvSet("TOP","/HALFT/exer/RW_test") epicsEnvSet("EPICS_BASE","/HALFT/EPICS/epics-base-7.0") cd "/HALFT/exer/RW_test" ## Register all support components dbLoadDatabase "dbd/RW_test.dbd" RW_test_registerRecordDeviceDriver pdbbase ## Load record instances dbLoadRecords("db/RW_test.db","user=user") cd "/HALFT/exer/RW_test/iocBoot/iocRW_test" iocInit Starting iocInit ############################################################################ ## EPICS R7.0.7.1-DEV ## Rev. 2023-09-25T20:50+0800 ## Rev. Date build date/time: ############################################################################ iocRun: All initialization complete ## Start any sequence programs #seq sncxxx,"user=user" epics> dbl devai devao epics>
6)验证是否读写成功
- 通过caput向devao写入数据
命令: caput devao 400 输出: Old : devao 0 New : devao 400
- 通过camonitor监视devao和devai的VAL
命令: camonitor devao devai 输出: devao 2024-05-19 15:39:06.439376 400 devai 2024-05-19 15:39:09.089002 400