backendinterface.py 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093
  1. """
  2. This is the interface to the backend.
  3. It is used to make the backend easily interchangable.
  4. All Functions that interface directly with the backend are prefixed with bk
  5. Functions only used internal in this module will be prefixed _bif_
  6. """
  7. import logging
  8. import time
  9. from datetime import datetime as dt
  10. import os
  11. import copy
  12. import shutil
  13. from PyQt4 import QtGui, QtCore
  14. import numpy as np
  15. from .backend import board
  16. from .backend.board import available_boards
  17. from .backend.DataSet import DataSet
  18. from .backend.CalibrationHandle import theCalibration
  19. from .groupedelements import Buttons, Elements, live_plot_windows, cuda_windows
  20. from . import storage
  21. from .. import config
  22. from . import kcgwidget as kcgw
  23. from .kcgwidget import error
  24. from .callbacks import callbacks
  25. from .log import log
  26. from .globals import glob as global_objects
  27. from time import sleep
  28. tr = kcgw.tr
  29. livePlotData = None
  30. PRINTDEBUG = False
  31. def initStatus(st):
  32. """
  33. Initialize Status variables. These variables are used to transfer status variables over different modules.
  34. :param st: variable to use (most likely a DummyStorage instance)
  35. :return: -
  36. """
  37. st.continuous_read = False
  38. st.calibrated = False
  39. st.synced = False
  40. st.defaults_set = False
  41. st.status_text = tr("sw", "Ready")
  42. st.time_scan = False
  43. st.wait_on_trigger = False
  44. st.last_file = None
  45. st.board_connected = True
  46. st.continuous_interval = 1000
  47. # -----------[ Backend Interface ]----------------------
  48. def _bif_enable_wait_cursor():
  49. """
  50. Show the "Wait Cursor"
  51. """
  52. QtGui.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.WaitCursor))
  53. def _bif_disable_wait_cursor():
  54. """
  55. Show the normal Cursor
  56. """
  57. QtGui.QApplication.restoreOverrideCursor()
  58. def _bif_continuous_read_is_enabled(board_id, popup_title_text=None):
  59. """
  60. Checks if continuous read is enabled and if yes shows a popup dialog to ask if it shall be disabled
  61. :param board_id: id of the board do manipulate
  62. :param popup_title_text: Text to display in the popup that asks to disable continuous read
  63. :return: bool (True if continuous read is enabled and not disabled in popup else False)
  64. """
  65. if board.get_board_status(board_id).continuous_read:
  66. if not available_boards.multi_board:
  67. popup = kcgw.PopupDialog(tr("Dialog", "Continuous read is currently active!\nStop continuous read and proceed?"),
  68. title=popup_title_text)
  69. else:
  70. popup = kcgw.PopupDialog(tr("Dialog", "Board {board_id}\nContinuous read is currently active!\nStop continuous read and proceed?").format(board_id=board_id),
  71. title=popup_title_text)
  72. popup.exec_()
  73. popup.deleteLater()
  74. if popup.get_return_value() is False:
  75. return False
  76. else:
  77. _bif_set_continuous_read_inactive(board_id)
  78. Elements.setChecked("continuousread_"+str(board_id), False)
  79. return True
  80. else:
  81. return False
  82. def bk_status_readout():
  83. """
  84. Read Status for every connected board
  85. """
  86. if not available_boards.has_boards:
  87. return
  88. for brd in available_boards.board_ids:
  89. _bif_status_readout(brd)
  90. def _bif_status_readout(board_id):
  91. """
  92. Read Status from board and update variables
  93. as well as enable and disable corresponding Buttons
  94. :return: -
  95. """
  96. # part_TODO: NOTE: Problem with this is that certain buttons will be greyed out until other buttons are pressed
  97. # part_TODO: even if only the gui is restarted and the board is in the same state
  98. """
  99. if kcgw.testing:
  100. return
  101. if board.is_active(board_id):
  102. Buttons.setEnabled("after_start_{}".format(board_id), True)
  103. Buttons.setEnabled("start_board_{}".format(board_id), False)
  104. Buttons.setEnabled("continuous_read_{}".format(board_id), True)
  105. # MenuItems.setEnabled("continuous_read", True)
  106. else:
  107. board.get_board_status(board_id).calibrated = False
  108. board.get_board_status(board_id).synced = False
  109. board.get_board_status(board_id).defaults_set = False
  110. Buttons.setEnabled("after_start_{}".format(board_id), False)
  111. Buttons.setEnabled("continuous_read_{}".format(board_id), False)
  112. Buttons.setEnabled("synchronize_{}".format(board_id), board.get_board_status(board_id).calibrated)
  113. Buttons.setEnabled("set_defaults_{}".format(board_id), board.get_board_status(board_id).synced)
  114. Buttons.setEnabled("acquire_{}".format(board_id), board.get_board_status(board_id).synced)
  115. Buttons.setEnabled("acquireTrigger_{}".format(board_id), board.get_board_status(board_id).synced)
  116. Elements.setEnabled("timing_{}".format(board_id), board.get_board_status(board_id).synced)
  117. """
  118. storage.get_board_specific_storage(board_id).update_LED()
  119. backup_readout = bk_status_readout
  120. class _bif_ProgressBar(QtGui.QProgressBar):
  121. """
  122. Simple Progressbar class.
  123. """
  124. def __init__(self, min, max, text):
  125. super(_bif_ProgressBar, self).__init__()
  126. self.setRange(min, max)
  127. self.setMaximumHeight(18)
  128. # kcgw.statusbar.clearMessage()
  129. self.label = QtGui.QLabel(text)
  130. global_objects.get_global('statusbar').insertWidget(0, self.label)
  131. global_objects.get_global('statusbar').insertWidget(1, self)
  132. self.setValue(0)
  133. QtGui.qApp.processEvents()
  134. def remove(self, timeout=None):
  135. """
  136. Remove this instance of a progressbar
  137. :param timeout: the time from calling this function to wanishing of the progressbar
  138. :return: -
  139. """
  140. def remove_progressbar():
  141. global_objects.get_global('statusbar').removeWidget(self)
  142. global_objects.get_global('statusbar').removeWidget(self.label)
  143. # kcgw.statusbar.showMessage(board.status.status_text)
  144. self.destroy()
  145. if timeout:
  146. QtCore.QTimer.singleShot(timeout, remove_progressbar)
  147. else:
  148. remove_progressbar()
  149. def bk_run_sequence(board_id, name):
  150. print("________________bk_run_sequence__________________________________________")
  151. log(board_id=board_id, additional=str(name))
  152. comment = board.get_board_config(board_id).get_sequence_comment(name)
  153. _bif_enable_wait_cursor()
  154. if _bif_continuous_read_is_enabled(board_id, tr("Button", "Calibrate Board")):
  155. _bif_disable_wait_cursor()
  156. return
  157. def fin():
  158. bk_status_readout()
  159. progressbar.remove(0)
  160. _bif_disable_wait_cursor()
  161. board.get_board_config(board_id).update('header', board.get_board_config(board_id)._config['header'])
  162. board.get_board_config(board_id).update('pilot_bunch', board.get_board_config(board_id)._config['pilot_bunch'])
  163. progressbar = _bif_ProgressBar(0, 10, tr("sw", comment))
  164. logging.info('Started ' + comment)
  165. try:
  166. if not board.get_board_config(board_id).run_sequence(name, progressbar):
  167. logging.error(comment + " failed")
  168. fin()
  169. return
  170. statval = board.get_board_config(board_id).get_sequence_status(name)
  171. if statval == "calibrated": board.get_board_status(board_id).calibrated = True
  172. elif statval == "synced": board.get_board_status(board_id).synced = True
  173. elif statval == "PLL500M": board.get_board_config(board_id).update('samplingrate', 1)
  174. elif statval == "PLL1G": board.get_board_config(board_id).update('samplingrate', 2)
  175. except board.BoardError as e:
  176. logging.error(comment + " failed: {}".format(str(e)))
  177. fin()
  178. return
  179. logging.info(comment + " successful!")
  180. fin()
  181. def bk_write_values(board_id, defaults=False):
  182. """
  183. Write values to board.
  184. :param board_id: id of the board do manipulate
  185. :param defaults: (bool) if True Writes default values
  186. :return: -
  187. """
  188. _bif_enable_wait_cursor()
  189. if defaults:
  190. log(board_id=board_id, additional="Set Default Values")
  191. else:
  192. log(board_id=board_id, additional="Update Values on board")
  193. if _bif_continuous_read_is_enabled(board_id, tr("Dialog", "Update Values on Board")):
  194. _bif_disable_wait_cursor()
  195. return
  196. if defaults:
  197. board.get_board_config(board_id)._set_defaults()
  198. logging.info("Setting default Values")
  199. else:
  200. logging.info("Updating Values")
  201. try:
  202. board.get_board_config(board_id).notify_all_observers(write=True)
  203. except board.BoardError as e:
  204. logging.error("Updating Values failed: {}".format(str(e)))
  205. _bif_disable_wait_cursor()
  206. bk_status_readout()
  207. return
  208. board.get_board_status(board_id).defaults_set = True
  209. if defaults:
  210. logging.info("Default values set successfully!")
  211. else:
  212. logging.info("Updated values successfully!")
  213. _bif_disable_wait_cursor()
  214. bk_status_readout()
  215. def bk_stop_board(board_id):
  216. """
  217. Stops the board and shuts it down
  218. :param board_id: id of the board do manipulate
  219. :return: -
  220. """
  221. _bif_enable_wait_cursor()
  222. log(board_id=board_id, additional="Stop Board")
  223. if _bif_continuous_read_is_enabled(board_id, tr("Dialog", "Soft Reset Board")):
  224. _bif_disable_wait_cursor()
  225. return
  226. try:
  227. logging.info("Switching Off Board {}".format(board_id))
  228. board.pci.write(board_id, '0x9040')
  229. board.pci.stop_dma(board_id)
  230. board.stop_board(board_id)
  231. time.sleep(0.5)
  232. except board.BoardError as e:
  233. logging.error("Sequence failed: {}".format(str(e)))
  234. _bif_disable_wait_cursor()
  235. bk_status_readout()
  236. return
  237. logging.info("Board switched off successfully!")
  238. Buttons.setEnabled("after_start_{}".format(board_id), False)
  239. Buttons.setEnabled("start_board_{}".format(board_id), True)
  240. board.get_board_status(board_id).calibrated = False
  241. _bif_disable_wait_cursor()
  242. bk_status_readout()
  243. def bk_soft_reset(board_id):
  244. """
  245. Perform a soft reset.
  246. :param board_id: id of the board do manipulate
  247. :return:
  248. """
  249. _bif_enable_wait_cursor()
  250. log(board_id=board_id, additional="Soft Reset")
  251. if _bif_continuous_read_is_enabled(board_id, tr("Dialog", "Soft Reset Board")):
  252. _bif_disable_wait_cursor()
  253. return
  254. try:
  255. logging.info("Soft-Resetting Board {}...".format(board_id))
  256. board.soft_reset(board_id)
  257. except board.BoardError as e:
  258. logging.error("Sequence failed: {}".format(str(e)))
  259. _bif_disable_wait_cursor()
  260. bk_status_readout()
  261. return
  262. _bif_disable_wait_cursor()
  263. bk_status_readout()
  264. board.get_board_config(board_id).update('header', True) # reset header (might be reset by soft reset)
  265. logging.info("Soft-Reset successful.")
  266. def bk_update_config(board_id, key, value, silent=False):
  267. """
  268. Interface to the update command of the BoardConfiguration class.
  269. :param board_id: id of the board do manipulate
  270. :param key: Key to update
  271. :param value: Value to set for key
  272. :param silent: (bool) if True do not inform observers on update
  273. :return: -
  274. """
  275. try:
  276. if silent:
  277. board.get_board_config(board_id).updateSilent(key, value)
  278. else:
  279. board.get_board_config(board_id).update(key, value)
  280. #print(key, value)
  281. except board.BoardError as e:
  282. logging.error("Setting value of {} failed: {}".format(key, str(e)))
  283. def bk_get_config(board_id, key,entry=None):
  284. """
  285. Interface to the get command of the BoardConfiguration class.
  286. :param board_id: id of the board do manipulate
  287. :param key: Key to get the value for
  288. :return: value stored for key
  289. """
  290. if entry == None:
  291. return board.get_board_config(board_id).get(key)
  292. else:
  293. return board.get_board_config(board_id).get(key)[entry]
  294. def bk_get_board_status(board_id, status_variable):
  295. """
  296. Interface to the status class for each board.
  297. :param board_id: id of the board do manipulate
  298. :param status_variable: Key to get the value for
  299. :return: value stored for key
  300. """
  301. return getattr(board.get_board_status(board_id), status_variable, None)
  302. def bk_get_status(board_id):
  303. """
  304. Interface to the get_status of the board
  305. NOTE: This is not get_board_status
  306. :return: status dictionary
  307. """
  308. return board.get_status(board_id)
  309. def bk_change_num_of_turns(board_id, value, silent=False):
  310. """
  311. Send new number of turns to board and update in config
  312. :param board_id: id of the board do manipulate
  313. :param value: the value to send
  314. :param silent: (bool) if True do not inform observers on update
  315. :return: -
  316. """
  317. bk_update_config(board_id, "turns_observe", value, silent=silent)
  318. def bk_change_num_of_skipped_turns(board_id, value, silent=False):
  319. """
  320. Send new number of turns to skip to board and update in config
  321. :param board_id: id of the board do manipulate
  322. :param value: the value to send
  323. :param silent: (bool) if True do not inform observers on update
  324. :return: -
  325. """
  326. bk_update_config(board_id, "turns_skip", value, silent=silent)
  327. def bk_change_count(board_id, value, silent=False):
  328. """
  329. Change the number of acquisitions you want to make.
  330. :param board_id: id of the board do manipulate
  331. :param value: (int) Number of acquisitions
  332. :param silent: (bool) if True do not inform observers on update
  333. :return: -
  334. """
  335. bk_update_config(board_id, "acquisition_count", value, silent=silent)
  336. def bk_change_wait(board_id, value, silent=False):
  337. """
  338. Change the time between acquisitions.
  339. :param board_id: id of the board do manipulate
  340. :param value: (bool) Time in seconds
  341. :param silent: (bool) if True do not inform observers on update
  342. :return: -
  343. """
  344. bk_update_config(board_id, "turns_wait_time", value, silent=silent)
  345. def bk_change_build_spectrograms(board_id, value, silent=False):
  346. """
  347. Change if spectrograms are built or not)
  348. :param board_id: id of the board do manipulate
  349. :param value: (bool) True or False built or not
  350. :param silent: (bool) if True do not inform observers on update
  351. :return:
  352. """
  353. bk_update_config(board_id, "build_spectrograms", value, silent=silent)
  354. def bk_change_pilot_bunch(board_id, value, silent=False):
  355. """
  356. Change if pilot bunch is simulated
  357. :param board_id: id of the board do manipulate
  358. :param value: (bool) True or False to simulate or not
  359. :param silent: (bool) if True do not inform observers on update
  360. :return:
  361. """
  362. bk_update_config(board_id, "pilot_bunch", value, silent=silent)
  363. def _bif_update_working_dir():
  364. """
  365. make Shure the currently set directory exists and a calibration File is present
  366. """
  367. path = str(os.path.join(storage.storage.save_location, storage.storage.subdirname))
  368. print(path)
  369. if not os.path.isdir(path):
  370. os.makedirs(path)
  371. shutil.copy(config.config_path("calibration.hdf"), path)
  372. theCalibration.openFile(os.path.join(path,'calibration.hdf'))
  373. return path
  374. def _bif_iterate_spectrograms(board_id, path):
  375. """
  376. BROKEN (DOES NOT GET ANY DATA)
  377. Built Spectrograms line by line
  378. :param board_id: id of the board do manipulate
  379. :param path: where to built the spectrogram
  380. :return: -
  381. """
  382. #print('_bif_iterate_spectrograms')
  383. return # because it is broken
  384. if not os.path.isdir(str(path)):
  385. return
  386. # how does this get data? dataset.data does not exist
  387. transform = dataset.data.fft(1, frm=0, to=-1)
  388. for i in range(config.bunches_per_turn - 1):
  389. filename = os.path.join(storage.storage.save_location, storage.storage.subdirname, str(path), "%i.hsp" % i)
  390. write_header = False
  391. if not os.path.isfile(filename):
  392. write_header = True
  393. f = open(filename, 'ab')
  394. if write_header:
  395. f.write("#hsp\n") # heb spectrogram magic number
  396. f.write("#"+str(board.get_board_config(board_id).get("turns_skip")))
  397. f.write("\n")
  398. line = transform[i, :]
  399. f.write('{:0.3f} '.format(time.time()))
  400. for e in line:
  401. f.write("%s " % np.absolute(e))
  402. f.write("\n")
  403. f.close()
  404. def _bif_read_data_and_save(board_id):
  405. """
  406. Tell the pci command to start acquisition and save data
  407. Also generates the filename from settings
  408. :param board_id: id of the board do manipulate
  409. :return:
  410. """
  411. if PRINTDEBUG: print(time.time(), '_bif_read_data_and_save start')
  412. now = time.time()
  413. _bif_update_working_dir()
  414. filename = os.path.join(storage.storage.save_location, storage.storage.subdirname,'{:0.3f}.out'.format(now))
  415. board.get_board_status(board_id).last_file = filename
  416. try:
  417. simulate = board.get_board_config(board_id).get("pilot_bunch")
  418. try:
  419. if PRINTDEBUG: print(time.time(), '_bif_read_data_and_save acquire_data')
  420. board.acquire_data(board_id, filename, simulate=simulate)
  421. if not os.path.isfile(filename):
  422. error(0x001, "No File Created")
  423. except IndexError:
  424. error(0x002, "Unexpected output of pci for number of turns to observe. Returning")
  425. return
  426. _bif_read_and_update_data_from_file(board_id, filename)
  427. except board.BoardError as e:
  428. logging.error("Reading failed: {}".format(str(e)))
  429. if PRINTDEBUG: print(time.time(), '_bif_read_data_and_save stop')
  430. def _bif_read_and_update_data_from_file(board_id, filename):
  431. """
  432. Proxy function for _bif_read_and_update to call with correct read_func
  433. :param board_id: id of the board do manipulate
  434. :param filename: filename to read data from
  435. :return: -
  436. """
  437. if kcgw.testing:
  438. filename = os.path.join(storage.storage.save_location, storage.storage.subdirname,'testing.out')
  439. _bif_read_and_update(board_id, 1, str(filename))
  440. def _bif_read_and_update_data_from_string(board_id, raw_data):
  441. """
  442. Proxy function for _bif_read_and_update to call with correct read_func
  443. :param board_id: id of the board do manipulate
  444. :param raw_data: Data as string
  445. :return: -
  446. """
  447. _bif_read_and_update(board_id, 0, raw_data)
  448. def _bif_read_and_update(board_id, read_type, dataOrName):
  449. """
  450. Function to read data from file or string (depending on read_func) and update plots etc.
  451. :param board_id: id of the board do manipulate
  452. :param read_type: 0 - from String; 1 - from File
  453. :param dataOrName: filename or raw_data
  454. :return: -
  455. """
  456. if PRINTDEBUG: print(time.time(), '_bif_read_and_update start')
  457. _bif_enable_wait_cursor()
  458. header = board.get_board_config(board_id).get('header')
  459. # TODO: force_read: meaning ignore cache and read new -> in the old gui this was a commandline option
  460. # TODO: cache_data: meaning cache already processed numpy data -> in the old gui this was a commandline option
  461. delays = {'c330':board.get_board_config(board_id).get('delay_330_th'), 'c25':board.get_board_config(board_id).get('delay_25_th'),
  462. 'c25b':board.get_board_config(board_id).get('delay_25_th_2'), 'f':board.get_board_config(board_id).get('chip_delay')}
  463. if read_type == 0:
  464. data = DataSet(stringData=dataOrName, delays=delays, tRev=config.tRev, bunchesPerTurn=config.bunches_per_turn, shiftFMC2=config.shiftFMC2)
  465. else:
  466. data = DataSet(filename=dataOrName, delays=delays, tRev=config.tRev, bunchesPerTurn=config.bunches_per_turn, shiftFMC2=config.shiftFMC2)
  467. board.get_board_config(board_id).update('lastDataSet', data)
  468. QtGui.qApp.processEvents()
  469. _bif_disable_wait_cursor()
  470. if PRINTDEBUG: print(time.time(), '_bif_read_and_update stop')
  471. def bk_acquire(board_id):
  472. """
  473. Toggle Acqisition
  474. :param board_id: id of the board do manipulate
  475. :return:
  476. """
  477. if not bk_get_config(board_id, 'use_trigger'):
  478. if board.get_board_status(board_id).acquisition == True:
  479. log(board_id=board_id, additional="Manually Stopped Acquisition\nPerformed Acquisitions: " + str(storage.storage.current_acquisition))
  480. _bif_stop_acquisition(board_id)
  481. else:
  482. _bif_start_acquisition(board_id)
  483. else:
  484. bk_toggle_wait_on_trigger(board_id)
  485. def _bif_stop_acquisition(board_id):
  486. """
  487. Stop acquisition
  488. This does stop the timer started by _bif_start_acquisition()
  489. :param board_id: id of the board do manipulate
  490. :return: -
  491. """
  492. board.get_board_status(board_id).acquisition = False
  493. storage.get_board_specific_storage(board_id).acquisition_progressbar.remove(0)
  494. storage.get_board_specific_storage(board_id).acquisition_timer.stop()
  495. # for elem in Elements.getElements("acquireTrigger_{}".format(board_id)):
  496. # if isinstance(elem, QtGui.QShortcut) or isinstance(elem, QtGui.QCheckBox):
  497. # continue
  498. # elem.setIcon(QtGui.QIcon(config.install_path + config.startIcon))
  499. # elem.setText(tr("Button", "Start Acquisition"))
  500. Elements.setEnabled('acquire_{}'.format(board_id), True)
  501. callbacks.callback('acquisition_stopped', board_id)
  502. def _bif_start_acquisition(board_id):
  503. """
  504. Start acquisition.
  505. This will start a timer to automatically acquire data.
  506. :param board_id: id of the board do manipulate
  507. :return: -
  508. """
  509. log(board_id=board_id, additional="Started Acquisition")
  510. board.get_board_status(board_id).acquisition = True
  511. Elements.setEnabled('acquire_{}'.format(board_id), False)
  512. # for elem in Elements.getElements("acquireTrigger_{}".format(board_id)):
  513. # if isinstance(elem, QtGui.QShortcut) or isinstance(elem, QtGui.QCheckBox):
  514. # continue
  515. # elem.setIcon(QtGui.QIcon(config.install_path + config.stopIcon))
  516. # elem.setText(tr("Button", "Stop Acquisition"))
  517. callbacks.callback('acquisition_started', board_id)
  518. storage.get_board_specific_storage(board_id).acquisition_timer = QtCore.QTimer()
  519. num_acquisitions = board.get_board_config(board_id).get("acquisition_count")
  520. storage.get_board_specific_storage(board_id).acquisition_progressbar = \
  521. _bif_ProgressBar(0, num_acquisitions, tr("sw", "Acquiring with board ")+str(board_id))
  522. # storage.storage.acquisition_progressbar = acquisition_progressbar
  523. # We increase already once because we do a single acquisition before the
  524. # timer is started, otherwise we have to wait until the timer fires the
  525. # first time.
  526. try:
  527. if isinstance(storage.storage.current_acquisition, dict):
  528. storage.storage.current_acquisition[board_id] = 1
  529. else:
  530. storage.storage.current_acquisition = {board_id: 1}
  531. except storage.StorageError:
  532. storage.storage.current_acquisition = {board_id: 1}
  533. _bif_read_data_and_save(board_id)
  534. if board.get_board_config(board_id).get("build_spectrograms"):
  535. spectrogram_dir = os.path.join(storage.storage.save_location, storage.storage.subdirname, "spectrograms_{:0.3f}".format(time.time()))
  536. os.makedirs(spectrogram_dir)
  537. _bif_iterate_spectrograms(board_id, spectrogram_dir) # TODO: not here?
  538. storage.get_board_specific_storage(board_id).acquisition_progressbar.setValue(storage.storage.current_acquisition[board_id])
  539. def on_timeout():
  540. '''Handler for the timeout of the acquisition timer. This does the acquisition'''
  541. if storage.storage.current_acquisition[board_id] < num_acquisitions:
  542. storage.storage.current_acquisition[board_id] += 1
  543. storage.get_board_specific_storage(board_id).acquisition_progressbar.setValue(storage.storage.current_acquisition[board_id])
  544. _bif_read_data_and_save(board_id)
  545. log(board_id=board_id, additional="Acquisition {:3d}\n".format(storage.storage.current_acquisition[board_id]) + "Filename: "+board.get_board_status(board_id).last_file.split('/')[-1])
  546. if board.get_board_config(board_id).get("build_spectrograms"):
  547. _bif_iterate_spectrograms(board_id, spectrogram_dir) # TODO: not here ?
  548. else:
  549. log(board_id=board_id, additional="Stopped Acquisition")
  550. _bif_stop_acquisition(board_id)
  551. storage.get_board_specific_storage(board_id).acquisition_progressbar.remove(0)
  552. storage.get_board_specific_storage(board_id).acquisition_timer.timeout.connect(on_timeout)
  553. storage.get_board_specific_storage(board_id).acquisition_timer.start(board.get_board_config(board_id).get('turns_wait_time') * 1000)
  554. def bk_single_read(board_id, info=""):
  555. """
  556. Perform a single read of data
  557. :param board_id: id of the board do manipulate
  558. :return:
  559. """
  560. if info != "":
  561. info += "\n"
  562. Elements.setEnabled("acquire_{}".format(board_id), False)
  563. _bif_read_data_and_save(board_id)
  564. log(board_id=board_id, additional="Single Read\n" + info + "Filename: "+board.get_board_status(board_id).last_file.split('/')[-1])
  565. Elements.setEnabled("acquire_{}".format(board_id), True)
  566. def _bif_set_continuous_read_active(board_id):
  567. """
  568. Enable continuous read
  569. :param board_id: id of the board do manipulate
  570. :return: -
  571. """
  572. Elements.setEnabled("acquireTrigger_{}".format(board_id), False)
  573. Elements.setEnabled("continuous_read_{}".format(board_id), False)
  574. board.get_board_status(board_id).continuous_read = True
  575. def _bif_set_continuous_read_inactive(board_id):
  576. """
  577. Disable continuous read
  578. :param board_id: id of the board do manipulate
  579. :return: -
  580. """
  581. if board.get_board_status(board_id).continuous_read:
  582. board.get_board_status(board_id).continuous_read = False
  583. storage.get_board_specific_storage(board_id).continuous_read_timer.stop()
  584. Elements.setEnabled('continuous_read_{}'.format(board_id), True)
  585. Elements.setEnabled('acquireTrigger_{}'.format(board_id), True)
  586. def bk_continuous_read(board_id, interval=100):
  587. """
  588. Toggle continuous read
  589. :param board_id: id of the board do manipulate
  590. :param interval: Time between two consecutive reads.
  591. :return: -
  592. """
  593. if not board.get_board_status(board_id).continuous_read:
  594. _bif_set_continuous_read_active(board_id)
  595. _bif_continuous_read(board_id, interval)
  596. else:
  597. _bif_set_continuous_read_inactive(board_id)
  598. def _bif_continuous_read(board_id, interval=None):
  599. """
  600. Perform continuous read based on a timer.
  601. :param interval:
  602. :return:
  603. """
  604. if interval is not None:
  605. # TODO: ueberall checken, dass der board specific storage verwendet wird
  606. storage.get_board_specific_storage(board_id).continuous_interval = interval
  607. storage.get_board_specific_storage(board_id).continuous_read_timer = QtCore.QTimer()
  608. logging.info("Start continuous read")
  609. def continuous_read_step():
  610. if board.get_board_status(board_id).continuous_read:
  611. _bif_read_data(board_id)
  612. storage.get_board_specific_storage(board_id).continuous_read_timer.singleShot(storage.storage.continuous_interval, continuous_read_step)
  613. storage.get_board_specific_storage(board_id).continuous_read_timer.singleShot(storage.storage.continuous_interval, continuous_read_step)
  614. def _bif_read_data(board_id, returnBack=False):
  615. """
  616. Reads data acquired by board.
  617. :param board_id: id of the board do manipulate
  618. :return:
  619. """
  620. board.pci.start = time.time()
  621. if PRINTDEBUG: print('{:4.3f} _bif_read_data start'.format(time.time()-board.pci.start))
  622. try:
  623. if board.get_board_config(board_id).get('pilot_bunch'):
  624. board.start_pilot_bunch_emulator(board_id)
  625. board.start_acquisition(board_id)
  626. try:
  627. board.wait_for_revolutions(board_id)
  628. except IndexError:
  629. error(0x002, "Unexpected output of pci for number of turns to observe. Returning")
  630. return None
  631. board.stop_acquisition(board_id)
  632. board.enable_transfer(board_id)
  633. if PRINTDEBUG: print('{:4.3f} _bif_read_data.read_data_to_variable'.format(time.time()-board.pci.start))
  634. data_raw = board.pci.read_data_to_variable(board_id)
  635. if PRINTDEBUG: print('{:4.3f} _bif_read_data.read_data_to_variable done'.format(time.time()-board.pci.start))
  636. if returnBack:
  637. if PRINTDEBUG: print('{:4.3f} _bif_read_data return data_raw'.format(time.time()-board.pci.start))
  638. return data_raw
  639. else:
  640. if PRINTDEBUG: print('{:4.3f} _bif_read_data call'.format(time.time()-board.pci.start))
  641. _bif_read_and_update_data_from_string(board_id, data_raw)
  642. if PRINTDEBUG: print('{:4.3f} _bif_read_data call done'.format(time.time()-board.pci.start))
  643. except board.BoardError as e:
  644. logging.error("Reading failed for board {}: {}".format(str(board_id), str(e)))
  645. if PRINTDEBUG: print('{:4.3f} _bif_read_data return None'.format(time.time()-board.pci.start))
  646. return None
  647. def bk_board_connected(board_id):
  648. """
  649. Interface to the board to check if it is connected.
  650. :param board_id: id of the board do manipulate
  651. :return: -
  652. """
  653. if not available_boards.has_boards:
  654. return False
  655. else:
  656. return board_id in available_boards.board_ids
  657. def bk_get_temperature(board_id):
  658. """
  659. Get Temperature from board and format it
  660. :param board_id: id of the board do manipulate
  661. :return: -
  662. """
  663. fpga_temp_raw_hex = board.pci.read(board_id, 1, '0x9110')[0]
  664. fpga_temp_raw_hex = fpga_temp_raw_hex[-3:]
  665. fpga_temp_raw_bin = '{0:012b}'.format(int(fpga_temp_raw_hex, 16))
  666. fpga_temp_encoded = board.get_dec_from_bits(fpga_temp_raw_bin, 9, 0)
  667. fpga_temp_celsius = '{0:2.2f}'.format(((fpga_temp_encoded * 503.975) / 1024) - 273.15)
  668. return fpga_temp_celsius
  669. backup_get_temp = bk_get_temperature
  670. def bk_check_for_board(board_id):
  671. """
  672. Check if board is connected
  673. Also overrides the bk_status_readout function with a function that does nothing (suppresses read attempts that
  674. generate errors - if no board is connected, there is nothing to read from)
  675. Also overrides the bk_get_temperature function as of the same reasons
  676. :param board_id: id of the board do manipulate
  677. :return: -
  678. """
  679. # global bk_status_readout, bk_get_temperature
  680. board_status = bk_board_connected(board_id)
  681. if board_status:
  682. if not hasattr(board.get_board_status(board_id), 'board_connected') or \
  683. not board.get_board_status(board_id).board_connected:
  684. globals()['bk_status_readout'] = backup_readout
  685. globals()['bk_get_temperature'] = backup_get_temp
  686. board.get_board_status(board_id).board_connected = True
  687. else:
  688. Elements.setEnabled('no_board_{}'.format(board_id), False)
  689. def do_nothing():
  690. pass
  691. def no_temp(board_id):
  692. return "-"
  693. globals()['bk_status_readout'] = do_nothing
  694. globals()['bk_get_temperature'] = no_temp
  695. board.get_board_status(board_id).board_connected = False
  696. if board_status == False:
  697. board.get_board_status(board_id).status_text = tr("sw", "Board {} not connected".format(board_id))
  698. elif board_status == None:
  699. board.get_board_status(board_id).status_text = tr("sw", "Software Interface not found")
  700. def bk_toggle_wait_on_trigger(board_id, num_of_acquisitions=None, skip=None, timeout=None, method=None):
  701. """
  702. Toggle waiting for trigger signal to acquire
  703. :param board_id: id of the board do manipulate
  704. :param num_of_acquisitions: number of acquisitions to wait for
  705. :param skip: how much trigger signals to skip between acquisitions
  706. :param timeout: the timeout for the pci to wait for date
  707. :param method: wait method to use
  708. 1 for wait in pci command and 2 for waiting until register is set that KAPTURE has read data
  709. NOTE: this also means that method 1 enables simultaneous read and write to dma and method 2 does write and
  710. read sequentially
  711. :return:
  712. """
  713. thread = storage.get_board_specific_storage(board_id).setdefault('TriggerThread', storage.ThreadStorage())
  714. if thread.running:
  715. Elements.getElements("acquireTrigger_{}".format(board_id))[0].setText(tr("Button", "Stopping Acquisition"))
  716. # FIXME: Button not updated otherwise:
  717. QtGui.qApp.processEvents()
  718. log(board_id=board_id, additional="Stop wait on trigger on board {}".format(board_id))
  719. thread.quit()
  720. thread.stop()
  721. # for elem in Elements.getElements("acquire_{}".format(board_id)):
  722. # if isinstance(elem, QtGui.QShortcut):
  723. # continue
  724. # elem.setText(tr("Button", "Stopping Acquisition"))
  725. # elem.setEnabled(False)
  726. else:
  727. log(board_id=board_id, additional="Start wait on trigger on board {}".format(board_id))
  728. # for elem in Elements.getElements("acquireTrigger_{}".format(board_id)):
  729. # if isinstance(elem, QtGui.QShortcut):
  730. # continue
  731. # elem.setIcon(QtGui.QIcon(config.install_path + config.stopIcon))
  732. # elem.setText(tr("Button", "Stop Acquisition"))
  733. Elements.setEnabled('acquire_{}'.format(board_id), False)
  734. callbacks.callback('acquisition_started', board_id)
  735. _bif_start_wait_on_trigger(board_id, num_of_acquisitions, skip, timeout, method)
  736. def _bif_start_wait_on_trigger(board_id, num_of_acquisitions=None, skip=None, timeout=None, method=None):
  737. """
  738. Start waiting on external acquisition trigger. This starts the timer
  739. :param board_id: id of the board do manipulate
  740. :param num_of_acquisitions: number of acquisitions to do
  741. :param count_label: Handle for the countlabel
  742. :param method: wait method to use
  743. 1 for wait in pci command and 2 for waiting until register is set that KAPTURE has read data
  744. NOTE: this also means that method 1 enables simultaneous read and write to dma and method 2 does write and
  745. read sequentially
  746. :return: -
  747. """
  748. # FIXme: This is a work around, for method 2 to work everytime a standard single read needs to be perform before acquisition is started
  749. board.acquire_data(board_id, '/dev/null')
  750. #with workaround no flush dema need, because it is done at end of board.acquire_data() anyway.
  751. #board.flush_dma(board_id) # TODO: really at begining and end of function necessary?
  752. thread = storage.get_board_specific_storage(board_id).setdefault('TriggerThread', storage.ThreadStorage())
  753. if thread.running:
  754. logging.info("Wait already running on board {}".format(board_id))
  755. return
  756. log(board_id=board_id, additional="Start wait on trigger")
  757. board.get_board_status(board_id).wait_on_trigger = True
  758. _bif_update_working_dir()
  759. if not num_of_acquisitions:
  760. num_of_acquisitions = bk_get_config(board_id, 'acquisition_count')
  761. if not skip:
  762. skip = bk_get_config(board_id, 'trigger_skip')
  763. if not timeout:
  764. timeout = bk_get_config(board_id, 'trigger_timeout')
  765. if not method:
  766. method = bk_get_config(board_id, 'trigger_method')
  767. storage.get_board_specific_storage(board_id).trigger_progressbar = \
  768. _bif_ProgressBar(0, num_of_acquisitions, tr("sw", "Acquiring with board ")+str(board_id))
  769. board.pci.write(board_id, hex(num_of_acquisitions), "9024")
  770. time.sleep(0.1)
  771. board.pci.write(board_id, hex(skip), "902C")
  772. # time.sleep(0.1)
  773. # board.pci.write(board_id, 'ff0', hex_mask='ff0') # TODO: This writes t/h 3/4 but enable_transfer etc do not
  774. # This seems to sometimes lead to segfaults of python it self. An Idea to prevent this
  775. # is to use copy.deepcopy in __init__. But there is no reason to think that that causes the problem. In fact
  776. # I don't have an idea why it crashes.
  777. # A possible reason could the os.rename be (or even the board.safe_call as that saves data to the disk) But I think
  778. # this is rather unlikely
  779. # ~~NOTE~~: the thread of calibration also triggered segfaults sometimes. But this seems to be miraculously solved.
  780. # Something that is likely to cause the problem is the log.debug in board.safe_call
  781. # Logging (using the logging module) is directly connected to the main thread and could cause problems
  782. class thread_wait_on_signal(QtCore.QObject):
  783. '''Class to run the wait on signal functionality in a thread'''
  784. countUpdate = QtCore.pyqtSignal(int)
  785. stopSignal = QtCore.pyqtSignal()
  786. finished = QtCore.pyqtSignal()
  787. liveplot = QtCore.pyqtSignal(int, str) # This has to be changed if board_id is no integer
  788. def __init__(self):
  789. super(thread_wait_on_signal, self).__init__()
  790. self.noa = None
  791. self.path = None
  792. self.timeout = None
  793. self._quit = False
  794. def init(self, num_of_acquisitions, path, timeout):
  795. '''initialise a new run'''
  796. self.noa = num_of_acquisitions
  797. self.path = path
  798. self.timeout = timeout
  799. self._quit = False
  800. # Elements.setEnabled('acquireTrigger_{}'.format(board_id), False) # exclude=Elements.getElements('wait_on_trigger_{}'.format(board_id)))
  801. def wait_rw_simul(self): # Method 1
  802. '''Wait simultaniously (with the pci command) for a trigger signal'''
  803. if board.get_board_config(board_id).is_KAPTURE2():
  804. board.pci.write(board_id, 'f00', hex_mask='f00') # TODO: This writes t/h 3/4 but enable_transfer etc do not
  805. else:
  806. board.pci.write(board_id, 'ff0', hex_mask='ff0') # TODO: This writes t/h 3/4 but enable_transfer etc do not
  807. for num_of_acq in range(self.noa):
  808. # def step():
  809. if self._quit:
  810. break
  811. timestamp = time.time()
  812. filename = os.path.join(self.path, '{:0.3f}.out'.format(timestamp))
  813. board.pci.read_data_to_file(board_id, filename=filename, timeout=(self.timeout*1000000))
  814. # rename with correct timestamp - last modified time
  815. self.countUpdate.emit(num_of_acq + 1)
  816. # file operations
  817. if not os.path.isfile(filename):
  818. error(0x001, "No File Created")
  819. continue
  820. newfile = os.path.join(self.path, 'trigger_{num:05}_{htime}_{unixtime}.out'.format(
  821. num=num_of_acq,
  822. htime=dt.fromtimestamp(os.path.getmtime(filename)).strftime('%Y-%m-%dT%Hh%Mm%Ss%f'),
  823. unixtime=np.round(timestamp, 3)
  824. ))
  825. os.rename(filename, newfile)
  826. if os.path.getsize(newfile) > 0:
  827. self.liveplot.emit(board_id, newfile)
  828. log(board_id=board_id, additional="Acquisition {:3d}\n".format(num_of_acq) + "Filename: " + newfile.split('/')[-1])
  829. else:
  830. logging.info("Acquired 0b, possible trigger timeout.")
  831. self.finished.emit()
  832. def wait_rw_seq(self): # Method 2
  833. '''Wait sequentially (in the gui) for a trigger signal'''
  834. for num_of_acq in range(self.noa):
  835. if board.get_board_config(board_id).is_KAPTURE2():
  836. board.pci.write(board_id, '00b00', hex_mask='C00') # enable readout
  837. else:
  838. board.pci.write(board_id, '00bf0', hex_mask='CF0') # enable readout
  839. pre_acq_num = board.pci.read(board_id, 1, '9034')[0]
  840. time_a = time.time()
  841. timeout = False
  842. while pre_acq_num == board.pci.read(board_id, 1, '9034')[0]:
  843. if time.time() - time_a > self.timeout:
  844. timeout = True
  845. break
  846. if self._quit:
  847. self.finished.emit()
  848. return
  849. if not timeout:
  850. if board.get_board_config(board_id).is_KAPTURE2():
  851. board.pci.write(board_id, '00000', hex_mask='800') # disable readout
  852. board.pci.write(board_id, '00700', hex_mask='C00') # enable transfer
  853. else:
  854. board.pci.write(board_id, '000f0', hex_mask='8F0') # disable readout
  855. board.pci.write(board_id, '007f0', hex_mask='CF0') # enable transfer
  856. filename = os.path.join(self.path,'{:0.3f}.out'.format(time.time()))
  857. board.pci.read_data_to_file(board_id, filename=filename, timeout=(self.timeout*1000000))
  858. # board.pci.write(board_id, '000f0', hex_mask='4F0') # disable transfer
  859. self.countUpdate.emit(copy.deepcopy(num_of_acq+1))
  860. if self._quit: # is this really the correct position? file is taken but not renamed!
  861. self.finished.emit()
  862. break
  863. if not os.path.isfile(filename):
  864. error(0x001, "No File Created")
  865. continue
  866. newfile = os.path.join(self.path, 'trigger_{num:05}_{htime}_{unixtime}.out'.format(
  867. num=num_of_acq,
  868. htime=dt.fromtimestamp(os.path.getmtime(filename)).strftime('%Y-%m-%dT%Hh%Mm%Ss%f'),
  869. unixtime=int(os.path.getmtime(filename))
  870. ))
  871. os.rename(filename, newfile)
  872. self.liveplot.emit(board_id, newfile)
  873. else:
  874. logging.info("Trigger timeout.")
  875. self.finished.emit()
  876. def quit(self):
  877. '''quit this thread'''
  878. self._quit = True
  879. def __del__(self):
  880. board.pci.write(board_id, '0', '9024')
  881. time.sleep(0.1)
  882. board.pci.write(board_id, '0', '902C')
  883. time.sleep(0.1)
  884. if board.get_board_config(board_id).is_KAPTURE2():
  885. board.pci.write(board_id, '300', hex_mask='f00') # TODO: This writes t/h 3/4 but enable_transfer etc do not
  886. else:
  887. board.pci.write(board_id, '3f0', hex_mask='ff0') # TODO: This writes t/h 3/4 but enable_transfer etc do not
  888. board.flush_dma(board_id)
  889. def finished():
  890. '''Handle the end of the thread'''
  891. board.pci.write(board_id, '0', '9024')
  892. time.sleep(0.1)
  893. board.pci.write(board_id, '0', '902C')
  894. time.sleep(0.1)
  895. if board.get_board_config(board_id).is_KAPTURE2():
  896. board.pci.write(board_id, '300', hex_mask='f00') # TODO: This writes t/h 3/4 but enable_transfer etc do not
  897. else:
  898. board.pci.write(board_id, '3f0', hex_mask='ff0') # TODO: This writes t/h 3/4 but enable_transfer etc do not
  899. board.flush_dma(board_id)
  900. thread.stop()
  901. board.get_board_status(board_id).wait_on_trigger = False
  902. storage.get_board_specific_storage(board_id).trigger_progressbar.remove(0)
  903. log(board_id=board_id, additional="Stop wait on trigger")
  904. Elements.setEnabled('acquire_{}'.format(board_id), True)
  905. callbacks.callback('acquisition_stopped', board_id)
  906. # for elem in Elements.getElements("acquire_{}".format(board_id)):
  907. # if isinstance(elem, QtGui.QShortcut):
  908. # continue
  909. # elem.setIcon(QtGui.QIcon(config.install_path + config.startIcon))
  910. # elem.setText(tr("Button", "Start Acquisition"))
  911. # elem.setEnabled(True)
  912. return
  913. # twt = thread_wait_on_signal(num_of_acquisitions, storage.storage.save_location + '/' + storage.storage.subdirname,
  914. # timeout)
  915. if not thread.is_registered():
  916. twt = thread_wait_on_signal()
  917. thread.register(twt)
  918. else:
  919. thread.disconnect('countUpdate', 'finished', 'liveplot')
  920. thread.init(num_of_acquisitions, os.path.join(storage.storage.save_location, storage.storage.subdirname), timeout)
  921. # reconnect signals to make sure the correct versions of methods are called
  922. thread.connect('countUpdate', storage.get_board_specific_storage(board_id).trigger_progressbar.setValue)
  923. thread.connect('finished', finished)
  924. thread.connect('liveplot', _bif_read_and_update_data_from_file)
  925. if method == 1:
  926. thread.start('wait_rw_simul')
  927. elif method == 2:
  928. thread.start('wait_rw_seq')
  929. else:
  930. raise ValueError("Wrong method")