core.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  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. # print param
  79. dest = config['server'] + config['script']
  80. url = dest + "?" + varname[param] + "&window=" + time_range
  81. data = requests.get(url,
  82. auth=(config['username'],
  83. config['password'])).content
  84. #print data
  85. # Date/Time, p_string2
  86. # 11-Aug-17 14:49:32.446682, 0.495
  87. tmp_data = data.splitlines()[-1]
  88. #print tmp_data
  89. last_value = tmp_data.split(",")[-1].strip()
  90. first_value = tmp_data.split(",")[-2].strip()
  91. try:
  92. #print last_value
  93. test_x = float(last_value)
  94. except ValueError:
  95. last_value = ""
  96. #print last_value
  97. try:
  98. time_buffer = first_value.split("-")
  99. time_buffer[1] = str(months[time_buffer[1]])
  100. first_value = "-".join(time_buffer)
  101. first_ts = calendar.timegm(datetime.datetime.strptime(first_value, "%d-%m-%y %H:%M:%S.%f").timetuple())
  102. except:
  103. first_ts = ""
  104. cache_data[param] = {'timestamp': first_ts, 'value': last_value}
  105. current_timestamp = strftime("%Y-%m-%d %H:%M:%S")
  106. cache_data['time'] = current_timestamp
  107. urlimage = (config['server'] + 'services/getimage.php' + "?" +
  108. varname[param] + "&window=" + time_image_range +
  109. "&frame_width=600&frame_height=400")
  110. image = requests.get(urlimage,
  111. auth=(config['username'],
  112. config['password']))
  113. # print "static/tmp/"+config['title'].lower()+"/images/"
  114. if not os.path.exists("static/tmp/"+config['title'].lower()+"/images/"):
  115. os.makedirs("static/tmp/"+config['title'].lower()+"/images/")
  116. with open("static/tmp/"+config['title'].lower()+"/images/" + param + ".png", 'wb') as handle:
  117. for chunk in image.iter_content():
  118. handle.write(chunk)
  119. #print "/tmp/" + config['title'] + ".tmp.yaml"
  120. with open("/tmp/" + config['title'] + ".tmp.yaml", 'w') as stream_tmp:
  121. stream_tmp.write(yaml.dump(cache_data, default_flow_style=False))
  122. src_file = "/tmp/" + config['title'] + ".tmp.yaml"
  123. dst_file = "/tmp/" + config['title'] + "cache.yaml"
  124. shutil.copy(src_file, dst_file)
  125. class BaseHandler(tornado.web.RequestHandler):
  126. def get_current(self):
  127. return self.get_secure_cookie("user")
  128. class ListHandler(tornado.web.RequestHandler):
  129. def get(self):
  130. with open("/tmp/" + config['title'] + "cache.yaml", 'r') as stream:
  131. try:
  132. response = yaml.load(stream)
  133. except yaml.YAMLError as exc:
  134. print(exc)
  135. if response is None:
  136. response = {"error": "No data entry."}
  137. self.write(response)
  138. class StartHandler(tornado.web.RequestHandler):
  139. def get(self):
  140. print "Start fetchData"
  141. rt.start()
  142. class StopHandler(tornado.web.RequestHandler):
  143. def get(self):
  144. print "Stop fetchData"
  145. rt.stop()
  146. class SetTimerHandler(tornado.web.RequestHandler):
  147. def get(self, duration):
  148. print "Set interval"
  149. rt.setInterval(float(duration))
  150. class DesignerHandler(tornado.web.RequestHandler):
  151. def get(self):
  152. print "In designer mode."
  153. with open("/tmp/" + config['title'] + "cache.yaml", 'r') as stream:
  154. try:
  155. cache_data = yaml.load(stream)
  156. except yaml.YAMLError as exc:
  157. print(exc)
  158. with open("style.yaml", 'r') as stream:
  159. try:
  160. style_data = yaml.load(stream)
  161. except yaml.YAMLError as exc:
  162. print(exc)
  163. if style_data:
  164. index_data = list(set(cache_data) | set(style_data))
  165. else:
  166. index_data = cache_data
  167. data = {
  168. "cache": cache_data,
  169. "style": style_data,
  170. "index": sorted(index_data),
  171. }
  172. if "background" in config:
  173. data["background"] = config["background"]
  174. if "title" in config:
  175. data["title"] = config["title"]
  176. else:
  177. data["title"] = "BORA"
  178. self.render('designer.html', data=data)
  179. class VersionHandler(tornado.web.RequestHandler):
  180. def get(self):
  181. response = {'version': '0.0.1',
  182. 'last_build': date.today().isoformat()}
  183. self.write(response)
  184. class BackupHandler(tornado.web.RequestHandler):
  185. def post(self):
  186. backup_dst = os.getcwd() + "/backup/" + config['title'] + "/"
  187. fname = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
  188. os.makedirs(backup_dst + fname)
  189. copyfile(os.getcwd() + "/varname.yaml", backup_dst +
  190. fname + "/varname.yaml")
  191. copyfile(os.getcwd() + "/style.yaml", backup_dst +
  192. fname + "/style.yaml")
  193. class SaveHandler(tornado.web.RequestHandler):
  194. def post(self):
  195. json_obj = json_decode(self.request.body)
  196. with open("style.yaml", 'w') as output:
  197. output.write(yaml.safe_dump(json_obj, encoding='utf-8',
  198. allow_unicode=True, default_flow_style=False))
  199. response = {"success": "Data entry inserted."}
  200. class StatusHandler(tornado.web.RequestHandler):
  201. def get(self):
  202. print "In status mode."
  203. with open("style.yaml", 'r') as stream:
  204. try:
  205. style_data = yaml.load(stream)
  206. except yaml.YAMLError as exc:
  207. print(exc)
  208. with open("varname.yaml", 'r') as vstream:
  209. try:
  210. varname_data = yaml.load(vstream)
  211. except yaml.YAMLError as exc:
  212. print(exc)
  213. with open("/tmp/" + config['title'] + "cache.yaml", 'r') as vstream:
  214. try:
  215. cache_data = yaml.load(vstream)
  216. except yaml.YAMLError as exc:
  217. print(exc)
  218. data = {
  219. "style": style_data,
  220. "varname": varname_data,
  221. "cache": cache_data
  222. }
  223. if "background" in config:
  224. data["background"] = config["background"]
  225. if "title" in config:
  226. data["title"] = config["title"]
  227. else:
  228. data["title"] = "BORA"
  229. if "server" in config:
  230. data["server"] = config["server"]
  231. else:
  232. data["server"] = "http://katrin.kit.edu/adei-katrin/"
  233. self.render('status.html', data=data)
  234. class UpdateHandler(tornado.web.RequestHandler):
  235. def get(self):
  236. print "Update Sensor Definition"
  237. new_data = {}
  238. rt.stop()
  239. with open("varname.yaml", 'r') as stream:
  240. try:
  241. cache_data = yaml.load(stream)
  242. except yaml.YAMLError as exc:
  243. print(exc)
  244. if config["type"] != "adei":
  245. print("Error: Wrong handler.")
  246. return
  247. for item in cache_data:
  248. tmp_data = cache_data[item]
  249. tmp_str = []
  250. tmp_store = []
  251. for adei_unit in tmp_data.split("&"):
  252. lhs, rhs = adei_unit.split("=")
  253. if lhs == "db_mask":
  254. tmp_str.append("db_mask=all")
  255. continue
  256. elif lhs == "db_server":
  257. db_server = rhs
  258. tmp_str.append(adei_unit)
  259. tmp_store.append(adei_unit)
  260. tmp_str.append("window=-1")
  261. query = "&".join(tmp_str)
  262. dest = config['server'] + config['script']
  263. url = dest + "?" + query
  264. data = requests.get(url, auth=(config['username'],
  265. config['password']))
  266. cr = data.content
  267. cr = cr.split(",")
  268. match_token = item
  269. if db_server != "lara" and db_server != "hiu":
  270. # parameter name stored in ADEI with '-IST_Val' suffix
  271. if "MOD" in item:
  272. match_token = item + "-MODUS_Val"
  273. elif "GRA" in item:
  274. match_token = item + "-GRAD_Val"
  275. elif "RPO" in item:
  276. match_token = item + "-ZUST_Val"
  277. elif "VYS" in item:
  278. match_token = item + "-ZUST_Val"
  279. elif "MSS" in item:
  280. match_token = item + "_Val"
  281. else:
  282. match_token = item + "-IST_Val"
  283. db_mask = None
  284. for i, iter_item in enumerate(cr):
  285. if match_token == iter_item.strip():
  286. db_mask = i - 1
  287. if db_mask is None:
  288. continue
  289. tmp_store.append("db_mask="+str(db_mask))
  290. new_data[item] = "&".join(tmp_store)
  291. with open("varname.yaml", 'w') as output:
  292. output.write(yaml.dump(new_data, default_flow_style=False))
  293. response = {"success": "Data entry inserted."}
  294. rt.start()
  295. class AdeiKatrinHandler(tornado.web.RequestHandler):
  296. def get(self, **params):
  297. sensor_name = str(params["sensor_name"])
  298. """
  299. {'db_group': u'320_KRY_Kryo_4K_CurLead',
  300. 'db_name': u'ControlSystem_CPS',
  301. 'sensor_name': u'320-RTP-8-1103',
  302. 'db_server': u'cscps',
  303. 'control_group': u'320_KRY_Kryo_3K'}
  304. """
  305. if config["type"] != "adei":
  306. print("Error: Wrong handler.")
  307. return
  308. dest = config['server'] + config['script']
  309. query_cmds = []
  310. query_cmds.append("db_server="+str(params['db_server']))
  311. query_cmds.append("db_name="+str(params['db_name']))
  312. query_cmds.append("db_group="+str(params['db_group']))
  313. query_cmds.append("db_mask=all")
  314. query_cmds.append("window=-1")
  315. query = "&".join(query_cmds)
  316. url = dest + "?" + query
  317. # get the db_masks
  318. # store the query command in varname
  319. data = requests.get(url, auth=(config['username'], config['password']))
  320. cr = data.content
  321. cr = cr.splitlines()
  322. cr = ",".join(cr)
  323. cr = cr.split(",")
  324. # handling the inconsistency on naming convention
  325. match_token = params['sensor_name']
  326. if (params["db_server"] != "lara" and params["db_server"] != "hiu" and
  327. params["db_server"] != "safety-first"):
  328. # parameter name stored in ADEI with '-IST_Val' suffix
  329. if "MOD" in params['sensor_name']:
  330. match_token = params['sensor_name'] + "-MODUS_Val"
  331. elif "GRA" in params['sensor_name']:
  332. match_token = params['sensor_name'] + "-GRAD_Val"
  333. elif "RPO" in params['sensor_name']:
  334. match_token = params['sensor_name'] + "-ZUST_Val"
  335. elif "VYS" in params['sensor_name']:
  336. match_token = params['sensor_name'] + "-ZUST_Val"
  337. elif "HVS" in params['sensor_name']:
  338. match_token = params['sensor_name'] + "-ZUST_Val"
  339. elif "VAO" in params['sensor_name']:
  340. match_token = params['sensor_name'] + "-ZUST_Val"
  341. elif "MSS" in params['sensor_name']:
  342. match_token = params['sensor_name'] + "_Val"
  343. else:
  344. match_token = params['sensor_name'] + "-IST_Val"
  345. db_mask = None
  346. for i, item in enumerate(cr):
  347. if "[" and "]" in item.strip():
  348. lhs = re.match(r"[^[]*\[([^]]*)\]", item.strip()).groups()[0]
  349. if lhs == params['sensor_name']:
  350. db_mask = i - 1
  351. else:
  352. if item.strip() == match_token:
  353. db_mask = i - 1
  354. if db_mask is None:
  355. response = {"Error": "Cannot find variable on ADEI server."}
  356. self.write(response)
  357. return
  358. query_cmds = []
  359. query_cmds.append("db_server="+str(params['db_server']))
  360. query_cmds.append("db_name="+str(params['db_name']))
  361. query_cmds.append("db_group="+str(params['db_group']))
  362. query_cmds.append("db_mask="+str(db_mask))
  363. query = "&".join(query_cmds)
  364. # column name available
  365. # store in yaml file
  366. with open("varname.yaml", 'r') as stream:
  367. try:
  368. cache_data = yaml.load(stream)
  369. except yaml.YAMLError as exc:
  370. print(exc)
  371. if cache_data is None:
  372. cache_data = {}
  373. cache_data[sensor_name] = query
  374. else:
  375. if sensor_name not in cache_data:
  376. cache_data[sensor_name] = query
  377. else:
  378. response = {"Error":
  379. "Variable already available in varname file."}
  380. self.write(response)
  381. return
  382. with open("varname.yaml", 'w') as output:
  383. output.write(yaml.dump(cache_data, default_flow_style=False))
  384. response = {"success": "Data entry inserted."}
  385. self.write(response)
  386. class GetDataHandler(tornado.web.RequestHandler):
  387. def get(self):
  388. with open("/tmp/" + config['title'] + "cache.yaml", 'r') as stream:
  389. try:
  390. cache_data = yaml.load(stream)
  391. except yaml.YAMLError as exc:
  392. print(exc)
  393. if cache_data is None:
  394. cache_data = {}
  395. self.write(cache_data)
  396. class AuthLoginHandler(BaseHandler):
  397. def get(self):
  398. try:
  399. errormessage = self.get_argument("error")
  400. except:
  401. errormessage = ""
  402. print errormessage
  403. self.render("login.html", errormessage=errormessage)
  404. def check_permission(self, password, username):
  405. if (username == config["username"] and
  406. password == config["pw_designer"]):
  407. return True
  408. return False
  409. def post(self):
  410. username = self.get_argument("username", "")
  411. password = self.get_argument("password", "")
  412. auth = self.check_permission(password, username)
  413. if auth:
  414. self.set_current_user(username)
  415. print "In designer mode."
  416. with open("/tmp/" + config['title'] + "cache.yaml", 'r') as stream:
  417. try:
  418. cache_data = yaml.load(stream)
  419. except yaml.YAMLError as exc:
  420. print(exc)
  421. with open("style.yaml", 'r') as stream:
  422. try:
  423. style_data = yaml.load(stream)
  424. except yaml.YAMLError as exc:
  425. print(exc)
  426. if style_data:
  427. index_data = list(set(cache_data) | set(style_data))
  428. else:
  429. index_data = cache_data
  430. data = {
  431. "cache": cache_data,
  432. "style": style_data,
  433. "index": index_data,
  434. }
  435. if "background" in config:
  436. data["background"] = config["background"]
  437. self.render('designer.html', data=data)
  438. else:
  439. error_msg = (u"?error=" +
  440. url_escape("Login incorrect"))
  441. self.redirect(u"/auth/login/" + error_msg)
  442. def set_current_user(self, user):
  443. if user:
  444. self.set_secure_cookie("user", tornado.escape.json_encode(user))
  445. else:
  446. self.clear_cookie("user")
  447. print "Loading initial config..."
  448. fetchDataADEI()
  449. rt = RepeatedTimer(int(config["polling"]), fetchDataADEI)
  450. print "Starting Web Server..."
  451. application = tornado.web.Application([
  452. (r"/auth/login/?", AuthLoginHandler),
  453. (r"/"+config['title'].lower()+"/version/?", VersionHandler),
  454. (r"/"+config['title'].lower()+"/list/?", ListHandler),
  455. (r"/"+config['title'].lower()+"/start/?", StartHandler),
  456. (r"/"+config['title'].lower()+"/backup/?", BackupHandler),
  457. (r"/"+config['title'].lower()+"/stop/?", StopHandler),
  458. (r"/"+config['title'].lower()+"/designer/?", DesignerHandler),
  459. (r"/"+config['title'].lower()+"/status/?", StatusHandler),
  460. (r"/"+config['title'].lower()+"/save/?", SaveHandler),
  461. (r"/"+config['title'].lower()+"/update/?", UpdateHandler),
  462. (r"/"+config['title'].lower()+"/getdata/?", GetDataHandler),
  463. (r"/"+config['title'].lower()+"/timer/(?P<duration>[^\/]+)/?",
  464. SetTimerHandler),
  465. (r"/"+config['title'].lower()+"/add/(?P<db_server>[^\/]+)/?"
  466. "(?P<db_name>[^\/]+)/?(?P<db_group>[^\/]+)/?(?P<sensor_name>[^\/]+)?",
  467. AdeiKatrinHandler)
  468. ], debug=True, static_path=os.path.join(root, 'static'),
  469. js_path=os.path.join(root, 'js'), login_url="/auth/login",
  470. cookie_secret='L8LwECiNRxq2N0N2eGxx9MZlrpmuMEimlydNX/vt1LM=')
  471. if __name__ == "__main__":
  472. application.listen(int(config["port"]))
  473. tornado.autoreload.start()
  474. tornado.ioloop.IOLoop.instance().start()