本文将介绍如何开发一个基于stream的EPICS IOC应用程序,其将作为一个简单的基于消息的设备(用于EPICS stream练习的设备模拟程序_yuyuyuliang00的博客-CSDN博客中最后一个python程序模拟的设备)的IOC控制程序。
1) 按如下步骤建立这个IOC程序的框架:
[blctrl@main-machine exer]$ mkdir simpleintrument
[blctrl@main-machine exer]$ cd simpleintrument/
[blctrl@main-machine simpleintrument]$ makeBaseApp.pl -t ioc streaminstrument
[blctrl@main-machine simpleintrument]$ makeBaseApp.pl -i -t ioc streaminstrument
Using target architecture linux-x86_64 (only one available)
The following applications are available:
streaminstrument
What application should the IOC(s) boot?
The default uses the IOC's name, even if not listed above.
Application name?
[blctrl@main-machine simpleintrument]$ ls
configure iocBoot Makefile streaminstrumentApp
2)编辑configure中RELEASE文件,添加这个IOC程序所需要支持模块的路径:
SUPPORT=/usr/local/EPICS/synApps/support
ASYN=$(SUPPORT)/asyn
STREAM=$(SUPPORT)/stream
3) 编辑simpleintrument/streaminstrumentApp/src/Makefile文件,添加这个IOC程序所需模块以及库文件:
TOP=../..
include $(TOP)/configure/CONFIG
#----------------------------------------
# ADD MACRO DEFINITIONS AFTER THIS LINE
#=============================
#=============================
# Build the IOC application
PROD_IOC = streaminstrument
# streaminstrument.dbd will be created and installed
DBD += streaminstrument.dbd
# streaminstrument.dbd will be made up from these files:
streaminstrument_DBD += base.dbd
streaminstrument_DBD += asyn.dbd
streaminstrument_DBD += drvAsynIPPort.dbd
streaminstrument_DBD += stream.dbd
# Include dbd files from all support applications:
#streaminstrument_DBD += xxx.dbd
# Add all the support libraries needed by this IOC
streaminstrument_LIBS += asyn
streaminstrument_LIBS += stream
# streaminstrument_registerRecordDeviceDriver.cpp derives from streaminstrument.dbd
streaminstrument_SRCS += streaminstrument_registerRecordDeviceDriver.cpp
# Build the main IOC entry point on workstation OSs.
streaminstrument_SRCS_DEFAULT += streaminstrumentMain.cpp
streaminstrument_SRCS_vxWorks += -nil-
# Add support from base/src/vxWorks if needed
#streaminstrument_OBJS_vxWorks += $(EPICS_BASE_BIN)/vxComLibrary
# Finally link to the EPICS Base libraries
streaminstrument_LIBS += $(EPICS_BASE_IOC_LIBS)
#===========================
include $(TOP)/configure/RULES
#----------------------------------------
# ADD RULES AFTER THIS LINE
4)退到这个IOC程序的顶层目录,执行make命令:
[blctrl@main-machine simpleintrument]$ make
make -C ./configure install
...
make[2]: Leaving directory '/home/blctrl/exer/simpleintrument/iocBoot/iocstreaminstrument'
make[1]: Leaving directory '/home/blctrl/exer/simpleintrument/iocBoot'
[blctrl@main-machine simpleintrument]$ ls
bin configure dbd iocBoot lib Makefile streaminstrumentApp
5)编辑一个协议文件,记录需要使用此协议文件,与设备进行交互:
[blctrl@main-machine Db]$ cat si.proto
Terminator = CR LF;
InTerminator = CR LF;
ReplayTimeout = 10000;
ReadTimeout = 10000;
# get Volts from the instrument to record
getVolt
{
out "VOLTS?";
in "VOLTS %f";
}
# set Volts from record to instrument
setVolt
{
out "VOLTS %f";
@init
{
getVolt;
}
}
# get current from the instrument
getCurr
{
out "CURR?";
in "CURR %f";
}
# instrumnet information
getIDN
{
out "*IDN?";
in "*IDN %#s";
}
getOnOff
{
out "ON?";
in "ON %d";
}
setOnOff
{
out "ON %d";
@init{
getOnOff;
}
}
setForReadLoad
{
out "LOAD?";
}
read_L1
{
PollPeriod = 100;
in "LOAD %f %*f %*f";
}
read_L2
{
PollPeriod = 100;
in "LOAD %*f %f %*f";
}
read_L3
{
PollPeriod = 100;
in "LOAD %*f %*f %f";
}
6) 用VisualDCT编辑记录,并且生成运行所需的文本数据库。
导出以上记录数据库的文本:
[blctrl@main-machine Db]$ cat si.db
#! Generated by VisualDCT v2.8.3
# Get the voltage of the instrument
record(ai, "$(INSTRUMENT):VOLTAGE_RBV") {
field(DESC, "Read Voltage")
field(SCAN, "Passive")
field(DTYP, "stream")
field(INP, "@si.proto getVolt $(PORT)")
field(PREC, "2")
field(LINR, "NO CONVERSION")
field(EGU, "V")
}
# Set Voltage for the instrument
record(ao, "$(INSTRUMENT):VOLTAGE") {
field(DESC, "Set Voltage")
field(SCAN, "Passive")
field(PINI, "YES")
field(DTYP, "stream")
field(FLNK, "$(INSTRUMENT):VOLTAGE_RBV")
field(OUT, "@si.proto setVolt $(PORT)")
field(PREC, "2")
field(LINR, "NO CONVERSION")
field(EGU, "V")
field(DRVH, "10")
field(DRVL, "-10")
}
# Get Current
record(ai, "$(INSTRUMENT):CURRENT_RBV") {
field(DESC, "Get Current")
field(SCAN, "Passive")
field(PINI, "YES")
field(DTYP, "stream")
field(INP, "@si.proto getCurr $(PORT)")
field(PREC, "2")
field(LINR, "NO CONVERSION")
field(EGU, "Amp")
}
# Set the ON/OFF state
record(bo, "$(INSTRUMENT):ONOFF") {
field(DESC, "Control curr output")
field(SCAN, "Passive")
field(PINI, "YES")
field(DTYP, "stream")
field(FLNK, "$(INSTRUMENT):ONOFF_RBV")
field(OUT, "@si.proto setOnOff $(PORT)")
field(ZNAM, "OFF")
field(ONAM, "ON")
field(ZSV, "NO_ALARM")
field(OSV, "MINOR")
}
# Get the on/off state of the instrument
record(bi, "$(INSTRUMENT):ONOFF_RBV") {
field(DESC, "ON or OFF status")
field(SCAN, "Passive")
field(PINI, "YES")
field(DTYP, "stream")
field(INP, "@si.proto getOnOff $(PORT)")
field(ZSV, "NO_ALARM")
field(OSV, "MINOR")
field(ZNAM, "OFF")
field(ONAM, "ON")
}
# Instrument Informantion
record(waveform, "$(INSTRUMENT):INSTRUMENT_INFO") {
field(DESC, "Instrument Info")
field(SCAN, "Passive")
field(PINI, "YES")
field(DTYP, "stream")
field(INP, "@si.proto getIDN $(PORT)")
field(NELM, "30")
field(FTVL, "CHAR")
}
record(ai, "$(INSTRUMENT):LOAD1") {
field(DESC, "Read CPU Load")
field(SCAN, "I/O Intr")
field(PINI, "NO")
field(DTYP, "stream")
field(INP, "@si.proto read_L1 $(PORT)")
field(PREC, "2")
field(LINR, "NO CONVERSION")
field(EGU, "%")
field(HIHI, "90")
field(HIGH, "60")
field(HHSV, "MAJOR")
field(LLSV, "NO_ALARM")
field(HSV, "MINOR")
field(LSV, "NO_ALARM")
}
record(calc, "$(INSTRUMENT):POWER") {
field(DESC, "Calc Power")
field(SCAN, "1 second")
field(CALC, "A*B")
field(INPA, "$(INSTRUMENT):CURRENT_RBV PP")
field(INPB, "$(INSTRUMENT):VOLTAGE_RBV")
field(EGU, "W")
field(PREC, "2")
}
# Set For Read Load
record(stringout, "$(INSTRUMENT):SetForLoad") {
field(DESC, "Set For Read Load")
field(SCAN, "1 second")
field(DTYP, "stream")
field(FLNK, "$(INSTRUMENT):INSTRUMENT_INFO")
field(OUT, "@si.proto setForReadLoad $(PORT)")
}
record(ai, "$(INSTRUMENT):LOAD2") {
field(DESC, "Read CPU Load")
field(SCAN, "I/O Intr")
field(PINI, "NO")
field(DTYP, "stream")
field(INP, "@si.proto read_L2 $(PORT)")
field(PREC, "2")
field(LINR, "NO CONVERSION")
field(EGU, "%")
field(HIHI, "90")
field(HIGH, "60")
field(HHSV, "MAJOR")
field(LLSV, "NO_ALARM")
field(HSV, "MINOR")
field(LSV, "NO_ALARM")
}
record(ai, "$(INSTRUMENT):LOAD3") {
field(DESC, "Read CPU Load")
field(SCAN, "I/O Intr")
field(PINI, "NO")
field(DTYP, "stream")
field(INP, "@si.proto read_L3 $(PORT)")
field(PREC, "2")
field(LINR, "NO CONVERSION")
field(EGU, "%")
field(HIHI, "90")
field(HIGH, "60")
field(HHSV, "MAJOR")
field(LLSV, "NO_ALARM")
field(HSV, "MINOR")
field(LSV, "NO_ALARM")
}
7) 将在第5步编写的协议文件si.proto和在第6步编写的记录数据库文件si.db放入以下目录streaminstrumentApp/Db,并且编写这个路径下的Makefile文件,在这个文件中添加以下内容:
DB += si.db
DB += si.proto
然后在这个路径下执行make命令。
8)切换到设备模拟程序所在的目录,执行python程序:
[blctrl@main-machine python_dir]$ ./simple_instrument.py --verbose
Serving on TCP 24742
Terminate with Ctrl-C
9) 进入iocBoot/iocstreaminstrument目录下,启动这个IOC应用程序,并用dbl查看当前IOC包含的记录:
[blctrl@main-machine simpleintrument]$ cd iocBoot/iocstreaminstrument/
[blctrl@main-machine iocstreaminstrument]$ ../../bin/linux-x86_64/streaminstrument st.cmd
#!../../bin/linux-x86_64/streaminstrument
< envPaths
epicsEnvSet("IOC","iocstreaminstrument")
epicsEnvSet("TOP","/home/blctrl/exer/simpleintrument")
epicsEnvSet("SUPPORT","/usr/local/EPICS/synApps/support")
epicsEnvSet("ASYN","/usr/local/EPICS/synApps/support/asyn")
epicsEnvSet("STREAM","/usr/local/EPICS/synApps/support/stream")
epicsEnvSet("EPICS_BASE","/usr/local/EPICS/base")
cd "/home/blctrl/exer/simpleintrument"
## Register all support components
dbLoadDatabase "dbd/streaminstrument.dbd"
streaminstrument_registerRecordDeviceDriver pdbbase
drvAsynIPPortConfigure("PORT","127.0.0.1:24742")
epicsEnvSet("STREAM_PROTOCOL_PATH", "/home/blctrl/exer/simpleintrument/db")
## Load record instances
dbLoadRecords("db/si.db","INSTRUMENT=SIMPLE:INSTRUMENT,PORT=PORT")
cd "/home/blctrl/exer/simpleintrument/iocBoot/iocstreaminstrument"
iocInit
Starting iocInit
############################################################################
## EPICS R7.0.3.1
## EPICS Base built Sep 8 2022
############################################################################
iocRun: All initialization complete
## Start any sequence programs
#seq sncxxx,"user=blctrl"
epics> dbl
SIMPLE:INSTRUMENT:VOLTAGE_RBV
SIMPLE:INSTRUMENT:CURRENT_RBV
SIMPLE:INSTRUMENT:LOAD1
SIMPLE:INSTRUMENT:LOAD2
SIMPLE:INSTRUMENT:LOAD3
SIMPLE:INSTRUMENT:ONOFF
SIMPLE:INSTRUMENT:POWER
SIMPLE:INSTRUMENT:VOLTAGE
SIMPLE:INSTRUMENT:INSTRUMENT_INFO
SIMPLE:INSTRUMENT:ONOFF_RBV
SIMPLE:INSTRUMENT:SetForLoad
epics>
10) 另开一个命令行窗口,进行测试:
[blctrl@main-machine ~]$ caget -S SIMPLE:INSTRUMENT:INSTRUMENT_INFO
SIMPLE:INSTRUMENT:INSTRUMENT_INFO US-PAS Instrument, Yoyodyne I
[blctrl@main-machine ~]$ caput SIMPLE:INSTRUMENT:ONOFF 1
Old : SIMPLE:INSTRUMENT:ONOFF OFF
New : SIMPLE:INSTRUMENT:ONOFF ON
[blctrl@main-machine ~]$ caput SIMPLE:INSTRUMENT:VOLTAGE 5
Old : SIMPLE:INSTRUMENT:VOLTAGE 0
New : SIMPLE:INSTRUMENT:VOLTAGE 5
[blctrl@main-machine ~]$ caget SIMPLE:INSTRUMENT:CURRENT_RBV
SIMPLE:INSTRUMENT:CURRENT_RBV 0.86756
[blctrl@main-machine ~]$ caget SIMPLE:INSTRUMENT:POWER
SIMPLE:INSTRUMENT:POWER 28.873
[blctrl@main-machine ~]$ caget SIMPLE:INSTRUMENT:LOAD1
SIMPLE:INSTRUMENT:LOAD1 0.02
[blctrl@main-machine ~]$ caget SIMPLE:INSTRUMENT:LOAD2
SIMPLE:INSTRUMENT:LOAD2 0.08
[blctrl@main-machine ~]$ caget SIMPLE:INSTRUMENT:LOAD3
SIMPLE:INSTRUMENT:LOAD3 0.16