pcilib_api_server.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573
  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_persistent(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. persistent = bool(data.get('persistent', False))
  218. try:
  219. if persistent:
  220. s.pcilib.try_lock_persistent(lock_id)
  221. else:
  222. s.pcilib.try_lock(lock_id)
  223. except Exception as e:
  224. s.error(str(e), data)
  225. return
  226. #Success! Create and send reply
  227. s.wrapMessageAndSend({'status': 'ok'}, data)
  228. elif(command == 'unlock'):
  229. #check required arguments
  230. if not 'lock_id' in data:
  231. s.error('message doesnt contains "lock_id" field, '
  232. 'which is required for "unlock" command', data)
  233. return
  234. #parse command arguments and convert them to string
  235. lock_id = str(data.get('lock_id'))
  236. persistent = bool(data.get('persistent', False))
  237. try:
  238. if persistent:
  239. s.pcilib.unlock_persistent(lock_id)
  240. else:
  241. s.pcilib.unlock(lock_id)
  242. except Exception as e:
  243. s.error(str(e), data)
  244. return
  245. #Success! Create and send reply
  246. s.wrapMessageAndSend({'status': 'ok'}, data)
  247. elif(command == 'get_scripts_list'):
  248. scripts = list()
  249. try:
  250. scripts = s.pcilib.get_scripts_list()
  251. except Exception as e:
  252. s.error(str(e), data)
  253. return
  254. #Success! Create and send reply
  255. s.wrapMessageAndSend({'status': 'ok', 'scripts': scripts}, data)
  256. elif(command == 'run_script'):
  257. #check required arguments
  258. if not 'script_name' in data:
  259. s.error('message doesnt contains "script_name" field, '
  260. 'which is required for "run_script" command', data)
  261. return
  262. #parse command arguments and convert them to string
  263. script_name = str(data.get('script_name'))
  264. value = data.get('value', None)
  265. out = None
  266. try:
  267. out = s.pcilib.run_script(script_name, value)
  268. except Exception as e:
  269. s.error(str(e), data)
  270. return
  271. #Success! Create and send reply
  272. if(type(out) == bytearray or type(out) == bytes):
  273. s.send_response(200)
  274. s.send_header('content-disposition', 'inline; filename=value')
  275. s.send_header('content-type', 'application/octet-stream')
  276. s.end_headers()
  277. s.wfile.write(out)
  278. else:
  279. s.wrapMessageAndSend({'status': 'ok', 'value': out}, data)
  280. #elif(command == 'lock_global'):
  281. # #check if global_lock already setted by server
  282. # try:
  283. # s.pcilib.lock_global()
  284. # except Exception as e:
  285. # s.error(str(e), data)
  286. # return
  287. #
  288. # #Success! Create and send reply
  289. # s.wrapMessageAndSend({'status': 'ok'}, data)
  290. #elif(command == 'unlock_global'):
  291. # try:
  292. # s.pcilib.unlock_global()
  293. # except Exception as e:
  294. # s.error(str(e), data)
  295. # return
  296. #
  297. # #Success! Create and send reply
  298. # s.wrapMessageAndSend({'status': 'ok'}, data)
  299. else:
  300. s.error('command "' + command + '" undefined', data)
  301. return
  302. else:
  303. s.error('message doesnt contains "command" field, which is required', data)
  304. return
  305. #open device context
  306. #def openPcilibInstance(s, device, model):
  307. # s.pcilib = pcipywrap.create_pcilib_instance(device, model)
  308. #Send help message
  309. def help(s, received_message = None):
  310. usage = str('Usage:\n'
  311. ' Server receive commands via http GET with json packet.\n'
  312. ' content-type should have value "application/json"\n'
  313. ' Server could handle only commands. to set command, you\n'
  314. ' should specify field "command" in packet with command name\n\n'
  315. ' If you use html server, you should call commands via GET request\n'
  316. ' like http://<server_host>:<port>/json/<command>?<arg1>=<arg_val1>&... \n'
  317. '\n'
  318. ' List of commands:\n'
  319. '\n'
  320. ' command: help - Get help. This will return usage\n'
  321. '\n'
  322. #' command: open - Opens context of device. It will be reopened if already open.\n'
  323. #' required fields\n'
  324. #' device: - path to the device file [/dev/fpga0]\n'
  325. #' optional fields\n'
  326. #' model: - specifies the model of hardware, autodetected if doesnt exists\n'
  327. #'\n'
  328. ' command: get_registers_list - Returns the list of registers provided by the hardware model.\n'
  329. ' optional fields\n'
  330. ' bank: - if set, only register within the specified bank will be returned\n'
  331. '\n'
  332. ' command: get_register_info - Returns the information about the specified register.\n'
  333. ' required fields\n'
  334. ' reg: - the name of the register\n'
  335. ' optional fields\n'
  336. ' bank: - if set, only register within the specified bank will be returned\n'
  337. '\n'
  338. ' command: get_property_list - Returns the list of properties available under the specified path.\n'
  339. ' optional fields\n'
  340. ' branch: - Path. If not set, will return the top-level properties\n'
  341. '\n'
  342. ' command: read_register - Reads the specified register.\n'
  343. ' required fields\n'
  344. ' reg: - the name of the register\n'
  345. ' optional fields\n'
  346. ' bank: - if set, only register within the specified bank will be processed\n'
  347. '\n'
  348. ' command: write_register - Writes to specified register.\n'
  349. ' required fields\n'
  350. ' reg: - the name of the register\n'
  351. ' value: - the register value to write. Should be int, float or string (with number)\n'
  352. ' optional fields\n'
  353. ' bank: - if set, only register within the specified bank will be processed\n'
  354. '\n'
  355. ' command: get_property - Reads / computes the property value.\n'
  356. ' required fields\n'
  357. ' prop: - full name including path\n'
  358. '\n'
  359. ' command: set_property - Writes the property value or executes the code associated with property.\n'
  360. ' required fields\n'
  361. ' prop: - full name including path\n'
  362. ' value: - the property value to write. Should be int, float or string (with number)\n'
  363. '\n'
  364. ' command: lock - function to acquire a lock, and wait till the lock can be acquire.\n'
  365. ' required fields\n'
  366. ' lock_id: - lock id\n'
  367. '\n'
  368. ' command: try_lock - this function will try to take a lock for the mutex pointed by \n'
  369. ' lockfunction to acquire a lock, but that returns immediatly if the\n'
  370. ' lock can\'t be acquired on first try\n'
  371. ' required fields\n'
  372. ' lock_id: - lock id\n'
  373. ' optional fields\n'
  374. ' persistent: - 1 - lock is persistent, 0 othervise (default: 0)\n'
  375. '\n'
  376. ' command: unlock - this function unlocks the lock.\n'
  377. ' required fields\n'
  378. ' lock_id: - lock id\n'
  379. ' optional fields\n'
  380. ' persistent: - 1 - lock is persistent, 0 othervise (default: 0)\n'
  381. '\n'
  382. ' command: get_scripts_list - Get aviable scripts with description\n'
  383. '\n'
  384. ' command: run_script - Run specified script\n'
  385. ' required fields\n'
  386. ' script_name: - script name (without extension)\n'
  387. ' value: - input value in json format\n'
  388. '\n'
  389. '\n')
  390. #send help as plain text
  391. s.send_response(200)
  392. s.send_header('content-type', 'text/plain')
  393. s.end_headers()
  394. if sys.version_info >= (3,0):
  395. s.wfile.write(bytes(usage, 'UTF-8'))
  396. else:
  397. s.wfile.write(usage)
  398. #Send error message with text description
  399. def error(s, info, received_message = None):
  400. out = dict()
  401. out['status'] = 'error'
  402. out['description'] = info
  403. out['note'] = 'send {"command" : "help"} to get help'
  404. s.wrapMessageAndSend(out, received_message, 400)
  405. def wrapMessageAndSend(s, message, received_message = None, response = 200):
  406. s.send_response(response)
  407. s.send_header('content-type', 'application/json')
  408. s.end_headers()
  409. if not received_message is None:
  410. message['received_message'] = received_message
  411. if sys.version_info >= (3,0):
  412. s.wfile.write(bytes(json.dumps(message), 'UTF-8'))
  413. else:
  414. s.wfile.write(json.dumps(message))
  415. class ApiServer(MultiThreadedHTTPServer):
  416. def __init__(self, device='/dev/fpga0', model=None, adress=('0.0.0.0', 9000)):
  417. #redirect logs to exeption
  418. pcilib.redirect_logs_to_exeption()
  419. #pass Pcipywrap to to server handler
  420. self.lib = pcilib.pcilib(device, model)
  421. def handler(*args):
  422. PcilibServerHandler(self.lib, *args)
  423. MultiThreadedHTTPServer.__init__(self, adress, handler)
  424. if __name__ == '__main__':
  425. #parce command line options
  426. parser = OptionParser()
  427. parser.add_option("-p", "--port", action="store",
  428. type="int", dest="port", default=9000,
  429. help="Set server port (9000)")
  430. parser.add_option("-d", "--device", action="store",
  431. type="string", dest="device", default=str('/dev/fpga0'),
  432. help="FPGA device (/dev/fpga0)")
  433. parser.add_option("-m", "--model", action="store",
  434. type="string", dest="model", default=None,
  435. help="Memory model (autodetected)")
  436. opts = parser.parse_args()[0]
  437. HOST_NAME = '0.0.0.0'
  438. PORT_NUMBER = opts.port
  439. MODEL = opts.model
  440. DEVICE = opts.device
  441. #start server
  442. httpd = ApiServer(DEVICE, MODEL, (HOST_NAME, PORT_NUMBER))
  443. print(time.asctime(), "Server Starts - %s:%s" % (HOST_NAME, PORT_NUMBER))
  444. try:
  445. httpd.serve_forever()
  446. except KeyboardInterrupt:
  447. pass
  448. httpd.server_close()
  449. print(time.asctime(), "Server Stops - %s:%s" % (HOST_NAME, PORT_NUMBER))