core.js 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. const async = require("async");
  2. const config = require("config");
  3. class DeferredPromise {
  4. constructor() {
  5. this.promise = new Promise((resolve, reject) => {
  6. this.reject = reject;
  7. this.resolve = resolve;
  8. });
  9. }
  10. }
  11. class MovingAverageCalculator {
  12. constructor() {
  13. this.count = 0;
  14. this._mean = 0;
  15. }
  16. update(newValue) {
  17. this.count++;
  18. const differential = (newValue - this._mean) / this.count;
  19. this._mean += differential;
  20. }
  21. get mean() {
  22. this.validate();
  23. return this._mean;
  24. }
  25. validate() {
  26. if (this.count === 0) throw new Error("Mean is undefined");
  27. }
  28. }
  29. class CoreClass {
  30. constructor(name) {
  31. this.name = name;
  32. this.status = "UNINITIALIZED";
  33. // this.log("Core constructor");
  34. this.jobQueue = async.priorityQueue(
  35. (job, callback) => this._runJob(job, callback),
  36. 10 // How many jobs can run concurrently
  37. );
  38. this.jobQueue.pause();
  39. this.runningJobs = [];
  40. this.priorities = {};
  41. this.stage = 0;
  42. this.jobStatistics = {};
  43. this.registerJobs();
  44. }
  45. setStatus(status) {
  46. this.status = status;
  47. this.log("INFO", `Status changed to: ${status}`);
  48. if (this.status === "READY") this.jobQueue.resume();
  49. else if (this.status === "FAIL" || this.status === "LOCKDOWN")
  50. this.jobQueue.pause();
  51. }
  52. getStatus() {
  53. return this.status;
  54. }
  55. setStage(stage) {
  56. this.stage = stage;
  57. }
  58. getStage() {
  59. return this.stage;
  60. }
  61. _initialize() {
  62. this.setStatus("INITIALIZING");
  63. this.initialize()
  64. .then(() => {
  65. this.setStatus("READY");
  66. this.moduleManager.onInitialize(this);
  67. })
  68. .catch((err) => {
  69. console.error(err);
  70. this.setStatus("FAILED");
  71. this.moduleManager.onFail(this);
  72. });
  73. }
  74. log() {
  75. let _arguments = Array.from(arguments);
  76. const type = _arguments[0];
  77. if (config.debug && config.debug.stationIssue === true && type === "STATION_ISSUE") {
  78. this.moduleManager.debugLogs.stationIssue.push(_arguments);
  79. return;
  80. }
  81. _arguments.splice(0, 1);
  82. const start = `|${this.name.toUpperCase()}|`;
  83. const numberOfTabsNeeded = 4 - Math.ceil(start.length / 8);
  84. _arguments.unshift(`${start}${Array(numberOfTabsNeeded).join("\t")}`);
  85. if (type === "INFO") {
  86. _arguments[0] = _arguments[0] + "\x1b[36m";
  87. _arguments.push("\x1b[0m");
  88. console.log.apply(null, _arguments);
  89. } else if (type === "ERROR") {
  90. _arguments[0] = _arguments[0] + "\x1b[31m";
  91. _arguments.push("\x1b[0m");
  92. console.error.apply(null, _arguments);
  93. }
  94. }
  95. registerJobs() {
  96. let props = [];
  97. let obj = this;
  98. do {
  99. props = props.concat(Object.getOwnPropertyNames(obj));
  100. } while ((obj = Object.getPrototypeOf(obj)));
  101. const jobNames = props
  102. .sort()
  103. .filter(
  104. (prop) =>
  105. typeof this[prop] == "function" &&
  106. prop === prop.toUpperCase()
  107. );
  108. jobNames.forEach((jobName) => {
  109. this.jobStatistics[jobName] = {
  110. successful: 0,
  111. failed: 0,
  112. total: 0,
  113. averageTiming: new MovingAverageCalculator(),
  114. };
  115. });
  116. }
  117. runJob(name, payload, options = {}) {
  118. let deferredPromise = new DeferredPromise();
  119. const job = { name, payload, onFinish: deferredPromise };
  120. if (config.debug && config.debug.stationIssue === true && config.debug.captureJobs && config.debug.captureJobs.indexOf(name) !== -1) {
  121. this.moduleManager.debugJobs.all.push(job);
  122. }
  123. if (options.bypassQueue) {
  124. this._runJob(job, () => {});
  125. } else {
  126. const priority = this.priorities[name] ? this.priorities[name] : 10;
  127. this.jobQueue.push(job, priority);
  128. }
  129. return deferredPromise.promise;
  130. }
  131. setModuleManager(moduleManager) {
  132. this.moduleManager = moduleManager;
  133. }
  134. _runJob(job, cb) {
  135. this.log("INFO", `Running job ${job.name}`);
  136. const startTime = Date.now();
  137. this.runningJobs.push(job);
  138. const newThis = Object.assign(
  139. Object.create(Object.getPrototypeOf(this)),
  140. this
  141. );
  142. newThis.runJob = (...args) => {
  143. if (args.length === 2) args.push({});
  144. args[2].bypassQueue = true;
  145. return this.runJob.apply(this, args);
  146. };
  147. this[job.name]
  148. .apply(newThis, [job.payload])
  149. .then((response) => {
  150. this.log("INFO", `Ran job ${job.name} successfully`);
  151. this.jobStatistics[job.name].successful++;
  152. if (config.debug && config.debug.stationIssue === true && config.debug.captureJobs && config.debug.captureJobs.indexOf(job.name) !== -1) {
  153. this.moduleManager.debugJobs.completed.push({ status: "success", job, response });
  154. }
  155. job.onFinish.resolve(response);
  156. })
  157. .catch((error) => {
  158. this.log("INFO", `Running job ${job.name} failed`);
  159. this.jobStatistics[job.name].failed++;
  160. if (config.debug && config.debug.stationIssue === true && config.debug.captureJobs && config.debug.captureJobs.indexOf(job.name) !== -1) {
  161. this.moduleManager.debugJobs.completed.push({ status: "error", job, error });
  162. }
  163. job.onFinish.reject(error);
  164. })
  165. .finally(() => {
  166. const endTime = Date.now();
  167. const executionTime = endTime - startTime;
  168. this.jobStatistics[job.name].total++;
  169. this.jobStatistics[job.name].averageTiming.update(
  170. executionTime
  171. );
  172. this.runningJobs.splice(this.runningJobs.indexOf(job), 1);
  173. cb();
  174. });
  175. }
  176. }
  177. module.exports = CoreClass;