core.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  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, options }, callback) => this._runJob(job, options, 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 = { isQuiet: false, bypassQueue: false }) {
  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) this._runJob(job, options, () => {});
  124. else {
  125. const priority = this.priorities[name] ? this.priorities[name] : 10;
  126. this.jobQueue.push({ job, options }, priority);
  127. }
  128. return deferredPromise.promise;
  129. }
  130. setModuleManager(moduleManager) {
  131. this.moduleManager = moduleManager;
  132. }
  133. _runJob(job, options, cb) {
  134. if (!options.isQuiet) this.log("INFO", `Running job ${job.name}`);
  135. const startTime = Date.now();
  136. this.runningJobs.push(job);
  137. const newThis = Object.assign(
  138. Object.create(Object.getPrototypeOf(this)),
  139. this
  140. );
  141. newThis.runJob = (...args) => {
  142. if (args.length === 2) args.push({});
  143. args[2].bypassQueue = true;
  144. return this.runJob.apply(this, args);
  145. };
  146. this[job.name]
  147. .apply(newThis, [job.payload])
  148. .then((response) => {
  149. if (!options.isQuiet) this.log("INFO", `Ran job ${job.name} successfully`);
  150. this.jobStatistics[job.name].successful++;
  151. if (config.debug && config.debug.stationIssue === true && config.debug.captureJobs && config.debug.captureJobs.indexOf(job.name) !== -1) {
  152. this.moduleManager.debugJobs.completed.push({ status: "success", job, response });
  153. }
  154. job.onFinish.resolve(response);
  155. })
  156. .catch((error) => {
  157. this.log("INFO", `Running job ${job.name} failed`);
  158. this.jobStatistics[job.name].failed++;
  159. if (config.debug && config.debug.stationIssue === true && config.debug.captureJobs && config.debug.captureJobs.indexOf(job.name) !== -1) {
  160. this.moduleManager.debugJobs.completed.push({ status: "error", job, error });
  161. }
  162. job.onFinish.reject(error);
  163. })
  164. .finally(() => {
  165. const endTime = Date.now();
  166. const executionTime = endTime - startTime;
  167. this.jobStatistics[job.name].total++;
  168. this.jobStatistics[job.name].averageTiming.update(
  169. executionTime
  170. );
  171. this.runningJobs.splice(this.runningJobs.indexOf(job), 1);
  172. cb();
  173. });
  174. }
  175. }
  176. module.exports = CoreClass;