core.py 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570
  1. import calendar
  2. import datetime
  3. import os
  4. import re
  5. import sys
  6. import yaml
  7. import time
  8. import requests
  9. import shutil
  10. import tornado.ioloop
  11. import tornado.web
  12. import tornado.autoreload
  13. from shutil import copyfile
  14. from datetime import date
  15. from time import gmtime, strftime
  16. from tornado.escape import json_decode, json_encode, url_escape
  17. from threading import Timer
  18. root = os.path.dirname(__file__)
  19. with open("config.yaml", 'r') as stream:
  20. try:
  21. config = yaml.load(stream)
  22. except yaml.YAMLError as exc:
  23. print(exc)
  24. if config is None:
  25. print("Error: Empty configuration file.")
  26. sys.exit(1)
  27. class RepeatedTimer(object):
  28. def __init__(self, interval, function, *args, **kwargs):
  29. self._timer = None
  30. self.interval = interval
  31. self.function = function
  32. self.args = args
  33. self.kwargs = kwargs
  34. self.is_running = False
  35. self.start()
  36. def _run(self):
  37. self.is_running = False
  38. self.start()
  39. self.function(*self.args, **self.kwargs)
  40. def start(self):
  41. if not self.is_running:
  42. self._timer = Timer(self.interval, self._run)
  43. self._timer.start()
  44. self.is_running = True
  45. def stop(self):
  46. self._timer.cancel()
  47. self.is_running = False
  48. def setInterval(self, interval):
  49. self.interval = interval
  50. months = {
  51. 'Jan' : 1,
  52. 'Feb' : 2,
  53. 'Mar' : 3,
  54. 'Apr' : 4,
  55. 'May' : 5,
  56. 'Jun' : 6,
  57. 'Jul' : 7,
  58. 'Aug' : 8,
  59. 'Sep' : 9,
  60. 'Oct' : 10,
  61. 'Nov' : 11,
  62. 'Dec' : 12
  63. }
  64. def fetchDataADEI():
  65. with open("varname.yaml", 'r') as stream:
  66. try:
  67. varname = yaml.load(stream)
  68. except yaml.YAMLError as exc:
  69. print(exc)
  70. if varname is None:
  71. print("Error: Empty varname file.")
  72. return
  73. cache_data = {}
  74. curtime = int(time.time())
  75. time_image_range = str((curtime-3600)) + "-" + str(curtime)
  76. time_range = "-1"
  77. for param in varname:
  78. dest = config['server'] + config['script']
  79. url = dest + "?" + varname[param] + "&window=" + time_range
  80. data = requests.get(url,
  81. auth=(config['username'],
  82. config['password'])).content
  83. tmp_data = data.splitlines()[-1]
  84. last_value = tmp_data.split(",")[-1].strip()
  85. first_value = tmp_data.split(",")[-2].strip()
  86. try:
  87. test_x = float(last_value)
  88. except ValueError:
  89. last_value = ""
  90. try:
  91. time_buffer = first_value.split("-")
  92. time_buffer[1] = str(months[time_buffer[1]])
  93. first_value = "-".join(time_buffer)
  94. first_ts = calendar.timegm(datetime.datetime.strptime(first_value, "%d-%m-%y %H:%M:%S.%f").timetuple())
  95. except:
  96. first_ts = ""
  97. cache_data[param] = {'timestamp': first_ts, 'value': last_value}
  98. current_timestamp = strftime("%Y-%m-%d %H:%M:%S")
  99. cache_data['time'] = current_timestamp
  100. """TEMPORARY COMMENT OUT THIS FEATURE
  101. urlimage = (config['server'] + 'services/getimage.php' + "?" +
  102. varname[param] + "&window=" + time_image_range +
  103. "&frame_width=600&frame_height=400")
  104. image = requests.get(urlimage,
  105. auth=(config['username'],
  106. config['password']))
  107. with open("static/"+config['title'].lower()+"/images/" + param + ".png", 'wb') as handle:
  108. for chunk in image.iter_content():
  109. handle.write(chunk)
  110. """
  111. with open(".tmp.yaml", 'w') as stream_tmp:
  112. stream_tmp.write(yaml.dump(cache_data, default_flow_style=False))
  113. src_file = os.getcwd() + "/.tmp.yaml"
  114. dst_file = os.getcwd() + "/cache.yaml"
  115. shutil.copy(src_file, dst_file)
  116. class BaseHandler(tornado.web.RequestHandler):
  117. def get_current(self):
  118. return self.get_secure_cookie("user")
  119. class ListHandler(tornado.web.RequestHandler):
  120. def get(self):
  121. with open("cache.yaml", 'r') as stream:
  122. try:
  123. response = yaml.load(stream)
  124. except yaml.YAMLError as exc:
  125. print(exc)
  126. if response is None:
  127. response = {"error": "No data entry."}
  128. self.write(response)
  129. class StartHandler(tornado.web.RequestHandler):
  130. def get(self):
  131. print "Start fetchData"
  132. rt.start()
  133. class StopHandler(tornado.web.RequestHandler):
  134. def get(self):
  135. print "Stop fetchData"
  136. rt.stop()
  137. class SetTimerHandler(tornado.web.RequestHandler):
  138. def get(self, duration):
  139. print "Set interval"
  140. rt.setInterval(float(duration))
  141. class DesignerHandler(tornado.web.RequestHandler):
  142. def get(self):
  143. print "In designer mode."
  144. with open("cache.yaml", 'r') as stream:
  145. try:
  146. cache_data = yaml.load(stream)
  147. except yaml.YAMLError as exc:
  148. print(exc)
  149. with open("style.yaml", 'r') as stream:
  150. try:
  151. style_data = yaml.load(stream)
  152. except yaml.YAMLError as exc:
  153. print(exc)
  154. if style_data:
  155. index_data = list(set(cache_data) | set(style_data))
  156. else:
  157. index_data = cache_data
  158. if index_data is not None:
  159. index_data = sorted(index_data)
  160. data = {
  161. "cache": cache_data,
  162. "style": style_data,
  163. "index": index_data,
  164. }
  165. if "background" in config:
  166. data["background"] = config["background"]
  167. if "title" in config:
  168. data["title"] = config["title"]
  169. else:
  170. data["title"] = "BORA"
  171. self.render('designer.html', data=data)
  172. class VersionHandler(tornado.web.RequestHandler):
  173. def get(self):
  174. response = {'version': '1.0.0',
  175. 'last_build': date.today().isoformat()}
  176. self.write(response)
  177. class BackupHandler(tornado.web.RequestHandler):
  178. def post(self):
  179. backup_dst = os.getcwd() + "/backup/"
  180. fname = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
  181. os.makedirs(backup_dst + fname)
  182. copyfile(os.getcwd() + "/varname.yaml", backup_dst +
  183. fname + "/varname.yaml")
  184. copyfile(os.getcwd() + "/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. output.write(yaml.safe_dump(json_obj, encoding='utf-8',
  191. allow_unicode=True, default_flow_style=False))
  192. response = {"success": "Data entry inserted."}
  193. class StatusHandler(tornado.web.RequestHandler):
  194. def get(self):
  195. print "In status mode."
  196. with open("style.yaml", 'r') as stream:
  197. try:
  198. style_data = yaml.load(stream)
  199. except yaml.YAMLError as exc:
  200. print(exc)
  201. with open("varname.yaml", 'r') as vstream:
  202. try:
  203. varname_data = yaml.load(vstream)
  204. except yaml.YAMLError as exc:
  205. print(exc)
  206. with open("cache.yaml", 'r') as vstream:
  207. try:
  208. cache_data = yaml.load(vstream)
  209. except yaml.YAMLError as exc:
  210. print(exc)
  211. data = {
  212. "style": style_data,
  213. "varname": varname_data,
  214. "cache": cache_data
  215. }
  216. if "background" in config:
  217. data["background"] = config["background"]
  218. if "title" in config:
  219. data["title"] = config["title"]
  220. else:
  221. data["title"] = "BORA"
  222. if "server" in config:
  223. data["server"] = config["server"]
  224. else:
  225. data["server"] = "http://katrin.kit.edu/adei-katrin/"
  226. self.render('status.html', data=data)
  227. class UpdateHandler(tornado.web.RequestHandler):
  228. def get(self):
  229. print "Update Sensor Definition"
  230. new_data = {}
  231. rt.stop()
  232. with open("varname.yaml", 'r') as stream:
  233. try:
  234. cache_data = yaml.load(stream)
  235. except yaml.YAMLError as exc:
  236. print(exc)
  237. if config["type"] != "adei":
  238. print("Error: Wrong handler.")
  239. return
  240. for item in cache_data:
  241. tmp_data = cache_data[item]
  242. tmp_str = []
  243. tmp_store = []
  244. for adei_unit in tmp_data.split("&"):
  245. lhs, rhs = adei_unit.split("=")
  246. if lhs == "db_mask":
  247. tmp_str.append("db_mask=all")
  248. continue
  249. elif lhs == "db_server":
  250. db_server = rhs
  251. tmp_str.append(adei_unit)
  252. tmp_store.append(adei_unit)
  253. tmp_str.append("window=-1")
  254. query = "&".join(tmp_str)
  255. dest = config['server'] + config['script']
  256. url = dest + "?" + query
  257. data = requests.get(url, auth=(config['username'],
  258. config['password']))
  259. cr = data.content
  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. if config["type"] != "adei":
  299. print("Error: Wrong handler.")
  300. return
  301. dest = config['server'] + config['script']
  302. query_cmds = []
  303. query_cmds.append("db_server="+str(params['db_server']))
  304. query_cmds.append("db_name="+str(params['db_name']))
  305. query_cmds.append("db_group="+str(params['db_group']))
  306. query_cmds.append("db_mask=all")
  307. query_cmds.append("window=-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(url, auth=(config['username'], config['password']))
  313. cr = data.content
  314. cr = cr.splitlines()
  315. cr = ",".join(cr)
  316. cr = cr.split(",")
  317. # handling the inconsistency on naming convention
  318. match_token = params['sensor_name']
  319. if (params["db_server"] != "lara" and params["db_server"] != "hiu" and
  320. params["db_server"] != "safety-first"):
  321. # parameter name stored in ADEI with '-IST_Val' suffix
  322. if "MOD" in params['sensor_name']:
  323. match_token = params['sensor_name'] + "-MODUS_Val"
  324. elif "GRA" in params['sensor_name']:
  325. match_token = params['sensor_name'] + "-GRAD_Val"
  326. elif "RPO" in params['sensor_name']:
  327. match_token = params['sensor_name'] + "-ZUST_Val"
  328. elif "VYS" in params['sensor_name']:
  329. match_token = params['sensor_name'] + "-ZUST_Val"
  330. elif "HVS" in params['sensor_name']:
  331. match_token = params['sensor_name'] + "-ZUST_Val"
  332. elif "VAO" in params['sensor_name']:
  333. match_token = params['sensor_name'] + "-ZUST_Val"
  334. elif "MSS" in params['sensor_name']:
  335. match_token = params['sensor_name'] + "_Val"
  336. else:
  337. match_token = params['sensor_name'] + "-IST_Val"
  338. db_mask = None
  339. for i, item in enumerate(cr):
  340. if "[" and "]" in item.strip():
  341. lhs = re.match(r"[^[]*\[([^]]*)\]", item.strip()).groups()[0]
  342. if lhs == params['sensor_name']:
  343. db_mask = i - 1
  344. else:
  345. if item.strip() == match_token:
  346. db_mask = i - 1
  347. if db_mask is None:
  348. response = {"Error": "Cannot find variable on ADEI server."}
  349. self.write(response)
  350. return
  351. query_cmds = []
  352. query_cmds.append("db_server="+str(params['db_server']))
  353. query_cmds.append("db_name="+str(params['db_name']))
  354. query_cmds.append("db_group="+str(params['db_group']))
  355. query_cmds.append("db_mask="+str(db_mask))
  356. query = "&".join(query_cmds)
  357. # column name available
  358. # store in yaml file
  359. with open("varname.yaml", 'r') as stream:
  360. try:
  361. cache_data = yaml.load(stream)
  362. except yaml.YAMLError as exc:
  363. print(exc)
  364. if cache_data is None:
  365. cache_data = {}
  366. cache_data[sensor_name] = query
  367. else:
  368. if sensor_name not in cache_data:
  369. cache_data[sensor_name] = query
  370. else:
  371. response = {"Error":
  372. "Variable already available in varname file."}
  373. self.write(response)
  374. return
  375. with open("varname.yaml", 'w') as output:
  376. output.write(yaml.dump(cache_data, default_flow_style=False))
  377. response = {"success": "Data entry inserted."}
  378. self.write(response)
  379. class GetDataHandler(tornado.web.RequestHandler):
  380. def get(self):
  381. cache_data = None
  382. with open("cache.yaml", 'r') as stream:
  383. try:
  384. cache_data = yaml.load(stream)
  385. except yaml.YAMLError as exc:
  386. print(exc)
  387. if cache_data is None:
  388. cache_data = {}
  389. self.write(cache_data)
  390. class AuthLoginHandler(BaseHandler):
  391. def get(self):
  392. try:
  393. errormessage = self.get_argument("error")
  394. except:
  395. errormessage = ""
  396. print errormessage
  397. self.render("login.html", errormessage=errormessage)
  398. def check_permission(self, password, username):
  399. if (username == config["username"] and
  400. password == config["pw_designer"]):
  401. return True
  402. return False
  403. def post(self):
  404. username = self.get_argument("username", "")
  405. password = self.get_argument("password", "")
  406. auth = self.check_permission(password, username)
  407. if auth:
  408. self.set_current_user(username)
  409. print "In designer mode."
  410. with open("cache.yaml", 'r') as stream:
  411. try:
  412. cache_data = yaml.load(stream)
  413. except yaml.YAMLError as exc:
  414. print(exc)
  415. with open("style.yaml", 'r') as stream:
  416. try:
  417. style_data = yaml.load(stream)
  418. except yaml.YAMLError as exc:
  419. print(exc)
  420. if style_data:
  421. index_data = list(set(cache_data) | set(style_data))
  422. else:
  423. index_data = cache_data
  424. data = {
  425. "cache": cache_data,
  426. "style": style_data,
  427. "index": index_data,
  428. }
  429. if "background" in config:
  430. data["background"] = config["background"]
  431. self.render('designer.html', data=data)
  432. else:
  433. error_msg = (u"?error=" +
  434. url_escape("Login incorrect"))
  435. self.redirect(u"/auth/login/" + error_msg)
  436. def set_current_user(self, user):
  437. if user:
  438. self.set_secure_cookie("user", tornado.escape.json_encode(user))
  439. else:
  440. self.clear_cookie("user")
  441. print "Running..."
  442. rt = RepeatedTimer(int(config["polling"]), fetchDataADEI)
  443. application = tornado.web.Application([
  444. (r"/auth/login/?", AuthLoginHandler),
  445. (r"/"+config['title'].lower()+"/version/?", VersionHandler),
  446. (r"/"+config['title'].lower()+"/list/?", ListHandler),
  447. (r"/"+config['title'].lower()+"/start/?", StartHandler),
  448. (r"/"+config['title'].lower()+"/backup/?", BackupHandler),
  449. (r"/"+config['title'].lower()+"/stop/?", StopHandler),
  450. (r"/"+config['title'].lower()+"/designer/?", DesignerHandler),
  451. (r"/"+config['title'].lower()+"/status/?", StatusHandler),
  452. (r"/"+config['title'].lower()+"/save/?", SaveHandler),
  453. (r"/"+config['title'].lower()+"/update/?", UpdateHandler),
  454. (r"/"+config['title'].lower()+"/getdata/?", GetDataHandler),
  455. (r"/"+config['title'].lower()+"/timer/(?P<duration>[^\/]+)/?",
  456. SetTimerHandler),
  457. (r"/"+config['title'].lower()+"/add/(?P<db_server>[^\/]+)/?"
  458. "(?P<db_name>[^\/]+)/?(?P<db_group>[^\/]+)/?(?P<sensor_name>[^\/]+)?",
  459. AdeiKatrinHandler)
  460. ], debug=True, static_path=os.path.join(root, 'static'),
  461. js_path=os.path.join(root, 'js'), login_url="/auth/login",
  462. cookie_secret='L8LwECiNRxq2N0N2eGxx9MZlrpmuMEimlydNX/vt1LM=')
  463. if __name__ == "__main__":
  464. application.listen(int(config["port"]))
  465. tornado.autoreload.start()
  466. tornado.ioloop.IOLoop.instance().start()