epics_widget.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. from PyQt4 import QtGui
  2. from ..base import kcgwidget as kcgw
  3. from ..base.globals import glob as global_objects
  4. from ..base import log
  5. from .. import config
  6. import sys
  7. if sys.version_info[:3] < (3,0):
  8. import ConfigParser as configparser
  9. else:
  10. import configparser
  11. import os
  12. import logging
  13. from functools import partial
  14. import numpy as np
  15. __widget_id__ = None
  16. no_epics = True
  17. try: # try to import epics
  18. import epics
  19. no_epics = False
  20. except ImportError:
  21. no_epics = True
  22. logging.error("Python Epics not found")
  23. def caget(pvname):
  24. """
  25. Wrapper for epics.caget to handle no_epics
  26. """
  27. if no_epics: return None
  28. try:
  29. return epics.caget(pvname)
  30. except:
  31. logging.error("Epics PV is not accessible (possible Timeout)")
  32. return None
  33. def caCheck(pvname):
  34. """
  35. check if PV is readable
  36. """
  37. try:
  38. if epics.caget(pvname) == None:
  39. return False
  40. else:
  41. return True
  42. except:
  43. return False
  44. class EpicsConfig(object):
  45. """
  46. need one instance at app startup
  47. is done by kcgGUI via
  48. from ..widgets import epics_widget
  49. epics_widget.epicsConfig = epics_widget.EpicsConfig()
  50. """
  51. def __init__(self):
  52. global no_epics
  53. if no_epics:
  54. logging.error("Epics package not found!")
  55. return
  56. self.loadConfig(False)
  57. os.environ["EPICS_BASE"] = self.getKey('epics_base_path') # config.epics_base_path
  58. try: # if import was successful, try to find libca and test for connectivity with epics_test_pv
  59. # sys.stderr = os.devnull # epics.ca.find_libca() prints a useless None to stderr if libca is not found
  60. epics.ca.find_libca()
  61. # sys.stderr = sys.__stderr__
  62. if epics.caget(self.getKey('epics_test_pv')) == None:
  63. no_epics = True
  64. epics_reachable = False
  65. logging.error("Epics is not accessible (possible Timeout)")
  66. except epics.ca.ChannelAccessException as e:
  67. if str(e) == "cannot find Epics CA DLL":
  68. no_epics = True
  69. logging.error("Epics CA DLL not found - check epics config")
  70. self.setLogger()
  71. def setLogger(self):
  72. if no_epics:
  73. return
  74. # first remove all epics entrys
  75. params = log.logger.predefined_parameters
  76. log.logger.predefined_parameters = []
  77. for item in params:
  78. if item[1][0] != epics.caget:
  79. log.logger.predefined_parameters.append(item)
  80. params = log.logger.parameter_functions
  81. log.logger.reset_parameters()
  82. for item in params:
  83. if item[0] != epics.caget:
  84. log.logger.register_parameter(item[2], item[0], item[1])
  85. # add epics entrys
  86. for entry in self.getKey('epics_log_entry_pvs'):#config.epics_log_entry_pvs:
  87. log.logger.predefined_parameters.append([entry[0], [epics.caget, entry[1]]])
  88. if entry[2] == "True":
  89. if caCheck(entry[1]): #only add PVs that are reachable
  90. log.logger.register_parameter(entry[0], epics.caget, entry[1])
  91. def loadConfig(self, updateLogger=True):
  92. epics_key = ["epics_log_entry_pvs", "epics_test_pv", "epics_base_path"]
  93. self.parser = configparser.ConfigParser()
  94. self.parser.optionxform = str
  95. if not os.path.isfile(config.config_path("epics.cfg")):
  96. with open(config.config_path("epics.cfg"), 'w') as f:
  97. self.parser['EPICS'] = {'epics_log_entry_pvs':'[("Beam Energy (GeV)", "A:SR:BeamInfo:01:Energy", "False", "True"),("Beam Current (mA)", "A:SR:BeamInfo:01:Current", "True", "True")]',
  98. 'epics_test_pv':'"A:SR:BeamInfo:01:Current"',
  99. 'epics_base_path':'"/opt/epics/base/"'
  100. }
  101. self.parser.write(f)
  102. # print("new Epics config")
  103. self.parser.read(config.config_path("epics.cfg"))
  104. #print("epics config loaded")
  105. #print(config.config_path("epics.cfg"))
  106. if updateLogger: self.setLogger()
  107. def saveConfig(self):
  108. with open(config.config_path("epics.cfg"), 'w+') as f:
  109. self.parser.write(f)
  110. self.setLogger()
  111. def getKey(self, key):
  112. return config.leval(self.parser.get('EPICS', key))
  113. def setKey(self,key, value):
  114. if value[0] != "'":
  115. value = "'" + value + "'"
  116. self.parser.set('EPICS', key, value)
  117. def getPVList(self):
  118. return self.getKey('epics_log_entry_pvs')
  119. def setPVList(self, pvList):
  120. self.parser.set('EPICS', 'epics_log_entry_pvs', self.generatePVKey(pvList))
  121. def addPV(self, name, pv, default, monitor=False):
  122. dat = self.getKey('epics_log_entry_pvs')
  123. dat.append((name,pv,default,monitor))
  124. self.parser.set('EPICS', 'epics_log_entry_pvs', self.generatePVKey(dat))
  125. def removePV(self, index):
  126. n = int(index)
  127. pvList = []
  128. for i, item in enumerate(self.getPVList()):
  129. if n != i: pvList.append((item[0], item[1], item[2], item[3]))
  130. self.setPVList(pvList)
  131. def generatePVKey(self, pvList):
  132. out = "[\n"
  133. i=0
  134. for item in pvList:
  135. if i: out = out + ",\n"
  136. out = out + "('" + str(item[0]) + "', '" + str(item[1]) + "', '" + str(item[2]) + "', '" + str(item[3]) +"')"
  137. i = i+1
  138. out = out + "\n]"
  139. return out
  140. # 8888888888 d8b 888 888d8b 888 888
  141. # 888 Y8P 888 o 888Y8P 888 888
  142. # 888 888 d8b 888 888 888
  143. # 8888888 88888b. 888 .d8888b.d8888b 888 d888b 888888 .d88888 .d88b. .d88b. 888888
  144. # 888 888 "88b888d88P" 88K 888d88888b888888d88" 888d88P"88bd8P Y8b888
  145. # 888 888 888888888 "Y8888b.88888P Y88888888888 888888 88888888888888
  146. # 888 888 d88P888Y88b. X888888P Y8888888Y88b 888Y88b 888Y8b. Y88b.
  147. # 888888888888888P" 888 "Y8888P 88888P'888P Y888888 "Y88888 "Y88888 "Y8888 "Y888
  148. # 888 888
  149. # 888 Y8b d88P
  150. # 888 "Y88P"
  151. #
  152. class EpicsWidget(kcgw.KCGWidgets):
  153. def __init__(self, unique_id, parent):
  154. super(EpicsWidget, self).__init__()
  155. self.id = unique_id
  156. self.par = parent
  157. self.monitorPVList = {}
  158. self.configList = []
  159. self.setWindowTitle("Epics Widget")
  160. #self.layout = QtGui.QVBoxLayout() #.QGridLayout()
  161. #self.setLayout(self.layout)
  162. self.layout = QtGui.QGridLayout()
  163. self.outerLayout = QtGui.QVBoxLayout()
  164. self.outerLayout.addLayout(self.layout)
  165. self.setLayout(self.outerLayout)
  166. if no_epics:
  167. self.layout.addWidget(self.createLabel('no Epics available'))
  168. return
  169. self.editInnerGroupBox = QtGui.QGroupBox("")
  170. self.editInnerGroupBoxLayout = QtGui.QGridLayout()
  171. self.editScrollArea = QtGui.QScrollArea()
  172. self.pvInputList = []
  173. self.editInnerGroupBoxLayout.addWidget(self.createLabel("Name"), 0,0)
  174. self.editInnerGroupBoxLayout.addWidget(self.createLabel("EPICS PV"), 0,1)
  175. self.editInnerGroupBoxLayout.addWidget(self.createLabel("in Log"), 0,2)
  176. self.editInnerGroupBoxLayout.addWidget(self.createLabel("Monitoring"), 0,3)
  177. self.editInnerGroupBoxLayout.addWidget(self.createLabel("Value"), 0,4)
  178. self.generateList(False)
  179. self.editInnerGroupBox.setLayout(self.editInnerGroupBoxLayout)
  180. self.editScrollArea.setWidget(self.editInnerGroupBox)
  181. self.editOuterGroupBox = QtGui.QGroupBox("Epics PV List")
  182. #self.editOuterGroupBox.clicked.connect(self.toggleEdit)
  183. self.editOuterGroupBoxLayout = QtGui.QGridLayout()
  184. self.editOuterGroupBoxLayout.addWidget(self.editScrollArea,0,0,1,5)
  185. self.readValues()
  186. self.button1 = self.createButton("refresh values", connect=self.readValues)
  187. self.button2 = self.createButton("apply changes", connect=self.apply)
  188. self.button3 = self.createButton("restore changes", connect=self.restore)
  189. self.button4 = self.createButton("apply + save", connect=self.save)
  190. self.button5 = self.createButton("load", connect=self.load)
  191. self.button6 = self.createButton("Edit Configfile", connect=self.editConfigs)
  192. self.editOuterGroupBoxLayout.addWidget(self.button1, 1,0)
  193. self.editOuterGroupBoxLayout.addWidget(self.button2, 1,1)
  194. self.editOuterGroupBoxLayout.addWidget(self.button3, 1,2)
  195. self.editOuterGroupBoxLayout.addWidget(self.button4, 1,3)
  196. self.editOuterGroupBoxLayout.addWidget(self.button5, 1,4)
  197. self.editOuterGroupBoxLayout.addWidget(self.button6, 2,0,1,5)
  198. self.editOuterGroupBox.setLayout(self.editOuterGroupBoxLayout)
  199. self.monitorLabel = self.createLabel("Monitor\n")
  200. self.layout.addWidget(self.monitorLabel)#,0,0)
  201. self.layout.addWidget(self.createCheckbox("Open PV List", connect=self.toggleEdit))#, 1,0)
  202. self.editOuterGroupBox.hide()
  203. self.layout.addWidget(self.editOuterGroupBox)#,2,0)
  204. # self.button6 = self.createButton("Edit Config-file", connect=self.editConfigs)
  205. #self.layout.addWidget(self.button6)
  206. self.pvList = []
  207. self.editHide = True
  208. # self.toggleEdit()
  209. self.startMonitor()
  210. # self.resize(self.monitorLabel.frameGeometry().width()+50, self.monitorLabel.frameGeometry().height()+10)
  211. self.outerLayout.addStretch(1)
  212. self.adjustSize()
  213. def closeEvent(self, event):
  214. global __widget_id__
  215. __widget_id__ = None
  216. if not no_epics:
  217. self.stopMonitor()
  218. del self.par.widgets[self.id]
  219. def generateList(self, updateMonitor=True):
  220. if updateMonitor:
  221. self.stopMonitor()
  222. if len(self.pvInputList):
  223. for item in self.pvInputList:
  224. for q in item:
  225. self.editInnerGroupBoxLayout.removeWidget(q)
  226. q.deleteLater()
  227. self.pvInputList = []
  228. for q in self.pvInputAdd:
  229. self.editInnerGroupBoxLayout.removeWidget(q)
  230. q.deleteLater()
  231. i = 0
  232. for item in epicsConfig.getKey('epics_log_entry_pvs'): #config.epics_log_entry_pvs:
  233. self.pvInputList.append([self.createInput(item[0]), self.createInput(item[1],width=300), self.createSwitch(item[2]=="True"),
  234. self.createSwitch(item[3]=="True"), self.createLabel("value"),
  235. self.createLabel(image=QtGui.QPixmap(config.icon_path('open-trash-can.png')), click=True, connect=partial(self.remove,i))
  236. ])
  237. self.editInnerGroupBoxLayout.addWidget(self.pvInputList[i][0], i+1, 0)
  238. self.editInnerGroupBoxLayout.addWidget(self.pvInputList[i][1], i+1, 1)
  239. self.editInnerGroupBoxLayout.addWidget(self.pvInputList[i][2], i+1, 2)
  240. self.editInnerGroupBoxLayout.addWidget(self.pvInputList[i][3], i+1, 3)
  241. self.editInnerGroupBoxLayout.addWidget(self.pvInputList[i][4], i+1, 4)
  242. self.editInnerGroupBoxLayout.addWidget(self.pvInputList[i][5], i+1, 5)
  243. i = i+1
  244. self.pvInputAdd = [ self.createInput('name?'), self.createInput("?",width=300),
  245. self.createSwitch(True), self.createSwitch(False),self.createButton("add",connect=self.add, tooltip="If PV name is wrong, it can take a bit")]
  246. self.editInnerGroupBoxLayout.addWidget(self.pvInputAdd[0], i+1, 0)
  247. self.editInnerGroupBoxLayout.addWidget(self.pvInputAdd[1], i+1, 1)
  248. self.editInnerGroupBoxLayout.addWidget(self.pvInputAdd[2], i+1, 2)
  249. self.editInnerGroupBoxLayout.addWidget(self.pvInputAdd[3], i+1, 3)
  250. self.editInnerGroupBoxLayout.addWidget(self.pvInputAdd[4], i+1, 4)
  251. if updateMonitor:
  252. self.startMonitor()
  253. def toggleEdit(self):
  254. if self.editHide:
  255. # self.resize(800, 800)
  256. self.editOuterGroupBox.show()
  257. self.restore()
  258. else:
  259. # self.resize(self.monitorLabel.frameGeometry().width()+10, self.monitorLabel.frameGeometry().height()+50)
  260. self.editOuterGroupBox.hide()
  261. self.editHide = not self.editHide
  262. #self.update()
  263. def editConfigs(self):
  264. if not len(self.configList):
  265. self.button6.hide()
  266. self.configBox = QtGui.QGroupBox("Edit Configfile")
  267. self.configBoxLayout = QtGui.QGridLayout()
  268. self.configList = []
  269. self.configBoxLayout.addWidget(self.createLabel("epics_test_pv"),0,0)
  270. self.configList.append(self.createInput(epicsConfig.getKey('epics_test_pv')))
  271. self.configBoxLayout.addWidget(self.configList[-1],0,1)
  272. self.configBoxLayout.addWidget(self.createLabel("epics_base_path"),1,0)
  273. self.configList.append(self.createInput(epicsConfig.getKey('epics_base_path')))
  274. self.configBoxLayout.addWidget(self.configList[-1],1,1)
  275. self.configBoxLayout.addWidget(self.createLabel("after saving a newstart is needed"),2,0,1,2)
  276. # self.configBoxLayout.addWidget(self.createButton("Save", connect=self.saveConfigs))
  277. # self.configBoxLayout.addWidget(self.createButton("Abort", connect=self.abortConfigs))
  278. self.configBox.setLayout(self.configBoxLayout)
  279. self.editOuterGroupBoxLayout.addWidget(self.configBox,2,0,1,5)
  280. pass
  281. def saveConfigs(self):
  282. if len(self.configList):
  283. epicsConfig.setKey('epics_test_pv', str(self.configList[0].text()))
  284. epicsConfig.setKey('epics_base_path', str(self.configList[1].text()))
  285. self.abortConfigs()
  286. pass
  287. def abortConfigs(self):
  288. if len(self.configList):
  289. while self.configBoxLayout.count() > 0:
  290. item = self.configBoxLayout.takeAt(0)
  291. if not item:
  292. continue
  293. w = item.widget()
  294. if w:
  295. w.deleteLater()
  296. self.configBox.deleteLater()
  297. self.configList = []
  298. self.button6.show()
  299. pass
  300. def readValues(self):
  301. for item in self.pvInputList:
  302. try:
  303. val = str(caget(str(item[1].text())))
  304. except:
  305. val = "ERROR"
  306. try:
  307. val = "{0:.5f}".format(float(val))
  308. except:
  309. pass
  310. item[4].setText(val)
  311. def add(self):
  312. if caCheck( self.pvInputAdd[1].text()):
  313. epicsConfig.addPV(self.pvInputAdd[0].text(), self.pvInputAdd[1].text(), self.pvInputAdd[2].state, self.pvInputAdd[3].state)
  314. self.generateList()
  315. self.readValues()
  316. else:
  317. pop = kcgw.PopupDialog('{} not readable'.format(self.pvInputAdd[1].text()), okonly=True)
  318. pop.exec_()
  319. pop.deleteLater()
  320. def remove(self, n):
  321. epicsConfig.removePV(n)
  322. self.generateList()
  323. self.readValues()
  324. def restore(self):
  325. self.generateList()
  326. self.readValues()
  327. self.abortConfigs()
  328. def load(self):
  329. epicsConfig.loadConfig()
  330. self.generateList()
  331. self.readValues()
  332. self.abortConfigs()
  333. def save(self):
  334. self.apply()
  335. self.saveConfigs()
  336. epicsConfig.saveConfig()
  337. def apply(self):
  338. pvList = []
  339. for item in self.pvInputList:
  340. pvList.append((item[0].text(), item[1].text(), item[2].state, item[3].state))
  341. epicsConfig.setPVList(pvList)
  342. self.generateList()
  343. self.readValues()
  344. def monitorPV(self, pvname=None, value=None, char_value=None, clearname="", **kw):
  345. if clearname != "":
  346. self.monitorPVList[str(pvname)] = [clearname, char_value]
  347. self.monitorPVList[str(pvname)][1] = char_value
  348. self.updateMonitor()
  349. def updateMonitor(self):
  350. string = "Monitor\n"
  351. for item in self.monitorPVList:
  352. string += str(self.monitorPVList[item][0]) + " = " + str(self.monitorPVList[item][1]) + "\n"
  353. self.monitorLabel.setText(string)
  354. def startMonitor(self):
  355. if len(self.pvList):
  356. self.stopMonitor()
  357. for item in epicsConfig.getKey('epics_log_entry_pvs'):
  358. if item[3] == 'True':
  359. try:
  360. if caget(item[1]) != None:
  361. self.pvList.append(item[1])
  362. self.monitorPV(item[1], char_value=caget(item[1]), clearname=item[0])
  363. epics.camonitor(item[1], callback=self.monitorPV)
  364. except:
  365. logging.info('epics add Failed', item[1])
  366. pass
  367. def stopMonitor(self):
  368. for item in self.pvList:
  369. try:
  370. epics.camonitor_clear(item)
  371. except:
  372. logging.info('epics remove Failed', item)
  373. pass
  374. self.pvList = []
  375. self.monitorPVList = {}
  376. def addEpicsWidget():
  377. global __widget_id__
  378. if __widget_id__:
  379. global_objects.get_global('area').widgets[__widget_id__].setFocus()
  380. else:
  381. nid = kcgw.idg.genid()
  382. __widget_id__ = nid
  383. w = EpicsWidget(nid, global_objects.get_global('area'))
  384. global_objects.get_global('area').newWidget(w, "Epics Widget", nid, widget_type=4)
  385. kcgw.register_widget(QtGui.QIcon(config.icon_path("EPICS_Logo.png")), "Epics Widget", addEpicsWidget, "Ctrl+E")