Recipe.js 5.1 KB

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