@cutos/core
Version:
The CUTOS (CUT Operating System) Core API is a JavaScript library that provides essential functionalities for LWA (Local Web Application) edge computing and communication in the CUTOS ecosystem.
1,479 lines (1,265 loc) • 43.4 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
var crypto = require('crypto');
var require$$1 = require('mqtt');
function getAugmentedNamespace(n) {
if (n.__esModule) return n;
var f = n.default;
if (typeof f == "function") {
var a = function a () {
if (this instanceof a) {
return Reflect.construct(f, arguments, this.constructor);
}
return f.apply(this, arguments);
};
a.prototype = f.prototype;
} else a = {};
Object.defineProperty(a, '__esModule', {value: true});
Object.keys(n).forEach(function (k) {
var d = Object.getOwnPropertyDescriptor(n, k);
Object.defineProperty(a, k, d.get ? d : {
enumerable: true,
get: function () {
return n[k];
}
});
});
return a;
}
var src = {};
var cutos = {};
const rnds8Pool = new Uint8Array(256); // # of random values to pre-allocate
let poolPtr = rnds8Pool.length;
function rng() {
if (poolPtr > rnds8Pool.length - 16) {
crypto.randomFillSync(rnds8Pool);
poolPtr = 0;
}
return rnds8Pool.slice(poolPtr, poolPtr += 16);
}
var REGEX = /^(?:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|00000000-0000-0000-0000-000000000000)$/i;
function validate(uuid) {
return typeof uuid === 'string' && REGEX.test(uuid);
}
/**
* Convert array of 16 byte values to UUID string format of the form:
* XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
*/
const byteToHex = [];
for (let i = 0; i < 256; ++i) {
byteToHex.push((i + 0x100).toString(16).slice(1));
}
function unsafeStringify(arr, offset = 0) {
// Note: Be careful editing this code! It's been tuned for performance
// and works in ways you may not expect. See https://github.com/uuidjs/uuid/pull/434
return byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + '-' + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + '-' + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + '-' + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + '-' + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]];
}
function stringify(arr, offset = 0) {
const uuid = unsafeStringify(arr, offset); // Consistency check for valid UUID. If this throws, it's likely due to one
// of the following:
// - One or more input array values don't map to a hex octet (leading to
// "undefined" in the uuid)
// - Invalid input values for the RFC `version` or `variant` fields
if (!validate(uuid)) {
throw TypeError('Stringified UUID is invalid');
}
return uuid;
}
//
// Inspired by https://github.com/LiosK/UUID.js
// and http://docs.python.org/library/uuid.html
let _nodeId;
let _clockseq; // Previous uuid creation time
let _lastMSecs = 0;
let _lastNSecs = 0; // See https://github.com/uuidjs/uuid for API details
function v1(options, buf, offset) {
let i = buf && offset || 0;
const b = buf || new Array(16);
options = options || {};
let node = options.node || _nodeId;
let clockseq = options.clockseq !== undefined ? options.clockseq : _clockseq; // node and clockseq need to be initialized to random values if they're not
// specified. We do this lazily to minimize issues related to insufficient
// system entropy. See #189
if (node == null || clockseq == null) {
const seedBytes = options.random || (options.rng || rng)();
if (node == null) {
// Per 4.5, create and 48-bit node id, (47 random bits + multicast bit = 1)
node = _nodeId = [seedBytes[0] | 0x01, seedBytes[1], seedBytes[2], seedBytes[3], seedBytes[4], seedBytes[5]];
}
if (clockseq == null) {
// Per 4.2.2, randomize (14 bit) clockseq
clockseq = _clockseq = (seedBytes[6] << 8 | seedBytes[7]) & 0x3fff;
}
} // UUID timestamps are 100 nano-second units since the Gregorian epoch,
// (1582-10-15 00:00). JSNumbers aren't precise enough for this, so
// time is handled internally as 'msecs' (integer milliseconds) and 'nsecs'
// (100-nanoseconds offset from msecs) since unix epoch, 1970-01-01 00:00.
let msecs = options.msecs !== undefined ? options.msecs : Date.now(); // Per 4.2.1.2, use count of uuid's generated during the current clock
// cycle to simulate higher resolution clock
let nsecs = options.nsecs !== undefined ? options.nsecs : _lastNSecs + 1; // Time since last uuid creation (in msecs)
const dt = msecs - _lastMSecs + (nsecs - _lastNSecs) / 10000; // Per 4.2.1.2, Bump clockseq on clock regression
if (dt < 0 && options.clockseq === undefined) {
clockseq = clockseq + 1 & 0x3fff;
} // Reset nsecs if clock regresses (new clockseq) or we've moved onto a new
// time interval
if ((dt < 0 || msecs > _lastMSecs) && options.nsecs === undefined) {
nsecs = 0;
} // Per 4.2.1.2 Throw error if too many uuids are requested
if (nsecs >= 10000) {
throw new Error("uuid.v1(): Can't create more than 10M uuids/sec");
}
_lastMSecs = msecs;
_lastNSecs = nsecs;
_clockseq = clockseq; // Per 4.1.4 - Convert from unix epoch to Gregorian epoch
msecs += 12219292800000; // `time_low`
const tl = ((msecs & 0xfffffff) * 10000 + nsecs) % 0x100000000;
b[i++] = tl >>> 24 & 0xff;
b[i++] = tl >>> 16 & 0xff;
b[i++] = tl >>> 8 & 0xff;
b[i++] = tl & 0xff; // `time_mid`
const tmh = msecs / 0x100000000 * 10000 & 0xfffffff;
b[i++] = tmh >>> 8 & 0xff;
b[i++] = tmh & 0xff; // `time_high_and_version`
b[i++] = tmh >>> 24 & 0xf | 0x10; // include version
b[i++] = tmh >>> 16 & 0xff; // `clock_seq_hi_and_reserved` (Per 4.2.2 - include variant)
b[i++] = clockseq >>> 8 | 0x80; // `clock_seq_low`
b[i++] = clockseq & 0xff; // `node`
for (let n = 0; n < 6; ++n) {
b[i + n] = node[n];
}
return buf || unsafeStringify(b);
}
function parse(uuid) {
if (!validate(uuid)) {
throw TypeError('Invalid UUID');
}
let v;
const arr = new Uint8Array(16); // Parse ########-....-....-....-............
arr[0] = (v = parseInt(uuid.slice(0, 8), 16)) >>> 24;
arr[1] = v >>> 16 & 0xff;
arr[2] = v >>> 8 & 0xff;
arr[3] = v & 0xff; // Parse ........-####-....-....-............
arr[4] = (v = parseInt(uuid.slice(9, 13), 16)) >>> 8;
arr[5] = v & 0xff; // Parse ........-....-####-....-............
arr[6] = (v = parseInt(uuid.slice(14, 18), 16)) >>> 8;
arr[7] = v & 0xff; // Parse ........-....-....-####-............
arr[8] = (v = parseInt(uuid.slice(19, 23), 16)) >>> 8;
arr[9] = v & 0xff; // Parse ........-....-....-....-############
// (Use "/" to avoid 32-bit truncation when bit-shifting high-order bytes)
arr[10] = (v = parseInt(uuid.slice(24, 36), 16)) / 0x10000000000 & 0xff;
arr[11] = v / 0x100000000 & 0xff;
arr[12] = v >>> 24 & 0xff;
arr[13] = v >>> 16 & 0xff;
arr[14] = v >>> 8 & 0xff;
arr[15] = v & 0xff;
return arr;
}
function stringToBytes(str) {
str = unescape(encodeURIComponent(str)); // UTF8 escape
const bytes = [];
for (let i = 0; i < str.length; ++i) {
bytes.push(str.charCodeAt(i));
}
return bytes;
}
const DNS = '6ba7b810-9dad-11d1-80b4-00c04fd430c8';
const URL = '6ba7b811-9dad-11d1-80b4-00c04fd430c8';
function v35(name, version, hashfunc) {
function generateUUID(value, namespace, buf, offset) {
var _namespace;
if (typeof value === 'string') {
value = stringToBytes(value);
}
if (typeof namespace === 'string') {
namespace = parse(namespace);
}
if (((_namespace = namespace) === null || _namespace === void 0 ? void 0 : _namespace.length) !== 16) {
throw TypeError('Namespace must be array-like (16 iterable integer values, 0-255)');
} // Compute hash of namespace and value, Per 4.3
// Future: Use spread syntax when supported on all platforms, e.g. `bytes =
// hashfunc([...namespace, ... value])`
let bytes = new Uint8Array(16 + value.length);
bytes.set(namespace);
bytes.set(value, namespace.length);
bytes = hashfunc(bytes);
bytes[6] = bytes[6] & 0x0f | version;
bytes[8] = bytes[8] & 0x3f | 0x80;
if (buf) {
offset = offset || 0;
for (let i = 0; i < 16; ++i) {
buf[offset + i] = bytes[i];
}
return buf;
}
return unsafeStringify(bytes);
} // Function#name is not settable on some platforms (#270)
try {
generateUUID.name = name; // eslint-disable-next-line no-empty
} catch (err) {} // For CommonJS default export support
generateUUID.DNS = DNS;
generateUUID.URL = URL;
return generateUUID;
}
function md5(bytes) {
if (Array.isArray(bytes)) {
bytes = Buffer.from(bytes);
} else if (typeof bytes === 'string') {
bytes = Buffer.from(bytes, 'utf8');
}
return crypto.createHash('md5').update(bytes).digest();
}
const v3 = v35('v3', 0x30, md5);
var v3$1 = v3;
var native = {
randomUUID: crypto.randomUUID
};
function v4(options, buf, offset) {
if (native.randomUUID && !buf && !options) {
return native.randomUUID();
}
options = options || {};
const rnds = options.random || (options.rng || rng)(); // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
rnds[6] = rnds[6] & 0x0f | 0x40;
rnds[8] = rnds[8] & 0x3f | 0x80; // Copy bytes to buffer, if provided
if (buf) {
offset = offset || 0;
for (let i = 0; i < 16; ++i) {
buf[offset + i] = rnds[i];
}
return buf;
}
return unsafeStringify(rnds);
}
function sha1(bytes) {
if (Array.isArray(bytes)) {
bytes = Buffer.from(bytes);
} else if (typeof bytes === 'string') {
bytes = Buffer.from(bytes, 'utf8');
}
return crypto.createHash('sha1').update(bytes).digest();
}
const v5 = v35('v5', 0x50, sha1);
var v5$1 = v5;
var nil = '00000000-0000-0000-0000-000000000000';
function version$1(uuid) {
if (!validate(uuid)) {
throw TypeError('Invalid UUID');
}
return parseInt(uuid.slice(14, 15), 16);
}
var esmNode = /*#__PURE__*/Object.freeze({
__proto__: null,
NIL: nil,
parse: parse,
stringify: stringify,
v1: v1,
v3: v3$1,
v4: v4,
v5: v5$1,
validate: validate,
version: version$1
});
var require$$0 = /*@__PURE__*/getAugmentedNamespace(esmNode);
/**
* Copyright (C) 2010-2023 - SipTimes Technologies Corporation - All rights reserved.
*/
const CORE = Object.freeze({ // core level api
PLATFORM: 'core-platform',
CONFIG: 'core-config',
BOX_INFO: 'core-box-info',
SIGN_INFO: 'core-sign-info',
DEVICE_INFO: 'core-device-info',
SIGN_GROUP_INFO: 'core-sign-group-info',
SHELL: 'core-shell',
DATABASE: 'core-database',
GET_VOLUME: 'core-get-volume',
SET_VOLUME: 'core-set-volume',
SET_HTTP_PROXY: 'core-set-http-proxy'
});
const CORE_NORESP = Object.freeze({ // core level api with no response
IPC: 'core-ipc',
HEARTBEAT_PING: 'core-heartbeat-ping',
HEARTBEAT_REGISTER: 'core-heartbeat-register',
HEARTBEAT_UNREGISTER: 'core-heartbeat-unregister',
LOGGER: 'core-logger'
});
const CORE_APP = Object.freeze({ // application level api, notification from core
NOTIFICATION: 'core-notification', HEARTBEAT_LISTENER: 'core-heartbeat-listener'
});
const DEVICE = Object.freeze({ // core level api
CHANNEL: 'device-channel',
DEFAULT: 'device-default',
ID_CARD_READER: 'device-id-card-reader',
NFC: 'device-nfc',
FINGERPRINT: 'device-fingerprint',
QR_CODE_SCANNER: 'device-qr-code-scanner',
PRINTER: 'device-printer',
});
const HEARTBEAT_STATUS = Object.freeze({
REGISTER: "register", ALIVE: "alive", ERROR: 'error', NOT_FOUND: 'not-found', SUCCESS: 'success'
});
const cutosAPI$2 = {
version: '3.0.0', CORE, CORE_NORESP, CORE_APP, DEVICE, HEARTBEAT_STATUS
};
var cutosApi = cutosAPI$2;
var name = "@cutos/core";
var version = "3.4.0";
var description = "The CUTOS (CUT Operating System) Core API is a JavaScript library that provides essential functionalities for LWA (Local Web Application) edge computing and communication in the CUTOS ecosystem. ";
var main = "dist/cutos.cjs.js";
var module$1 = "dist/cutos.esm.js";
var scripts = {
"build:rollup": "rollup -c rollup.config.cjs"
};
var keywords = [
"CUTOS",
"core",
"database",
"IPC",
"notification",
"logging",
"device&driver"
];
var author = "SipTimes";
var dependencies = {
mqtt: "^5.3.6"
};
var devDependencies = {
"@rollup/plugin-commonjs": "25.0.7",
"@rollup/plugin-json": "6.1.0",
"@rollup/plugin-node-resolve": "15.2.3",
rollup: "4.9.4",
uuid: "9.0.1"
};
var files = [
"dist/cutos.cjs.js",
"dist/cutos.esm.js",
"package.json"
];
var require$$3 = {
name: name,
version: version,
description: description,
main: main,
module: module$1,
scripts: scripts,
keywords: keywords,
author: author,
dependencies: dependencies,
devDependencies: devDependencies,
files: files
};
/**
* Copyright (C) 2010-2024 - SipTimes Technologies Corporation - All rights reserved.
*/
const {v4: uuidv4} = require$$0;
const mqtt = require$$1;
const cutosAPI$1 = cutosApi;
const callbackMap = new Map();
const callbackInterval = 500; // 500ms
const callbackTimeout = 5000; // 5s
const listenerMap = new Map();
const ipcListenerMap = new Map();
const packageJson = require$$3;
const ipformat = /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
let client = null;
let callbackTimer = null;
let coreApiInitialized = false;
let _logger = null;
let _notification = null;
function publishRequest(topic, callback, body, destAddress) {
let request = {};
request.context = {};
request.context.id = uuidv4();
request.context.destAddress = destAddress;
request.body = body;
if (typeof callback === "function") {
let callbackItem = {};
callbackItem.callback = callback;
callbackItem.timestamp = Date.now();
saveCallback(request.context.id, callbackItem);
}
client.publish(topic, JSON.stringify(request));
return true;
}
function publishResponse(topic, request, result) {
if (!request.context || !request.context.id) {
return false;
}
let response = {};
response.id = request.context.id;
response.result = result;
client.publish(topic + '-callback', JSON.stringify(response));
return true;
}
function saveCallback(id, item) {
callbackMap.set(id, item);
if (callbackTimer) {
return;
}
// process timeout callback
callbackTimer = setInterval(() => {
let currentTimestamp = new Date().getTime();
callbackMap.forEach((callbackItem, id) => {
if (currentTimestamp - callbackItem.timestamp > callbackTimeout) {
console.warn("callback is timeout");
callbackMap.delete(id);
}
});
}, callbackInterval);
}
function doCallback(id, body, err) {
if (!callbackMap.has(id)) {
return false;
}
try {
if (err) {
callbackMap.get(id).callback(null, new CutosError('CUTOS CORE error', err));
} else {
callbackMap.get(id).callback(body);
}
} catch (e) {
console.warn(e.message);
}
callbackMap.delete(id);
return true;
}
/**
* @Database
* The basic table structure of CUTOS database is in Key-Value format, and Value can be any json object.
*/
class Database {
KEY_TYPE_TEXT = "TEXT";
KEY_TYPE_INTEGER = "INTEGER";
/**
* The constructor of Database
* @param {String} [db] - name of database, can be null
*/
constructor(db) {
this.db = db;
}
/**
* Connect to database
* @param {Function} callback
*/
connect(callback) {
let body = {
cmd: "db.connect", db: this.db
};
publishRequest(cutosAPI$1.CORE.DATABASE, callback, body);
}
/**
* Run sql
* @param {String} sql - execute sql statement
* @param {Function} callback
*/
run(sql, callback) {
let body = {
db: this.db, cmd: "db.run", sql: sql
};
return publishRequest(cutosAPI$1.CORE.DATABASE, callback, body);
}
setupMirror(gwIPC, lwaName, confirm = false) {
this.mirror = {gwIPC, lwaName, confirm};
}
}
Database.Table = class {
/**
* @constructor The constructor of Table
* @param {String} name - name of table, can not be null
* @param {Object} database - database object, can not be null
*/
constructor(name, database) {
this.name = name;
this.database = database;
this.db = database.db;
if (!this.name || !this.database) {
throw new Error("name or database cannot be empty.");
}
}
/**
* Create a Table
* @param {Object} [opts]
* @param {Function} callback
*/
create(opts, callback) {
if (typeof opts === "function") {
callback = opts;
opts = {};
}
if (!opts) {
opts = {};
}
opts.keyName = opts.keyName ? opts.keyName : 'id';
opts.keyType = opts.keyType ? opts.keyType : 'INTEGER';
if (opts.keyType !== 'INTEGER' && opts.keyType !== 'TEXT') {
throw new Error("keyType MUST be INTEGER or TEXT");
}
this.opts = opts;
let body = {
db: this.db, cmd: "db.table.create", name: this.name,
retentionTime: opts.retentionTime,
keyType: opts.keyType,
keyName: opts.keyName
};
this.publishRequestMirror(cutosAPI$1.CORE.DATABASE, callback, body);
}
/**
* Insert data
* @param {Object} value
* @param {Object} [opts]
* @param {Function} callback
*/
insert(value, opts, callback) {
if (typeof opts === "function") {
callback = opts;
opts = {};
}
let body = {
db: this.db, cmd: "db.table.insert", name: this.name, value: value, tid: opts?.tid
};
this.publishRequestMirror(cutosAPI$1.CORE.DATABASE, callback, body);
}
insertById(id, value, opts, callback) {
if (typeof opts === "function") {
callback = opts;
opts = {};
}
let body = {
db: this.db, cmd: "db.table.insertById", name: this.name, id: id, value: value, tid: opts?.tid
};
this.publishRequestMirror(cutosAPI$1.CORE.DATABASE, callback, body);
}
/**
* Insert data by key
* @param {String} key
* @param {Object} value
* @param {Object} [opts] - opts.tid - if exist it contains tid
* @param {Function} callback
*/
insertByKey(key, value, opts, callback) {
if (typeof opts === "function") {
callback = opts;
opts = {};
}
let body = {
db: this.db, cmd: "db.table.insertByKey", name: this.name, key: key, value: value, tid: opts?.tid
};
this.publishRequestMirror(cutosAPI$1.CORE.DATABASE, callback, body);
}
/**
* Update data
* @param {Number} id
* @param {Object} value
* @param {Function} callback
*/
update(id, value, callback) {
let body = {
db: this.db, cmd: "db.table.update", name: this.name, id: id, value: value
};
publishRequest(cutosAPI$1.CORE.DATABASE, callback, body);
}
updateByKey(key, value, callback) {
let body = {
db: this.db, cmd: "db.table.updateByKey", name: this.name, key: key, value: value
};
publishRequest(cutosAPI$1.CORE.DATABASE, callback, body);
}
/**
* delete data
* @param {Number} id
* @param {Function} callback
*/
delete(id, callback) {
let body = {
db: this.db, cmd: "db.table.delete", name: this.name, id: id
};
publishRequest(cutosAPI$1.CORE.DATABASE, callback, body);
}
/**
* query data
* @param {Number} id
* @param {Function} callback
*/
query(id, callback) {
let body = {
db: this.db, cmd: "db.table.query", name: this.name, id: id
};
publishRequest(cutosAPI$1.CORE.DATABASE, callback, body);
}
/**
* query data by key
* @param {String} key
* @param {Function} callback
*/
queryByKey(key, callback) {
let body = {
db: this.db, cmd: "db.table.queryByKey", name: this.name, key: key
};
publishRequest(cutosAPI$1.CORE.DATABASE, callback, body);
}
/**
* query data by tid
* @param {Number} tid
* @param {Function} callback
*/
queryByTid(tid, callback) {
let body = {
db: this.db, cmd: "db.table.queryByTid", name: this.name, tid: tid
};
publishRequest(cutosAPI$1.CORE.DATABASE, callback, body);
}
/**
* query unsynced data by tid
* @param {Number} tid
* @param {Function} callback
*/
queryUnsynced(opts, callback) {
if (typeof opts === "function") {
callback = opts;
opts = {limit: 100};
}
if (!opts) opts = {};
if (opts.limit <= 0) opts.limit = 100;
let body = {
db: this.db, cmd: "db.table.queryUnsynced", name: this.name, limit: opts.limit
};
publishRequest(cutosAPI$1.CORE.DATABASE, callback, body);
}
/**
* query all data
* @param {Function} callback
*/
queryAll(callback) {
let body = {
db: this.db, cmd: "db.table.queryAll", name: this.name
};
publishRequest(cutosAPI$1.CORE.DATABASE, callback, body);
}
/**
* sync data
* @param {Number} id
* @param {Function} callback
*/
sync(id, callback) {
let body = {
db: this.db, cmd: "db.table.sync", name: this.name, id: id
};
publishRequest(cutosAPI$1.CORE.DATABASE, callback, body);
}
publishRequestMirror(topic, callback, body) {
let mirror = this.database.mirror;
let cb = callback;
if (mirror && mirror.gwIPC) { // mirror enabled
cb = (dbResult) => {
let confirmCallback = !mirror.confirm ? null : (gwResult) => {
if (gwResult.status === "success") {
this.sync(dbResult.row);
}
};
if (dbResult.status === true) {
switch (body.cmd) {
case "db.table.create":
break;
case "db.table.insert":
body.id = dbResult.msg.row;
body.timestamp = dbResult.msg.timestamp;
break;
case "db.table.insertById":
case "db.table.insertByKey":
body.timestamp = dbResult.msg.timestamp;
break;
}
mirror.gwIPC.mirror(body, confirmCallback, {lwaName: mirror.lwaName});
}
callback && callback(dbResult);
};
}
return publishRequest(topic, cb, body);
}
};
class Logger {
/**
* The constructor of logger
*/
constructor() {
}
/**
* Information
* @param {String} remark
* @param {String} content
* @param {String} type
*/
info(remark, content, type = "LWA") {
let body = {
type: type, level: "info", remark: remark, content: content
};
return publishRequest(cutosAPI$1.CORE_NORESP.LOGGER, null, body);
}
/**
* Warning
* @param {String} remark
* @param {String} content
* @param {String} type
*/
warning(remark, content, type = "LWA") {
let body = {
type: type, level: "warning", remark: remark, content: content
};
return publishRequest(cutosAPI$1.CORE_NORESP.LOGGER, null, body);
}
/**
* Error
* @param {String} remark
* @param {String} content
* @param {String} type
*/
error(remark, content, type = "LWA") {
let body = {
type: type, level: "error", remark: remark, content: content
};
return publishRequest(cutosAPI$1.CORE_NORESP.LOGGER, null, body);
}
/**
* Debug
* @param {String} remark
* @param {String} content
* @param {String} type
*/
debug(remark, content, type = "LWA") {
let body = {
type: type, level: "debug", remark: remark, content: content
};
return publishRequest(cutosAPI$1.CORE_NORESP.LOGGER, null, body);
}
}
class IPC {
constructor(destAddress) {
if (destAddress === undefined || destAddress === null || destAddress.length === 0 || destAddress === 'localhost' || destAddress === '127.0.0.1') {
this.destAddress = undefined;
} else if (destAddress.match(ipformat)) {
this.destAddress = destAddress;
} else {
throw new TypeError(destAddress + " is NOT an ip address.");
}
}
/**
* send message to channel
* @param channel
* @param args
* @param callback (result) response from receiver
* @returns {boolean}
*/
sendTo(channel, args, callback = null) {
let body = {};
body.channel = channel;
body.args = args;
let topic = cutosAPI$1.CORE_NORESP.IPC + (this.destAddress ? "" : "-endpoint");
return publishRequest(topic, callback, body, this.destAddress);
}
/**
* internal function, used to response sender with the result
* @param context contain sender information
* @param result
*/
_sendResponse(context, result) {
let response = {};
response.context = {};
response.context.destAddress = context.sourceAddress;
response.id = context.id;
response.result = result;
let topic = cutosAPI$1.CORE_NORESP.IPC + (this.destAddress ? "" : "-endpoint");
client.publish(topic, JSON.stringify(response));
}
/**
* listening channel
* @param channel
* @param listener (args, respond(result)) args is the data from sender, respond is used to send data back to sender's callback
*/
on(channel, listener) {
if (listener) {
if (!ipcListenerMap.has(channel)) {
ipcListenerMap.set(channel, new Map());
}
if (!ipcListenerMap.get(channel).has(listener)) {
let innerListener = (args, context) => {
listener(args, (result) => {
this._sendResponse(context, result);
});
};
ipcListenerMap.get(channel).set(listener, innerListener);
}
}
}
/**
* unsubscribe from channel, if listener is null, all listeners will remove
* @param channel
* @param listener
*/
removeListener(channel, listener = null) {
if (!ipcListenerMap.has(channel)) {
return;
}
if (!listener) {
ipcListenerMap.delete(channel);
} else {
ipcListenerMap.get(channel).delete(listener);
}
}
}
class Notification {
/**
* The constructor of Notification
*/
constructor() {
}
/**
* Register notification listener function
* @param {Function} listener - listener function (data)
*/
register(listener) {
listenerMap.set(cutosAPI$1.CORE_APP.NOTIFICATION, listener);
}
/**
* Unregister, used to cancel the registration of notification
*/
unregister() {
listenerMap.delete(cutosAPI$1.CORE_APP.NOTIFICATION);
}
/**
* Cancel registration, used to cancel the registration of notification
* @param {String} event - event name
* @param {String} msg - event content
*/
emit(event, msg) {
client.publish(cutosAPI$1.CORE_APP.NOTIFICATION, JSON.stringify({event: event, msg: msg}));
return true;
}
}
class Heartbeat {
STATUS = cutosAPI$1.HEARTBEAT_STATUS;
constructor(pinger, counter) {
this.registered = false;
this.request = {};
this.request.counter = counter;
this.request.pinger = pinger;
this.request.subscriber = cutosAPI$1.CORE_NORESP.HEARTBEAT_PING + '-' + pinger;
}
register() {
this.request.msg = {};
this.request.msg.status = this.STATUS.REGISTER;
this.request.msg.info = `Start to monitor ${this.request.pinger}.`;
client.publish(cutosAPI$1.CORE_NORESP.HEARTBEAT_REGISTER, JSON.stringify(this.request));
this.registered = true;
}
ping(status, info = {}) {
if (!this.registered) {
throw ("The heartbeat is NOT registered!")
}
this.request.msg = {};
this.request.msg.status = status;
this.request.msg.info = info;
client.publish(cutosAPI$1.CORE_NORESP.HEARTBEAT_PING, JSON.stringify(this.request));
}
}
class Device {
/**
* The constructor of Device
* @param name
* @param type
*/
constructor(name = 'default-device-name', type = cutosAPI$1.DEVICE.DEFAULT) {
this.name = name;
this.type = type;
this.topicData = cutosAPI$1.DEVICE.CHANNEL + '-' + type + '-data';
this.topicResponse = cutosAPI$1.DEVICE.CHANNEL + '-' + type + '-response';
this.topicCommand = cutosAPI$1.DEVICE.CHANNEL + '-' + type + '-command';
this.data = {};
if (Device.objectMap === undefined) {
Device.objectMap = new Map();
}
Device.objectMap.set(this.type, this);
if (Device.heartbeatListener === undefined) {
Device.heartbeatListener = function (msg) {
for (const [key, obj] of Device.objectMap) {
if (obj.statusListener !== undefined) {
if (obj.type === "default-device" || obj.type === msg.pinger) {
obj.statusListener(msg);
obj.msg = msg;
}
}
}
//console.debug(JSON.stringify(msg));
};
listenerMap.set(cutosAPI$1.CORE_APP.HEARTBEAT_LISTENER, Device.heartbeatListener);
}
// subscribe command callback
client.subscribe(this.topicCommand + '-callback');
}
/**
* Device initialize
* @param callback(result, error)
* @param opts
*/
init(opts, callback) {
if (typeof opts === "function") {
callback = opts;
opts = null;
}
let command = {};
command.cmd = "init";
command.args = {name: this.name, type: this.type, opts};
command.topicResponse = this.topicResponse;
let defaultTopic = cutosAPI$1.DEVICE.CHANNEL + '-' + cutosAPI$1.DEVICE.DEFAULT + '-command';
publishRequest(defaultTopic, null, command);
// subscribe response callback, currently only used for init
client.subscribe(this.topicResponse); // receive response from device
listenerMap.set(this.topicResponse, (result) => {
if (result.status) {
callback && callback(`Driver ${this.type} loaded`);
} else {
callback && callback(null, result.msg);
}
});
}
/**
* Get device status
* @param {Function} listener
*/
onStatus(listener) {
this.statusListener = listener;
}
/**
* Get device status
* @param {Function} listener
*/
getCurrentStatus() {
return this.msg;
}
/**
*
* @param command - command description object {cmd: 'command', args: (''|{}) }
* @param callback - (result) => {}, the result is normal make up of {status: true|false, msg: (''| {})}
*/
sendCommand(command, callback) {
// exam: command = {cmd: "connect", args: ""}
publishRequest(this.topicCommand, callback, command);
}
/**
* Receive data sent by the driver
* @param listener - listener(data)
*/
onData(listener) {
if (!listenerMap.has(this.topicData)) {
client.subscribe(this.topicData); // receive data from device
}
listenerMap.set(this.topicData, (data) => {
this.data = data;
listener(data);
});
}
getCurrentData() {
return this.data;
}
/**
*
* @param callback (result) => {}, the result is normal make up of {status: true|false, msg: (''| {})}
*/
readDeviceInfo(callback) {
let msg = {};
msg.cmd = "read-device-info";
this.sendCommand(msg, callback);
}
}
class Driver {
/**
* The constructor of Driver
* @param name
* @param type
* @param counter
*/
constructor(name = 'default-driver-name', type = cutosAPI$1.DEVICE.DEFAULT, counter = 10) {
this.name = name;
this.type = type;
this.heartbeat = new Heartbeat(type, counter);
this.statusInfo = {};
this.beatInterval = 3000;
this.topicData = cutosAPI$1.DEVICE.CHANNEL + '-' + type + '-data';
this.topicResponse = cutosAPI$1.DEVICE.CHANNEL + '-' + type + '-response';
this.topicCommand = cutosAPI$1.DEVICE.CHANNEL + '-' + type + '-command';
}
startBeat() {
this.heartbeat.register();
let _this = this;
setInterval(function () {
if (_this.statusInfo.status !== undefined && _this.statusInfo.status !== _this.heartbeat.STATUS.REGISTER) {
_this.heartbeat.ping(_this.statusInfo.status, _this.statusInfo.info);
}
}, _this.beatInterval);
}
updateStatusInfo(status = 'default-status', info = 'default-info') {
this.statusInfo.status = status;
this.statusInfo.info = info;
}
/**
* Send data to the device
* @param data
*/
sendData(data) {
client.publish(this.topicData, JSON.stringify(data));
}
sendResponse(data, topic = this.topicResponse) {
client.publish(topic, JSON.stringify(data));
}
/**
* Receive device commands
* @param listener(command, respond(result))
*/
onCommand(listener) {
if (listener) {
let cmdListener = (message) => {
// process command callback
// message.context
// message.context.id
// message.body
listener(message.body, result => {
publishResponse(this.topicCommand, message, result);
});
};
client.subscribe(this.topicCommand); // receive command from app
listenerMap.set(this.topicCommand, cmdListener);
}
}
}
class CutosError {
constructor(name, message) {
this.name = name;
this.message = message;
}
}
const CoreAPI$2 = {
/**
* Initialization of CUTOS core
* @param {string} host CUTOS address.When host is equal to null, it takes the value 'localhost'. During development, it can be changed to the target address (the device address where CUTOS is installed), such as: '192.168.1.11'
* @param {Function} callback callback(result, error)
*/
init(host = 'localhost', callback) {
let brokerUrl;
if (host && host.startsWith('ws://') && host.endsWith(':1883')) {
brokerUrl = host;
} else {
if (!host) {
host = 'localhost';
}
if (host !== 'localhost' && !host.match(ipformat)) {
throw new Error("invalid host address");
}
brokerUrl = `ws://${host}:1883`;
}
if (callback && typeof (callback) !== 'function') {
throw new Error("callback must be a function");
}
client = mqtt.connect(brokerUrl);
client.on('connect', function () {
console.log("CUTOS CORE connected.");
for (let api in cutosAPI$1.CORE) {
client.subscribe(cutosAPI$1.CORE[api] + '-callback');
}
for (let api in cutosAPI$1.CORE_NORESP) {
client.subscribe(cutosAPI$1.CORE_NORESP[api] + '-endpoint');
}
for (let api in cutosAPI$1.CORE_APP) {
client.subscribe(cutosAPI$1.CORE_APP[api]);
}
!coreApiInitialized && callback && callback('CoreAPI init success');
coreApiInitialized = true;
});
client.on('message', function (topic, message, context) {
// message is Buffer
// console.debug(topic.toString() + message.toString());
let _message = JSON.parse(message);
if (topic === cutosAPI$1.CORE_NORESP.IPC + '-endpoint') { // ipc
// process response message
if (_message.id) {
doCallback(_message.id, _message.result);
return
}
let listenerMap = ipcListenerMap.get(_message.body.channel);
if (listenerMap) {
listenerMap.forEach(listener => listener(_message.body.args, _message.context));
}
} else if (topic === cutosAPI$1.CORE_APP.NOTIFICATION || topic === cutosAPI$1.CORE_APP.HEARTBEAT_LISTENER
|| topic.startsWith(cutosAPI$1.DEVICE.CHANNEL) && !topic.endsWith('-callback')) {
let listener = listenerMap.get(topic);
if (listener !== undefined) {
listener(_message);
}
} else { // cutos core api callback
doCallback(_message.id, _message.result, _message.err);
}
});
client.on("close", () => {
console.log("CUTOS CORE closed.");
});
client.on("error", (err) => {
callback && callback(null, new CutosError('CUTOS CORE error', err.message));
console.error("CUTOS CORE error:", err.message);
});
client.stream.on("error", (err) => {
callback && callback(null, new CutosError('CUTOS CORE error', err.message));
console.error("CUTOS CORE error:", err.message);
});
},
connected() {
return !!client && client.connected;
},
/**
* Get platform version information
*/
getVersion() {
return packageJson.version ;
},
/**
* Get platform information
* @param {Function} callback - callback(result)
*/
getPlatform(callback) {
return publishRequest(cutosAPI$1.CORE.PLATFORM, callback);
},
/**
* Get configuration information
* @param {Function} callback - callback(result)
*/
getConfig(callback) {
return publishRequest(cutosAPI$1.CORE.CONFIG, callback);
},
/**
* Get host information
* @param {Function} callback - callback(result)
*/
getBoxInfo(callback) {
return publishRequest(cutosAPI$1.CORE.BOX_INFO, callback);
},
/**
* Get sign information
* @param {Function} callback - callback(result)
*/
getSignInfo(callback) {
return publishRequest(cutosAPI$1.CORE.SIGN_INFO, callback);
},
/**
* Get device information
* @param {Function} callback - callback(result)
*/
getDeviceInfo(callback) {
return publishRequest(cutosAPI$1.CORE.DEVICE_INFO, callback);
},
getSignGroupInfo(callback) {
return publishRequest(cutosAPI$1.CORE.SIGN_GROUP_INFO, callback);
},
/**
* Get IPC instance object
* @param {String} destAddress - optional parameter, target IP address, used for inter-device process communication
*/
getIPC(destAddress) {
return new IPC(destAddress);
},
/**
* Get notification information instance object
*/
getNotification() {
if (!_notification) {
_notification = new Notification();
}
return _notification;
},
/**
* Get the log instance object
*/
getLogger() {
if (!_logger) {
_logger = new Logger();
}
return _logger;
},
/**
* Shell command execution
* @param {String} command - command
* @param {Function} callback - callback(result, error)
*/
shell(command, callback) {
return publishRequest(cutosAPI$1.CORE.SHELL, callback, command);
},
/**
* Get the volume
* @param {Function} callback - callback(result)
*/
getVolume(callback) {
return publishRequest(cutosAPI$1.CORE.GET_VOLUME, callback);
},
/**
* Set the volume
* @param {Number} vol - volume:[0-100]
* @param {Function} callback - callback(result)
*/
setVolume(vol, callback) {
return publishRequest(cutosAPI$1.CORE.SET_VOLUME, callback, vol);
},
/**
* Set proxy to solve LWA cross-domain problem
* @param {String} api - interface name
* @param {String} target - target address
* @param {Function} callback - callback(result, error)
*/
setHttpProxy(api, target, callback) {
return publishRequest(cutosAPI$1.CORE.SET_HTTP_PROXY, callback, {api, target});
}
};
cutos.CoreAPI = CoreAPI$2;
cutos.CoreDefine = {
DEVICE: cutosAPI$1.DEVICE, HEARTBEAT_STATUS: cutosAPI$1.HEARTBEAT_STATUS
};
cutos.CoreClass = {
Device, Driver, Database
};
cutos.cutosAPI = cutosAPI$1;
// simulate cutos core for development
const {CoreAPI: CoreAPI$1, CoreClass: CoreClass$1} = cutos;
const CoreSimulator$1 = {
start: function (driver, port = 1883) {
CoreAPI$1.init(`ws://localhost:${port}`, (result, error) => {
if (error) {
console.log('CUTOS Simulator error', error.message);
return;
}
console.log('CUTOS Simulator started and listening on port ', port);
let drvDefault = new CoreClass$1.Driver();
drvDefault.onCommand((command) => {
console.log('drvDefault onCommand', JSON.stringify(command));
if (command.cmd === "init") {
let result = {status: true};
try {
driver.init(command.args);
} catch (e) {
result.msg = e.message;
result.status = false;
}
drvDefault.sendResponse(result, command.topicResponse);
}
});
});
}
};
var coreSimulator = {CoreSimulator: CoreSimulator$1};
const {CoreAPI, CoreDefine, CoreClass, cutosAPI} = cutos;
const {CoreSimulator} = coreSimulator;
var CoreAPI_1 = src.CoreAPI = CoreAPI;
var CoreDefine_1 = src.CoreDefine = CoreDefine;
var CoreClass_1 = src.CoreClass = CoreClass;
var cutosAPI_1 = src.cutosAPI = cutosAPI;
var CoreSimulator_1 = src.CoreSimulator = CoreSimulator;
exports.CoreAPI = CoreAPI_1;
exports.CoreClass = CoreClass_1;
exports.CoreDefine = CoreDefine_1;
exports.CoreSimulator = CoreSimulator_1;
exports.cutosAPI = cutosAPI_1;
exports.default = src;