core.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517
  1. from __future__ import print_function
  2. import logging
  3. import calendar
  4. import datetime
  5. import os
  6. import re
  7. import sys
  8. import yaml
  9. import time
  10. import requests
  11. import shutil
  12. import tornado.ioloop
  13. import tornado.web
  14. import tornado.autoreload
  15. from shutil import copyfile
  16. from datetime import date
  17. from time import gmtime, strftime
  18. from tornado.escape import json_decode, json_encode, url_escape
  19. from threading import Timer
  20. import subprocess
  21. python_version = sys.version_info.major
  22. root = os.path.dirname(__file__)
  23. def setup_custom_logger(name):
  24. formatter = logging.Formatter(fmt='%(asctime)s %(levelname)-8s %(message)s',
  25. datefmt='%Y-%m-%d %H:%M:%S')
  26. handler = logging.FileHandler('log.txt', mode='w')
  27. handler.setFormatter(formatter)
  28. screen_handler = logging.StreamHandler(stream=sys.stdout)
  29. screen_handler.setFormatter(formatter)
  30. logger = logging.getLogger(name)
  31. logger.setLevel(logging.DEBUG)
  32. logger.addHandler(handler)
  33. logger.addHandler(screen_handler)
  34. return logger
  35. logger = setup_custom_logger('BORA')
  36. class RepeatedTimer(object):
  37. def __init__(self, interval, function, *args, **kwargs):
  38. self._timer = None
  39. self.interval = interval
  40. self.function = function
  41. self.args = args
  42. self.kwargs = kwargs
  43. self.is_running = False
  44. self.start()
  45. def _run(self):
  46. self.is_running = False
  47. self.start()
  48. self.function(*self.args, **self.kwargs)
  49. def start(self):
  50. if not self.is_running:
  51. self._timer = Timer(self.interval, self._run)
  52. self._timer.start()
  53. self.is_running = True
  54. def stop(self):
  55. self._timer.cancel()
  56. self.is_running = False
  57. def setInterval(self, interval):
  58. self.interval = interval
  59. months = {
  60. 'Jan' : 1,
  61. 'Feb' : 2,
  62. 'Mar' : 3,
  63. 'Apr' : 4,
  64. 'May' : 5,
  65. 'Jun' : 6,
  66. 'Jul' : 7,
  67. 'Aug' : 8,
  68. 'Sep' : 9,
  69. 'Oct' : 10,
  70. 'Nov' : 11,
  71. 'Dec' : 12
  72. }
  73. def fetchDataADEI():
  74. with open("varname.yaml", 'r') as stream:
  75. try:
  76. varname = yaml.load(stream)
  77. except yaml.YAMLError as exc:
  78. print(exc)
  79. if varname is None:
  80. print("Error: Empty varname file.")
  81. return
  82. cache_data = {}
  83. curtime = int(time.time())
  84. time_image_range = str((curtime-3600)) + "-" + str(curtime)
  85. time_range = "3600,-1"
  86. for param in varname:
  87. dest = os.environ["BORA_ADEI_SERVER"] + 'services/getdata.php'
  88. url = dest + "?" + varname[param] + "&window=" + time_range + "&experiment=*-*&rt=full&cache=1"
  89. data = requests.get(url,
  90. auth=(os.environ["BORA_ADEI_USERNAME"],
  91. os.environ["BORA_ADEI_PASSWORD"])).content
  92. if python_version == 3:
  93. data = data.decode("utf-8")
  94. if data == "":
  95. logger.info(str(param) + ': Empty data!')
  96. continue
  97. tmp_data = data.splitlines()[-1]
  98. if "ERROR" in tmp_data:
  99. logger.error(str(param) + ': Query')
  100. continue
  101. last_value = tmp_data.split(",")[-1].strip()
  102. first_value = tmp_data.split(",")[-2].strip()
  103. try:
  104. test_x = float(last_value)
  105. except ValueError:
  106. last_value = ""
  107. try:
  108. time_buffer = first_value.split("-")
  109. time_buffer[1] = str(months[time_buffer[1]])
  110. first_value = "-".join(time_buffer)
  111. first_ts = calendar.timegm(datetime.datetime.strptime(first_value, "%d-%m-%y %H:%M:%S.%f").timetuple())
  112. except:
  113. first_ts = ""
  114. cache_data[param] = {'timestamp': first_ts, 'value': last_value}
  115. current_timestamp = strftime("%Y-%m-%d %H:%M:%S")
  116. cache_data['time'] = current_timestamp
  117. with open("./bora/.tmp.yaml", 'w') as stream_tmp:
  118. stream_tmp.write(yaml.dump(cache_data, default_flow_style=False))
  119. src_file = os.getcwd() + "/bora/.tmp.yaml"
  120. dst_file = os.getcwd() + "/bora/cache.yaml"
  121. shutil.copy(src_file, dst_file)
  122. class BaseHandler(tornado.web.RequestHandler):
  123. def get_current(self):
  124. return self.get_secure_cookie("user")
  125. class ListHandler(tornado.web.RequestHandler):
  126. def get(self):
  127. with open("./bora/cache.yaml", 'r') as stream:
  128. try:
  129. response = yaml.load(stream)
  130. except yaml.YAMLError as exc:
  131. print(exc)
  132. if response is None:
  133. response = {"error": "No data entry."}
  134. self.write(response)
  135. class StartHandler(tornado.web.RequestHandler):
  136. def get(self):
  137. print("Start fetchData")
  138. rt.start()
  139. class StopHandler(tornado.web.RequestHandler):
  140. def get(self):
  141. print("Stop fetchData")
  142. rt.stop()
  143. class SetTimerHandler(tornado.web.RequestHandler):
  144. def get(self, duration):
  145. print("Set interval")
  146. rt.setInterval(float(duration))
  147. class DesignerHandler(tornado.web.RequestHandler):
  148. def get(self):
  149. print("In designer mode.")
  150. with open("./bora/cache.yaml", 'r') as stream:
  151. try:
  152. cache_data = yaml.load(stream)
  153. except yaml.YAMLError as exc:
  154. print(exc)
  155. with open("style.yaml", 'r') as stream2:
  156. try:
  157. style_data = yaml.load(stream2)
  158. except yaml.YAMLError as exc:
  159. print(exc)
  160. if style_data:
  161. index_data = list(set(cache_data) | set(style_data))
  162. else:
  163. index_data = cache_data
  164. if index_data is not None:
  165. index_data = sorted(index_data)
  166. data = {
  167. "cache": cache_data,
  168. "style": style_data,
  169. "index": index_data,
  170. }
  171. data["title"] = os.environ["BORA_TITLE"]
  172. self.render('designer.html', data=data)
  173. class VersionHandler(tornado.web.RequestHandler):
  174. def get(self):
  175. response = {'version': '1.0.0'}
  176. self.write(response)
  177. class BackupHandler(tornado.web.RequestHandler):
  178. def post(self):
  179. backup_dst = os.getcwd() + "/backup/"
  180. fname = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
  181. os.makedirs(backup_dst + fname)
  182. copyfile("varname.yaml", backup_dst +
  183. fname + "/varname.yaml")
  184. copyfile("style.yaml", backup_dst +
  185. fname + "/style.yaml")
  186. class SaveHandler(tornado.web.RequestHandler):
  187. def post(self):
  188. json_obj = json_decode(self.request.body)
  189. with open("style.yaml", 'w') as output:
  190. if python_version == 3:
  191. output.write(yaml.safe_dump(json_obj, allow_unicode=True, default_flow_style=False))
  192. else:
  193. output.write(yaml.safe_dump(json_obj, encoding='utf-8',
  194. allow_unicode=True, default_flow_style=False))
  195. response = {"success": "Data entry inserted."}
  196. class StatusHandler(tornado.web.RequestHandler):
  197. def get(self):
  198. print("In status mode.")
  199. with open("style.yaml", 'r') as stream:
  200. try:
  201. style_data = yaml.load(stream)
  202. except yaml.YAMLError as exc:
  203. print(exc)
  204. with open("varname.yaml", 'r') as vstream:
  205. try:
  206. varname_data = yaml.load(vstream)
  207. except yaml.YAMLError as exc:
  208. print(exc)
  209. if not os.path.isfile("./bora/cache.yaml"):
  210. print("BORA is loading data, please refresh the page again in a moment.")
  211. open("./bora/cache.yaml","w")
  212. with open("./bora/cache.yaml", 'r') as vstream:
  213. try:
  214. cache_data = yaml.load(vstream)
  215. except yaml.YAMLError as exc:
  216. print(exc)
  217. data = {
  218. "style": style_data,
  219. "varname": varname_data,
  220. "cache": cache_data
  221. }
  222. data["title"] = os.environ["BORA_TITLE"]
  223. data["server"] = os.environ["BORA_ADEI_SERVER"]
  224. self.render('status.html', data=data)
  225. class UpdateHandler(tornado.web.RequestHandler):
  226. def get(self):
  227. print("Update Sensor Definition")
  228. new_data = {}
  229. rt.stop()
  230. with open("varname.yaml", 'r') as stream:
  231. try:
  232. cache_data = yaml.load(stream)
  233. except yaml.YAMLError as exc:
  234. print(exc)
  235. for item in cache_data:
  236. tmp_data = cache_data[item]
  237. tmp_str = []
  238. tmp_store = []
  239. for adei_unit in tmp_data.split("&"):
  240. lhs, rhs = adei_unit.split("=")
  241. if lhs == "db_mask":
  242. tmp_str.append("db_mask=all")
  243. continue
  244. elif lhs == "db_server":
  245. db_server = rhs
  246. tmp_str.append(adei_unit)
  247. tmp_store.append(adei_unit)
  248. tmp_str.append("window=3600,-1")
  249. tmp_str.append("experiment=*-*")
  250. tmp_str.append("rt=full")
  251. tmp_str.append("cache=1")
  252. query = "&".join(tmp_str)
  253. dest = os.environ["BORA_ADEI_SERVER"] + 'services/getdata.php'
  254. url = dest + "?" + query
  255. data = requests.get(url, auth=(os.environ["BORA_ADEI_USERNAME"],
  256. os.environ["BORA_ADEI_PASSWORD"]))
  257. cr = data.content
  258. if python_version == 3:
  259. cr = cr.decode("utf-8")
  260. cr = cr.split(",")
  261. match_token = item
  262. if db_server != "lara" and db_server != "hiu":
  263. # parameter name stored in ADEI with '-IST_Val' suffix
  264. if "MOD" in item:
  265. match_token = item + "-MODUS_Val"
  266. elif "GRA" in item:
  267. match_token = item + "-GRAD_Val"
  268. elif "RPO" in item:
  269. match_token = item + "-ZUST_Val"
  270. elif "VYS" in item:
  271. match_token = item + "-ZUST_Val"
  272. elif "MSS" in item:
  273. match_token = item + "_Val"
  274. else:
  275. match_token = item + "-IST_Val"
  276. db_mask = None
  277. for i, iter_item in enumerate(cr):
  278. if match_token == iter_item.strip():
  279. db_mask = i - 1
  280. if db_mask is None:
  281. continue
  282. tmp_store.append("db_mask="+str(db_mask))
  283. new_data[item] = "&".join(tmp_store)
  284. with open("varname.yaml", 'w') as output:
  285. output.write(yaml.dump(new_data, default_flow_style=False))
  286. response = {"success": "Data entry inserted."}
  287. rt.start()
  288. class AdeiKatrinHandler(tornado.web.RequestHandler):
  289. def get(self, **params):
  290. sensor_name = str(params["sensor_name"])
  291. """
  292. {'db_group': u'320_KRY_Kryo_4K_CurLead',
  293. 'db_name': u'ControlSystem_CPS',
  294. 'sensor_name': u'320-RTP-8-1103',
  295. 'db_server': u'cscps',
  296. 'control_group': u'320_KRY_Kryo_3K'}
  297. """
  298. dest = os.environ["BORA_ADEI_SERVER"] + 'services/getdata.php'
  299. query_cmds = []
  300. query_cmds.append("db_server="+str(params['db_server']))
  301. query_cmds.append("db_name="+str(params['db_name']))
  302. query_cmds.append("db_group="+str(params['db_group']))
  303. query_cmds.append("db_mask=all")
  304. query_cmds.append("window=3600,-1")
  305. query_cmds.append("experiment=*-*")
  306. query_cmds.append("rt=full")
  307. query_cmds.append("cache=1")
  308. query = "&".join(query_cmds)
  309. url = dest + "?" + query
  310. # get the db_masks
  311. # store the query command in varname
  312. data = requests.get(
  313. url,
  314. auth=(os.environ["BORA_ADEI_USERNAME"],
  315. os.environ["BORA_ADEI_PASSWORD"])
  316. )
  317. cr = data.content
  318. if python_version == 3:
  319. cr = cr.decode("utf-8")
  320. cr = cr.splitlines()
  321. cr = ",".join(cr)
  322. cr = cr.split(",")
  323. # handling the inconsistency on naming convention
  324. match_token = params['sensor_name']
  325. if (params["db_server"] != "lara" and params["db_server"] != "hiu" and
  326. params["db_server"] != "safety-first"):
  327. # parameter name stored in ADEI with '-IST_Val' suffix
  328. if "MOD" in params['sensor_name']:
  329. match_token = params['sensor_name'] + "-MODUS_Val"
  330. elif "GRA" in params['sensor_name']:
  331. match_token = params['sensor_name'] + "-GRAD_Val"
  332. elif "RPO" in params['sensor_name']:
  333. match_token = params['sensor_name'] + "-ZUST_Val"
  334. elif "VYS" in params['sensor_name']:
  335. match_token = params['sensor_name'] + "-ZUST_Val"
  336. elif "HVS" in params['sensor_name']:
  337. match_token = params['sensor_name'] + "-ZUST_Val"
  338. elif "VAO" in params['sensor_name']:
  339. match_token = params['sensor_name'] + "-ZUST_Val"
  340. elif "MSS" in params['sensor_name']:
  341. match_token = params['sensor_name'] + "_Val"
  342. else:
  343. match_token = params['sensor_name'] + "-IST_Val"
  344. db_mask = None
  345. for i, item in enumerate(cr):
  346. if "[" and "]" in item.strip():
  347. lhs = re.match(r"[^[]*\[([^]]*)\]", item.strip()).groups()[0]
  348. if lhs == params['sensor_name']:
  349. db_mask = i - 1
  350. else:
  351. if item.strip() == match_token:
  352. db_mask = i - 1
  353. if db_mask is None:
  354. response = {"Error": "Cannot find variable on ADEI server."}
  355. self.write(response)
  356. return
  357. query_cmds = []
  358. query_cmds.append("db_server="+str(params['db_server']))
  359. query_cmds.append("db_name="+str(params['db_name']))
  360. query_cmds.append("db_group="+str(params['db_group']))
  361. query_cmds.append("db_mask="+str(db_mask))
  362. query = "&".join(query_cmds)
  363. # column name available
  364. # store in yaml file
  365. with open("varname.yaml", 'r') as stream:
  366. try:
  367. cache_data = yaml.load(stream)
  368. except yaml.YAMLError as exc:
  369. print(exc)
  370. if cache_data is None:
  371. cache_data = {}
  372. cache_data[sensor_name] = query
  373. else:
  374. if sensor_name not in cache_data:
  375. cache_data[sensor_name] = query
  376. else:
  377. response = {"Error":
  378. "Variable already available in varname file."}
  379. self.write(response)
  380. return
  381. with open("varname.yaml", 'w') as output:
  382. output.write(yaml.dump(cache_data, default_flow_style=False))
  383. response = {"success": "Data entry inserted."}
  384. self.write(response)
  385. class GetDataHandler(tornado.web.RequestHandler):
  386. def get(self):
  387. cache_data = None
  388. if not os.path.isfile("./bora/cache.yaml"):
  389. print("BORA is loading data, please refresh the page again in a moment.")
  390. open("./bora/cache.yaml","w")
  391. with open("./bora/cache.yaml", 'r') as stream:
  392. try:
  393. cache_data = yaml.load(stream)
  394. except yaml.YAMLError as exc:
  395. print(exc)
  396. if cache_data is None:
  397. cache_data = {}
  398. self.write(cache_data)
  399. print("Running...")
  400. rt = RepeatedTimer(int(os.environ["BORA_POLLING"]), fetchDataADEI)
  401. application = tornado.web.Application([
  402. (r"/version/?", VersionHandler),
  403. (r"/list/?", ListHandler),
  404. (r"/start/?", StartHandler),
  405. (r"/backup/?", BackupHandler),
  406. (r"/stop/?", StopHandler),
  407. (r"/designer/?", DesignerHandler),
  408. (r"/", StatusHandler),
  409. (r"/save/?", SaveHandler),
  410. (r"/update/?", UpdateHandler),
  411. (r"/getdata/?", GetDataHandler),
  412. (r"/timer/(?P<duration>[^\/]+)/?", SetTimerHandler),
  413. (r"/add/(?P<db_server>[^\/]+)/?"
  414. "(?P<db_name>[^\/]+)/?(?P<db_group>[^\/]+)/?(?P<sensor_name>[^\/]+)?",
  415. AdeiKatrinHandler)
  416. ], debug=True, static_path=os.path.join(root, 'static'),
  417. cookie_secret='L8LwECiNRxq2N0N2eGxx9MZlrpmuMEimlydNX/vt1LM=')
  418. if __name__ == "__main__":
  419. application.listen(int(os.environ["BORA_PORT"]))
  420. tornado.autoreload.start()
  421. tornado.ioloop.IOLoop.instance().start()