api_server.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557
  1. import os
  2. import sys
  3. import pcilib
  4. import time
  5. import json
  6. from optparse import OptionParser
  7. from multiprocessing import Process
  8. if sys.version_info >= (3,0):
  9. from http.server import HTTPServer, BaseHTTPRequestHandler
  10. from socketserver import ThreadingMixIn
  11. else:
  12. from SocketServer import ThreadingMixIn
  13. from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
  14. class MultiThreadedHTTPServer(ThreadingMixIn, HTTPServer):
  15. pass
  16. class PcilibServerHandler(BaseHTTPRequestHandler):
  17. def __init__(s, pcilib, *args):
  18. s.pcilib = pcilib
  19. BaseHTTPRequestHandler.__init__(s, *args)
  20. def do_HEAD(s):
  21. s.send_response(200)
  22. s.send_header('content-type', 'application/json')
  23. s.end_headers()
  24. def do_GET(s):
  25. #run request in separate process
  26. p = Process(target=s.do_GET_worker, args=())
  27. p.start()
  28. p.join()
  29. def do_GET_worker(s):
  30. length = int(s.headers['Content-Length'])
  31. #deserialize input data
  32. data = json.loads(s.rfile.read(length).decode('utf-8'))
  33. if 'command' in data:
  34. command = data['command']
  35. if(command == 'help'):
  36. s.help(data)
  37. #elif(command == 'open'):
  38. # #check required arguments
  39. # if not 'device' in data:
  40. # s.error('message doesnt contains "device" field, '
  41. # 'which is required for "open" command', data)
  42. # return
  43. # #parse command arguments and convert them to string
  44. # device = str(data.get('device', None))
  45. # model = data.get('model', None)
  46. # if not model is None:
  47. # model = str(model)
  48. #
  49. # try:
  50. # s.openPcilibInstance(device, model)
  51. # except Exception as e:
  52. # s.error(str(e), data)
  53. # return
  54. #
  55. # #Success! Create and send reply
  56. # out = dict()
  57. # out['status'] = 'ok'
  58. # s.wrapMessageAndSend(out, data)
  59. elif(command == 'get_registers_list'):
  60. #parse command arguments and convert them to string
  61. bank = data.get('bank', None)
  62. if not bank is None:
  63. bank = str(bank)
  64. registers = dict()
  65. try:
  66. registers = s.pcilib.get_registers_list(bank)
  67. except Exception as e:
  68. s.error(str(e), data)
  69. return
  70. #Success! Create and send reply
  71. out = dict()
  72. out['status'] = 'ok'
  73. out['registers'] = registers
  74. s.wrapMessageAndSend(out, data)
  75. elif(command == 'get_register_info'):
  76. #check required arguments
  77. if not 'reg' in data:
  78. s.error('message doesnt contains "reg" field, '
  79. 'which is required for "get_register_info" command', data)
  80. return
  81. #parse command arguments and convert them to string
  82. reg = str(data.get('reg', None))
  83. bank = data.get('bank', None)
  84. if not bank is None:
  85. bank = str(bank)
  86. register = dict()
  87. try:
  88. register = s.pcilib.get_register_info(reg, bank)
  89. except Exception as e:
  90. s.error(str(e), data)
  91. return
  92. #Success! Create and send reply
  93. s.wrapMessageAndSend({'status': 'ok', 'register': register}, data)
  94. elif(command == 'get_property_list'):
  95. #parse command arguments and convert them to string
  96. branch = data.get('branch', None)
  97. if not branch is None:
  98. branch = str(branch)
  99. properties = dict()
  100. try:
  101. properties = s.pcilib.get_property_list(branch)
  102. except Exception as e:
  103. s.error(str(e), data)
  104. return
  105. #Success! Create and send reply
  106. out = dict()
  107. out['status'] = 'ok'
  108. out['properties'] = properties
  109. s.wrapMessageAndSend(out, data)
  110. elif(command == 'read_register'):
  111. #check required arguments
  112. if not 'reg' in data:
  113. s.error('message doesnt contains "reg" field, '
  114. 'which is required for "read_register" command', data)
  115. return
  116. #parse command arguments and convert them to string
  117. reg = str(data.get('reg', None))
  118. bank = data.get('bank', None)
  119. if(not bank is None):
  120. bank = str(bank)
  121. value = 0
  122. try:
  123. value = s.pcilib.read_register(reg, bank)
  124. except Exception as e:
  125. s.error(str(e), data)
  126. return
  127. #Success! Create and send reply
  128. out = dict()
  129. out['status'] = 'ok'
  130. out['value'] = value
  131. s.wrapMessageAndSend(out, data)
  132. elif(command == 'write_register'):
  133. #check required arguments
  134. if not 'reg' in data:
  135. s.error('message doesnt contains "reg" field, '
  136. 'which is required for "write_register" command', data)
  137. return
  138. if not 'value' in data:
  139. s.error('message doesnt contains "value" field, '
  140. 'which is required for "write_register" command', data)
  141. return
  142. #parse command arguments and convert them to string
  143. reg = str(data.get('reg', None))
  144. value = data.get('value', None)
  145. bank = data.get('bank', None)
  146. if(not bank is None):
  147. bank = str(bank)
  148. try:
  149. s.pcilib.write_register(value, reg, bank)
  150. except Exception as e:
  151. s.error(str(e), data)
  152. return
  153. #Success! Create and send reply
  154. s.wrapMessageAndSend({'status': 'ok'}, data)
  155. elif(command == 'get_property'):
  156. #check required arguments
  157. if not 'prop' in data:
  158. s.error('message doesnt contains "prop" field, '
  159. 'which is required for "get_property" command', data)
  160. return
  161. #parse command arguments and convert them to string
  162. prop = str(data.get('prop', None))
  163. value = 0
  164. try:
  165. value = s.pcilib.get_property(prop)
  166. except Exception as e:
  167. s.error(str(e), data)
  168. return
  169. #Success! Create and send reply
  170. out = dict()
  171. out['status'] = 'ok'
  172. out['value'] = value
  173. s.wrapMessageAndSend(out, data)
  174. elif(command == 'set_property'):
  175. #check required arguments
  176. if not 'prop' in data:
  177. s.error('message doesnt contains "prop" field, '
  178. 'which is required for "set_property" command', data)
  179. return
  180. if not 'value' in data:
  181. s.error('message doesnt contains "value" field, '
  182. 'which is required for "set_property" command', data)
  183. return
  184. #parse command arguments and convert them to string
  185. prop = str(data.get('prop', None))
  186. value = data.get('value', None)
  187. try:
  188. s.pcilib.set_property(value, prop)
  189. except Exception as e:
  190. s.error(str(e), data)
  191. return
  192. #Success! Create and send reply
  193. s.wrapMessageAndSend({'status': 'ok'}, data)
  194. elif(command == 'lock'):
  195. #check required arguments
  196. if not 'lock_id' in data:
  197. s.error('message doesnt contains "lock_id" field, '
  198. 'which is required for "lock" command', data)
  199. return
  200. #parse command arguments and convert them to string
  201. lock_id = str(data.get('lock_id'))
  202. try:
  203. s.pcilib.lock(lock_id)
  204. except Exception as e:
  205. s.error(str(e), data)
  206. return
  207. #Success! Create and send reply
  208. s.wrapMessageAndSend({'status': 'ok'}, data)
  209. elif(command == 'try_lock'):
  210. #check required arguments
  211. if not 'lock_id' in data:
  212. s.error('message doesnt contains "lock_id" field, '
  213. 'which is required for "try_lock" command', data)
  214. return
  215. #parse command arguments and convert them to string
  216. lock_id = str(data.get('lock_id'))
  217. try:
  218. s.pcilib.try_lock(lock_id)
  219. except Exception as e:
  220. s.error(str(e), data)
  221. return
  222. #Success! Create and send reply
  223. s.wrapMessageAndSend({'status': 'ok'}, data)
  224. elif(command == 'unlock'):
  225. #check required arguments
  226. if not 'lock_id' in data:
  227. s.error('message doesnt contains "lock_id" field, '
  228. 'which is required for "unlock" command', data)
  229. return
  230. #parse command arguments and convert them to string
  231. lock_id = str(data.get('lock_id'))
  232. try:
  233. s.pcilib.unlock(lock_id)
  234. except Exception as e:
  235. s.error(str(e), data)
  236. return
  237. #Success! Create and send reply
  238. s.wrapMessageAndSend({'status': 'ok'}, data)
  239. elif(command == 'get_scripts_list'):
  240. scripts = list()
  241. try:
  242. scripts = s.pcilib.get_scripts_list()
  243. except Exception as e:
  244. s.error(str(e), data)
  245. return
  246. #Success! Create and send reply
  247. s.wrapMessageAndSend({'status': 'ok', 'scripts': scripts}, data)
  248. elif(command == 'run_script'):
  249. #check required arguments
  250. if not 'script_name' in data:
  251. s.error('message doesnt contains "script_name" field, '
  252. 'which is required for "run_script" command', data)
  253. return
  254. #parse command arguments and convert them to string
  255. script_name = str(data.get('script_name'))
  256. value = data.get('value', None)
  257. out = None
  258. try:
  259. out = s.pcilib.run_script(script_name, value)
  260. except Exception as e:
  261. s.error(str(e), data)
  262. return
  263. #Success! Create and send reply
  264. if(type(out) == bytearray or type(out) == bytes):
  265. s.send_response(200)
  266. s.send_header('content-disposition', 'inline; filename=value')
  267. s.send_header('content-type', 'application/octet-stream')
  268. s.end_headers()
  269. s.wfile.write(out)
  270. else:
  271. s.wrapMessageAndSend({'status': 'ok', 'value': out}, data)
  272. #elif(command == 'lock_global'):
  273. # #check if global_lock already setted by server
  274. # try:
  275. # s.pcilib.lock_global()
  276. # except Exception as e:
  277. # s.error(str(e), data)
  278. # return
  279. #
  280. # #Success! Create and send reply
  281. # s.wrapMessageAndSend({'status': 'ok'}, data)
  282. #elif(command == 'unlock_global'):
  283. # try:
  284. # s.pcilib.unlock_global()
  285. # except Exception as e:
  286. # s.error(str(e), data)
  287. # return
  288. #
  289. # #Success! Create and send reply
  290. # s.wrapMessageAndSend({'status': 'ok'}, data)
  291. else:
  292. s.error('command "' + command + '" undefined', data)
  293. return
  294. else:
  295. s.error('message doesnt contains "command" field, which is required', data)
  296. return
  297. #open device context
  298. #def openPcilibInstance(s, device, model):
  299. # s.pcilib = pcipywrap.create_pcilib_instance(device, model)
  300. #Send help message
  301. def help(s, received_message = None):
  302. usage = str('Usage:\n'
  303. ' Server receive commands via http GET with json packet.\n'
  304. ' content-type should have value "application/json"\n'
  305. ' Server could handle only commands. to set command, you\n'
  306. ' should specify field "command" in packet with command name\n'
  307. ' List of commands:\n'
  308. '\n'
  309. ' command: help - Get help. This will return usage\n'
  310. '\n'
  311. ' command: open - Opens context of device. It will be reopened if already open.\n'
  312. ' required fields\n'
  313. ' device: - path to the device file [/dev/fpga0]\n'
  314. ' optional fields\n'
  315. ' model: - specifies the model of hardware, autodetected if doesnt exists\n'
  316. '\n'
  317. ' command: get_registers_list - Returns the list of registers provided by the hardware model.\n'
  318. ' optional fields\n'
  319. ' bank: - if set, only register within the specified bank will be returned\n'
  320. '\n'
  321. ' command: get_register_info - Returns the information about the specified register.\n'
  322. ' required fields\n'
  323. ' reg: - the name of the register\n'
  324. ' optional fields\n'
  325. ' bank: - if set, only register within the specified bank will be returned\n'
  326. '\n'
  327. ' command: get_property_list - Returns the list of properties available under the specified path.\n'
  328. ' optional fields\n'
  329. ' branch: - Path. If not set, will return the top-level properties\n'
  330. '\n'
  331. ' command: read_register - Reads the specified register.\n'
  332. ' required fields\n'
  333. ' reg: - the name of the register\n'
  334. ' optional fields\n'
  335. ' bank: - if set, only register within the specified bank will be processed\n'
  336. '\n'
  337. ' command: write_register - Writes to specified register.\n'
  338. ' required fields\n'
  339. ' reg: - the name of the register\n'
  340. ' value: - the register value to write. Should be int, float or string (with number)\n'
  341. ' optional fields\n'
  342. ' bank: - if set, only register within the specified bank will be processed\n'
  343. '\n'
  344. ' command: get_property - Reads / computes the property value.\n'
  345. ' required fields\n'
  346. ' prop: - full name including path\n'
  347. '\n'
  348. ' command: set_property - Writes the property value or executes the code associated with property.\n'
  349. ' required fields\n'
  350. ' prop: - full name including path\n'
  351. ' value: - the property value to write. Should be int, float or string (with number)\n'
  352. '\n'
  353. ' command: lock - function to acquire a lock, and wait till the lock can be acquire.\n'
  354. ' required fields\n'
  355. ' lock_id: - lock id\n'
  356. '\n'
  357. ' command: try_lock - this function will try to take a lock for the mutex pointed by \n'
  358. ' lockfunction to acquire a lock, but that returns immediatly if the\n'
  359. ' lock can\'t be acquired on first try\n'
  360. ' lock_id: - lock id\n'
  361. '\n'
  362. ' command: unlock - this function unlocks the lock.\n'
  363. ' required fields\n'
  364. ' lock_id: - lock id\n'
  365. '\n'
  366. ' command: get_scripts_list - Get aviable scripts with description\n'
  367. '\n'
  368. ' command: run_script - Run specified script\n'
  369. ' required fields\n'
  370. ' script_name: - script name (without extension)\n'
  371. ' value: - input value in json format\n'
  372. '\n'
  373. '\n')
  374. #send help as plain text
  375. s.send_response(200)
  376. s.send_header('content-type', 'text/plain')
  377. s.end_headers()
  378. if sys.version_info >= (3,0):
  379. s.wfile.write(bytes(usage, 'UTF-8'))
  380. else:
  381. s.wfile.write(usage)
  382. #Send error message with text description
  383. def error(s, info, received_message = None):
  384. out = dict()
  385. out['status'] = 'error'
  386. out['description'] = info
  387. out['note'] = 'send {"command" : "help"} to get help'
  388. s.wrapMessageAndSend(out, received_message, 400)
  389. def wrapMessageAndSend(s, message, received_message = None, response = 200):
  390. s.send_response(response)
  391. s.send_header('content-type', 'application/json')
  392. s.end_headers()
  393. if not received_message is None:
  394. message['received_message'] = received_message
  395. if sys.version_info >= (3,0):
  396. s.wfile.write(bytes(json.dumps(message), 'UTF-8'))
  397. else:
  398. s.wfile.write(json.dumps(message))
  399. class ApiServer(MultiThreadedHTTPServer):
  400. def __init__(self, device='/dev/fpga0', model=None, adress=('0.0.0.0', 9000)):
  401. #redirect logs to exeption
  402. pcilib.redirect_logs_to_exeption()
  403. #pass Pcipywrap to to server handler
  404. self.lib = pcilib.pcilib(device, model)
  405. def handler(*args):
  406. PcilibServerHandler(self.lib, *args)
  407. MultiThreadedHTTPServer.__init__(self, adress, handler)
  408. if __name__ == '__main__':
  409. #parce command line options
  410. parser = OptionParser()
  411. parser.add_option("-p", "--port", action="store",
  412. type="int", dest="port", default=9000,
  413. help="Set server port (9000)")
  414. parser.add_option("-d", "--device", action="store",
  415. type="string", dest="device", default=str('/dev/fpga0'),
  416. help="FPGA device (/dev/fpga0)")
  417. parser.add_option("-m", "--model", action="store",
  418. type="string", dest="model", default=None,
  419. help="Memory model (autodetected)")
  420. opts = parser.parse_args()[0]
  421. HOST_NAME = '0.0.0.0'
  422. PORT_NUMBER = opts.port
  423. MODEL = opts.model
  424. DEVICE = opts.device
  425. #start server
  426. httpd = ApiServer(DEVICE, MODEL, (HOST_NAME, PORT_NUMBER))
  427. print(time.asctime(), "Server Starts - %s:%s" % (HOST_NAME, PORT_NUMBER))
  428. try:
  429. httpd.serve_forever()
  430. except KeyboardInterrupt:
  431. pass
  432. httpd.server_close()
  433. print(time.asctime(), "Server Stops - %s:%s" % (HOST_NAME, PORT_NUMBER))