分布式EPICS设置
1) 操作接口
2) 输入/输出控制器(IOC)
IOC
1) 数据库:数据流,基本上周期运行
2) sequencer:状态机,基本上按需
"硬" IOCs运行vxWorks并且直接连接到A/D, D/A, LLRF...硬件。
"软"IOCs运行在Linux等上,并且除了串口或网络设备(Moxa到电机控制器...)外没有I/O硬件。
IOC数据库
1) 'iocCore'软件装载并且执行'记录'--记录的配置替代了自定义编写代码。
2) 所有控制系统工具箱都有:
- GUI工具
- 网络协议
- 硬件驱动
但很少有一个相当的数据库。
示例:基本的温度控制
任务:
1) 读取温度
2) 根据需要闭合/打开开关
3) 重复
一个鱼缸数据库
由三个记录组成:temp, check以及switch。
1) 模拟输入记录temp用于表示读取鱼缸温度,SCAN字段表示1秒种读取一次,DTYP和INP字段表示硬件读取接口和硬件读取需要的格式,PREC是精度小数点后1位,LINR表示用表格转换温度,EGU表示工程单位。LOPR和HOPR表示上下温度限制,SMOO是读取所用平滑参数,HIGH表示上阈值,HSV表示当读取的值超过了HIGH设定值时,将触发MAJOR严重性警报。
2) 计算输出记录check,INPA表示用通道访问过程被动的方式(即:temp记录运行结束时本记录开始运行)从temp记录VAL字段读取输入,将这个输入与10进行比较,如果新产生的结果与上次结果不同时,将此次的VAL字段输出到switch记录。
3) 二进制输出记录,DTYP和OUT字段决定了硬件类型和输出的格式,当check记录运行结束后,如果其VAL发生变化了,则VAL的值将输出到switch记录,switch根据得到的值是0或1,对加热开关进行控制。IVOA字段决定了,当switch得到一个无效输入时,采取的行动是把输出值设置为0,即断开加热开关。
数据库 = 记录 + 字段 + 链接
1) IOC装载并且执行一个或多个数据库。
2) 每个数据库有若干记录
3)每个记录有:
- 名称(在整个网络上唯一)
- 类型(决定字段和它们的功能)
- 字段(属性,可以在运行时被读取,大部分也可以被写入)
记录是活动的
1) 记录做事情:它们能做什么,取决于记录类型,字段值以及设备支持
- 从硬件或其它记录获取数据
- 执行计算
- 检查值范围,产生警报
- 写到其它记录或硬件
2) 当它们被运行时:除非一个记录被运行,否则什么事情都不会发生
记录周期地或者被事件或者其它记录触发而运行。
示例数据库"first.db"
[blctrl@rockygu db]$ cat first.db
# 最简单地记录,它做某件事情并且产生变化地数值
record(calc, "$(USER):random")
{
field(SCAN, "1 second")
field(INPA, "10")
field(CALC, "RNDM*A")
}
1) 执行 softIoc -m USER="TEST" -d first.db加载这个数据库:
[blctrl@rockygu db]$ softIoc -m USER="TEST" -d first.db
dbLoadDatabase("/EPICS/base-R7.0.4.1/bin/linux-x86_64/../../dbd/softIoc.dbd")
softIoc_registerRecordDeviceDriver(pdbbase)
dbLoadRecords("first.db", "USER=TEST")
iocInit()
Starting iocInit
############################################################################
## EPICS R7.0.4.1
## Rev. 2022-06-14T15:34+0800
############################################################################
iocRun: All initialization complete
epics>
2) 在另一个终端执行:camonitor TEST:random
[blctrl@rockygu ~]$ camonitor TEST:random
TEST:random 2023-03-19 23:10:08.702282 2.3917
TEST:random 2023-03-19 23:10:09.702263 8.34287
TEST:random 2023-03-19 23:10:10.702296 1.3489
^C
3) 在ioc环境中尝试dbl, dbpr, dbpr命令:
epics> dbl
TEST:random
epics> dbpr TEST:random
A : 10 ASG : B : 0 C : 0
CALC: RNDM*A D : 0 DESC: DISA: 0
DISP: 0 DISV: 1 E : 0 F : 0
G : 0 H : 0 I : 0 J : 0
K : 0 L : 0 NAME: TEST:random SEVR: NO_ALARM
STAT: NO_ALARM TPRO: 0 VAL : 6.53513389791714
epics> dbpf TEST:random VAL
DBF_DOUBLE: 0.425726710918
epics> dbpf TEST:random VAL
DBF_DOUBLE: 5.78789959564
记录类型
1) ai/ao:模拟输入/输出。读取/写入数值,映射成工程单位
2)bi/bo:二进制输入/输出。读/写单个bit位,映射成字符串
3) calc:公式计算
4) mbbi/mbbo:多位二进制输入/输出。读/写16位数值,映射位模式为字符串
5) stringin/stringout, longin/longout, seq, compress, histogram, waveform, sub, ...
公共字段
1) 设置时:
a) NAME:记录名,在网络上唯一。
b) DESC:描述。
c) SCAN:扫描机制。
d) PHAS:扫描阶段。
e) PINI:在初始化时运行一次。
f) FLNK:转发的链接。
2) 运行时:
a) TIME:时间戳。
b) SEVR, STAT:警报严重性,状态。
c) PACT:PROCESS活跃的。
d) UDF:未定义?从不被运行。
e) PROC:强制运行。
3) 二者都有:
TPRO:跟踪运行,设置为1来调试记录运行。
记录扫描
1) SCAN字段:
a) 当由其它记录使其运行:Passive(默认)
b) 周期地:".1 second", ".2 second", ".5 second", "1 second", "2 second", "5 second", "10 second",
c) 遇到事件:
- "Event": (EVNT字段选择事件)
- "I/O Intr":(如果设备支持允许这个选项)
2) PHAS字段
为在相同周期扫描上地记录添加顺序: 首先PHAS=0, 接着PHAS=1, ...
3) PINI:
设置"Yes"强制在启动时运行记录一次。对"操作输入"记录是个好办法,其几乎从不更改,因而它们有一个初始值。
4) PROC:
写入这个字段将运行一个记录。
数据库 vs IOC地其它部分
1) 记录扫描运行的优先级高于通道访问或PV访问:在高CPU负载时,在记录仍然被运行时,PV可能未连接。
2) 0.1秒扫描运行的优先级高于10秒扫描。
3) PRIO字段为异步完成以及事件扫描的记录选择优先级。
你的里程会不不同:RTOS或普通的Linux?
公共的输入/输出记录字段
1) DTYP:设备类型。
2) INP/OUT:如何读/写,格式依赖于DTYP。
3) RVAL:原始值(例如:16位整数)
4) VAL:工程单位值(例如:64位浮点数)
仅限输出
1) DOL:所需的输出链接。输出记录读取这个链接来获取VAL,接着写到OUT...
2) OMSL:如果Output Mode Select = closed_loop。
3) IVOA:无效输出时的操作。
4) DRVL, DRVH:驱动限制。
扩展"first.db"
# 一个斜率从0到'limit', 限制可以通过一个单独记录被设置
# 使用模拟输出记录,输入也也有作用,由于没有进行读或写的硬件,但仅输出记录有DRVH...
record(ao, "$(S):limit")
{
field(DRVH, "100")
field(DOL, "10")
field(PINI, "YES")
}
# 读取输入:A=我自己当前的值,B等于limit记录的值
# 在自身值小于limit的值时,每次1秒运行一次,自身值增加1,自身值等于limit值时,
# 下次运行,自身值变为0
record(calc, "$(S):ramp")
{
field(SCAN, "1 second")
field(INPA, "$(S):ramp")
field(INPB, "$(S):limit")
field(CALC, "A<B ? A + 1 : 0")
}
模拟记录字段
1) EGU:工程单位名称。
2) SMOO:平滑。
- VAL = (1-SMOO) * new_value + SMOO * last_value
- SMOO=0:VAL=new_value, 默认行为。
- SMOO=1:VAL=last_value, 不再起作用的行为。
- 0<SMOO<1:VAL ’平滑‘按照最新的读取。
3) LINR:线性(无,斜率,断点表):EGUL,EGUF,ESLO,EOFF:用于LINR的参数。
4) LOLO, LOW, HIGH, HIHI:警报限制。LLSV, LSV,HSV,HHSV是对应相关联的警报严重性。
二进制记录字段
1) ZNAM,ONAM:对应"zero", "one"的状态名。
2) ZSV, OSV:警报严重性。
记录链接
1) 输入或输出链接可能是:
a)其它记录的字段的名称:"other", "other.VAL", "other.A"
- 如果其它记录是在相同IOC中:"数据库链接"
- 如果名称未找到:"通道访问链接"
b) 硬件链接
- 依赖设备支持的详情
- DTYP字段选择设备支持
- 格式示例:"@plc12 some_tag", "C1 S4"
2) 输入链接可能是:常量"0", "3.14", "-1.6e-19"
3) 一个记录的FLNK字段在当前记录结束后运行另一个记录。
数据库链接
1) 格式:”record.field {flags}“
VAL是用于field的默认。
2) 标记:
- PP:运行一个被动的目标记录。INP,DOL:在读取前;OUT:在写入后
- NPP:非运行被动(默认)
- MS:最大化严重性
- NMS:不最大化严重性(默认)
- MSS:最大化严重性和状态。
- MSI:当severity=INVALID时,最大化严重性。
3) 示例:field(INP, "other_rec.VAL PP MS")
通道访问链接
当链接的记录不在这个IOC中时,自动使用通道访问链接。
标记:
1) PP:忽略。不触发在其它IOC上的运行。
2) MS,MSI:最大化严重性(当无效时)
通道访问链接标记
1) CA:即使目标在相同IOC中,强制为CA链接,可以用于打破’锁集‘
2) CP:对于INP链接,在接收的CA监视器上运行。一般在链接的值变化时引起运行。细节取决于源的MDEL。
3) CPP:CP,但仅在SCAN=Passive时。
转发链接
1) 触发运行,但不传递数据
2) 如果目标记录SCAN=Passive, 运行目标记录。
3) 可以使用CA链接
- 必须使用FLNK=”other.PROC“(其它IOC)或者使用FLNK="other.PROC CA"(相同IOC)
- 总是触发运行,即使对于SCAN!="Passive"。
运行链
1)
Input_1, Calculation_1和Output_1都是按从左到右顺序每0.1秒运行一次,一个时刻仅运行一个记录。运行顺序受控于记录的PHAS字段,从小到大运行。
2)
Input_2记录每0.1秒运行一次,在其获取数据并结束运行后,通过FLINK使得Calculation_2记录运行,运行过程中INPA从Input_2记录的VAL字段获取值,并且根据设置进行计算,在其运行结束后,通过FLNK使得Output_2记录运行,在运行过程中DOL从Calculation_2的VAL获取值,并进行转换,将结果输出。
3)
Output_3记录每0.1秒运行一次,在其运行中,它将从Calculation_3的VAL获取值,但由于这个记录SCAN=Passive并且链接属性时PP,则Calculation_3记录开始运行,然后INPA字段将从Input_3记录的VAL字段读取输入值,但由于SCAN=Passive并且输入链接是PP属性,则Input_3记录开始运行,获取一个输入值,并且放到了VAL字段,Input_3记录到此运行结束,运行返回到Calculation_3记录,此时INPA获取了输入值,进行内部计算,并把结果放入到VAL,此时Calculation_3记录运行结束,运行返回到了Output_3记录,从Calculation_3记录VAL获取了值,进行转换后输出。
4) Calulation_1记录不会被运行。Output_1记录每0.1秒运行一次,其DOL字段是输入链接,在其获取输入前,会使Calculation_2记录运行,同样INPA字段是输入链接,在其获取输入前,使Input_1记录运行。Input_1记录和Calculation_1记录INPA之间的链接是NPP,而Calculation_1记录SCAN=Passive,所有这个记录不会运行。
5)同一个锁集中Output_1和Calculation_2都是0.1秒运行一次,而这两个记录通过输入链接使得上游数据源记录都可以运行,所以Input_1记录每0.1秒能够运行两次。(假设整个记录链运行时间相较于扫描时间0.1秒可以忽略不计)。
6)当通过输入链接和链接PP属性,记录运行权转移到了Input_1记录时,当其运行结束时,通过FLNK使得Calculation_1记录运行,而Calculation_1记录的INPA记录通过PP链接读取Input_1记录的VAL时,会使Input_1记录再次运行,运行权将在Input_1和Calculation_1之间轮流,所以其余记录将不会再运行了。
变化率的示例
计算一个输入的"变化率"
INPA获取获取前1秒的输入数据,因为它不请求ai记录Input运行。INPB获取当前数据,因为它请求ai记录Input运行,并且获取其运行后的数据。这两个值相减反映了压力读取的'变化率'(差别/秒)。
仿真模式
当在仿真模式中,AO记录不调用设备支持并且AI记录从AO记录获取它的输入。
多次扫描触发
具有快速变边响应的慢速周期扫描:AI记录每5秒种以及当AO记录被更改时得到运行。这提供了对操作变化的即刻响应,即使正常扫描速率非常慢。对电源设置的更改被BO记录继承,此记录代表本地/远程切换。
设备支持
1)记录(AI, AO,..)靠其自身仅从其他记录读或写。
2) 设备支持连接它们到硬件。
3) 硬件设备支持是EPICS 'base'之外的。根据需要添加到IOC。
4) DTYP选择一个设备支持模块。
5) INP/OUT提供具体连接。
同步/异步
1) "快速",同步设备支持在记录被运行时读或写一个记录的值。
2) "慢速",异步设备支持在记录被运行时启动取读或写入。这个记录保持PACT=true状态,并且当完成了数据的读取或写入时,设备支持触发运行结束。
3) 通道访问'获取/写入'当前值,无论这个记录是否正在运行。
4) 当运行结束时,通道访问‘get/put回调’将结束。
'软'设备支持
EPICS base包含了DTYP=
1) "Soft Channel"用于AI, AO,BI,BO,...:读取/写入VAL字段。
2) “Raw Soft Channel”用于AI,AO,BI,BO, ...:读取/写入RVAL字段,转换成VAL,或者从VAL转换。
3) "Async Soft Channel"用于AI,AO,BI,BO:执行一个get/put回调,等待结束。
以下记录链的问题:Output_1记录0.1秒运行一次,其引起ai记录0.1秒运行一次,Output_2记录5秒运行一次,其通过Calculation_2触发Input_1记录运行时,发现Input_1记录正在运行,则其将直接读取其当前值。
锁集
1) 由链接连接起来的记录组。
2) 运行一个记录锁定其锁集:
- 防止由多个线程运行。
- 类似,但技术上与PACT分开
趋于透明地避免问题:除非你非常不幸运,则使用"CA"标记了打破锁集
记录上锁 vs PACT
1) 取决于其设备支持,一个记录可以"运行"很长时间:运行设置PACT并且触发驱动获取数据。一段时间后,驱动再次运行,并且清除PACT。
2) 当以下情况时,在锁集中地记录被锁定:
- 运行启动,在其结束时,再次运行,但在这之间的时间中不运行。
- 读取一个字段。
- 写一个字段。
警报
1) 公共字段:
SEVR:警报严重性:None,MINOR, MAJOR, INVALID
STAT: 警报状态:UDF, READ,WRITE, CALC, HIGH,STATE, ...
2) 二进制记录:
ZSV, OSV:对应"zero"和"one"状态的严重性。
3) 模拟记录:
a) LOLO, LOW, HIGH, HIHI:阈值
b) LLSV, LSV, HSV, HHSV:相关联阈值的严重性。
c) HYST:回滞
警报示例:
# 当温度接近沸点时产生警报
record(ai , $(USER):tank)
{
field(DESC, "Water Temperature")
field(SCAN, "1 second")
field(INP, "xxxx")
field(EGU, "C")
field(PREC "1")
field(HIGH, "90")
field(HSV, "MINOR")
field(HIHI, "100")
field(HHSV, "MAJOR")
}
监视死区
模拟记录发送更新给CA客户端:
1) MDEL:对于大部分客户端变化阈值
2) ADEL:用于存档客户端
记录隐含提示
1) 使用AO,模拟量输出记录,用于用户输入:DRVL, DRVH可以限制输出的范围。
2)BO记录可以用于计时器:
- HIGH字段:当写值VAL=1时,保持1时长HIGH秒。
- 对操作接口按钮有用:按钮写1,在HIGH=1时,记录恢复为0。
3) MBBI, MBBO记录映射状态:
- 通过ZRVL/ZRST, ONVL/ONST, TWVL/TWST, ...定义值和状态。
- 可以像BI/BO一样使用:ZRVL=0, ONVL=1, ZRST ZNAM, ONST ONAM
- 对于除了0,1外的值:ZRVL=0, ONVL=255
- 从位解码限位开关状态:
ZRVL=0 ZRST="Moving"
ONVL=1 ONST="At Left Limit"
TWVL=2 TWST="At Right Limit"
THVL=3` THST="Broken", THSV=MAJOR
4) 使用CALCOUT用于if-then-else逻辑:
- INP*和CALC同在CALC记录中国
- OOPT "On Change", "When Zero", "Transition to Zero"等。
- 输出可以使用CALC或者一个单独的OCAL
5) SEQ, FANOUT, DFANOUT可以运行一个记录列表:
- 仅运行或者写值
- SEL可以从运行所有变成运行所选的记录
6) COMPRESS记录:
- 在一个环形缓存中维持最新的N个值
- 计算数组的平均,最小或最大值
7) ASUB记录可以调用C代码:
- INAM, SNAM:initial()和sub()的名称。
- 很多INP*和VAL*字段。
8) EVENT记录可以提交数据库事件:触发SCAN=Event并且EVNT=那个事件的记录。
Calcout记录详情
1) 组合了calc记录和模拟输出功能:
- INPA,INPB,..., INPL和CALC来计算VAL
- OUT指向了写VAL的位置
2) OOPT确定了是否/何时写OUT:
"Every Time", "On Change" , "When Non-zero", "Transition to Zero"
3) 默认,VAL被写,但也可以配置
- field(OCAL ,"A/B+.."):计算备选的OVAL
- field(DOPT, "Use OCAL"):选择写OVAL,替代写VAL到OUT
在一个记录中两个计算以及一个'if'类型选择器。
calcout示例:
record(calcout, "Corrector")
{
field(SCAN, ".1 second")
field(INPA, "Enable")
field(INPB, "SetPoint")
field(INPC, "Readback")
field(INPD, "17.54")
field(CALC, "A")
field(OOPT, "When None-zero")
field(DOPT, "Use OCAL")
field(OCAL, "D*(B-c)")
field(OUT, "SteeringMagnet PP")
}
CALC确定我们是否应该做任何事情,OCAL用于实际计算。
EPICS base外的synApps记录
1)MOTOR记录:一个用于控制电机的整个生态系统。
2) BUSY记录可以用于支持put-callback:
- 某些setpoint记录FLNKs,逻辑设置BUSY记录=1
- 当设备达到设定点,设置BUSY记录VAL=0
CA 'put-callback'到设置点将在设备到达这个设定点后结束。
电机记录
1) 具有100+字段的记录。
2)基本控制:
- VAL:所想要的电机位置。支持Put-callback
- RBV:回读值,实际电机位置
- DONE:我们结束了移动这个电机吗?
- HLS,LLS:我们在高或低限位吗?
- STOP
3) 分辨率:
- MRES,DIR,OFF,EGU...:把电机刻度变为"mm"或"角度"
- 它是步进电机或伺服电机?我们有编码器吗,如何标定它。
4) 移动:
- VBAS, VMAX,ACCL,HVEL, JVEL, ...:速度,加速度
- HLM,LLM,...:电机范围限制。
- BDST, BACC, RTRY, ...:齿隙补偿,重试。
5) 更多:Homing, jog, tweak, status
大型或小型记录?
一个Motor记录?
motor.VAL 设定点
motor.RBL 回读
motor.DONE 结束?
motor.HLS 在高限位
motor.MRES 每个单位的步数
记录的集合?
record(ao, "$(M)_Set")
record(ai, "$(M)_Pos")
record(bi, "$(M)_Done")
record(bi, "$(M)_AtHighLim")
record(ao, "$(M)_StepsPerUnit")
record(waveform, "$(M)_Profile")
一个伺服电机步使用每个单位步数。步进电机支持一个移动profile。
没有一般化共享的'电源', “相机”, “PID”..记录。首选方法是记录集合。
时间戳
1) TIME一般被设置成这个记录上次被运行时的事件。
2) TSE=-2:设备支持已经设置了TIME,例如,设为从硬件获取的确切触发时间。
3) TSE=1...255:设置TIME为从计时系统获取的事件1..255的上次发生。
4) TSEL:允许从另一个记录获取时间戳。
线性转换
模拟记录转成RVAL为VAL:
LINR=NO CONVERSION:VAL=RVAL
LINR=SLOPAE:VAL=(RVAL)*ESLO + EOFF
这认为设备支持填充(整数)RVAL字段。如果设备支持已经有一个浮点值,它放置到(double)VAL字段。其余转换,则使用CALC记录。
断点表转换
模拟记录可以设置LINR=typeKdegC:这在一个*.dbd文件中: