core.py 16 KB

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