ChefWorker.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. /**
  2. * Web Worker to handle communications between the front-end and the core.
  3. *
  4. * @author n1474335 [n1474335@gmail.com]
  5. * @copyright Crown Copyright 2017
  6. * @license Apache-2.0
  7. */
  8. import "babel-polyfill";
  9. import Chef from "./Chef.js";
  10. import OperationConfig from "./config/MetaConfig.js";
  11. import OpModules from "./config/modules/Default.js";
  12. // Add ">" to the start of all log messages in the Chef Worker
  13. import loglevelMessagePrefix from "loglevel-message-prefix";
  14. loglevelMessagePrefix(log, {
  15. prefixes: [],
  16. staticPrefixes: [">"],
  17. prefixFormat: "%p"
  18. });
  19. // Set up Chef instance
  20. self.chef = new Chef();
  21. self.OpModules = OpModules;
  22. self.OperationConfig = OperationConfig;
  23. // Tell the app that the worker has loaded and is ready to operate
  24. self.postMessage({
  25. action: "workerLoaded",
  26. data: {}
  27. });
  28. /**
  29. * Respond to message from parent thread.
  30. *
  31. * Messages should have the following format:
  32. * {
  33. * action: "bake" | "silentBake",
  34. * data: {
  35. * input: {string},
  36. * recipeConfig: {[Object]},
  37. * options: {Object},
  38. * progress: {number},
  39. * step: {boolean}
  40. * } | undefined
  41. * }
  42. */
  43. self.addEventListener("message", function(e) {
  44. // Handle message
  45. const r = e.data;
  46. log.debug("ChefWorker receiving command '" + r.action + "'");
  47. switch (r.action) {
  48. case "bake":
  49. bake(r.data);
  50. break;
  51. case "silentBake":
  52. silentBake(r.data);
  53. break;
  54. case "docURL":
  55. // Used to set the URL of the current document so that scripts can be
  56. // imported into an inline worker.
  57. self.docURL = r.data;
  58. break;
  59. case "highlight":
  60. calculateHighlights(
  61. r.data.recipeConfig,
  62. r.data.direction,
  63. r.data.pos
  64. );
  65. break;
  66. case "setLogLevel":
  67. log.setLevel(r.data, false);
  68. break;
  69. default:
  70. break;
  71. }
  72. });
  73. /**
  74. * Baking handler
  75. *
  76. * @param {Object} data
  77. */
  78. async function bake(data) {
  79. // Ensure the relevant modules are loaded
  80. loadRequiredModules(data.recipeConfig);
  81. try {
  82. const response = await self.chef.bake(
  83. data.input, // The user's input
  84. data.recipeConfig, // The configuration of the recipe
  85. data.options, // Options set by the user
  86. data.progress, // The current position in the recipe
  87. data.step // Whether or not to take one step or execute the whole recipe
  88. );
  89. self.postMessage({
  90. action: "bakeComplete",
  91. data: response
  92. });
  93. } catch (err) {
  94. self.postMessage({
  95. action: "bakeError",
  96. data: err
  97. });
  98. }
  99. }
  100. /**
  101. * Silent baking handler
  102. */
  103. function silentBake(data) {
  104. const duration = self.chef.silentBake(data.recipeConfig);
  105. self.postMessage({
  106. action: "silentBakeComplete",
  107. data: duration
  108. });
  109. }
  110. /**
  111. * Checks that all required modules are loaded and loads them if not.
  112. *
  113. * @param {Object} recipeConfig
  114. */
  115. function loadRequiredModules(recipeConfig) {
  116. recipeConfig.forEach(op => {
  117. let module = self.OperationConfig[op.op].module;
  118. if (!OpModules.hasOwnProperty(module)) {
  119. log.info("Loading module " + module);
  120. self.sendStatusMessage("Loading module " + module);
  121. self.importScripts(self.docURL + "/" + module + ".js");
  122. self.sendStatusMessage("");
  123. }
  124. });
  125. }
  126. /**
  127. * Calculates highlight offsets if possible.
  128. *
  129. * @param {Object[]} recipeConfig
  130. * @param {string} direction
  131. * @param {Object} pos - The position object for the highlight.
  132. * @param {number} pos.start - The start offset.
  133. * @param {number} pos.end - The end offset.
  134. */
  135. function calculateHighlights(recipeConfig, direction, pos) {
  136. pos = self.chef.calculateHighlights(recipeConfig, direction, pos);
  137. self.postMessage({
  138. action: "highlightsCalculated",
  139. data: pos
  140. });
  141. }
  142. /**
  143. * Send status update to the app.
  144. *
  145. * @param {string} msg
  146. */
  147. self.sendStatusMessage = function(msg) {
  148. self.postMessage({
  149. action: "statusMessage",
  150. data: msg
  151. });
  152. };
  153. /**
  154. * Send an option value update to the app.
  155. *
  156. * @param {string} option
  157. * @param {*} value
  158. */
  159. self.setOption = function(option, value) {
  160. self.postMessage({
  161. action: "optionUpdate",
  162. data: {
  163. option: option,
  164. value: value
  165. }
  166. });
  167. };
  168. /**
  169. * Send register values back to the app.
  170. *
  171. * @param {number} opIndex
  172. * @param {number} numPrevRegisters
  173. * @param {string[]} registers
  174. */
  175. self.setRegisters = function(opIndex, numPrevRegisters, registers) {
  176. self.postMessage({
  177. action: "setRegisters",
  178. data: {
  179. opIndex: opIndex,
  180. numPrevRegisters: numPrevRegisters,
  181. registers: registers
  182. }
  183. });
  184. };