log.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. """
  2. This is a custom logfile creation module
  3. """
  4. import datetime
  5. import os
  6. from PyQt4 import QtGui, QtCore
  7. # from backend import board
  8. from backend.board import available_boards
  9. import backendinterface as bif
  10. import numpy as np
  11. import codecs
  12. from .. import config
  13. import storage
  14. import logging
  15. epics_reachable = True
  16. try: # try to import epics
  17. import epics
  18. no_epics = False
  19. os.environ["EPICS_BASE"] = config.epics_base_path
  20. try: # if import was successful, try to find libca and test for connectivity with config.epics_test_pv
  21. # sys.stderr = os.devnull # epics.ca.find_libca() prints a useless None to stderr if libca is not found
  22. epics.ca.find_libca()
  23. # sys.stderr = sys.__stderr__
  24. if epics.caget(config.epics_test_pv) == None:
  25. no_epics = True
  26. epics_reachable = False
  27. logging.error("Epics is not accessible (possible Timeout)")
  28. except epics.ca.ChannelAccessException as e:
  29. if str(e) == "cannot find Epics CA DLL":
  30. no_epics = True
  31. logging.error("Epics CA DLL not found")
  32. except ImportError:
  33. no_epics = True
  34. # after = datetime.datetime.now()
  35. # if (after-before).total_seconds() > 3.0:
  36. # no_epics = True
  37. # logging.error("Epics is not accessible (Timeout)")
  38. # tr = kcgw.tr
  39. tr = lambda _, x: x # log entries will not be translated
  40. class LogLevels: # Keep every new value an integer. Every level with higher value will include those with lower level
  41. NONE = -1
  42. INFO = 0
  43. DEBUG = 1
  44. TWO_COLUMNS = False
  45. BOARDID = "BOARD_ID"
  46. class MeasurementLogger(object):
  47. """
  48. Logfile creator class
  49. It will automatically get the info needed to create a logfile entry from registered functions.
  50. """
  51. def __init__(self, filename=None, level=LogLevels.INFO, oneline=False):
  52. """
  53. Initialise the logfile creator
  54. :param filename: (str) filename of the logfile THIS IS IGNORED AS OF NOW
  55. :param level: (int) valid log level (see LogLevels)
  56. :param oneline: (bool) whether to format the logfile entries in multilines or one line
  57. :return: -
  58. """
  59. self.level = level
  60. self.filename = filename
  61. self.parameter_functions = []
  62. self.oneline = oneline
  63. self.dumper = None
  64. self.predefined_parameters = [ # format: [logstring, [function (args,)]]
  65. ["Number of Orbits", [bif.bk_get_config, (BOARDID, 'orbits_observe')]],
  66. ["Number of Skipped Orbits", [bif.bk_get_config, (BOARDID, 'orbits_skip')]],
  67. ["Number of Acquisitions", [bif.bk_get_config, (BOARDID, 'acquisition_count')]],
  68. ["Time between Acquisitions", [bif.bk_get_config, (BOARDID, 'orbits_wait_time')]],
  69. ["Pilot Bunch Simulator", [bif.bk_get_config, (BOARDID, 'pilot_bunch')]],
  70. ["Header saved", [bif.bk_get_config, (BOARDID, 'header')]],
  71. ["T/H Delay", [bif.bk_get_config, (BOARDID, 'th_delay')]],
  72. ["ADC 1 Delay", [bif.bk_get_config, (BOARDID, 'chip_1_delay')]],
  73. ["ADC 2 Delay", [bif.bk_get_config, (BOARDID, 'chip_2_delay')]],
  74. ["ADC 3 Delay", [bif.bk_get_config, (BOARDID, 'chip_3_delay')]],
  75. ["ADC 4 Delay", [bif.bk_get_config, (BOARDID, 'chip_4_delay')]]
  76. ]
  77. self.gui_values_number = len(self.predefined_parameters)
  78. if not no_epics:
  79. for entr in config.epics_log_entry_pvs:
  80. self.predefined_parameters.append([entr[0], [epics.caget, entr[1]]])
  81. def __now(self):
  82. """
  83. Get the current time in %Y-%m-%d %H:%M:%S format
  84. :return: the time
  85. """
  86. return datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
  87. def _logging_formatter(self, info):
  88. """
  89. Formatter for the logfile entries
  90. Override this to implement custom formatters
  91. """
  92. if self.oneline:
  93. return self.__now() + " " + ", ".join(info)
  94. return self.__now() + "\n\t" + "\n\t".join(info)
  95. def _log(self, board_id, stuff):
  96. """
  97. Write entries to logfile
  98. This will also make shure the directory the logfile is located is available and if not creates it
  99. :param stuff: (str) the entrie to write
  100. :return: -
  101. """
  102. # if not self.filename:
  103. # filename = storage.storage.save_location + '/' + storage.storage.subdirname + "/Measurement.log"
  104. # else:
  105. # filename = self.filename
  106. filename = storage.storage.save_location + '/' + \
  107. storage.storage.subdirname + '/' + "/Measurement_board_" + \
  108. available_boards.get_board_name_from_id(board_id).replace(' ', '_') + ".log"
  109. if not os.path.isdir(str(storage.storage.save_location + '/' + storage.storage.subdirname)):
  110. os.makedirs(str(storage.storage.save_location + '/' + storage.storage.subdirname))
  111. f = codecs.open(filename, 'a', encoding='utf-8')
  112. f.write(stuff+'\n\n')
  113. f.close()
  114. def register_parameter(self, logstring, func, args):
  115. """
  116. This function is used to register parameters for the logfile entries
  117. :param logstring: (str) the string to describe the value in the logfile
  118. :param func: (callable) the function used to get the value
  119. :param args: arguments to pass to func upon gathering of information
  120. :return: -
  121. """
  122. if not isinstance(args, tuple):
  123. args = (args,)
  124. self.parameter_functions.append([func, args, logstring])
  125. # self.parameter_functions[logstring] = [func, args, logstring]
  126. def reset_parameters(self):
  127. """
  128. Resets all parameters (unregisteres all parameters)
  129. """
  130. self.parameter_functions = []
  131. def register_dumper(self, func):
  132. """
  133. Register a dumper that is used to dump a log of data into the logfile
  134. :param func: (callable) Function that is used as dumper
  135. :return: -
  136. """
  137. self.dumper = func
  138. def dump(self, board_id):
  139. """
  140. Perform a dump (see register_dumper)
  141. :return: -
  142. """
  143. self.do_log(board_id=board_id, c=self.__now() + " " + self.dumper())
  144. def do_log(self, c=None, additional=None, board_id=None, level=LogLevels.INFO):
  145. """
  146. Perform a log. This creates a log entry in the logfile
  147. :param c: (str) if this is not None this is the only text written to the logfile (in addition to the current time)
  148. :param additional: (str) This text is written below the current time to customize log entries
  149. :param board_id: the id of the board to log for, if this is not given, all boards are logged
  150. :return: -
  151. """
  152. if level > self.level:
  153. return
  154. if c:
  155. if board_id is not None:
  156. self._log(board_id, c)
  157. else:
  158. for bid in available_boards:
  159. self._log(bid, c)
  160. else:
  161. def do_the_log(bid):
  162. st = []
  163. if additional:
  164. s = additional.split('\n')[0]
  165. for line in additional.split('\n')[1:]:
  166. s += "\n\t"+line
  167. st.append(s)
  168. for par in self.parameter_functions:
  169. par[1] = list(par[1])
  170. for i, p in enumerate(par[1]):
  171. if p == BOARDID:
  172. par[1][i] = bid
  173. st.append(par[2] + ": " + str(par[0](*par[1])))
  174. return st
  175. if board_id is not None:
  176. st = do_the_log(board_id)
  177. self._log(board_id, self._logging_formatter(st))
  178. else:
  179. for bid in available_boards:
  180. st = do_the_log(bid)
  181. self._log(bid, self._logging_formatter(st))
  182. logger = None
  183. dump_general = False
  184. def log(c=None, additional=None, dump=False, board_id=None, level=LogLevels.INFO):
  185. """
  186. Execute logging if logger was registered
  187. :param (str) c: if this is given c is the only entry that is created (additional is ignored)
  188. :param (str) additional: the additional text that is written below the current time
  189. :param (bool) dump: if this is true, a dump of every log value is performed
  190. :param board_id: the board id to log for, if this is not given, all boards are logged
  191. :return: -
  192. """
  193. global logger, dump_general
  194. if logger:
  195. if dump or dump_general:
  196. logger.dump(board_id=board_id)
  197. else:
  198. logger.do_log(c, additional, board_id=board_id, level=level)
  199. else:
  200. print "No Logger registered"
  201. class ConfigureLog(QtGui.QDialog):
  202. """
  203. Class that is basically a QDialog to configure the logger
  204. """
  205. def __init__(self, parent=None): # parameter parent does nothing when adding parent to the folowing __init__
  206. # no entry in taskbar is made
  207. super(ConfigureLog, self).__init__()
  208. self.setWindowTitle("Configure Measurement Log Entries")
  209. self.setWindowIcon(QtGui.QIcon(config.install_path + config.guiIcon))
  210. self.sl = QtGui.QVBoxLayout()
  211. self.setLayout(self.sl)
  212. self.scrollArea = QtGui.QScrollArea()
  213. if TWO_COLUMNS:
  214. self.scrollArea.setMinimumSize(410, 300)
  215. else:
  216. self.scrollArea.setMinimumSize(250, 520)
  217. self.sl.addWidget(self.scrollArea)
  218. self.widget = QtGui.QWidget()
  219. if TWO_COLUMNS:
  220. self.layout = QtGui.QGridLayout()
  221. else:
  222. self.layout = QtGui.QVBoxLayout()
  223. self.widget.setLayout(self.layout)
  224. self.parameter = []
  225. for par in logger.predefined_parameters:
  226. self.parameter.append((QtGui.QCheckBox(par[0]), par[1]))
  227. self.i = 0
  228. def add(w):
  229. if w == 'sep':
  230. sep = QtGui.QFrame()
  231. sep.setFrameShape(QtGui.QFrame.HLine)
  232. w = sep
  233. self.layout.addWidget(w, self.i//2, self.i%2)
  234. self.i += 1
  235. for i, (cb, _) in enumerate(self.parameter):
  236. if TWO_COLUMNS:
  237. if i == logger.gui_values_number:
  238. # sep1 = QtGui.QFrame()
  239. # sep1.setFrameShape(QtGui.QFrame.HLine)
  240. # sep2 = QtGui.QFrame()
  241. # sep2.setFrameShape(QtGui.QFrame.HLine)
  242. if logger.gui_values_number%2 == 1:
  243. self.i += 1
  244. # add(sep1)
  245. # add(sep2)
  246. add('sep')
  247. add('sep')
  248. add(cb)
  249. else:
  250. if i == logger.gui_values_number:
  251. sep = QtGui.QFrame()
  252. sep.setFrameShape(QtGui.QFrame.HLine)
  253. self.layout.addWidget(sep)
  254. self.layout.addWidget(cb)
  255. self.all = QtGui.QCheckBox(tr("Label", "All of the above"))
  256. self.all.clicked.connect(self.all_changed)
  257. if TWO_COLUMNS:
  258. if (len(logger.predefined_parameters)-1)%2 == 1:
  259. self.i += 1
  260. add('sep')
  261. add('sep')
  262. add(self.all)
  263. else:
  264. sepa = QtGui.QFrame()
  265. sepa.setFrameShape(QtGui.QFrame.HLine)
  266. self.layout.addWidget(sepa)
  267. self.layout.addWidget(self.all)
  268. self.buttonLayout = QtGui.QHBoxLayout()
  269. self.sl.addLayout(self.buttonLayout)
  270. self.ok = QtGui.QPushButton(tr("Button", "OK"))
  271. self.cancel = QtGui.QPushButton(tr("Button", "Cancel"))
  272. self.buttonLayout.addWidget(self.ok)
  273. self.buttonLayout.addWidget(self.cancel)
  274. self.ok.pressed.connect(self.do)
  275. self.cancel.pressed.connect(self.close)
  276. self.scrollArea.setWidget(self.widget)
  277. self.initial_ticks()
  278. def initial_ticks(self):
  279. pf = np.array(logger.parameter_functions)
  280. for cb, _ in self.parameter:
  281. cb.setChecked(str(cb.text()) in pf[:, 2])
  282. def all_changed(self):
  283. if self.all.isChecked():
  284. for cb, _ in self.parameter:
  285. cb.setEnabled(False)
  286. else:
  287. for cb, _ in self.parameter:
  288. cb.setEnabled(True)
  289. def do(self):
  290. global logger
  291. logger.reset_parameters()
  292. def nif(cb, f, args):
  293. if cb.isChecked() or self.all.isChecked():
  294. logger.register_parameter(str(cb.text()), f, args)
  295. for cb, par in self.parameter:
  296. nif(cb, par[0], par[1])
  297. self.close()