index.html 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  1. <!doctype html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="utf-8">
  5. <title>ArduPilot Custom Firmware Builder</title>
  6. <meta name="description"
  7. content="ArduPilot Custom Firmware Builder. It allows to build custom ArduPilot firmware by selecting the wanted features.">
  8. <meta name="author" content="ArduPilot Team">
  9. <meta name="viewport" content="width=device-width, initial-scale=1">
  10. <!-- OG Meta Tags to improve the way the post looks when you share the page on LinkedIn, Facebook, Google+ -->
  11. <meta property="og:site_name" content="ArduPilot"/>
  12. <meta property="og:site" content=""/>
  13. <meta property="og:title" content="ArduPilot Custom Firmware Builder"/>
  14. <meta property="og:description"
  15. content="ArduPilot Custom Firmware Builder. It allows to build custom ArduPilot firmware by selecting the wanted features."/>
  16. <!-- description shown in the actual shared post -->
  17. <meta property="og:type" content="website">
  18. <meta property="og:url" content="https://custom.ardupilot.org/">
  19. <meta property="og:image" content="https://ardupilot.org/application/files/6315/7552/1962/ArduPilot-Motto.png">
  20. <link rel="stylesheet" type="text/css" href="{{ url_for('static',filename='styles/main.css') }}">
  21. <script type="text/javascript" src="{{ url_for('static', filename='js/CollapsibleLists.js')}}"></script>
  22. </head>
  23. <body onload="javascript: init()">
  24. <div id="main">
  25. <a href="https://custom.ardupilot.org/">
  26. <div id="logo">
  27. </div>
  28. </a>
  29. <div id="menu">
  30. <h2>ArduPilot Custom Firmware Builder</h2>
  31. <form action="/generate" method="post">
  32. <div id="vehicle_list">
  33. <label for="vehicle">Choose a vehicle:
  34. <select name="vehicle" id="vehicle" onchange="requestBranches(this.value);">
  35. {% for vehicle in get_vehicle_names() %}
  36. <option value="{{vehicle}}" {% if vehicle == get_default_vehicle_name() %} selected {% endif %}>{{vehicle}}</option>
  37. {% endfor %}
  38. </select>
  39. </label>
  40. </div>
  41. <p></p>
  42. <div id="branch_list"></div>
  43. <p></p>
  44. <div id="board_list"></div>
  45. <p></p>
  46. <div id="build_options"></div>
  47. <br>
  48. <input type="submit" value="Generate" id="submit" disabled>
  49. <input type="button" value="Reset option defaults" id="reset_def" onclick="requestDefaults();">
  50. </form>
  51. </div>
  52. <hr>
  53. <p>Exisiting builds (click on the status of a build to view it):</p>
  54. <div id="build_status"></div>
  55. <br/>
  56. <script>
  57. // this object contains defines and their corresponding labels for build options
  58. var define_labels = {};
  59. function init() {
  60. refresh_builds();
  61. requestBranches(document.getElementById("vehicle").value);
  62. }
  63. function refresh_builds() {
  64. var output = document.getElementById('build_status');
  65. var xhr = new XMLHttpRequest();
  66. xhr.open('GET', "/builds/status.html");
  67. // disable cache, thanks to: https://stackoverflow.com/questions/22356025/force-cache-control-no-cache-in-chrome-via-xmlhttprequest-on-f5-reload
  68. xhr.setRequestHeader("Cache-Control", "no-cache, no-store, max-age=0");
  69. xhr.setRequestHeader("Expires", "Tue, 01 Jan 1980 1:00:00 GMT");
  70. xhr.setRequestHeader("Pragma", "no-cache");
  71. xhr.onload = function () {
  72. if (xhr.status === 200) {
  73. output.innerHTML = xhr.responseText;
  74. }
  75. setTimeout(refresh_builds, 5000)
  76. }
  77. xhr.send();
  78. }
  79. function requestBoardsAndFeatures(branch) {
  80. var xhr = new XMLHttpRequest();
  81. xhr.open('GET', '/boards_and_features/'+branch); // branch consists of both remote and branch_name in format - remote/branch_name. e.g. upstream/master
  82. document.getElementById('board_list').innerHTML = "<p>Please wait. Fetching boards on branch "+branch+" ...";
  83. document.getElementById('build_options').innerHTML = "<p>Please wait. Fetching build options on branch "+branch+" ...";
  84. document.getElementById("submit").disabled = true;
  85. document.getElementById("reset_def").disabled = true;
  86. document.getElementById("branch").disabled = true;
  87. xhr.onload = function () {
  88. if (xhr.status == 200) {
  89. response_json = JSON.parse(xhr.response);
  90. boards = response_json['boards'];
  91. default_board = response_json['default_board'];
  92. features = response_json['features'];
  93. fillBoards(boards, default_board);
  94. fillBuildOptions(features);
  95. requestDefaults();
  96. document.getElementById("submit").disabled = false;
  97. document.getElementById("reset_def").disabled = false;
  98. } else {
  99. document.getElementById('board_list').innerHTML = "Something went wrong. Please try again. (Response says: "+xhr.response+")";
  100. document.getElementById('build_options').innerHTML = "";
  101. }
  102. document.getElementById("branch").disabled = false;
  103. }
  104. xhr.send();
  105. }
  106. function fillBoards(boards, default_board) {
  107. var output = document.getElementById('board_list');
  108. output.innerHTML = "<p>Please select the required options for the custom firmware build, then hit 'Generate'.</p>"+
  109. "<label for='board'>Choose a board: "+
  110. "<select name='board' id='board'>"+
  111. "</select>"+
  112. "</label>";
  113. boardList = document.getElementById("board")
  114. boards.forEach(board => {
  115. opt = document.createElement('option');
  116. opt.value = board;
  117. opt.innerHTML = board;
  118. opt.selected = (board === default_board);
  119. boardList.appendChild(opt);
  120. });
  121. }
  122. function fillBuildOptions(buildOptions) {
  123. var output = document.getElementById('build_options');
  124. output.innerHTML = "<label for='features'>Select Features: "+
  125. "<ul class='collapsibleList' id='outer_list'></ul>"+
  126. "</label>";
  127. outerList = document.getElementById("outer_list");
  128. define_labels = [];
  129. buildOptions.forEach(category => {
  130. outerListItem = document.createElement('li');
  131. outerListItem.innerHTML = category['name'];
  132. innerList = document.createElement('ul');
  133. category['options'].forEach(option => {
  134. innerListItem = document.createElement('li');
  135. checkBox = document.createElement('input');
  136. checkBox.type = "checkbox";
  137. checkBox.name = option['label'];
  138. checkBox.id = option['label'];
  139. checkBox.value = "1";
  140. checkBox.checked = (option['default'] == 1);
  141. checkBox.addEventListener('click', function handleClick(event) {
  142. dependencies(option['label'], option['dependency']);
  143. });
  144. innerListItem.appendChild(checkBox);
  145. innerListItem.appendChild(document.createTextNode(option['description']));
  146. innerList.appendChild(innerListItem);
  147. define_labels[option['define']] = option['label'];
  148. });
  149. outerListItem.appendChild(innerList);
  150. outerList.appendChild(outerListItem);
  151. });
  152. CollapsibleLists.apply();
  153. }
  154. function dependencies(f_label, f_dependency1) {
  155. cb = document.getElementById(f_label);
  156. switch (cb.name) {
  157. case f_label:
  158. const f_dependency = f_dependency1.split(",")
  159. var arrayLength = f_dependency.length;
  160. for (var i = 0; i < arrayLength; i++) {
  161. if (document.getElementById(f_dependency[i]).checked == false) {
  162. document.getElementById(f_dependency[i]).checked = cb.checked;
  163. }
  164. }
  165. break;
  166. }
  167. }
  168. function requestBranches(vehicle) {
  169. var xhr = new XMLHttpRequest();
  170. xhr.open('GET', '/get_allowed_branches/'+vehicle);
  171. document.getElementById("submit").disabled = true;
  172. document.getElementById("reset_def").disabled = true;
  173. document.getElementById("vehicle").disabled = true;
  174. xhr.onload = function () {
  175. if (xhr.status == 200) {
  176. response = JSON.parse(xhr.response);
  177. branches = response['branches'];
  178. default_branch = response['default_branch'];
  179. old_branch = document.getElementById("branch");
  180. fillBranches(branches);
  181. document.getElementById("submit").disabled = false;
  182. document.getElementById("reset_def").disabled = false;
  183. if (old_branch == null || old_branch.value != default_branch){
  184. // branch has changed
  185. // fetch boards and features again
  186. requestBoardsAndFeatures(default_branch);
  187. } else {
  188. // the new branch and the current branch are same
  189. // branches need not to be changed
  190. // Since the vehicle has changed, reset the defaults
  191. requestDefaults();
  192. }
  193. } else {
  194. document.getElementById('branch_list').innerHTML = "Something went wrong. Please try again. (Response says: "+xhr.response+")";
  195. }
  196. document.getElementById("vehicle").disabled = false;
  197. }
  198. xhr.send();
  199. }
  200. function fillBranches(branches) {
  201. var output = document.getElementById('branch_list');
  202. output.innerHTML = "<label for='branch'>Choose a branch: "+
  203. "<select name='branch' id='branch' onchange='requestBoardsAndFeatures(this.value);'>"+
  204. "</select>"+
  205. "</label>";
  206. branchList = document.getElementById("branch");
  207. branches.forEach(branch => {
  208. opt = document.createElement('option');
  209. opt.value = branch['full_name'];
  210. opt.innerHTML = branch['label'];
  211. opt.selected = (branch['full_name'] === default_branch);
  212. branchList.appendChild(opt);
  213. });
  214. }
  215. function requestDefaults() {
  216. branch = document.getElementById('branch').value;
  217. vehicle = document.getElementById('vehicle').value;
  218. board = document.getElementById('board').value;
  219. document.getElementById("reset_def").disabled = true;
  220. var xhr = new XMLHttpRequest();
  221. xhr.open('GET', '/get_defaults/'+vehicle+'/'+branch+'/'+board);
  222. xhr.onload = function () {
  223. if (xhr.status == 200) {
  224. parsed_response = JSON.parse(xhr.response);
  225. setDefaults(parsed_response);
  226. } else {
  227. console.log(xhr.response);
  228. }
  229. document.getElementById("reset_def").disabled = false;
  230. }
  231. xhr.send();
  232. }
  233. function setDefaults(defines_arr) {
  234. for (var i=0; i<defines_arr.length; i++) {
  235. var select_opt = (defines_arr[i][0] != '!');
  236. var sanitised_define = (select_opt ? defines_arr[i] : defines_arr[i].substring(1)); // this removes the leading '!' from define if it contatins
  237. var opt_label = define_labels[sanitised_define];
  238. if (opt_label != undefined) {
  239. document.getElementById(opt_label).checked = select_opt;
  240. }
  241. }
  242. return;
  243. }
  244. </script>
  245. </div>
  246. </body>
  247. <hr>
  248. <footer>Created by Will Piper, <a href=https://github.com/ArduPilot/CustomBuild>Source Code</a>.</footer>
  249. </html>