UNPKG

@eva-ics/webengine

Version:
1,524 lines (1,523 loc) 76.9 kB
var j = Object.defineProperty; var P = (e, t, s) => t in e ? j(e, t, { enumerable: !0, configurable: !0, writable: !0, value: s }) : e[t] = s; var n = (e, t, s) => (P(e, typeof t != "symbol" ? t + "" : t, s), s), $ = (e, t, s) => { if (!t.has(e)) throw TypeError("Cannot " + s); }; var N = (e, t, s) => ($(e, t, "read from private field"), s ? s.call(e) : t.get(e)), x = (e, t, s) => { if (t.has(e)) throw TypeError("Cannot add the same private member more than once"); t instanceof WeakSet ? t.add(e) : t.set(e, s); }, B = (e, t, s, i) => ($(e, t, "write to private field"), i ? i.call(e, s) : t.set(e, s), s); import { Logger } from "bmat/log"; import { cookies } from "bmat/dom"; var m = Object.defineProperty, M = (e, t, s) => t in e ? m(e, t, { enumerable: !0, configurable: !0, writable: !0, value: s }) : e[t] = s, g = (e, t, s) => (M(e, typeof t != "symbol" ? t + "" : t, s), s), W = (e, t, s) => { if (!t.has(e)) throw TypeError("Cannot " + s); }, u = (e, t, s) => (W(e, t, "read from private field"), s ? s.call(e) : t.get(e)), E = (e, t, s) => { if (t.has(e)) throw TypeError("Cannot add the same private member more than once"); t instanceof WeakSet ? t.add(e) : t.set(e, s); }, h = (e, t, s, i) => (W(e, t, "write to private field"), i ? i.call(e, s) : t.set(e, s), s); class C { constructor() { g(this, "subscribers"), g(this, "subtopics"), g(this, "subtopics_by_regex"), g(this, "subtopics_any"), g(this, "sub_any"), this.subscribers = /* @__PURE__ */ new Set(), this.subtopics = /* @__PURE__ */ new Map(), this.subtopics_by_regex = [], this.sub_any = /* @__PURE__ */ new Set(); } isEmpty() { return this.subscribers.size === 0 && this.subtopics.size === 0 && this.subtopics_by_regex.length === 0 && this.subtopics_any === void 0 && this.sub_any.size === 0; } } var l, _, f, p, y; class I { constructor() { g(this, "subscriptions"), g(this, "subscribed_topics"), E(this, l, void 0), E(this, _, void 0), E(this, f, void 0), E(this, p, void 0), E(this, y, void 0), this.subscriptions = new C(), this.subscribed_topics = /* @__PURE__ */ new Map(), h(this, l, 0), h(this, _, "/"), h(this, p, /* @__PURE__ */ new Set(["?"])), h(this, y, /* @__PURE__ */ new Set(["*"])); } separator(t) { return h(this, _, t), this; } regexPrefix(t) { return h(this, f, t), this; } matchAny(t) { return Array.isArray(t) ? h(this, p, new Set(t)) : h(this, p, /* @__PURE__ */ new Set([t])), this; } wildcard(t) { return Array.isArray(t) ? h(this, y, new Set(t)) : h(this, y, /* @__PURE__ */ new Set([t])), this; } listClients() { return Array.from(this.subscriptions.subscribers); } listTopics(t) { return Array.from(this.subscribed_topics.get(t) || []); } isEmpty() { return this.subscribed_topics.size === 0; } registerClient(t) { return this.subscribed_topics.has(t) ? !1 : (this.subscribed_topics.set(t, /* @__PURE__ */ new Set()), !0); } unregisterClient(t) { const s = this.subscribed_topics.get(t); if (s === void 0) return !1; for (const i of s) d( this.subscriptions, i.split(u(this, _)), 0, t, u(this, y), u(this, p), u(this, f) ), h(this, l, u(this, l) - 1); return this.subscribed_topics.delete(t), !0; } subscribe(t, s) { const i = this.subscribed_topics.get(s); return i === void 0 ? !1 : (i.has(t) || (R( this.subscriptions, t.split(u(this, _)), 0, s, u(this, y), u(this, p), u(this, f) ), i.add(t), h(this, l, u(this, l) + 1)), !0); } unsubscribe(t, s) { const i = this.subscribed_topics.get(s); return i === void 0 ? !1 : (i.has(t) && (d( this.subscriptions, t.split(u(this, _)), 0, s, u(this, y), u(this, p), u(this, f) ), i.delete(t), h(this, l, u(this, l) - 1)), !0); } unsubscribeAll(t) { const s = this.subscribed_topics.get(t); if (s === void 0) return !1; for (const i of s) d( this.subscriptions, i.split(u(this, _)), 0, t, u(this, y), u(this, p), u(this, f) ); return h(this, l, u(this, l) - s.size), s.clear(), !0; } getSubscribers(t) { const s = /* @__PURE__ */ new Set(); return z( this.subscriptions, t.split(u(this, _)), 0, u(this, f), s ), s; } isSubscribed(t) { return v( this.subscriptions, t.split(u(this, _)), 0, u(this, f) ); } subscriptionCount() { return u(this, l); } clientCount() { return this.subscribed_topics.size; } } l = /* @__PURE__ */ new WeakMap(), _ = /* @__PURE__ */ new WeakMap(), f = /* @__PURE__ */ new WeakMap(), p = /* @__PURE__ */ new WeakMap(), y = /* @__PURE__ */ new WeakMap(); function R(e, t, s, i, a, r, o) { if (s < t.length) { const c = t[s]; if (a.has(c)) e.sub_any.add(i); else if (r.has(c)) e.subtopics_any || (e.subtopics_any = new C()), R( e.subtopics_any, t, s + 1, i, a, r, o ); else if (o && c.startsWith(o)) { const A = c.slice(o.length); try { const k = new RegExp(A); let b = e.subtopics_by_regex.find( (T) => T.regex.source === k.source ); b || (b = { regex: k, sub: new C() }, e.subtopics_by_regex.push(b)), R( b.sub, t, s + 1, i, a, r, o ); } catch { } } else e.subtopics.has(c) || e.subtopics.set(c, new C()), R( e.subtopics.get(c), t, s + 1, i, a, r, o ); } else e.subscribers.add(i); } function d(e, t, s, i, a, r, o) { if (s < t.length) { const c = t[s]; if (a.has(c)) e.sub_any.delete(i); else if (r.has(c)) e.subtopics_any && (d( e.subtopics_any, t, s + 1, i, a, r, o ), e.subtopics_any.isEmpty() && (e.subtopics_any = void 0)); else if (o && c.startsWith(o)) { const A = c.slice(o.length); try { const k = new RegExp(A), b = e.subtopics_by_regex.findIndex( (T) => T.regex.source === k.source ); if (b !== -1) { const T = e.subtopics_by_regex[b].sub; d( T, t, s + 1, i, a, r, o ), T.isEmpty() && e.subtopics_by_regex.splice(b, 1); } } catch { } } else { const A = e.subtopics.get(c); A && (d( A, t, s + 1, i, a, r, o ), A.isEmpty() && e.subtopics.delete(c)); } } else e.subscribers.delete(i); } function z(e, t, s, i, a) { if (s < t.length) { const r = t[s]; for (const o of e.sub_any) a.add(o); if (i && r.startsWith(i)) { const o = r.slice(i.length); try { const c = new RegExp(o); for (const [A, k] of e.subtopics) c.test(A) && z(k, t, s + 1, i, a); } catch { } } else { const o = e.subtopics.get(r); o && z(o, t, s + 1, i, a); } for (const o of e.subtopics_by_regex) o.regex.test(r) && z(o.sub, t, s + 1, i, a); e.subtopics_any && z( e.subtopics_any, t, s + 1, i, a ); } else for (const r of e.subscribers) a.add(r); } function v(e, t, s, i) { if (s < t.length) { const a = t[s]; if (e.sub_any.size > 0) return !0; if (i && a.startsWith(i)) { const r = a.slice(i.length); try { const o = new RegExp(r); for (const [c, A] of e.subtopics) if (o.test(c) && v(A, t, s + 1, i)) return !0; } catch { } } else { const r = e.subtopics.get(a); if (r && v(r, t, s + 1, i)) return !0; } for (const r of e.subtopics_by_regex) if (r.regex.test(a) && v(r.sub, t, s + 1, i)) return !0; if (e.subtopics_any && v( e.subtopics_any, t, s + 1, i )) return !0; } else if (e.subscribers.size > 0) return !0; return !1; } const eva_webengine_version = "0.9.13", WILDCARDS = ["*", "#"], MATCH_ANY = ["+", "?"], isMask = (e) => { for (let t of e) if (WILDCARDS.includes(t) || MATCH_ANY.includes(t)) return !0; return !1; }; var SerializationKind = /* @__PURE__ */ ((e) => (e.JSON = "json", e.MsgPack = "msgpack", e))(SerializationKind || {}), EvaErrorKind = /* @__PURE__ */ ((e) => (e[e.NOT_FOUND = -32001] = "NOT_FOUND", e[e.ACCESS_DENIED = -32002] = "ACCESS_DENIED", e[e.SYSTEM_ERROR = -32003] = "SYSTEM_ERROR", e[e.OTHER = -32004] = "OTHER", e[e.NOT_READY = -32005] = "NOT_READY", e[e.UNSUPPORTED = -32006] = "UNSUPPORTED", e[e.CORE_ERROR = -32007] = "CORE_ERROR", e[e.TIMEOUT = -32008] = "TIMEOUT", e[e.INVALID_DATA = -32009] = "INVALID_DATA", e[e.FUNC_FAILED = -32010] = "FUNC_FAILED", e[e.ABORTED = -32011] = "ABORTED", e[e.ALREADY_EXISTS = -32012] = "ALREADY_EXISTS", e[e.BUSY = -32013] = "BUSY", e[e.METHOD_NOT_IMPLEMENTED = -32014] = "METHOD_NOT_IMPLEMENTED", e[e.TOKEN_RESTRICTED = -32015] = "TOKEN_RESTRICTED", e[e.IO = -32016] = "IO", e[e.REGISTRY = -32017] = "REGISTRY", e[e.EVAHI_AUTH_REQUIRED = -32018] = "EVAHI_AUTH_REQUIRED", e[e.ACCESS_DENIED_MORE_DATA_REQUIRED = -32022] = "ACCESS_DENIED_MORE_DATA_REQUIRED", e[e.PARSE = -32700] = "PARSE", e[e.INVALID_REQUEST = -32600] = "INVALID_REQUEST", e[e.METHOD_NOT_FOUND = -32601] = "METHOD_NOT_FOUND", e[e.INVALID_PARAMS = -32602] = "INVALID_PARAMS", e[e.INTERNAL_RPC = -32603] = "INTERNAL_RPC", e[e.BUS_CLIENT_NOT_REGISTERED = -32113] = "BUS_CLIENT_NOT_REGISTERED", e[e.BUS_DATA = -32114] = "BUS_DATA", e[e.BUS_IO = -32115] = "BUS_IO", e[e.BUS_OTHER = -32116] = "BUS_OTHER", e[e.BUS_NOT_SUPPORTED = -32117] = "BUS_NOT_SUPPORTED", e[e.BUS_BUSY = -32118] = "BUS_BUSY", e[e.BUS_NOT_DELIVERED = -32119] = "BUS_NOT_DELIVERED", e[e.BUS_TIMEOUT = -32120] = "BUS_TIMEOUT", e[e.BUS_ACCESS = -32121] = "BUS_ACCESS", e))(EvaErrorKind || {}), EventKind = /* @__PURE__ */ ((e) => (e.HeartbeatSuccess = "heartbeat.success", e.HeartbeatError = "heartbeat.error", e.LoginSuccess = "login.success", e.LoginFailed = "login.failed", e.LoginOTPRequired = "login.otp_required", e.LoginOTPInvalid = "login.otp_invalid", e.LoginOTPSetup = "login.otp_setup", e.WsEvent = "ws.event", e.ServerReload = "server.reload", e.ServerRestart = "server.restart", e.LogRecord = "log.record", e.LogPostProcess = "log.postprocess", e.WASMError = "wasm.error", e))(EventKind || {}), StateProp = /* @__PURE__ */ ((e) => (e.Status = "status", e.Value = "value", e.Any = "any", e))(StateProp || {}); const GLOBAL_BLOCK_NAME = ".", ERR_REQUIRE_LOGGED_IN = "Not logged in"; var IntervalKind = /* @__PURE__ */ ((e) => (e.AjaxReload = "ajax_reload", e.AjaxLogReload = "log_reload", e.ActionWatch = "action_watch", e.Heartbeat = "heartbeat", e.Reload = "reload", e.Restart = "restart", e.WSBufTTL = "ws_buf_ttl", e))(IntervalKind || {}); class EvaError { constructor(t, s, i) { n(this, "code"); n(this, "message"); n(this, "data"); this.code = t, this.message = s, this.data = i; } } class EvaBulkRequestPartHandler { constructor() { n(this, "fn_ok"); n(this, "fn_err"); } then(t) { return this.fn_ok = t, this; } catch(t) { return this.fn_err = t, this; } } class EvaBulkRequest { constructor(t) { n(this, "requests"); n(this, "payload"); n(this, "eva"); this.requests = /* @__PURE__ */ new Map(), this.payload = [], this.eva = t; } /** * Prepare API function call for bulk calling * * Calls any available SFA API function * * @param p1 item OID (if required) or API call params * @param p2 extra call params or empty object * @param fn_ok function which is executed on successfull call * @parma fn_err function which is executed on error * * @returns Part handler object */ prepare(t, s, i) { let a; typeof s == "string" || Array.isArray(s) ? (a = i || {}, a.i = s) : a = s; let r = this.eva._prepare_call_params(a), o = this.eva._prepare_api_call(t, r), c = new EvaBulkRequestPartHandler(); return this.requests.set(o.id, c), this.payload.push(o), c; } /** * Perform bulk API call */ call() { let t = `${this.eva.api_uri}/jrpc`; return this.eva._debug("call_bulk", `${t}`), new Promise((s, i) => { if (this.eva.allow_logged_in_calls_only && !this.eva.logged_in) throw new EvaError(-32002, ERR_REQUIRE_LOGGED_IN); if (this.payload.length == 0) { s(!0); return; } this.eva.external.fetch(t, { method: "POST", headers: { "Content-Type": "application/json" }, redirect: "error", body: JSON.stringify(this.payload) }).then((a) => { if (a.ok) a.json().then((r) => { if (this.eva._debug("call_bulk success"), Array.isArray(r)) r.forEach((o) => { if (typeof o.id > "u" || typeof o.result > "u" && typeof o.error > "u") i({ code: -32009, message: "Invalid server response", data: o }); else { let c = o.id, A = this.requests.get(c), k, b; A !== void 0 && (k = A.fn_ok, b = A.fn_err), o.error !== void 0 ? (this.eva._debug( "call_bulk req", `${c} failed: ${o.error.code} (${o.error.message})` ), b && b({ code: o.error.code, message: o.error.message, data: o })) : (this.eva.debug == 2 && this.eva.log.info( `call_bulk API ${c} ${A.func} response`, o.result ), k && k(o.result)); } }), s(!0); else { let o = -32009, c = "Invalid server response (not an array)"; this.eva._debug("call_bulk", `failed: ${o} (${c})`), i(new EvaError(o, c, r)); } }).catch((r) => { let o = -32009, c = "Invalid server response"; this.eva._debug("call_bulk", `failed: ${o} (${c})`), i(new EvaError(o, c, r)); }); else { let r = -32007, o = "Server error"; this.eva._debug("call_bulk", `failed: ${r} (${o})`), i(new EvaError(r, o)); } }).catch((a) => { let r = -32007, o = "Server error"; this.eva._debug("call_bulk", `failed: ${r} (${o})`), i(new EvaError(r, o, a)); }); }); } } class Eva_ACTION { constructor(t) { n(this, "eva"); this.eva = t; } /** * Call unit action with value=1 * * @param oid {string} unit OID * @param wait {boolean} wait until the action is completed (default: true) */ async start(t, s = !0) { return this.exec(t, { v: 1 }, s); } /** * Call unit action with value=0 * * @param oid {string} unit OID * @param wait {boolean} wait until the action is completed (default: true) */ async stop(t, s = !0) { return this.exec(t, { v: 0 }, s); } /** * Call unit action to toggle its value * * @param oid {string} unit OID * @param wait {boolean} wait until the action is completed (default: true) */ async toggle(t, s = !0) { return this._act("action.toggle", t, {}, s); } /** * Call unit action * * @param oid {string} unit OID * @param params {object} action params * @param wait {boolean} wait until the action is completed (default: true) */ exec(t, s, i = !0) { return this._act("action", t, s, i); } /** * Terminate all unit actions * * @param oid {string} unit OID */ async kill(t) { await this.eva.call("action.kill", t); } /** * Terminate a unit action * * @param uuid {string} action uuid */ async terminate(t) { let s = "action.terminate"; await this.eva.call(s, { u: t }); } /** * Run lmacro * * @param oid {string} lmacro oid * @param params {object} call params * @param wait {boolean} wait until completed (default: true) */ async run(t, s, i = !0) { return this._act("run", t, s, i); } _act(t, s, i, a = !1) { return new Promise((r, o) => { this.eva.call(t, s, i).then((c) => { a === !1 ? r(c) : this.eva.watch_action(c.uuid, (A) => { A.uuid !== void 0 ? A.finished && r(A) : o(A); }); }).catch((c) => { o(c); }); }); } } class Eva_LVAR { constructor(t) { n(this, "eva"); this.eva = t; } /** * Reset lvar (set status to 1) * * @param oid {string} lvar oid */ async reset(t) { await this.eva.call("lvar.reset", t); } /** * Clear lvar (set status to 0) * * @param oid {string} lvar oid */ async clear(t) { await this.eva.call("lvar.clear", t); } /** * Toggle lvar status * * @param oid {string} lvar oid */ async toggle(t) { await this.eva.call("lvar.toggle", t); } /** * Increment lvar value * * @param oid {string} lvar oid * * @returns the new value */ async incr(t) { return (await this.eva.call("lvar.incr", t)).result; } /** * Decrement lvar value * * @param oid {string} lvar oid * * @returns the new value */ async decr(t) { return (await this.eva.call("lvar.decr", t)).result; } /** * Set lvar state * * @param oid {string} lvar oid * @param status {numberr} lvar status * @param value lvar value */ async set(t, s, i) { let a = {}; s !== void 0 && (a.status = s), i !== void 0 && (a.value = i), Object.keys(a).length && await this.eva.call("lvar.set", t, a); } /** * Set lvar status * * @param oid {string} lvar oid * @param status {number} lvar status */ async set_status(t, s) { await this.set(t, s); } /** * Set lvar value * * @param oid {string} lvar oid * @param value lvar value */ async set_value(t, s) { await this.set(t, s = s); } /** * Get lvar expiration time left * * @param lvar_oid {string} lvar OID * * @returns seconds to expiration, -1 if expired, -2 if stopped */ expires(t) { let s = this.eva.state(t); if (s === void 0 || s.t === void 0) return; if (!s.meta || s.meta.expires === void 0 || s.meta.expires == 0) return null; if (s.status == 0) return -2; if (s.status == -1) return -1; let i = s.meta.expires - (/* @__PURE__ */ new Date()).getTime() / 1e3 + this.eva.tsdiff + s.t; return i < 0 && (i = 0), i; } } class _EvaStream { constructor(t, s, i) { n(this, "oid"); n(this, "eva"); n(this, "name"); n(this, "ws"); n(this, "onStart"); n(this, "onData"); n(this, "onError"); n(this, "onEOS"); this.oid = t, this.name = s, this.eva = i, this.ws = null, this.onStart = () => { }, this.onData = (a) => { }, this.onError = (a) => { this.eva.log.error(`Stream ${this.name} error`, a); }, this.onEOS = () => { }; } _restart() { this._stop(), this._start(); } _start() { if (!this.eva.ws_mode) { this.onError( new EvaError( -32006, "WebSocket mode is disabled in EVA ICS WebEngine" ) ); return; } if (!this.eva.api_token) { this.onError(new EvaError(-32002, "Not logged in")); return; } let t = this.eva._get_ws_uri(); t += `k=${this.eva.api_token}`; const s = new this.eva.external.WebSocket(t); s.binaryType = "arraybuffer", s.onerror = (i) => { this.onError( new EvaError(-32010, `WebSocket error: ${i.type}`) ), this._stop(); }, s.onmessage = (i) => { if (typeof i.data == "string") { let a = JSON.parse(i.data); if (a.s !== "stream") return; switch (a.d) { case "start": this.onStart(); break; case "eos": this.onEOS(); break; case "forbidden": this.onError( new EvaError( -32002, `Stream ${this.name} access denied (${this.oid})` ) ), this._stop(); break; } } else this.onData(i.data); }, s.onclose = () => { this._stop(); }, s.addEventListener("open", () => { let i = { m: "stream.start", p: { i: this.oid } }; s.send(JSON.stringify(i)), s.send(""); }), this.ws = s; } _stop() { const t = this.ws; if (t) try { t.onclose = null, t.onmessage = () => { }, t.onerror = () => { }, t.close(); } catch { setTimeout(() => { try { t == null || t.close(); } catch { } }, 100); } } } class _EvaStateBlock { constructor(t, s, i) { n(this, "state_updates"); n(this, "eva"); n(this, "name"); n(this, "_ajax_reloader"); this.name = t, this.state_updates = s, this.eva = i; } _start() { this.eva.ws_mode && this.eva._start_ws(this.state_updates, this.name), this.eva._load_states(this.state_updates, this.name).then(() => { if (this.eva.ws_mode) { const t = this.eva._intervals.get( "reload" /* Reload */ ); t && (this._ajax_reloader = setInterval(() => { this.eva._load_states(this.state_updates, this.name); }, t * 1e3)); } else this._ajax_reloader = setInterval( () => { this.eva._load_states(this.state_updates, this.name); }, this.eva._intervals.get( "ajax_reload" /* AjaxReload */ ) ); }); } _restart() { this._stop(), this._start(); } _stop() { this._ajax_reloader && clearInterval(this._ajax_reloader); let t = this.eva.ws.get(this.name); if (t) { this.eva.ws.delete(this.name); try { t.onclose = null, t.onerror = function() { }, t.close(); } catch { setTimeout(() => { try { t == null || t.close(); } catch { } }, 100); } } } } var TokenMode = /* @__PURE__ */ ((e) => (e.Normal = "normal", e.ReadOnly = "readonly", e))(TokenMode || {}), SessionAuthKind = /* @__PURE__ */ ((e) => (e.Token = "token", e.Key = "key", e.Login = "login", e.No = "unauthorized", e))(SessionAuthKind || {}), ACLOp = /* @__PURE__ */ ((e) => (e.Log = "log", e.Developer = "developer", e.Moderator = "moderator", e.Supervisor = "supervisor", e))(ACLOp || {}), LoginState = /* @__PURE__ */ ((e) => (e.Active = "active", e.Starting = "starting", e.Stopping = "stopping", e.Inactive = "inactive", e.Failed = "failed", e.OTPRequired = "otp.required", e.OTPInvalid = "otp.invalid", e.OTPSetup = "otp.setup", e))(LoginState || {}); const defaultSessionState = () => ({ login: "inactive", error: null, otp: null }); var EventTopic = /* @__PURE__ */ ((e) => (e.ItemState = "ST", e.Server = "SERVER", e.Supervisor = "SUPERVISOR", e.WeSession = "WE/SESSION", e.WeItemState = "WE/ST", e))(EventTopic || {}), S, O; class Eva { constructor() { n(this, "action"); n(this, "lvar"); n(this, "api_uri"); n(this, "ws_uri"); x(this, S, void 0); n(this, "api_token"); //api_version: number | null; n(this, "authorized_user"); n(this, "clear_unavailable"); n(this, "debug"); n(this, "allow_logged_in_calls_only"); n(this, "external"); n(this, "evajw"); n(this, "in_evaHI"); n(this, "log_params"); n(this, "log"); n(this, "logged_in"); n(this, "login"); n(this, "login_xopts"); n(this, "log_level_names"); x(this, O, void 0); n(this, "set_auth_cookies"); n(this, "state_updates"); n(this, "tsdiff"); n(this, "version"); n(this, "wasm"); n(this, "ws_mode"); n(this, "server_info"); n(this, "ignore_password_set_on_next_login"); n(this, "_event_map"); n(this, "_api_call_id"); n(this, "_handlers"); n(this, "_intervals"); n(this, "_ws_handler_registered"); n(this, "_heartbeat_reloader"); n(this, "_ajax_reloader"); n(this, "_log_reloader"); n(this, "_scheduled_restarter"); n(this, "_states"); n(this, "_blocks"); n(this, "_streams"); n(this, "_last_ping"); n(this, "_last_pong"); n(this, "ws"); n(this, "_action_states"); n(this, "_action_watch_functions"); n(this, "_log_subscribed"); n(this, "_log_started"); n(this, "_log_first_load"); n(this, "_log_loaded"); n(this, "_update_state_functions"); n(this, "_update_state_mask_functions"); n(this, "_lr2p"); n(this, "_deserializePromise", (e, t) => { switch (t) { case "msgpack": return new Promise((s, i) => { e.arrayBuffer().then((a) => { try { const r = this.external.msgpack.decode(a); s(r); } catch (r) { i( new EvaError( -32009, "Invalid MsgPack response", r ) ); } }); }); case "json": return e.json(); default: throw new EvaError( -32006, "Unknown serialization kind" ); } }); this.version = eva_webengine_version, this.log = new Logger(), this.login = "", B(this, O, ""), this.login_xopts = null, B(this, S, ""), this.api_uri = "", this.ws_uri = "/ws", this.set_auth_cookies = !0, this.api_token = "", this.authorized_user = null, this.logged_in = !1, this.ignore_password_set_on_next_login = !1, this.debug = !1, this.allow_logged_in_calls_only = !1, this.state_updates = !0, this.wasm = !1, this.clear_unavailable = !1, this._ws_handler_registered = !1, this.ws_mode = !0, this.ws = /* @__PURE__ */ new Map(), this._api_call_id = 0, this.tsdiff = 0, this._last_ping = /* @__PURE__ */ new Map(), this._last_ping.set(GLOBAL_BLOCK_NAME, null), this._last_pong = /* @__PURE__ */ new Map(), this._last_pong.set(GLOBAL_BLOCK_NAME, null), this._streams = /* @__PURE__ */ new Map(), this._log_subscribed = !1, this._log_started = !1, this._log_first_load = !1, this._log_loaded = !1, this._lr2p = [], this._event_map = null, this.in_evaHI = typeof navigator < "u" && typeof navigator.userAgent == "string" && navigator.userAgent.startsWith("evaHI "), this.log_params = { level: 20, records: 200 }, this._update_state_functions = /* @__PURE__ */ new Map(), this._update_state_mask_functions = /* @__PURE__ */ new Map(), this._handlers = /* @__PURE__ */ new Map(), this._handlers.set("heartbeat.error", this.restart), this._handlers.set("wasm.error", (e) => { this.log.error(e), this._critical("WASM load error", !0, !0); }), this._states = /* @__PURE__ */ new Map(), this._states.set(GLOBAL_BLOCK_NAME, /* @__PURE__ */ new Map()), this._blocks = /* @__PURE__ */ new Map(), this._intervals = /* @__PURE__ */ new Map([ ["ajax_reload", 2], ["log_reload", 2], ["action_watch", 0.5], ["heartbeat", 5], ["reload", 5], ["restart", 1], ["ws_buf_ttl", 0] ]), this.log_level_names = /* @__PURE__ */ new Map([ [10, "DEBUG"], [20, "INFO"], [30, "WARNING"], [40, "ERROR"], [50, "CRITICAL"] ]), this._heartbeat_reloader = null, this._ajax_reloader = null, this._log_reloader = null, this._scheduled_restarter = null, this._action_watch_functions = /* @__PURE__ */ new Map(), this._action_states = /* @__PURE__ */ new Map(), this._clear(), this._clear_watchers(), this.action = new Eva_ACTION(this), this.lvar = new Eva_LVAR(this), this.evajw = null, this.external = {}, this.server_info = null, typeof window < "u" ? typeof window.fetch < "u" && (this.external.fetch = window.fetch.bind(window)) : typeof fetch < "u" ? this.external.fetch = fetch : this.external.fetch = null, typeof WebSocket < "u" ? this.external.WebSocket = WebSocket : this.external.WebSocket = null, typeof window < "u" && typeof window.QRious < "u" ? this.external.QRious = window.QRious : this.external.QRious = null; } // wasm override /** * Enables pub/sub event map. Usually not required to be called manually, as * called automatically as soon as there is a subscription performed. */ enable_event_map() { this._event_map === null && (this._event_map = new I().matchAny(MATCH_ANY).wildcard(WILDCARDS).regexPrefix("r~").separator("/")); } // wasm override /** * Subscribe to an event topic * * @param topic {string} event topic * @param fn {EventHandler} event handler * * @returns true if subscription was successful */ subscribe_event_topic(e, t) { return this.enable_event_map(), this._event_map.registerClient(t), this._event_map.subscribe(e, t), !0; } // wasm override /** * Subscribe to multiple event topics * * @param topics {Array<string>} event topics * @param fn {EventHandler} event handler * @returns true if subscription was successful */ subscribe_event_topics(e, t) { this.enable_event_map(), this._event_map.registerClient(t); for (let s of e) this._event_map.subscribe(s, t); return !0; } // wasm override /** * Unsubscribe from an event topic * * @param topic {string} event topic * @param fn {EventHandler} event handler */ unsubscribe_event_topic(e, t) { var s; (s = this._event_map) == null || s.unsubscribe(e, t); } // wasm override /** * Unsubscribe from multiple event topics * * @param topics {Array<string>} event topics * @param fn {EventHandler} event handler */ unsubscribe_event_topics(e, t) { var s; for (let i of e) (s = this._event_map) == null || s.unsubscribe(i, t); } // wasm override /** * Unsubscribe from all event topics * * @param fn {EventHandler} event handler */ unsubscribe_all_event_topics(e) { var t, s; (t = this._event_map) == null || t.unsubscribeAll(e), (s = this._event_map) == null || s.unregisterClient(e); } // WASM override _push_event_topic(e, t) { if (this._event_map) { const s = this._event_map.getSubscribers(e); for (const i of s) i(e, t); } } /** * Set engine login credentials * * @param login {string|null} login * @param password {string|null} password */ set_login_password(e, t) { this.login = e || "", B(this, O, t || ""), B(this, S, ""); } /** * Set/clear engine API key * * @param apikey {string | null} API key */ set_api_key(e) { B(this, S, e || ""), this.login = "", B(this, O, ""); } /** * Is engine password set * * @returns true if password is set */ is_password_set() { return N(this, O) !== ""; } /** * Is engine authentication set * * @returns true if auth is set */ is_auth_set() { return N(this, S) !== "" || this.login !== "" && N(this, O) !== ""; } /** * Clear engine authenication credentials */ clear_auth() { this.login = "", B(this, O, ""), B(this, S, ""); } /** * Start a binary stream * * @param params {EvaStreamParameters} stream parameters */ start_stream(e) { const t = this._streams.get(e.oid); t && t._stop(); const s = new _EvaStream(e.oid, e.name, this); e.onStart && (s.onStart = e.onStart), e.onData && (s.onData = e.onData), e.onError && (s.onError = e.onError), e.onEOS && (s.onEOS = e.onEOS), this._streams.set(e.name, s), s._start(); } /** * Stop a binary stream * * @param name {string} stream name */ stop_stream(e) { let t = this._streams.get(e); t && (t._stop(), this._streams.delete(e)); } /** * Register a state block * * @param name {string} block name * @param state_updates {boolean | Array<string>} state updates */ register_state_block(e, t) { if (e == GLOBAL_BLOCK_NAME) throw new EvaError( -32602, `WebEngine state block name ${GLOBAL_BLOCK_NAME} is reserved` ); check_state_updates(t); let s = this._blocks.get(e); s && (console.error( `WebEngine state block ${e} has been already registered, removing the old instance` ), s._stop()); let i = new _EvaStateBlock(e, t, this); this.logged_in && i._start(), this._blocks.set(e, i), this._init_block(e), this._push_event_topic(`WE/ST/${e}`, t); } /** * Unregister a state block * * @param name {string} block name */ unregister_state_block(e) { let t = this._blocks.get(e); t && (t._stop(), this._delete_block(e), this._blocks.delete(e), this._push_event_topic(`WE/ST/${e}`, null)); } /** * Unregister all state blocks */ unregister_all_state_blocks() { for (let [e, t] of this._blocks) t._stop(), this._delete_block(e), this._push_event_topic(`WE/ST/${e}`, null); this._blocks.clear(); } bulk_request() { return new EvaBulkRequest(this); } // WASM override /** * Get engine mode * @returns "js" or "wasm" */ get_mode() { return "js"; } /** * Start the engine * * After calling the function authenticates user, opens a WebSocket (in * case of WS mode) or schedule AJAXs refresh interval. */ start() { if (this._cancel_scheduled_restart(), this._debug("EVA ICS WebEngine", `version: ${this.version}`), typeof fetch > "u") { this.log.error( '"fetch" function is unavailable. Upgrade your web browser or connect polyfill' ); return; } if (this.logged_in) { this._debug("start", "already logged in"); return; } this.wasm && !this.evajw ? this._start_evajw() : this._start_engine(); } _start_engine() { this._push_event_topic("WE/SESSION", { login: "starting", error: null, otp: null }), this._clear_last_pings(); let e = {}; if (N(this, S)) e = { k: N(this, S) }, this.login_xopts && (e.xopts = this.login_xopts), this._debug("start", "logging in with API key"); else if (this.api_token) e = { a: this.api_token }, this._debug("start", "logging in with existing auth token"); else if (N(this, O) && !this.ignore_password_set_on_next_login) e = { u: this.login, p: N(this, O) }, this.api_token && (e.a = this.api_token), this.login_xopts && (e.xopts = this.login_xopts), this._debug("start", "logging in with password"); else if (this.set_auth_cookies) { let s = cookies.read("auth"); s && (e = { a: s }, this._debug("start", "logging in with cookie-cached auth token")); } Object.keys(e).length === 0 && this._debug("start", "logging in without credentials"); let t; return this.ignore_password_set_on_next_login = !1, this._api_call("login", e).then((s) => (this.api_token = s.token, t = s.user, this._set_token_cookie(), Promise.all([ this._load_states(this.state_updates, GLOBAL_BLOCK_NAME), this._heartbeat(!0), this._start_ws(this.state_updates, GLOBAL_BLOCK_NAME) ]))).then(() => { if (!this.ws_mode) this._ajax_reloader && clearInterval(this._ajax_reloader), this._ajax_reloader = setInterval( () => { this._load_states(this.state_updates, GLOBAL_BLOCK_NAME).catch( () => { } ); }, this._intervals.get( "ajax_reload" /* AjaxReload */ ) * 1e3 ); else { this._ajax_reloader && clearInterval(this._ajax_reloader); let s = this._intervals.get( "reload" /* Reload */ ); s && (this._ajax_reloader = setInterval(() => { this._load_states(this.state_updates, GLOBAL_BLOCK_NAME).catch( () => { } ); }, s * 1e3)); } this._heartbeat_reloader && clearInterval(this._heartbeat_reloader), this._heartbeat_reloader = setInterval( () => { this._heartbeat(!1).catch(() => { }); }, this._intervals.get( "heartbeat" /* Heartbeat */ ) * 1e3 ), this._debug("start", `login successful, user: ${t}`), this.logged_in = !0, this.authorized_user = t, this._invoke_handler( "login.success" /* LoginSuccess */ ), this._push_event_topic("WE/SESSION", { login: "active", error: null, otp: null }); for (let [s, i] of this._blocks) i._restart(); }).catch((s) => { this._debug("start", s), this.logged_in = !1, (s == null ? void 0 : s.code) === void 0 && (s = new EvaError(-32004, "Unknown error")), this._debug("start", `login failed: ${s.code} (${s.message})`), this._stop_engine(), (s.code != -32007 || s.message != "Server error") && this.erase_token_cookie(), this.error_handler(s, "login"); }), !0; } /** * Get system name * * @returns the system name or null if the engine is not logged in */ system_name() { return this.server_info ? this.server_info.system_name : null; } /** * Sleep the number of seconds * * @param sec {number} seconds to sleep */ async sleep(e) { return new Promise((t) => setTimeout(t, e * 1e3)); } /** * Start log processing * * Starts log processing. The engine must be already logged in. * * @param log_level {number} log processing level (optional) */ log_start(e) { this._log_started = !0, e !== void 0 && (this.log_params.level = e), (!this.ws_mode || this._log_first_load) && (this._log_loaded = !1, this._load_log_entries(!0), this.ws_mode || (this._log_reloader = setInterval( () => { this._load_log_entries(!1); }, this._intervals.get( "log_reload" /* AjaxLogReload */ ) * 1e3 ))); } /** * Set state updates without restart required * * @param state_updates {boolean} true/false or a string array * @param clear_existing {boolean} clear existing states * */ async set_state_updates(e, t) { check_state_updates(e), this.state_updates = e, this._push_event_topic("WE/ST", e); let s = this.ws.get(GLOBAL_BLOCK_NAME); if (s && s.readyState === 1) { let i = { m: "unsubscribe.state" }; if (s.send(JSON.stringify(i)), s.send(""), this.state_updates) { let a = { m: "subscribe.state" }, r; this.state_updates == !0 ? r = ["#"] : r = this.state_updates, a.p = r, s.send(JSON.stringify(a)), s.send(""); } } t && this._clear_states(GLOBAL_BLOCK_NAME), await this._load_states(this.state_updates, GLOBAL_BLOCK_NAME); } /** * Change log processing level * * @param log_level {number} log processing level */ set_log_level(e) { this.log_params.level = e, this._set_ws_log_level(e), this._load_log_entries(!0); } /** * Restart the engine * * e.g. used on heartbeat error or if subscription parameters are changed */ restart() { this._cancel_scheduled_restart(), this._debug("restart", "performing restart"), this.stop(!0, !0).then(() => { this._schedule_restart(); }).catch(() => { this._schedule_restart(); }); } /** * Erase auth token cookie * * It is recommended to call this function when login form is displayed to * prevent old token caching */ erase_token_cookie() { this.api_token = "", this.authorized_user = null, this._set_token_cookie(); } /** * Load JSON configuration * * @param config_path {string} config path (default: config.json) * * @returns Promise object */ load_config(e) { return new Promise((t, s) => { const i = e || "config.json"; this.log.debug("Eva::load_config", `loading configuration from ${i}`), this.external.fetch(i).then((a) => a.json()).then((a) => { this.apply_config(a), t(a); }).catch((a) => s(a)); }); } /** * Apply configuration from an object * * @param config {object} configuration object */ apply_config(e) { const t = e.engine; t && (t.api_uri && (this.api_uri = t.api_uri), t.apikey && B(this, S, t.apikey), t.debug !== void 0 && (this.debug = t.debug), t.login && (this.login = t.login), t.password && B(this, O, t.password), t.set_auth_cookies !== void 0 && (this.set_auth_cookies = t.set_auth_cookies), t.state_updates !== void 0 && (this.state_updates = t.state_updates, this._push_event_topic("WE/ST", this.state_updates)), t.wasm !== void 0 && (this.wasm = t.wasm), t.ws_mode !== void 0 && (this.ws_mode = t.ws_mode), t.log_params && (this.log_params = t.log_params), t.interval && Object.keys(t.interval).forEach((s) => { const i = s; this.set_interval(i, t.interval[i]); })); } async api_call({ method: e, params: t, serialization_kind: s }) { return this.call(e, t, void 0, s); } /** * Call API function * * Calls any available SFA API function * * @param method {string} API method * @param p1 {object} call parameters. if specified as a string/object, transformed to i=val * @param p2 {object} additional call parameters if p1 is a string * * @returns Promise object */ async call(e, t, s, i) { if (this.allow_logged_in_calls_only && !this.logged_in) throw new EvaError(-32002, ERR_REQUIRE_LOGGED_IN); let a; typeof t == "string" || Array.isArray(t) ? (a = s || {}, a.i = t) : a = t; let r = this._prepare_call_params(a); return this._api_call(e, r, i); } /** * Ask server to set the token read-only (e.g. after idle) * * (EVA ICS 3.3.2+) * * the current mode can be obtained from $eva.server_info.aci.token_mode */ set_readonly() { return new Promise((e, t) => { this.call("session.set_readonly").then(() => { this.server_info && (this.server_info.aci.token_mode = "readonly"), this._push_event_topic("SERVER", this.server_info), e(); }).catch((s) => { t(s); }); }); } /** * Ask server to return the token to normal mode * * (EVA ICS 3.3.2+) * * @param u {string} login * @param p {string} password * @param xopts {object} extra options (e.g. OTP) */ set_normal(e, t, s) { let i = {}; return typeof t > "u" || t === null ? i = { k: e } : i = { u: e, p: t }, i.a = this.api_token, s !== void 0 && (i.xopts = s), this._api_call("login", i).then(() => { this.server_info && (this.server_info.aci.token_mode = "normal"), this._invoke_handler( "login.success" /* LoginSuccess */ ); }).catch((a) => { this.error_handler(a, "set_normal"); }), !0; } error_handler(e, t) { if (e.code == -32022) { let s = this.parse_svc_message(e.message); if (s.method = t, s && s.kind == "OTP") switch (s.message) { case "REQ": this._invoke_handler("login.otp_required", s), this._push_event_topic("WE/SESSION", { login: "otp.required", error: null, otp: s }); return; case "INVALID": this._invoke_handler("login.otp_invalid", s), this._push_event_topic("WE/SESSION", { login: "otp.invalid", error: null, otp: s }); return; case "SETUP": this._invoke_handler("login.otp_setup", s), this._push_event_topic("WE/SESSION", { login: "otp.setup", error: null, otp: s }); return; } } this._invoke_handler("login.failed", e), t == "login" && (e.data = void 0, this._push_event_topic("WE/SESSION", { login: "failed", error: e, otp: null })); } /** * Set event handler function * * A single kind of event can have a single handler only * * @param event {EventKind} engine event kind * @param func {function} function called on event */ on(e, t) { this._handlers.set(e, t), this._debug("on", `setting handler for ${e}`), e == "ws.event" && (this._ws_handler_registered = !0); } /** * Set intervals * * @param interval_id {IntervalKind} interval kind * @param value {number} interval value (in seconds) */ set_interval(e, t) { this._intervals.set(e, t); } /* * Converts local date to server date * * @param date {Date} local date * * @returns {Date} server date */ date_local_to_server(e) { return new Date(e.getTime() - this.tsdiff * 1e3); } /* * Converts server date to local date * * @param date {Date} * * @returns {Date} */ date_server_to_local(e) { return new Date(e.getTime() + this.tsdiff * 1e3); } /** * Watch item state updates * * Registers the function to be called in case of state change event (or at * first state load). * * If state is already loaded, function will be called immediately. One item * (or item mask, set with "*" or a traditional OID mask) can have multiple * watchers. * * @param oid {string} item oid (e.g. sensor:env/temp1, sensor:env/\*, sensor:+/temp1) * @param func {function} function to be called * @param ignore_initial {boolean} skip initial state callback * @param prot {boolean} protected (not removed on global unwatch) * */ // WASM override watch(e, t, s = !1, i = !1) { if (isMask(e)) { let a = this._update_state_mask_functions, r = a == null ? void 0 : a.get(e); if (r === void 0 && (r = [], a == null || a.set(e, r)), r.push({ func: t, prot: i }), !s) { let o = this.state(e); Array.isArray(o) ? o.map(t) : o !== void 0 && t(o); } } else { let a = this._update_state_functions, r = a == null ? void 0 : a.get(e); if (r === void 0 && (r = [], a == null || a.set(e, r)), r.push({ func: t, prot: i }), !s) { let o = this.state(e); o !== void 0 && t(o); } } } /** * Watch action state by uuid * * Registers the function to be called in case of action status change * event (or at first state load). * * If status is already loaded, function will be called immediately. * Otherwise status is polled from the server with "action_watch" interval * (default: 500ms). * * There is no unwatch function as watching is stopped as soon as the * action is completed (or server error is occurred) * * @param uuid {string} action uuid * @param func {function} function to be called * */ watch_action(e, t) { let s = this._action_watch_functions.get(e); if (s === void 0) { s = [], this._action_watch_functions.set(e, s), s.push(t); const i = () => { this.call("action.result", { u: e }).then((a) => { let r = this._action_states.get(e); if (r === void 0 || r.status != a.status) { this._action_states.set(e, a); let o = this._action_watch_functions.get(e); o !== void 0 && o.map((c) => c(a)); } a.finished ? (this._action_watch_functions.delete(e), this._action_states.delete(e)) : setTimeout( i, this._intervals.get( "action_watch" /* ActionWatch */ ) * 1e3 ); }).catch((a) => { let r = this._action_watch_functions.get(e); r && r.map((o) => o(a)), this._action_watch_functions.delete(e), this._action_states.delete(e); }); }; setTimeout( i, this._intervals.get( "action_watch" /* ActionWatch */ ) * 1e3 ); } else { s.push(t); let i = this._action_states.get(e); i !== void 0 && t(i); } } /** * Stop watching item state updates * * If item oid or function is not specified, all watching functions are * removed for a single oid (mask) or for all the items watched. * * @param oid {string} item OID or a mask * @param func {function} function to be removed */ unwatch(e, t) { e ? isMask(e) ? t ? this._unwatch_mask_func(e, t) : this._unwatch_mask_all(e) : t ? this._unwatch_func(e, t) : this._unwatch_all(e) : this._clear_watchers(); } // WASM override _unwatch_func(e, t) { let s = this._update_state_functions, i = s == null ? void 0 : s.get(e); if (i !== void 0) { const a = i.filter((r) => r.func !== t); a.length > 0 ? s == null || s.set(e, a) : s == null || s.delete(e); } } // WASM override _unwatch_all(e) { let t = this._update_state_functions, s = t == null ? void 0 : t.get(e); if (s === void 0) return; const i = s.filter((a) => a.prot); i.length > 0 ? t == null || t.set(e, i) : t == null || t.delete(e); } // WASM override (not supported) _unwatch_mask_func(e, t) { let s = this._update_state_mask_functions, i = s == null ? void 0 : s.get(e); i !== void 0 && (s == null || s.set( e, i.filter((a) => a.func !== t) )); } // WASM override _unwatch_mask_all(e) { let t = this._update_state_mask_functions, s = t == null ? void 0 : t.get(e); if (s === void 0) return; const i = s.filter((a) => a.prot); i.length > 0 ? t == null || t.set(e, i) : t == null || t.delete(e); } /** * Get item status * * @param oid {string} item OID * * @returns item status(int) or undefined if no object found */ // WASM override status(e) { let t = this.state(e); if (t != null) return t.status; } /** * Get item value * * @param oid {string} item OID * * @returns item value or undefined if no item found */ // WASM override value(e) { let t = this.s