目录
- 一、Windows EPICS 环境部署
- 1. EPICS Base安装
- 2. Python环境配置
- 3. 网络配置
- 二、Python代码编写
- 1. 代码结构设计
- 2. 核心功能实现
- (1)LEDControl类 - LED硬件控制层
- (2)EPICSMonitor类 - 数据监控层
- (3)PVLEDControlSystem类 - 应用控制层
- (4)核心业务逻辑
- (5)日志系统
- 四、功能测试发布
- 五、开发流程总结
- 1. 技术架构
- 2. 注意事项
- End
软件说明
- 10通道PV实时监控:同时监控10个EPICS过程变量
- 5区域LED显示:每2个PV对应1个LED显示区域
- 触发机制:支持上升沿、下降沿、双边沿触发
- 文本格式:支持多行文本、开头空格、特殊字符
- 配置保存:所有设置自动保存和加载
- 日志记录:完整的操作记录和故障诊断
环境依赖
- 软件库:
PyQt5、pyepics、winshell - 硬件依赖:
lv_led_MBCS_64.dll
技术框架
- PyQt5构建图形界面
- EPICS实时数据采集
- LED屏幕硬件控制
- 配置管理和日志系统
开发流程步骤:
一、Windows EPICS 环境部署
1. EPICS Base安装
- 下载EPICS Base for Windows
- EPICSWindowsTools1.44-x64 ;CA-3.15.6-windows-x64.zip
- a. 新建系统环境变量( EPICS_CA_ADDR_LIST)
- b. 添加Path环境变量( C:\Program Files\EPICS Windows Tools)
- c. 配置完成后所有窗口点「确定」保存,必须重启 CMD 窗口环境变量才会生效
- d. 验证
caget -h
2. Python环境配置
- 安装Python 3.7+
- 配置项目虚拟环境Venv(推荐)
python -m venv venv(创建虚拟环境)venv\scripts\activate(激活)- pip install PyQt5 pyepics /
pip install -r requirements.txt
- 安装必要的Python包(安装依赖)
pip freeze > requirements.txt(导出安装包)
3. 网络配置
- 与EPICS IOC服务器的网络连通
- LED屏幕参数
- IP:10.0.21.50
- 卡类型:T8
- 屏幕参数:宽:1792 、高:64
二、Python代码编写
1. 代码结构设计
PVLEDControlSystem/
├── LedControl_20251119.py # 主程序文件
├── config/ # 配置目录(自动生成)
│ └── pv_led_config.json # 配置文件
├── logs/ # 日志目录(自动生成)
│ └── pv_led.log # 日志文件
└── lv_led_MBCS_64.dll # LED控制库(厂家二开SDK)
面向对象的设计模式(整体架构)
PVLEDControlSystem (主窗口类)
├── EPICSMonitor (PV监控类)
├── LEDControl (LED控制类)
└── 各种UI组件和配置管理
2. 核心功能实现
(1)LEDControl类 - LED硬件控制层
- 多路径DLL加载:网络路径、当前目录、程序目录三种DLL加载方式
- 硬件参数封装:将LED屏幕的技术参数封装在类内部
- 结构体映射:使用ctypes将C++结构体映射到Python
classLEDControl:"""LED屏幕控制核心类,负责与硬件DLL交互"""def__init__(self):# DLL路径配置 - 支持多个备选路径self.dll_paths=[r"\\10.0.21.30\Project\13报警\LED\Debug\lv_led_MBCS_64.dll","lv_led_MBCS_64.dll",os.path.join(os.path.dirname(__file__),"lv_led_MBCS_64.dll")]# 硬件参数配置self.led_type=4# T8卡类型self.led_width=1792# 屏幕宽度self.led_height=64# 屏幕高度self.led_ip="10.0.21.50"# LED屏幕IP地址defdefine_structures(self):"""定义与DLL交互的数据结构"""classCOMMUNICATIONINFO(Structure):_fields_=[("LEDType",c_int),# LED类型("SendType",c_int),# 发送类型(TCP/UDP)("IpStr",c_char*16),# IP地址字符串("Commport",c_int),# 通信端口("Baud",c_int),# 波特率("LedNumber",c_int),# LED编号("OutputDir",c_char*260),# 输出目录("networkIdStr",c_char*19)# 网络ID]defrefresh_single_area(self,area_number,text):"""刷新独立LED区域的方法"""# 1. 格式化文本(保留所有空格和换行)formatted_text=self.format_text_for_led(text)# 2. 内码指令字符串# 格式: %disp{区域号-1}:1{文本内容}refresh_string=f"%disp{area_number-1}:1{formatted_text}"# 3. 调用DLL函数发送指令comm_info=self.get_communication_info()result=self.led_dll.LV_RefreshNeiMaArea(byref(comm_info),refresh_string.encode('gbk')# 使用GBK编码)LED控制模块 (LEDControl类)
- DLL动态加载和结构体定义
- 内码字符串格式化处理
- TCP/IP通信协议实现
- 错误处理和连接测试
(2)EPICSMonitor类 - 数据监控层
- 观察者模式:通过信号机制通知观察者(主窗口)状态变化
- 状态模式:管理10个通道的不同状态
- 适配器模式:将EPICS回调接口适配到PyQt信号系统
classEPICSMonitor(QObject):"""EPICS PV实时监控器,采用观察者模式"""# 定义信号 - PyQt的信号槽机制pv_changed=pyqtSignal(int,bool,object)# PV值变化信号pv_connection_changed=pyqtSignal(int,bool,str)# 连接状态变化信号pv_value_updated=pyqtSignal(int,object)# 值更新信号def__init__(self):super().__init__()# 10个通道的状态管理数组self.pv_connections=[None]*10# PV连接对象self.pv_states=[False]*10# PV状态值self.pv_values=[None]*10# PV当前数值self.pv_connection_status=[False]*10# 连接状态self.pv_last_change=[None]*10# 最后变化时间defconnect_pv(self,index,pv_name):"""连接EPICS PV的核心方法"""try:# 创建EPICS PV对象,设置自动监控和回调pv=epics.PV(pv_name,auto_monitor=True,# 自动监控值变化connection_callback=self._create_connection_callback(index))pv.add_callback(self._create_callback(index))# 状态初始化self.pv_connections[index]=pv self.pv_states[index]=pv.get()# ... 其他状态初始化def_create_callback(self,index):"""创建PV值变化回调函数 - 工厂方法"""defcallback(pvname=None,value=None,**kw):old_value=self.pv_states[index]self.pv_values[index]=value# 发送值更新信号(用于界面显示)self.pv_value_updated.emit(index,value)# 如果值发生变化,发送变化信号(用于触发LED)ifvalue!=old_value:self.pv_states[index]=value self.pv_last_change[index]=datetime.now()self.pv_changed.emit(index,value,self.pv_values[index])returncallbackEPICS监控模块 (EPICSMonitor类)
- PV连接管理和状态监控
- 回调函数机制实现
- 数据更新和事件触发
(3)PVLEDControlSystem类 - 应用控制层
- MVC模式:界面(View)、数据(Model)、控制(Controller)分离
- 组合模式:集成多个功能模块
- 单例模式:配置管理和日志系统
- 命令模式:按钮操作封装
classPVLEDControlSystem(QMainWindow):"""主控制系统,集成监控、显示、配置所有功能"""def__init__(self):# 组合模式:包含其他功能类self.epics_monitor=EPICSMonitor()self.led_control=LEDControl()# 配置数据管理self.pv_configs=[{"name":"","display_text":"","auto_connect":False,"trigger_edge":"上升沿"}for_inrange(10)]# 业务逻辑映射self.area_mapping={0:1,1:1,# PV1, PV2 -> LED区域12:2,3:2,# PV3, PV4 -> LED区域2# ... 其他映射}definit_ui(self):"""界面初始化"""# 1. 主窗口设置self.setWindowTitle("EPICS PV监控与LED内码刷新系统")self.setGeometry(100,100,1400,900)# 宽1400, 高900# 2. CSS样式表 - 专业美观的界面self.setStyleSheet(""" QMainWindow { background-color: #f0f0f0; } QGroupBox { font-weight: bold; border: 1px solid #cccccc; border-radius: 5px; } QPushButton { background-color: #e0e0e0; border: 1px solid #aaaaaa; border-radius: 3px; padding: 5px 10px; font-weight: bold; } QPlainTextEdit { background-color: white; # 白色背景输入框 border: 1px solid #cccccc; border-radius: 3px; font-family: 'Courier New'; # 等宽字体显示空格 } """)# 3. 标签页布局tab_widget=QTabWidget()pv_tab=self.create_pv_monitor_tab()# PV监控标签页led_tab=self.create_led_config_tab()# LED配置标签页tab_widget.addTab(pv_tab,"PV监控")tab_widget.addTab(led_tab,"LED配置")defsave_config(self):"""配置保存 - 使用JSON格式持久化"""config_data={"pv_configs":[],"led_config":{"ip":self.led_ip_edit.text(),"type":self.led_type_combo.currentText(),"width":self.led_width_spin.value(),"height":self.led_height_spin.value()},"auto_start":self.auto_start_enabled}# 保存10个通道的配置foriinrange(10):# 使用toPlainText()保留所有格式(空格、换行等)display_text=self.display_text_edits[i].toPlainText()config_data["pv_configs"].append({"name":self.pv_name_edits[i].text().strip(),"display_text":display_text,# 保留原始格式"auto_connect":self.pv_configs[i]["auto_connect"],"trigger_edge":self.pv_configs[i]["trigger_edge"]})# 写入JSON文件withopen(self.config_file,'w',encoding='utf-8')asf:json.dump(config_data,f,indent=4,ensure_ascii=False)主界面系统 (PVLEDControlSystem类)
- GUI界面设计
- 多标签页布局
- 实时状态显示和操作控制
- 配置管理和日志系统
(4)核心业务逻辑
defon_pv_changed(self,index,value,current_value):"""PV值变化处理 - 业务逻辑核心"""trigger_edge=self.pv_configs[index]["trigger_edge"]# 触发条件判断should_trigger=Falseiftrigger_edge=="上升沿"andvalue:# 从False变为Trueshould_trigger=Trueeliftrigger_edge=="下降沿"andnotvalue:# 从True变为Falseeliftrigger_edge=="双边沿":# 任何变化都触发ifshould_trigger:# 获取对应的LED区域area_number=self.area_mapping[index]# 获取显示文本(保留所有格式)display_text=self.display_text_edits[index].toPlainText()ifdisplay_text.strip():try:# 调用LED控制刷新显示self.led_control.refresh_single_area(area_number,display_text)self.log_message(f"通道{index+1}触发,文本已发送到LED区域{area_number}")exceptExceptionase:self.log_message(f"LED刷新失败:{str(e)}")(5)日志系统
defsetup_logging(self):"""日志系统配置"""self.log_handler=RotatingFileHandler(self.current_log_file,maxBytes=self.max_log_size,# 10MB文件大小限制backupCount=self.backup_count,# 保留5个备份文件encoding='utf-8')formatter=logging.Formatter('[%(asctime)s] %(levelname)s: %(message)s',datefmt='%Y-%m-%d %H:%M:%S')self.log_handler.setFormatter(formatter)四、功能测试发布
- 单元测试
- LED控制功能测试
- EPICS连接测试
- 配置保存/加载测试
- 界面操作测试
- 集成测试
- PV触发LED刷新测试
- 多通道并发操作测试
- 长时间运行稳定性测试
- 网络异常恢复测试
五、开发流程总结
1. 技术架构
应用层: PyQt5 GUI + 业务逻辑
服务层: EPICS监控 + LED控制
驱动层: pyepics库 + ctypes DLL调用
硬件层: EPICS IOC + LED显示屏
2. 注意事项
- 编码处理
# GBK编码确保中文正确显示refresh_string.encode('gbk')# UTF-8编码确保配置文件中文正确json.dump(config_data,f,indent=4,ensure_ascii=False)
- 文本格式保留
# 使用QPlainTextEdit而不是QLineEditdisplay_edit=QPlainTextEdit()display_edit.setPlainText(text)# 设置文本,保留格式text=display_edit.toPlainText()# 获取文本,保留所有空格和换行`
- 确保DLL文件与程序在同一目录
- 验证EPICS PV名称的正确性
- 检查DLL加载状态