core.py 18 KB

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