Recipe.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  1. var OperationConfig = require("../config/OperationConfig.js"),
  2. Operation = require("./Operation.js");
  3. /**
  4. * The Recipe controls a list of Operations and the Dish they operate on.
  5. *
  6. * @author n1474335 [n1474335@gmail.com]
  7. * @copyright Crown Copyright 2016
  8. * @license Apache-2.0
  9. *
  10. * @class
  11. * @param {Object} recipeConfig
  12. */
  13. var Recipe = module.exports = function(recipeConfig) {
  14. this.opList = [];
  15. if (recipeConfig) {
  16. this._parseConfig(recipeConfig);
  17. }
  18. };
  19. /**
  20. * Reads and parses the given config.
  21. *
  22. * @private
  23. * @param {Object} recipeConfig
  24. */
  25. Recipe.prototype._parseConfig = function(recipeConfig) {
  26. for (var c = 0; c < recipeConfig.length; c++) {
  27. var operationName = recipeConfig[c].op;
  28. var operationConfig = OperationConfig[operationName];
  29. var operation = new Operation(operationName, operationConfig);
  30. operation.setIngValues(recipeConfig[c].args);
  31. operation.setBreakpoint(recipeConfig[c].breakpoint);
  32. operation.setDisabled(recipeConfig[c].disabled);
  33. this.addOperation(operation);
  34. }
  35. };
  36. /**
  37. * Returns the value of the Recipe as it should be displayed in a recipe config.
  38. *
  39. * @returns {*}
  40. */
  41. Recipe.prototype.getConfig = function() {
  42. var recipeConfig = [];
  43. for (var o = 0; o < this.opList.length; o++) {
  44. recipeConfig.push(this.opList[o].getConfig());
  45. }
  46. return recipeConfig;
  47. };
  48. /**
  49. * Adds a new Operation to this Recipe.
  50. *
  51. * @param {Operation} operation
  52. */
  53. Recipe.prototype.addOperation = function(operation) {
  54. this.opList.push(operation);
  55. };
  56. /**
  57. * Adds a list of Operations to this Recipe.
  58. *
  59. * @param {Operation[]} operations
  60. */
  61. Recipe.prototype.addOperations = function(operations) {
  62. this.opList = this.opList.concat(operations);
  63. };
  64. /**
  65. * Set a breakpoint on a specified Operation.
  66. *
  67. * @param {number} position - The index of the Operation
  68. * @param {boolean} value
  69. */
  70. Recipe.prototype.setBreakpoint = function(position, value) {
  71. try {
  72. this.opList[position].setBreakpoint(value);
  73. } catch (err) {
  74. // Ignore index error
  75. }
  76. };
  77. /**
  78. * Remove breakpoints on all Operations in the Recipe up to the specified position. Used by Flow
  79. * Control Fork operation.
  80. *
  81. * @param {number} pos
  82. */
  83. Recipe.prototype.removeBreaksUpTo = function(pos) {
  84. for (var i = 0; i < pos; i++) {
  85. this.opList[i].setBreakpoint(false);
  86. }
  87. };
  88. /**
  89. * Returns true if there is an Flow Control Operation in this Recipe.
  90. *
  91. * @returns {boolean}
  92. */
  93. Recipe.prototype.containsFlowControl = function() {
  94. for (var i = 0; i < this.opList.length; i++) {
  95. if (this.opList[i].isFlowControl()) return true;
  96. }
  97. return false;
  98. };
  99. /**
  100. * Returns the index of the last Operation index that will be executed, taking into account disabled
  101. * Operations and breakpoints.
  102. *
  103. * @param {number} [startIndex=0] - The index to start searching from
  104. * @returns (number}
  105. */
  106. Recipe.prototype.lastOpIndex = function(startIndex) {
  107. var i = startIndex + 1 || 0,
  108. op;
  109. for (; i < this.opList.length; i++) {
  110. op = this.opList[i];
  111. if (op.isDisabled()) return i-1;
  112. if (op.isBreakpoint()) return i-1;
  113. }
  114. return i-1;
  115. };
  116. /**
  117. * Executes each operation in the recipe over the given Dish.
  118. *
  119. * @param {Dish} dish
  120. * @param {number} [startFrom=0] - The index of the Operation to start executing from
  121. * @returns {number} - The final progress through the recipe
  122. */
  123. Recipe.prototype.execute = function(dish, startFrom) {
  124. startFrom = startFrom || 0;
  125. var op, input, output, numJumps = 0;
  126. for (var i = startFrom; i < this.opList.length; i++) {
  127. op = this.opList[i];
  128. if (op.isDisabled()) {
  129. continue;
  130. }
  131. if (op.isBreakpoint()) {
  132. return i;
  133. }
  134. try {
  135. input = dish.get(op.inputType);
  136. if (op.isFlowControl()) {
  137. // Package up the current state
  138. var state = {
  139. "progress" : i,
  140. "dish" : dish,
  141. "opList" : this.opList,
  142. "numJumps" : numJumps
  143. };
  144. state = op.run(state);
  145. i = state.progress;
  146. numJumps = state.numJumps;
  147. } else {
  148. output = op.run(input, op.getIngValues());
  149. dish.set(output, op.outputType);
  150. }
  151. } catch (err) {
  152. var e = typeof err == "string" ? { message: err } : err;
  153. e.progress = i;
  154. if (e.fileName) {
  155. e.displayStr = op.name + " - " + e.name + " in " +
  156. e.fileName + " on line " + e.lineNumber +
  157. ".<br><br>Message: " + (e.displayStr || e.message);
  158. } else {
  159. e.displayStr = op.name + " - " + (e.displayStr || e.message);
  160. }
  161. throw e;
  162. }
  163. }
  164. return this.opList.length;
  165. };
  166. /**
  167. * Returns the recipe configuration in string format.
  168. *
  169. * @returns {string}
  170. */
  171. Recipe.prototype.toString = function() {
  172. return JSON.stringify(this.getConfig());
  173. };
  174. /**
  175. * Creates a Recipe from a given configuration string.
  176. *
  177. * @param {string} recipeStr
  178. */
  179. Recipe.prototype.fromString = function(recipeStr) {
  180. var recipeConfig = JSON.parse(recipeStr);
  181. this._parseConfig(recipeConfig);
  182. };