core.js 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. import async from "async";
  2. import config from "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 += 1;
  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. export default 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") this.jobQueue.pause();
  50. }
  51. getStatus() {
  52. return this.status;
  53. }
  54. setStage(stage) {
  55. this.stage = stage;
  56. }
  57. getStage() {
  58. return this.stage;
  59. }
  60. _initialize() {
  61. this.setStatus("INITIALIZING");
  62. this.initialize()
  63. .then(() => {
  64. this.setStatus("READY");
  65. this.moduleManager.onInitialize(this);
  66. })
  67. .catch(err => {
  68. console.error(err);
  69. this.setStatus("FAILED");
  70. this.moduleManager.onFail(this);
  71. });
  72. }
  73. log(...args) {
  74. const _arguments = Array.from(args);
  75. const type = _arguments[0];
  76. if (config.debug && config.debug.stationIssue === true && type === "STATION_ISSUE") {
  77. this.moduleManager.debugLogs.stationIssue.push(_arguments);
  78. return;
  79. }
  80. _arguments.splice(0, 1);
  81. const start = `|${this.name.toUpperCase()}|`;
  82. const numberOfTabsNeeded = 4 - Math.ceil(start.length / 8);
  83. _arguments.unshift(`${start}${Array(numberOfTabsNeeded).join("\t")}`);
  84. if (type === "INFO") {
  85. _arguments[0] += "\x1b[36m";
  86. _arguments.push("\x1b[0m");
  87. console.log.apply(null, _arguments);
  88. } else if (type === "ERROR") {
  89. _arguments[0] += "\x1b[31m";
  90. _arguments.push("\x1b[0m");
  91. console.error.apply(null, _arguments);
  92. }
  93. }
  94. registerJobs() {
  95. let props = [];
  96. let obj = this;
  97. do {
  98. props = props.concat(Object.getOwnPropertyNames(obj));
  99. // eslint-disable-next-line no-cond-assign
  100. } while ((obj = Object.getPrototypeOf(obj)));
  101. const jobNames = props.sort().filter(prop => typeof this[prop] === "function" && prop === prop.toUpperCase());
  102. jobNames.forEach(jobName => {
  103. this.jobStatistics[jobName] = {
  104. successful: 0,
  105. failed: 0,
  106. total: 0,
  107. averageTiming: new MovingAverageCalculator()
  108. };
  109. });
  110. }
  111. runJob(name, payload, options = { isQuiet: false, bypassQueue: false }) {
  112. const deferredPromise = new DeferredPromise();
  113. const job = { name, payload, onFinish: deferredPromise };
  114. if (
  115. config.debug &&
  116. config.debug.stationIssue === true &&
  117. config.debug.captureJobs &&
  118. config.debug.captureJobs.indexOf(name) !== -1
  119. ) {
  120. this.moduleManager.debugJobs.all.push(job);
  121. }
  122. if (options.bypassQueue) this._runJob(job, options, () => {});
  123. else {
  124. const priority = this.priorities[name] ? this.priorities[name] : 10;
  125. this.jobQueue.push({ job, options }, priority);
  126. }
  127. return deferredPromise.promise;
  128. }
  129. setModuleManager(moduleManager) {
  130. this.moduleManager = moduleManager;
  131. }
  132. _runJob(job, options, cb) {
  133. if (!options.isQuiet) this.log("INFO", `Running job ${job.name}`);
  134. const startTime = Date.now();
  135. this.runningJobs.push(job);
  136. const newThis = Object.assign(Object.create(Object.getPrototypeOf(this)), this);
  137. newThis.runJob = (...args) => {
  138. if (args.length === 1) args.push({});
  139. args[1].bypassQueue = true;
  140. return this.runJob(...args);
  141. };
  142. this[job.name]
  143. .apply(newThis, [job.payload])
  144. .then(response => {
  145. if (!options.isQuiet) this.log("INFO", `Ran job ${job.name} successfully`);
  146. this.jobStatistics[job.name].successful += 1;
  147. if (
  148. config.debug &&
  149. config.debug.stationIssue === true &&
  150. config.debug.captureJobs &&
  151. config.debug.captureJobs.indexOf(job.name) !== -1
  152. ) {
  153. this.moduleManager.debugJobs.completed.push({
  154. status: "success",
  155. job,
  156. response
  157. });
  158. }
  159. job.onFinish.resolve(response);
  160. })
  161. .catch(error => {
  162. this.log("INFO", `Running job ${job.name} failed`);
  163. this.jobStatistics[job.name].failed += 1;
  164. if (
  165. config.debug &&
  166. config.debug.stationIssue === true &&
  167. config.debug.captureJobs &&
  168. config.debug.captureJobs.indexOf(job.name) !== -1
  169. ) {
  170. this.moduleManager.debugJobs.completed.push({
  171. status: "error",
  172. job,
  173. error
  174. });
  175. }
  176. job.onFinish.reject(error);
  177. })
  178. .finally(() => {
  179. const endTime = Date.now();
  180. const executionTime = endTime - startTime;
  181. this.jobStatistics[job.name].total += 1;
  182. this.jobStatistics[job.name].averageTiming.update(executionTime);
  183. this.runningJobs.splice(this.runningJobs.indexOf(job), 1);
  184. cb();
  185. });
  186. }
  187. }