UNPKG

jahmin

Version:

A JavaScript framework to build browser friendly Human Machine Interfaces for automation

198 lines (197 loc) 8.6 kB
import { DataCommsEngine } from '../DataCommsEngine.js'; import { VarResponse, systemError, ErrorCodes, Actions, VarStatusCodes } from '../DataModels/Types.js'; export class JsonPollEngine extends DataCommsEngine { constructor(sysName, config) { super(sysName); this.readInterval_ms = config.readInterval_ms || 5000; this.host = config.host || ""; this.readPrefix = config.readPrefix || ""; this.writePrefix = config.writePrefix || ""; this.subscribePrefix = config.subscribePrefix || ""; this.unsubscribePrefix = config.unsubscribePrefix || ""; this.mode = config.mode || 'cors'; // no-cors, *cors, same-origin this.cache = config.cache || 'no-cache'; // *default, no-cache, reload, force-cache, only-if-cached this.credentials = config.credentials || 'same-origin'; // include, *same-origin, omit this.redirect = config.redirect || 'follow'; // manual, *follow, error this.referrerPolicy = config.referrerPolicy || 'no-referrer'; // no-referrer this.headers = config.headers || {}; this.readMethod = config.readMethod || "POST"; this.headers['Content-Type'] = 'application/json'; this.shortIntervalID = window.setInterval(this._read_in_intervals.bind(this), this.readInterval_ms); this.longIntervalID = null; this.failed_request_counter = 0; this.isLongPoll = false; } async _read_in_intervals() { let subscriber_list = Array.from(this.subscribedVar.keys()).map(v => this.deserializeSysObject(v)); if (subscriber_list.length === 0) return; let payload = this.packReadData(subscriber_list); let response = await this.netRequest(this.readPrefix, payload, Actions.Read); if (!response.success) this.handleFailedRequest(); else this.handleSuccessRequest(); let vars = this.unpackReadData(response, subscriber_list); this.UpdateVars(vars, VarStatusCodes.Subscribed, Actions.Read); } handleFailedRequest() { if (this.failed_request_counter > 10 && this.isLongPoll === false) { clearInterval(this.shortIntervalID); // setting interval to 2 min this.longIntervalID = window.setInterval(this._read_in_intervals.bind(this), 2 * 60 * 1000); this.isLongPoll = true; } this.failed_request_counter++; } handleSuccessRequest() { this.failed_request_counter = 0; if (this.isLongPoll === true) { clearInterval(this.longIntervalID); // resetting the short poll intervall this.shortIntervalID = window.setInterval(this._read_in_intervals.bind(this), this.readInterval_ms); this.isLongPoll = false; } } async Initialize() { return { success: true }; } async Subscribe(variables) { return await this.Read(variables); } async Unsubscribe(variables) { return variables.map((v) => { return new VarResponse(true, v.name, v.system, null); }); } async Write(targets, values) { let payload = this.packWriteData(targets, values); let response = await this.netRequest(this.writePrefix, payload, Actions.Write); return this.unpackWriteData(response, targets); } async Read(request) { let payload = this.packReadData(request); let response = await this.netRequest(this.readPrefix, payload, Actions.Read); return this.unpackReadData(response, request); } packWriteData(request, Values) { if (request.length !== Values.length) throw new Error("Bad data, Names and Values must contain samenumber of elements."); // note this engine does not support multiple systems let Names = request.map(v => v.name); return { names: Names, values: Values }; } unpackWriteData(response, request) { return this.unpackData(response, request, Actions.Write); } packReadData(targets) { // Note this specific engine does not support multiple system (just one) let Names = targets.map(t => t.name); return { names: Names }; } unpackReadData(response, request) { return this.unpackData(response, request, Actions.Read); } unpackData(response, request, action) { let variables = []; if (response.success) { // this engine does not support multiple systems let system = request[0].system; for (let node of response.data) { let var_idx = null; if (typeof node.Success === "undefined" || typeof node.Name !== "string" || node.Name === "" || typeof node.ErrorCode !== "string") { // something is wrong var_idx = new VarResponse(false, node.Name || "Uknown", system, null); var_idx.setError(ErrorCodes.BadData); this.manager.CreateAndDispatchError(system, ErrorCodes.BadData, node.Name || "Uknown", action); } else { var_idx = new VarResponse(node.Success, node.Name, system, node.Value); if (!node.Success) var_idx.setError(node.ErrorCode); } variables.push(var_idx); } } else { for (let v of request) { let var_idx = new VarResponse(false, v.name, v.system, null); var_idx.setError(response.error.code, response.error.message); variables.push(var_idx); } } return variables; } async netRequest(prefix, data, action = Actions.Unknown) { // faking response in case of Net Error let response = { ok: false, status: 1000, json: () => { } }; let _method = (action === Actions.Read) ? this.readMethod : 'POST'; let options = { method: _method, mode: this.mode, cache: this.cache, credentials: this.credentials, headers: this.headers, redirect: this.redirect, referrerPolicy: this.referrerPolicy, }; try { if (_method === "POST") { options = Object.assign({ body: JSON.stringify(data) }, options); } response = await fetch(this.host + '/' + prefix, options); } catch (e) { console.error(e.message); } let resp_data = null; let err = null; let status = response.status; if (response.ok) { try { resp_data = await response.json(); return { success: true, data: resp_data }; } catch (e) { err = { code: ErrorCodes.BadValue, message: "Failed to parse JSON response" }; return { success: false, data: null, error: err }; } } else { switch (status) { case (400): err = { code: ErrorCodes.BadReq, message: "Bad request, data is not understood by the server." }; break; case (401): err = { code: ErrorCodes.Unauthorized, message: "Unauthoriazed request." }; break; case (403): err = { code: ErrorCodes.Unauthorized, message: "Unauthoriazed request." }; break; case (404): err = { code: ErrorCodes.NotFound, message: `Url '${this.host}/${prefix}' not found ` }; break; case (500): err = { code: ErrorCodes.ServerError, message: "Server Error" }; break; case (1000): err = { code: ErrorCodes.NetError, message: "Network Error" }; break; default: err = { code: ErrorCodes.UnknownError, message: "Unknown Error, HTTP status code: " + status.toString() }; } let sys_err = new systemError(this.name, err.code, this.name, action); err.message = err.message; this.manager.DispatchError(sys_err); this.OnHTTPError(err); return { success: false, data: null, error: err }; } } OnHTTPError(err) { } }