ChefWorker.js 5.4 KB

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