app.py 8.3 KB

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