QML控件访问EPICS过程变量的思路

前言 写这篇文章的想法是使用自定义QML控件的方式访问EPICS过程变量,现有的Qt EPICS框架之前也介绍过了,还有配套的QEGui工具,框架的基本功能已经比较完善,但实际使用过程中还是遇到了一些问题。我总结有以下2点: QEGui工具只能加载基于Widget的.ui界面文件,文件只能包含布局、控件和控件属性,不能嵌入代码(C++代码必须编译)。用户的操作(如:输入、点击按钮等)的相应槽函数封装在框架自定义的控件里实现,这样极大方便了用户的开发,用户只需要设计界面,填写变量名就可以实现变量访问,但相应的也失去了很大的灵活性,程序的功能变得很单一。 QEGui工具不一定符合实际开发过程中的需求,仅使用QEGui加载.ui界面,脱离了C++代码,程序很难实现用户想要的效果。而更好的选择是调用Qt EPICS框架动态库提供的控件,结合C++代码开发应用程序,但却不得不进行编译操作。 那么,有没有更好的基于Qt的EPICS框架方案呢? 刚好最近我也学习实践了QML相关的内容,这种前后端分离开发的方式给了我一些灵感。我们完全可以在Qt EPICS框架的基础上,实现自定义QML控件访问EPICS过程变量,用户使用QML编写界面,调用自定义QML控件即可,可以实现和Qt EPICS框架加载.ui界面文件类似的功能。但QML在动画、3D显示等方面明显具有优势,还有一点是基于Widget的.ui界面文件不具备的:QML本身可以嵌入javascript函数,动态控制界面的显示、切换等,甚至可以实现和底层C++接口的交互。而QML本身也不需要进行编译,完全可以只使用QML语言实现程序开发,且具有很高灵活性。 实现思路 Qt EPICS 框架提供了QCaObject类访问EPICS过程变量,但该类几乎是完全为QEWidget服务的,并没有声明属性(property),无法直接在QML中使用。 所以第1步需要对QCaObject做一层封装,将EPICS过程变量的字段声明为QmlPvObject类的属性,然后就可以在QML中访问EPICS过程变量了。 第2步是将QmlPvObject封装进自定义的QML控件QmlPvControl,实现数据的显示、输入等操作。 第3步将QmlPvControl导入(import)到QML文件,在外部QML文件中使用自定义控件。 代码示例 由于完整的代码较多,这里只放部分代码。 QmlPvObject类的定义 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 /* qmlpvobject.h */ class QmlPvObject : public QObject { Q_OBJECT public: enum epicsAlarmSeverity { NO_ALARM, /**< No alarm */ MINOR_ALARM, /**< Minor alarm severity */ MAJOR_ALARM, /**< Major alarm severity */ INVALID_ALARM,/**< Invalid alarm severity */ ALARM_NSEV /**< Number of alarm severities */ }; Q_ENUM(epicsAlarmSeverity) private: Q_PROPERTY(QString pvName READ pvName WRITE setPvName NOTIFY pvNameChanged FINAL) Q_PROPERTY(QVariant value READ value WRITE setValue NOTIFY valueChanged FINAL) Q_PROPERTY(QString hostName READ hostName NOTIFY hostNameChanged FINAL) Q_PROPERTY(QString fieldType READ fieldType NOTIFY fieldTypeChanged FINAL) Q_PROPERTY(QString descriptor READ descriptor NOTIFY descriptorChanged FINAL) Q_PROPERTY(QString egu READ egu NOTIFY eguChanged FINAL) Q_PROPERTY(QCaDateTime dateTime READ dateTime NOTIFY dateTimeChanged FINAL) Q_PROPERTY(quint16 status READ status NOTIFY statusChanged FINAL) Q_PROPERTY(QmlPvObject::epicsAlarmSeverity severity READ severity NOTIFY severityChanged FINAL) Q_PROPERTY(QString statusName READ statusName NOTIFY statusNameChanged FINAL) Q_PROPERTY(QString severityName READ severityName NOTIFY severityNameChanged FINAL) Q_PROPERTY(bool readAccess READ readAccess NOTIFY readAccessChanged FINAL) Q_PROPERTY(bool writeAccess READ writeAccess NOTIFY writeAccessChanged FINAL) public: explicit QmlPvObject(QObject *parent = Q_NULLPTR); QString pvName() const; QVariant value() const; QString hostName() const; QString fieldType() const; QString descriptor() const; QString egu() const; QCaDateTime dateTime() const; quint16 status() const; QmlPvObject::epicsAlarmSeverity severity() const; QString statusName() const; QString severityName() const; bool readAccess() const; bool writeAccess() const; public Q_SLOTS: virtual void setPvName(const QString &pvname) = 0; virtual void setValue(const QVariant &value) = 0; virtual void onConnectionChanged(QCaConnectionInfo& connectionInfo, const uint& variableIndex); virtual void onDataChanged(const QVariant& value, QCaAlarmInfo& alarmInfo, QCaDateTime& timeStamp, const uint& variableIndex); Q_SIGNALS: void pvNameChanged(const QString&); void valueChanged(const QVariant&); void dateTimeChanged(const QCaDateTime&); void fieldTypeChanged(const QString&); void descriptorChanged(const QString&); void eguChanged(const QString&); void hostNameChanged(const QString&); void statusChanged(const quint16); void severityChanged(const epicsAlarmSeverity); void statusNameChanged(const QString); void severityNameChanged(const QString); void readAccessChanged(const bool); void writeAccessChanged(const bool); protected: QPointer<qcaobject::QCaObject> m_caobject; // ... }; 注意到QmlPvObject类有两个虚函数setPvName和setValue,这两个函数需要子类实现。 ...

二月 20, 2025 · 4 分钟 · 822 字

EPICS IOC 使用 SNCSEQ

IOC 配置SNCSEQ: 1 2 3 4 5 6 # example/configure/RELEASE.local EPICS_BASE = /path/to/your/build/of/epics-base SUPPORT = ${EPICS_BASE}/../epics-modules PVXS = ${SUPPORT}/pvxs # 添加 SNCSEQ SNCSEQ = ${SUPPORT}/sequencer example工程中相关的代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 # example/iocExampleApp/src/Makefile ... # Link in the code from our support library iocExample_LIBS += iocExampleSupport # To build SNL programs, SNCSEQ must be defined # in the <top>/configure/RELEASE file ifneq ($(SNCSEQ),) # Build sncExample into iocExampleSupport sncExample_SNCFLAGS += +r iocExample_DBD += sncExample.dbd # A .stt sequence program is *not* pre-processed: iocExampleSupport_SRCS += sncExample.stt iocExampleSupport_LIBS += seq pv iocExample_LIBS += seq pv # Build sncProgram as a standalone program PROD_HOST += sncProgram sncProgram_SNCFLAGS += +m # A .st sequence program *is* pre-processed: sncProgram_SRCS += sncProgram.st sncProgram_LIBS += seq pv sncProgram_LIBS += $(EPICS_BASE_HOST_LIBS) endif ... 1 2 3 4 5 6 7 # sncExample.dbd # The name below is derived from the name of the SNL program # inside the source file, not from its filename. Here the # program is called sncExample, but is compiled in both the # sncExample.stt and sncProgram.st source files. registrar(sncExampleRegistrar) 1 2 3 # sncProgram.st #include "../sncExample.stt" 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 # sncExample.stt program sncExample double v; assign v to "{user}:aiExample"; monitor v; ss ss1 { state init { when (delay(10)) { printf("sncExample: Startup delay over\n"); } state low } state low { when (v > 5.0) { printf("sncExample: Changing to high\n"); } state high } state high { when (v <= 5.0) { printf("sncExample: Changing to low\n"); } state low } } 修改启动脚本,在IOC启动时运行SNL程序: ...

一月 13, 2025 · 2 分钟 · 316 字

EPICS PVAccess 使用方法研究

前言 EPICS V7 中引入了 pvAccess、pvData等相关模块,增加了对结构化数据的支持。 pvData (Process Variable Data, 过程变量数据) 是EPICS核心软件的一部分,它是一个运行时类型系统,具有用于处理结构化数据的序列化和内省(introspection)功能。 pvData有四种类型的数据字段:scalar、scalarArray、structure和structureArray。scalar(标量)可以是以下标量类型之一:Boolean、Byte、Short、Int、Long、U(nsigned)Byte、Unsigned Short、Unsigned Int、Unsigned Long、Float、Double和String。scalarArray是一维数组,元素类型为任何标量类型。structure(结构体)是一组有序的字段,其中每个字段都有一个名称和类型。structureArray是结构体数组,由于字段可以是结构,因此可以创建复杂的结构。 QSRV是一个使用PVAccess协议的网络服务器,在EPICS IOC进程中运行,允许客户端请求访问其中的过程变量(PV)。 PVXS是一个PVAccess协议客户端/服务器的程序模块。功能等同于 pvDataCPP,并希望最终能取代CPP模块。 PVAccess 默认端口:5076 环境变量表: Variable Client Server EPICS_PVA_ADDR_LIST × × EPICS_PVAS_BEACON_ADDR_LIST × EPICS_PVA_AUTO_ADDR_LIST × × EPICS_PVAS_AUTO_BEACON_ADDR_LIST × EPICS_PVAS_INTF_ADDR_LIST × EPICS_PVA_SERVER_PORT × × EPICS_PVAS_SERVER_PORT × EPICS_PVA_BROADCAST_PORT × × EPICS_PVAS_BROADCAST_PORT × EPICS_PVAS_IGNORE_ADDR_LIST × EPICS_PVA_CONN_TMO × × EPICS_PVA_NAME_SERVERS × 快速使用 使用softIocPVA软件。 1 2 3 4 5 6 7 8 9 cat <<EOF > p2pexample.db record(calc, "p2p:example:counter") { field(INPA, "p2p:example:counter") field(CALC, "A+1") field(SCAN, "1 second") } EOF ./bin/linux-x86_64/softIocPVA -d p2pexample.db 添加 QSRV 到 IOC 如果使用EPICS V7创建IOC,那么可以看到程序已经默认添加了QSRV到IOC中。如: ...

一月 13, 2025 · 3 分钟 · 566 字

EPICS IOC GPIO设备支持的使用

前言 之前讲过Linux的GPIO操作,主要是使用程序访问Linux系统提供的用户空间接口。这次是介绍如何使用EPICS IOC控制GPIO的输入或输出。EPICS有许多设备支持程序,其中就包括Linux GPIO驱动,对于某些需要使用EPICS控制设备GPIO的情况十分有用。 编译GPIO设备支持模块 使用到的软件模块 epics-base - (launchpad.net) / epics-base/epics-base / EPICS Base (anl.gov) ffeldbauer/epics-devgpio: EPICS device support to control GPIOs on the BeagleBone Black / Raspberry Pi via the /sys/class/gpio interface 需要先安装好EPICS Base. 编译 devgpio 注意:gpio设备支持用到了Linux 5.x 内核提供的gpio应用程序接口。 Version 2 of this device support uses the new V2 ABI for GPIO character device (c.f. /usr/include/linux/gpio.h) which was introduced in Kernel 5.x. 如果你使用的是旧版的linxu内核,可能需要使用R1-0-6。 交叉编译时gpio.h的路径:${SDKTARGETSYSROOT}/usr/include/linux/gpio.h 编译步骤: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 cd epics-devgpio touch configure/RELEASE.local vi configure/RELEASE.local # 修改成和EPICS Base一样的架构 # EPICS_HOST_ARCH=linux-loong64 # EPICS Base路径(示例) EPICS_BASE=/home/ubuntu/epics/base-7.0.8.1 # 直接编译 # make # 交叉编译(示例) # make LD=loongarch64-linux-gnu-ld CC=loongarch64-linux-gnu-gcc CCC=loongarch64-linux-gnu-g++ make 使用GPIO的设备支持库 为IOC程序添加GPIO设备支持,和其他设备支持程序使用方法一样。 ...

十二月 2, 2024 · 2 分钟 · 306 字

ecmc构建指南

软件包 epics-base/epics-base: The C/C++ core of the EPICS Base control system toolkit epics-modules/ecmc: EPICS Support for EtherCAT Motion Controller (ECMC) and Generic IO Controller paulscherrerinstitute/exprtk-ecmc: ESS Customized exprtk : C++ Mathematical Expression Parsing And Evaluation Library paulscherrerinstitute/ecmccfg: Module to handle configuration scripts for EtherCATMotion Controller (ECMC) epics-modules/asyn: EPICS module for driver and device support EuropeanSpallationSource/motor: APS BCDA synApps module: motor pantor/ruckig: Motion Generation for Robots and Machines. EtherLab / EtherCAT Master · GitLab 构建顺序 epics-base asyn motor etherlab ruckig ecmc epics-base 正常交叉编译。 ...

十一月 26, 2024 · 7 分钟 · 1384 字

EPICS IOC 访问安全

原文:IOC Access Security 功能 访问安全功能用于保护IOC数据库,限制来自未经授权的CA或pvAccess客户端访问。访问安全性基于以下几点: Who 客户端的用户ID(Channel Access/pvAccess)。 Where 用户登录的主机 ID。客户端运行的主机,但不会分辨用户是本地用户或远程登录到主机的用户。 What 记录的各个字段都受到保护。每条记录都有一个字段包含记录的访问安全组(ASG)。每个字段都有一个访问安全级别(ASL0或ASL1)。安全级别在记录定义文件(.dbd)中定义。 When 访问规则可以包含类似于CALC Record的输入计算。 定义 ASL 访问安全级别 ASG 访问安全组 UAG 用户访问组 HAG 主机访问组 快速上手 为了启用特定 IOC 的访问安全性,需要完成以下操作: 创建访问安全文件(.acf) 可能需要修改IOC数据库 记录实例可能需要设置访问安全组ASG字段。如果ASG为空,记录将会使用“DEFAULT”访问安全组。 访问安全文件可以在iocInit之后通过asSubInit和asSubProcess作为关联的子程序重新加载。将值1写入此记录将导致重新加载。 必须启动脚本在的iocInit之前包含以下命令: 1 2 3 4 asSetFilename("/full/path/to/accessSecurityFile") /* 下面是一个可选命令 */ /* 使用宏替换 */ asSetSubstitutions("var1=sub1,var2=sub2,...") 如果在iocInit之前未执行asSetFilename,就不会启用访问安全限制。 如果给定asSetFilename,但在首次初始化访问安全性时发生错误,则对该IOC的所有访问都会被拒绝。 成功启动访问安全性后,尝试重新启动时出现错误,将会保持上次的访问安全配置。 启动IOC并启用访问安全后,可以通过asSetFilename、asSetSubstitutions和asInit来更改访问安全规则。也可以使用函数asInitialize、asInitFile和asInitFP。 在启动IOC之后重新初始化访问安全配置操作是“非常昂贵”的操作,尽量不要这样做。 访问安全配置文件 本节介绍包含用户访问组(UAG)、主机访问组(HAG)和访问安全组(ASG)。IOC会读取访问配置文件(建议使用扩展名.acf)然后创建访问配置数据库。首先给出一个简单的例子,然后是完整的语法描述。 简单示例 1 2 3 4 5 6 7 8 9 UAG(uag) {user1,user2} HAG(hag) {host1,host2} ASG(DEFAULT) { RULE(1,READ) RULE(1,WRITE) { UAG(uag) HAG(hag) } } 上面的规则提供了无限制的读权限(READ),而位于主机host1和host2上的用户user1和user2则拥有写权限(WRITE)。 ...

三月 18, 2024 · 3 分钟 · 580 字

龙芯开发板移植 IgH EtherCAT Master

前言 IgH EtherCAT Master 是一个开源的EtherCAT主站驱动程序,用于管理EtherCAT从站设备,支持Linux操作系统,工控上使用的比较多。 dls ethercat是由英国钻石光源开发的用于 EPICS 控制系统 EtherCAT 设备的支持程序,基于 IgH Master 主站程序开发,实现对 EtherCAT 总线设备的读写。 交叉编译环境:Ubuntu 运行开发板:龙芯2K0500金龙开发板 内核版本:Linux LS-GD 5.10.0-rt17.lsgd #1 PREEMPT_RT 相关软件包下载地址 epics-base - (launchpad.net) / epics-base/epics-base / EPICS Base (anl.gov) epics-modules/asyn: EPICS module for driver and device support epics-modules/busy: APS BCDA synApps module: busy epics-modules/autosave: APS BCDA synApps module: autosave dls-controls/ethercat: EPICS support to read/write to ethercat based hardware IgH EtherCAT Master for Linux PREEMPT RT patch 配置交叉编译环境 关于这一节,之前的文章已经详细讲过,参考配置交叉编译环境。 ...

二月 23, 2024 · 14 分钟 · 2863 字

EPICS的MODBUS模块的编译和使用

前言 MODBUS是一种应用层消息传递协议,通常用于 I/O 系统通信和可编程逻辑控制器(PLC)通信。 链接类型 描述 MODBUS TCP TCP/IP 使用502端口 MODBUS RTU RTU通常通过串行通信链路运行,即RS-232、 RS-422 或 RS-485。RTU 使用额外的 CRC 进行数据包检查。协议直接将每个字节作为 8 个数据位传输,因此使用“二进制” 而不是 ASCII 编码。使用串行链路开始和结束时,消息帧是按时间而不是按特定字符检测的。 MODBUS ASCII 串行协议,通常在串行通信链路上运行,即 RS-232、RS-422 或 RS-485。串行 ASCII 使用额外的 LRC 数据包检查。该协议将每个字节编码为 2 个 ASCII 字符。消息帧的开始和结束由特定字符检测 (“:” 开始消息,CR/LF 结束消息)。该协议效率低于 Modbus RTU,但在某些环境中可能更可靠。 Modbus 提供对以下 4 种类型的数据的访问: 主表 对象类型 访问 说明 离散输入 1bit 只读 这种类型的数据可以由 I/O 系统提供。 线圈 1bit 读写 此类数据可由应用程序更改。 输入寄存器 16位字(2字节) 只读 这种类型的数据可以由 I/O 系统提供。 保持寄存器 16位字(2字节) 读写 此类数据可由应用程序更改。 Modbus 通信由从 Modbus 客户端发送到 Modbus 服务器的请求消息组成。服务器使用响应消息进行回复。Modbus 请求消息包含: ...

一月 2, 2024 · 6 分钟 · 1276 字

交叉编译 ACAI

关于 ACAI ACAI 是一个C++封装的Channel Access协议应用开发接口(API),提供异步通道访问接口。 ACAI Channel Access Interface EPICS Qt依赖ACAI提供的Channel Access接口。 前置步骤 这篇笔记是交叉编译EPICS和IOC内容的补充。 在进行下面步骤前,请完成配置交叉编译环境和编译 EPICS Base。 这里依旧以龙芯架构为例。 EPICS base 编译完成后,可以看到bin目录下有linux-loong64、linux-x86_64两个目录,linux-x86_64目录下比linux-loong64目录多出了许多perl脚本,我们需要把这些脚本复制到龙架构的目录下,下面编译需要用到。 1 $ cp ./bin/linux-x86_64/*.pl ./bin/linux-loong64/ 编译 在EPICS-Qt安装中已经介绍过编译ACAI。这次是使用交叉编译方式,步骤略有不同。 1 2 3 4 5 6 7 8 9 10 11 12 13 cd ~/loongson/ git clone https://github.com/andrewstarritt/acai.git cd acai vi configure/RELEASE.local # 修改交叉编译的目标架构,和EPICS base中保持一致 # EPICS_HOST_ARCH=linux-loong64 # 修改EPICS_BASE路径,例: EPICS_BASE=/home/ubuntu/loongson/base-7.0.8 # make LD=loongarch64-linux-gnu-ld CC=loongarch64-linux-gnu-gcc CCC=loongarch64-linux-gnu-g++ make # 等待编译完成 编译完成后可以在lib/linux-loong64/目录下找到libacai.so。 ...

十二月 26, 2023 · 1 分钟 · 67 字

交叉编译EPICS和IOC

前言 之前已经讲过在龙芯3A5000(loongarch64)上编译运行EPICS,不过这种情况只适用于有完整开发环境的情况下进行编译。一些时候,我们只有编译器,而缺少make,perl等工具,比如一些开发板厂商提供的开发套件。这种情况下,就需要通过交叉编译(cross-compiling)的方式来编译EPICS。 这里以龙芯金龙2K500先锋开发板为例,我们使用Ubuntu-20.04作为构建系统,详细讲解如何构建出可以在开发板上运行的EPICS工具包,并部署在开发板上。 由于开发板上没有开发环境,即使编译出目标平台的EPICS Base,我们依然不能直接在开发板上创建和编译IOC。所以,我们还是使用Ubuntu-20.04作为构建系统,创建并编译IOC,最后在开发板上运行。 配置交叉编译环境 关于这一节,之前的文章已经详细讲过,参考配置交叉编译环境。 如果你使用的是其他开发套件,请按照开发手册安装配置好环境。 编译 EPICS Base 首先,下载、解压Base,参考以前的文章。 在龙芯3A5000(loongarch64)上编译运行EPICS中我已经详细讲解了如何在龙架构上编译EPICS,这次,需要在原来对源码修改的基础上,再增加对交叉编译的支持。 添加configure/os/CONFIG.linux-x86_64.linux-loong64 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 # CONFIG.linux-x86_64.linux-loong64 # # Definitions for linux-x86_64 host - linux-loong64 target builds # Sites may override these in CONFIG_SITE.linux-x86_64.linux-loong64 #------------------------------------------------------- VALID_BUILDS = Ioc Command GNU_TARGET = loongarch64-linux-gnu # prefix of compiler tools CMPLR_SUFFIX = CMPLR_PREFIX = $(addsuffix -,$(GNU_TARGET)) # Provide a link-time path for readline if needed OP_SYS_INCLUDES += $(READLINE_DIR:%=-I%/include) READLINE_LDFLAGS = $(READLINE_DIR:%=-L%/lib) RUNTIME_LDFLAGS_READLINE_YES_NO = $(READLINE_DIR:%=-Wl,-rpath,%/lib) RUNTIME_LDFLAGS += \ $(RUNTIME_LDFLAGS_READLINE_$(LINKER_USE_RPATH)_$(STATIC_BUILD)) SHRLIBDIR_LDFLAGS += $(READLINE_LDFLAGS) PRODDIR_LDFLAGS += $(READLINE_LDFLAGS) # Library flags STATIC_LDFLAGS_YES= -Wl,-Bstatic STATIC_LDFLAGS_NO= STATIC_LDLIBS_YES= -Wl,-Bdynamic STATIC_LDLIBS_NO= 添加configure/os/CONFIG_SITE.linux-x86_64.linux-loong64 ...

十二月 12, 2023 · 4 分钟 · 712 字