app.py 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253
  1. #!/usr/bin/env python3
  2. import uuid
  3. import os
  4. import subprocess
  5. import zipfile
  6. import urllib.request
  7. import gzip
  8. from io import BytesIO
  9. import time
  10. import json
  11. import pathlib
  12. from flask import Flask, render_template, request, flash
  13. from threading import Thread, Lock
  14. queue_lock = Lock()
  15. '''
  16. Directory structure:
  17. - all paths relative to where the app starts
  18. - waf builds go into build
  19. - json queued build files go into buildqueue
  20. - resulting builds go into done
  21. - ardupilot is in ardupilot directory
  22. - templates in CustomBuild/templates
  23. '''
  24. def get_template(filename):
  25. return render_template(filename)
  26. def remove_directory_recursive(dirname):
  27. '''remove a directory recursively'''
  28. if not os.path.exists(dirname):
  29. return
  30. f = pathlib.Path(dirname)
  31. if f.is_file():
  32. f.unlink()
  33. else:
  34. for child in f.iterdir():
  35. rmtree(child)
  36. f.rmdir()
  37. def create_directory(dir_path):
  38. '''create a directory, don't fail if it exists'''
  39. pathlib.Path(dir_path).mkdir(parents=True, exist_ok=True)
  40. def run_build(taskfile, builddir):
  41. # run a build with parameters from task
  42. task = json.loads(open(taskfile).read())
  43. remove_directory_recursive(builddir)
  44. create_directory(builddir)
  45. subprocess.run(['git', 'submodule',
  46. 'update', '--recursive',
  47. '--force', '--init'])
  48. subprocess.run(['./waf', 'configure',
  49. '--board', task['board'],
  50. '--out', builddir,
  51. '--extra-hwdef', task['extra_hwdef']],
  52. cwd = task['sourcedir'])
  53. subprocess.run(['./waf', 'clean'], cwd = task['sourcedir'])
  54. subprocess.run(['./waf', task['vehicle']], cwd = task['sourcedir'])
  55. # background thread to check for queued build requests
  56. def check_queue():
  57. while(1):
  58. queue_lock.acquire()
  59. listing = os.listdir('buildqueue')
  60. queue_lock.release()
  61. if listing:
  62. for token in listing:
  63. builddir = 'build'
  64. buildqueue_dir = os.path.join('buildqueue', token)
  65. done_dir = os.path.join('done', token)
  66. # check if build exists
  67. if os.path.isdir(builddir):
  68. print("Build already exists")
  69. else:
  70. # run build and rename build directory
  71. f = open(os.path.join(buildqueue_dir, 'q.json'))
  72. task = json.load(f)
  73. run_build(os.path.join(buildqueue_dir, 'q.json'), builddir)
  74. os.rename(os.path.join(builddir, task['board']), done_dir)
  75. # remove working files
  76. os.remove(os.path.join(buildqueue_dir, 'extra_hwdef.dat'))
  77. os.remove(os.path.join(buildqueue_dir, 'q.json'))
  78. os.rmdir(os.path.join(buildqueue_dir))
  79. print("Build successful")
  80. # define source and app directories
  81. sourcedir = os.path.abspath('ardupilot/')
  82. appdir = os.path.abspath(os.curdir)
  83. if not os.path.isdir('buildqueue'):
  84. os.mkdir('buildqueue')
  85. thread = Thread(target=check_queue, args=())
  86. thread.daemon = True
  87. thread.start()
  88. # Directory of this file
  89. this_path = os.path.dirname(os.path.realpath(__file__))
  90. # Where the user requested tile are stored
  91. output_path = os.path.join(this_path, '..', 'userRequestFirmware')
  92. # Where the data database is
  93. tile_path = os.path.join(this_path, '..', 'data', 'tiles')
  94. # The output folder for all gzipped build requests
  95. app = Flask(__name__, static_url_path='/builds', static_folder=output_path, template_folder='templates')
  96. #def compressFiles(fileList, uuidkey):
  97. # create a zip file comprised of dat.gz tiles
  98. #zipthis = os.path.join(output_path, uuidkey + '.zip')
  99. # create output dirs if needed
  100. #try:
  101. # os.makedirs(output_path)
  102. #except OSError:
  103. # pass
  104. #try:
  105. # os.makedirs(tile_path)
  106. #except OSError:
  107. # pass
  108. #try:
  109. # with zipfile.ZipFile(zipthis, 'w') as terrain_zip:
  110. # for fn in fileList:
  111. # if not os.path.exists(fn):
  112. # #download if required
  113. # print("Downloading " + os.path.basename(fn))
  114. # g = urllib.request.urlopen('https://terrain.ardupilot.org/data/tiles/' +
  115. # os.path.basename(fn))
  116. # print("Downloaded " + os.path.basename(fn))
  117. # with open(fn, 'b+w') as f:
  118. # f.write(g.read())
  119. # need to decompress file and pass to zip
  120. # with gzip.open(fn, 'r') as f_in:
  121. # myio = BytesIO(f_in.read())
  122. # print("Decomp " + os.path.basename(fn))
  123. # and add file to zip
  124. # terrain_zip.writestr(os.path.basename(fn)[:-3], myio.read(),
  125. # compress_type=zipfile.ZIP_DEFLATED)
  126. #except Exception as ex:
  127. # print("Unexpected error: {0}".format(ex))
  128. # return False
  129. #return True
  130. @app.route('/')
  131. def index():
  132. return get_template('index.html')
  133. @app.route('/generate', methods=['GET', 'POST'])
  134. def generate():
  135. #if request.method == 'POST':
  136. features = []
  137. task = {}
  138. # fetch features from user input
  139. for i in range(1,8):
  140. value = request.form["option" + str(i)]
  141. features.append(value)
  142. undefine = "undef " + value.split()[1]
  143. features.insert(0,undefine)
  144. extra_hwdef = '\n'.join(features)
  145. #print("features: ", features)
  146. queue_lock.acquire()
  147. # create extra_hwdef.dat file and obtain md5sum
  148. file = open('buildqueue/extra_hwdef.dat',"w")
  149. file.write(extra_hwdef)
  150. file.close()
  151. md5sum = subprocess.check_output(['md5sum', 'buildqueue/extra_hwdef.dat'],
  152. encoding = 'utf-8')
  153. md5sum = md5sum[:len(md5sum)-29]
  154. os.remove('buildqueue/extra_hwdef.dat')
  155. # obtain git-hash of source
  156. git_hash = subprocess.check_output(['git', 'rev-parse', 'HEAD'],
  157. cwd = sourcedir,
  158. encoding = 'utf-8')
  159. git_hash = git_hash[:len(git_hash)-1]
  160. # create directories using concatenated token of git-hash and md5sum of hwdef
  161. token = git_hash + "-" + md5sum
  162. buildqueue_dir = os.path.join(appdir, 'buildqueue', token)
  163. if not os.path.isdir(buildqueue_dir):
  164. os.mkdir(buildqueue_dir)
  165. file = open(os.path.join(buildqueue_dir, 'extra_hwdef.dat'),"w")
  166. file.write(extra_hwdef)
  167. file.close()
  168. # fill dictionary of variables and create json file
  169. task['hwdef_md5sum'] = md5sum
  170. task['git_hash'] = git_hash
  171. task['sourcedir'] = sourcedir
  172. task['extra_hwdef'] = os.path.join(buildqueue_dir, 'extra_hwdef.dat')
  173. task['board'] = request.form["board"]
  174. task['vehicle'] = request.form["vehicle"]
  175. jfile = open(os.path.join(buildqueue_dir, 'q.json'), "w")
  176. jfile.write(json.dumps(task))
  177. jfile.close()
  178. queue_lock.release()
  179. #print(task)
  180. return get_template('building.html')
  181. # remove duplicates
  182. #filelist = list(dict.fromkeys(filelist))
  183. #print(filelist)
  184. #compress
  185. #success = compressFiles(filelist, uuidkey)
  186. # as a cleanup, remove any generated terrain older than 24H
  187. #for f in os.listdir(output_path):
  188. # if os.stat(os.path.join(output_path, f)).st_mtime < time.time() - 24 * 60 * 60:
  189. # print("Removing old file: " + str(os.path.join(output_path, f)))
  190. # os.remove(os.path.join(output_path, f))
  191. #if success:
  192. # print("Generated " + "/terrain/" + uuidkey + ".zip")
  193. # return get_template('generate.html', urlkey="/terrain/" + uuidkey + ".zip",
  194. # uuidkey=uuidkey, outsideLat=outsideLat)
  195. #else:
  196. # print("Failed " + "/terrain/" + uuidkey + ".zip")
  197. # return get_template('generate.html', error="Cannot generate terrain",
  198. # uuidkey=uuidkey)
  199. #else:
  200. # print("Bad get")
  201. # return get_template('generate.html', error="Need to use POST, not GET")
  202. @app.route('/home', methods=['POST'])
  203. def home():
  204. return get_template('index.html')
  205. if __name__ == "__main__":
  206. app.run()