UNPKG

yoctolib-esm

Version:

Yoctopuce library for TypeScript/JavaScript, as an ECMAScript 2015 module

1,295 lines (1,294 loc) 559 kB
/********************************************************************* * * $Id: yocto_api.ts 72760 2026-04-16 08:58:35Z mvuilleu $ * * High-level programming interface, common to all modules * * - - - - - - - - - License information: - - - - - - - - - * * Copyright (C) 2011 and beyond by Yoctopuce Sarl, Switzerland. * * Yoctopuce Sarl (hereafter Licensor) grants to you a perpetual * non-exclusive license to use, modify, copy and integrate http * file into your software for the sole purpose of interfacing * with Yoctopuce products. * * You may reproduce and distribute copies of this file in * source or object form, as long as the sole purpose of this * code is to interface with Yoctopuce products. You must retain * this notice in the distributed source file. * * You should refer to Yoctopuce General Terms and Conditions * for additional information regarding your rights and * obligations. * * THE SOFTWARE AND DOCUMENTATION ARE PROVIDED "AS IS" WITHOUT * WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING * WITHOUT LIMITATION, ANY WARRANTY OF MERCHANTABILITY, FITNESS * FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO * EVENT SHALL LICENSOR BE LIABLE FOR ANY INCIDENTAL, SPECIAL, * INDIRECT OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA, * COST OF PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY OR * SERVICES, ANY CLAIMS BY THIRD PARTIES (INCLUDING BUT NOT * LIMITED TO ANY DEFENSE THEREOF), ANY CLAIMS FOR INDEMNITY OR * CONTRIBUTION, OR OTHER SIMILAR COSTS, WHETHER ASSERTED ON THE * BASIS OF CONTRACT, TORT (INCLUDING NEGLIGENCE), BREACH OF * WARRANTY, OR OTHERWISE. * *********************************************************************/ //--- (generated code: YFunction return codes) // Yoctopuce error codes, also used by default as function return value export const YAPI_SUCCESS = 0; // everything worked all right export const YAPI_NOT_INITIALIZED = -1; // call yInitAPI() first ! export const YAPI_INVALID_ARGUMENT = -2; // one of the arguments passed to the function is invalid export const YAPI_NOT_SUPPORTED = -3; // the operation attempted is (currently) not supported export const YAPI_DEVICE_NOT_FOUND = -4; // the requested device is not reachable export const YAPI_VERSION_MISMATCH = -5; // the device firmware is incompatible with this API version export const YAPI_DEVICE_BUSY = -6; // the device is busy with another task and cannot answer export const YAPI_TIMEOUT = -7; // the device took too long to provide an answer export const YAPI_IO_ERROR = -8; // there was an I/O problem while talking to the device export const YAPI_NO_MORE_DATA = -9; // there is no more data to read from export const YAPI_EXHAUSTED = -10; // you have run out of a limited resource, check the documentation export const YAPI_DOUBLE_ACCES = -11; // you have two process that try to access to the same device export const YAPI_UNAUTHORIZED = -12; // unauthorized access to password-protected device export const YAPI_RTC_NOT_READY = -13; // real-time clock has not been initialized (or time was lost) export const YAPI_FILE_NOT_FOUND = -14; // the file is not found export const YAPI_SSL_ERROR = -15; // Error reported by mbedSSL export const YAPI_RFID_SOFT_ERROR = -16; // Recoverable error with RFID tag (eg. tag out of reach), check YRfidStatus for details export const YAPI_RFID_HARD_ERROR = -17; // Serious RFID error (eg. write-protected, out-of-boundary), check YRfidStatus for details export const YAPI_BUFFER_TOO_SMALL = -18; // The buffer provided is too small export const YAPI_DNS_ERROR = -19; // Error during name resolutions (invalid hostname or dns communication error) export const YAPI_SSL_UNK_CERT = -20; // The certificate is not correctly signed by the trusted CA export const YAPI_UNCONFIGURED = -21; // Remote hub is not yet configured export const YAPI_INVALID_INT = 0x7fffffff; export const YAPI_INVALID_UINT = -1; export const YAPI_INVALID_LONG = 0x7fffffffffffffff; export const YAPI_INVALID_DOUBLE = -Number.MAX_VALUE; export const YAPI_INVALID_STRING = '!INVALID!'; // TLS / SSL definitions export const YAPI_NO_TRUSTED_CA_CHECK = 1; // Disables certificate checking export const YAPI_NO_EXPIRATION_CHECK = 2; // Disables certificate expiration date checking export const YAPI_NO_HOSTNAME_CHECK = 4; // Disable hostname checking export const YAPI_LEGACY = 8; // Allow non-secure connection (similar to v1.10) //--- (end of generated code: YFunction return codes) export const YAPI_MIN_DOUBLE = -Number.MAX_VALUE; export const YAPI_MAX_DOUBLE = Number.MAX_VALUE; export const Y_FUNCTIONDESCRIPTOR_INVALID = YAPI_INVALID_STRING; // yInitAPI constants (not really useful in Javascript, but defined for code portability) export const Y_DETECT_NONE = 0; export const Y_DETECT_USB = 1; export const Y_DETECT_NET = 2; export const Y_DETECT_ALL = (Y_DETECT_USB | Y_DETECT_NET); const DEFAULT_DEVICE_LIST_VALIDITY_MS = 10000; const DEFAULT_NETWORK_TIMEOUT_MS = 20000; // calibration types const YOCTO_CALIB_TYPE_OFS = 30; const NOTIFY_NETPKT_NAME = '0'; const NOTIFY_NETPKT_CHILD = '2'; const NOTIFY_NETPKT_FUNCNAME = '4'; const NOTIFY_NETPKT_FUNCVAL = '5'; const NOTIFY_NETPKT_LOG = '7'; const NOTIFY_NETPKT_FUNCNAMEYDX = '8'; const NOTIFY_NETPKT_CONFCHGYDX = 's'; const NOTIFY_NETPKT_FLUSHV2YDX = 't'; const NOTIFY_NETPKT_FUNCV2YDX = 'u'; const NOTIFY_NETPKT_TIMEV2YDX = 'v'; const NOTIFY_NETPKT_DEVLOGYDX = 'w'; const NOTIFY_NETPKT_TIMEVALYDX = 'x'; const NOTIFY_NETPKT_FUNCVALYDX = 'y'; const NOTIFY_NETPKT_TIMEAVGYDX = 'z'; const NOTIFY_NETPKT_NOT_SYNC = '@'; const NOTIFY_NETPKT_STOP = 10; // =\n const NOTIFY_V2_LEGACY = 0; // unused (reserved for compatibility with legacy notifications) const NOTIFY_V2_6RAWBYTES = 1; // largest type: data is always 6 bytes const NOTIFY_V2_TYPEDDATA = 2; // other types: first data byte holds the decoding format const NOTIFY_V2_FLUSHGROUP = 3; // no data associated const PUBVAL_LEGACY = 0; // 0-6 ASCII characters (normally sent as YSTREAM_NOTICE) const PUBVAL_1RAWBYTE = 1; // 1 raw byte (=2 characters) const PUBVAL_2RAWBYTES = 2; // 2 raw bytes (=4 characters) const PUBVAL_3RAWBYTES = 3; // 3 raw bytes (=6 characters) const PUBVAL_4RAWBYTES = 4; // 4 raw bytes (=8 characters) const PUBVAL_5RAWBYTES = 5; // 5 raw bytes (=10 characters) const PUBVAL_6RAWBYTES = 6; // 6 hex bytes (=12 characters) (sent as V2_6RAWBYTES) const PUBVAL_C_LONG = 7; // 32-bit C signed integer const PUBVAL_C_FLOAT = 8; // 32-bit C float const PUBVAL_YOCTO_FLOAT_E3 = 9; // 32-bit Yocto fixed-point format (e-3) const PUBVAL_YOCTO_FLOAT_E6 = 10; // 32-bit Yocto fixed-point format (e-6) const YOCTO_PUBVAL_LEN = 16; const YOCTO_PUBVAL_SIZE = 6; const YOCTO_HASH_BUF_SIZE = 28; const YOCTO_BASETYPE_FUNCTION = 0; const YOCTO_BASETYPE_SENSOR = 1; const Y_BASETYPES = { Function: YOCTO_BASETYPE_FUNCTION, Sensor: YOCTO_BASETYPE_SENSOR }; export class YErrorMsg { constructor(msg = '') { this.msg = msg; } } export class YoctoError extends Error { constructor(...params) { // Pass remaining arguments (including vendor specific ones) to parent constructor super(...params); this.errorMsg = this.name; this.name = 'YoctoError'; // Maintains proper stack trace for where our error was thrown (only available on V8) if ('captureStackTrace' in Error) { // @ts-ignore Error.captureStackTrace(this, YoctoError); } } } export class _YY_UrlInfo { constructor(str_url) { this.orgUrl = str_url; let proto = 'auto'; let user = ''; let pass = ''; let port = 4444; let host; let dom = ''; if (str_url.slice(0, 7) == 'http://') { proto = 'http'; str_url = str_url.slice(7); } else if (str_url.slice(0, 5) == 'ws://') { proto = 'ws'; str_url = str_url.slice(5); } else if (str_url.slice(0, 8) == 'https://') { proto = 'https'; port = 4443; str_url = str_url.slice(8); } else if (str_url.slice(0, 6) == 'wss://') { proto = 'wss'; port = 4443; str_url = str_url.slice(6); } else if (str_url.slice(0, 7) == 'auto://') { str_url = str_url.slice(7); } else if (str_url.slice(0, 9) == 'secure://') { str_url = str_url.slice(9); port = 4443; proto = 'secure'; } str_url = str_url.replace('/not.byn', ''); if (str_url[str_url.length - 1] == '/') { str_url = str_url.slice(0, str_url.length - 1); } let pos = str_url.indexOf('/'); if (pos > 0) { dom = str_url.slice(pos); str_url = str_url.slice(0, pos); } let authpos = str_url.indexOf('@'); if (authpos >= 0) { let auth = str_url.slice(0, authpos); let passpos = auth.indexOf(':'); if (passpos >= 0) { user = auth.slice(0, passpos); pass = auth.slice(passpos + 1); } else { user = auth; } str_url = str_url.slice(authpos + 1); } let endv6 = str_url.indexOf(']'); pos = str_url.indexOf(':'); if (pos > 0 && endv6 > 0 && pos < endv6) { // ipv6 URL pos = str_url.indexOf(':', endv6); } if (pos < 0) { host = str_url; if (dom != '') { if (proto == 'http') { port = 80; } else if (proto == 'https') { port = 443; } } } else { host = str_url.slice(0, pos); port = YAPIContext.imm_atoi(str_url.slice(pos + 1)); } if (host == 'callback') { port = 4444; } this.proto = proto; this.user = user; this.pass = pass; this.host = host; this.port = port; this.domain = dom; } imm_getHost() { return this.host; } imm_getPass() { return this.pass; } imm_getPort() { return this.port; } imm_getUser() { return this.user; } imm_getUrl(withProto = false, withUserPass = true, withEndSlash = false) { if (this.proto == "usb") { return "usb"; } let url = ""; if (withProto) { url += this.proto + "://"; } if (withUserPass && this.user != "") { url += this.user; if (this.pass != "") { url += ":"; url += this.pass; } url += "@"; } url += this.host; url += ":"; url += this.port; url += this.domain; if (withEndSlash && url[url.length - 1] != '/') { url += '/'; } return url; } imm_getRootUrl() { return this.imm_getUrl(true, false, true); } imm_getProto() { return this.proto; } imm_useWebSocket() { return this.proto.startsWith("ws") || this.proto == "auto" || this.proto == "secure"; } /** * @return subdomain (starting with a /) */ imm_getSubDomain() { let dom = this.domain; return dom; } imm_hasAuthParam() { return this.user != ""; } imm_useSecureSocket() { return "wss" == this.proto || "https" == this.proto || "secure" == this.proto; } imm_testInfoJson() { return this.proto == "auto" || this.proto == "secure" || this.proto == "http" || this.proto == "https"; } imm_updateBestProto(proto, port) { this.port = port; if (this.proto != "http" && this.proto != "https") { this.proto = proto; } } imm_updateForRedirect(host, port, is_secure) { this.host = host; this.port = port; if (this.imm_useWebSocket()) { this.proto = is_secure ? "wss" : "ws"; } else { this.proto = is_secure ? "https" : "http"; } } imm_updatePortInfo(proto, port) { this.proto = proto; this.port = port; } imm_getOriginalURL() { return this.orgUrl; } imm_updateFrom(urlInfo) { this.proto = urlInfo.proto; this.user = urlInfo.user; this.pass = urlInfo.pass; this.host = urlInfo.host; this.port = urlInfo.port; this.domain = urlInfo.domain; this.orgUrl = urlInfo.orgUrl; } } /** * MD5 hash computation code * * This code is derived from the MD5 implementation from Sergey Lyubka, author of SHTTPD. * Any other implementation would do as well, but we chose to translate this one to JS. * This code has been published by Sergey under his "THE BEER-WARE LICENSE" (Revision 42): * * Sergey Lyubka wrote this software. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. * */ class Y_MD5Ctx { constructor() { this.buf = new Uint32Array(4); this.bits = new Uint32Array(2); this.inBuf = new ArrayBuffer(64); this.in8 = new Uint8Array(this.inBuf); this.in32 = new Uint32Array(this.inBuf); this.in32[0] = 1; this.bigEndian = (this.in8[0] != 1); this.buf[0] = 0x67452301 >>> 0; this.buf[1] = 0xefcdab89 >>> 0; this.buf[2] = 0x98badcfe >>> 0; this.buf[3] = 0x10325476 >>> 0; this.bits[0] = 0; this.bits[1] = 0; } _byteReverseIn() { for (let i = 0; i < 16; i++) { let a = this.in32[i]; this.in32[i] = ((a >>> 24) | ((a & 0xff) << 24) | ((a & 0xff0000) >>> 8) | ((a & 0xff00) << 8)) >>> 0; } } _transform() { let F1 = ((x, y, z) => ((z ^ (x & (y ^ z)))) >>> 0); let F2 = ((x, y, z) => F1(z, x, y)); let F3 = ((x, y, z) => ((x ^ y ^ z)) >>> 0); let F4 = ((x, y, z) => ((y ^ (x | ~z))) >>> 0); let MD5STEP = ((f, w, x, y, z, data, s) => { w = (w + f(x, y, z) + (data >>> 0)) >>> 0; w = (((w << s) >>> 0) | (w >>> (32 - s))) >>> 0; return (w + x) >>> 0; }); let a = this.buf[0]; let b = this.buf[1]; let c = this.buf[2]; let d = this.buf[3]; let dataIn = this.in32; a = MD5STEP(F1, a, b, c, d, dataIn[0] + 0xd76aa478, 7); d = MD5STEP(F1, d, a, b, c, dataIn[1] + 0xe8c7b756, 12); c = MD5STEP(F1, c, d, a, b, dataIn[2] + 0x242070db, 17); b = MD5STEP(F1, b, c, d, a, dataIn[3] + 0xc1bdceee, 22); a = MD5STEP(F1, a, b, c, d, dataIn[4] + 0xf57c0faf, 7); d = MD5STEP(F1, d, a, b, c, dataIn[5] + 0x4787c62a, 12); c = MD5STEP(F1, c, d, a, b, dataIn[6] + 0xa8304613, 17); b = MD5STEP(F1, b, c, d, a, dataIn[7] + 0xfd469501, 22); a = MD5STEP(F1, a, b, c, d, dataIn[8] + 0x698098d8, 7); d = MD5STEP(F1, d, a, b, c, dataIn[9] + 0x8b44f7af, 12); c = MD5STEP(F1, c, d, a, b, dataIn[10] + 0xffff5bb1, 17); b = MD5STEP(F1, b, c, d, a, dataIn[11] + 0x895cd7be, 22); a = MD5STEP(F1, a, b, c, d, dataIn[12] + 0x6b901122, 7); d = MD5STEP(F1, d, a, b, c, dataIn[13] + 0xfd987193, 12); c = MD5STEP(F1, c, d, a, b, dataIn[14] + 0xa679438e, 17); b = MD5STEP(F1, b, c, d, a, dataIn[15] + 0x49b40821, 22); a = MD5STEP(F2, a, b, c, d, dataIn[1] + 0xf61e2562, 5); d = MD5STEP(F2, d, a, b, c, dataIn[6] + 0xc040b340, 9); c = MD5STEP(F2, c, d, a, b, dataIn[11] + 0x265e5a51, 14); b = MD5STEP(F2, b, c, d, a, dataIn[0] + 0xe9b6c7aa, 20); a = MD5STEP(F2, a, b, c, d, dataIn[5] + 0xd62f105d, 5); d = MD5STEP(F2, d, a, b, c, dataIn[10] + 0x02441453, 9); c = MD5STEP(F2, c, d, a, b, dataIn[15] + 0xd8a1e681, 14); b = MD5STEP(F2, b, c, d, a, dataIn[4] + 0xe7d3fbc8, 20); a = MD5STEP(F2, a, b, c, d, dataIn[9] + 0x21e1cde6, 5); d = MD5STEP(F2, d, a, b, c, dataIn[14] + 0xc33707d6, 9); c = MD5STEP(F2, c, d, a, b, dataIn[3] + 0xf4d50d87, 14); b = MD5STEP(F2, b, c, d, a, dataIn[8] + 0x455a14ed, 20); a = MD5STEP(F2, a, b, c, d, dataIn[13] + 0xa9e3e905, 5); d = MD5STEP(F2, d, a, b, c, dataIn[2] + 0xfcefa3f8, 9); c = MD5STEP(F2, c, d, a, b, dataIn[7] + 0x676f02d9, 14); b = MD5STEP(F2, b, c, d, a, dataIn[12] + 0x8d2a4c8a, 20); a = MD5STEP(F3, a, b, c, d, dataIn[5] + 0xfffa3942, 4); d = MD5STEP(F3, d, a, b, c, dataIn[8] + 0x8771f681, 11); c = MD5STEP(F3, c, d, a, b, dataIn[11] + 0x6d9d6122, 16); b = MD5STEP(F3, b, c, d, a, dataIn[14] + 0xfde5380c, 23); a = MD5STEP(F3, a, b, c, d, dataIn[1] + 0xa4beea44, 4); d = MD5STEP(F3, d, a, b, c, dataIn[4] + 0x4bdecfa9, 11); c = MD5STEP(F3, c, d, a, b, dataIn[7] + 0xf6bb4b60, 16); b = MD5STEP(F3, b, c, d, a, dataIn[10] + 0xbebfbc70, 23); a = MD5STEP(F3, a, b, c, d, dataIn[13] + 0x289b7ec6, 4); d = MD5STEP(F3, d, a, b, c, dataIn[0] + 0xeaa127fa, 11); c = MD5STEP(F3, c, d, a, b, dataIn[3] + 0xd4ef3085, 16); b = MD5STEP(F3, b, c, d, a, dataIn[6] + 0x04881d05, 23); a = MD5STEP(F3, a, b, c, d, dataIn[9] + 0xd9d4d039, 4); d = MD5STEP(F3, d, a, b, c, dataIn[12] + 0xe6db99e5, 11); c = MD5STEP(F3, c, d, a, b, dataIn[15] + 0x1fa27cf8, 16); b = MD5STEP(F3, b, c, d, a, dataIn[2] + 0xc4ac5665, 23); a = MD5STEP(F4, a, b, c, d, dataIn[0] + 0xf4292244, 6); d = MD5STEP(F4, d, a, b, c, dataIn[7] + 0x432aff97, 10); c = MD5STEP(F4, c, d, a, b, dataIn[14] + 0xab9423a7, 15); b = MD5STEP(F4, b, c, d, a, dataIn[5] + 0xfc93a039, 21); a = MD5STEP(F4, a, b, c, d, dataIn[12] + 0x655b59c3, 6); d = MD5STEP(F4, d, a, b, c, dataIn[3] + 0x8f0ccc92, 10); c = MD5STEP(F4, c, d, a, b, dataIn[10] + 0xffeff47d, 15); b = MD5STEP(F4, b, c, d, a, dataIn[1] + 0x85845dd1, 21); a = MD5STEP(F4, a, b, c, d, dataIn[8] + 0x6fa87e4f, 6); d = MD5STEP(F4, d, a, b, c, dataIn[15] + 0xfe2ce6e0, 10); c = MD5STEP(F4, c, d, a, b, dataIn[6] + 0xa3014314, 15); b = MD5STEP(F4, b, c, d, a, dataIn[13] + 0x4e0811a1, 21); a = MD5STEP(F4, a, b, c, d, dataIn[4] + 0xf7537e82, 6); d = MD5STEP(F4, d, a, b, c, dataIn[11] + 0xbd3af235, 10); c = MD5STEP(F4, c, d, a, b, dataIn[2] + 0x2ad7d2bb, 15); b = MD5STEP(F4, b, c, d, a, dataIn[9] + 0xeb86d391, 21); this.buf[0] = ((this.buf[0] + a) & 0xffffffff) >>> 0; this.buf[1] = ((this.buf[1] + b) & 0xffffffff) >>> 0; this.buf[2] = ((this.buf[2] + c) & 0xffffffff) >>> 0; this.buf[3] = ((this.buf[3] + d) & 0xffffffff) >>> 0; } addData(buf) { let len = buf.length; let pos = 0; let t = this.bits[0]; this.bits[0] = (t + (len << 3)) >>> 0; if (this.bits[0] < t) { this.bits[1]++; } this.bits[1] += len >>> 29; t = (t >>> 3) & 0x3f; while (pos < len) { while (pos < len && t < 64) { this.in8[t++] = buf[pos++]; } if (t < 64) return; if (this.bigEndian) this._byteReverseIn(); this._transform(); t = 0; } } calculate() { let t = (this.bits[0] >>> 3) & 0x3f; this.in8[t++] = 0x80; if (t > 56) { while (t < 64) { this.in8[t++] = 0; } if (this.bigEndian) this._byteReverseIn(); this._transform(); for (t = 0; t < 14; t++) { this.in32[t] = 0; } } else { while (t < 56) { this.in8[t++] = 0; } if (this.bigEndian) this._byteReverseIn(); } this.in32[14] = this.bits[0]; this.in32[15] = this.bits[1]; this._transform(); let res = new Uint8Array(16); for (t = 0; t < 16; t++) { res[t] = (this.buf[t >>> 2] >>> (8 * (t & 3))) & 0xff; } return res; } } // // YFunctionType Class (used internally) // // Instances of this class stores everything we know about a given type of function: // Mapping between function logical names and Hardware ID as discovered on hubs, // and existing instances of YFunction (either already connected or simply requested). // To keep it simple, this implementation separates completely the name resolution // mechanism, implemented using the yellow pages, and the storage and retrieval of // existing YFunction instances. // class YFunctionType { constructor(yapi, classname) { this._yapi = yapi; this._className = classname; this._connectedFns = {}; // functions requested and available, by Hardware Id this._requestedFns = {}; // functions requested but not yet known, by any type of name this._hwIdByName = {}; // hash table of function Hardware Id by logical name this._nameByHwId = {}; // hash table of function logical name by Hardware Id this._valueByHwId = {}; // hash table of function advertised value by logical name this._baseType = 0; // default to no abstract base type (generic YFunction) } /** Index a single function given by HardwareId and logical name; store any advertised value * * @returns true iff there was a logical name discrepancy */ imm_reindexFunction(str_hwid, str_name, str_val, int_basetype) { let currname = this._nameByHwId[str_hwid]; let res = false; if (currname == undefined || currname == '') { if (str_name != '') { this._nameByHwId[str_hwid] = str_name; res = true; } } else if (currname != str_name) { if (this._hwIdByName[currname] == str_hwid) { delete this._hwIdByName[currname]; } if (str_name != '') { this._nameByHwId[str_hwid] = str_name; } else { delete this._nameByHwId[str_hwid]; } res = true; } if (str_name != '') { this._hwIdByName[str_name] = str_hwid; } if (str_val) { this._valueByHwId[str_hwid] = str_val; } else { if (this._valueByHwId[str_hwid] == undefined) { this._valueByHwId[str_hwid] = ''; } } if (int_basetype) { if (this._baseType == 0) { this._baseType = int_basetype; } } return res; } /** Forget a disconnected function given by HardwareId */ imm_forgetFunction(str_hwid) { let currname = this._nameByHwId[str_hwid]; if (currname != undefined) { if (currname != '' && this._hwIdByName[currname] == str_hwid) { delete this._hwIdByName[currname]; } delete this._nameByHwId[str_hwid]; } if (this._valueByHwId[str_hwid] != undefined) { delete this._valueByHwId[str_hwid]; } // Move the function object to the disconnected list let con_fn = this._connectedFns[str_hwid]; if (con_fn) { this._requestedFns[str_hwid] = con_fn; delete this._connectedFns[str_hwid]; } } /** Find the exact Hardware Id of the specified function, if currently connected * If device is not known as connected, return a clean error * This function will not cause any network access */ imm_resolve(str_func) { let res; let dotpos = str_func.indexOf('.'); if (dotpos < 0) { // First case: str_func is the logicalname of a function res = this._hwIdByName[str_func]; if (res != undefined) { return { errorType: YAPI_SUCCESS, errorMsg: 'no error', result: String(res) }; } // fallback to assuming that str_func is a logicalname or serial number of a module // with an implicit function name (like serial.module for instance) dotpos = str_func.length; str_func += '.' + this._className.substr(0, 1).toLowerCase() + this._className.substr(1); } // Second case: str_func is in the form: device_id.function_id // quick lookup for a known pure hardware id if (this._valueByHwId[str_func] != undefined) { return { errorType: YAPI_SUCCESS, errorMsg: 'no error', result: String(str_func) }; } let serial = ''; let funcid; if (dotpos > 0) { // either the device id is a logical name, or the function is unknown let devid = str_func.substr(0, dotpos); funcid = str_func.substr(dotpos + 1); let dev = this._yapi.imm_getDevice(devid); if (!dev) { return { errorType: YAPI_DEVICE_NOT_FOUND, errorMsg: 'Device [' + devid + '] not online', result: '' }; } serial = dev.imm_getSerialNumber(); res = serial + '.' + funcid; if (this._valueByHwId[res] != undefined) { return { errorType: YAPI_SUCCESS, errorMsg: 'no error', result: String(res) }; } // not found neither, may be funcid is a function logicalname let i, nfun = dev.imm_functionCount(); for (i = 0; i < nfun; i++) { res = serial + '.' + dev.imm_functionId(i); let name = this._nameByHwId[res]; if (name != undefined && name == funcid) { return { errorType: YAPI_SUCCESS, errorMsg: 'no error', result: String(res) }; } } } else { funcid = str_func.substr(1); for (let hwid_str in this._connectedFns) { let pos = hwid_str.indexOf('.'); let str_function = hwid_str.substr(pos + 1); if (str_function == funcid) { return { errorType: YAPI_SUCCESS, errorMsg: 'no error', result: String(hwid_str) }; } } } return { errorType: YAPI_DEVICE_NOT_FOUND, errorMsg: 'No function [' + funcid + '] found on device [' + serial + ']', result: '' }; } /** Find the friendly name (use logical name if available) of the specified function, if currently connected * If device is not known as connected, return a clean error * This function will not cause any network access */ imm_getFriendlyName(str_func) { let resolved = this.imm_resolve(str_func); let name; if (resolved.errorType != YAPI_SUCCESS) { return resolved; } let hwId = resolved.result; if (this._className == 'Module') { let friend = hwId; name = this._nameByHwId[friend]; if (name != undefined && name != '') { friend = this._nameByHwId[friend]; } return { errorType: YAPI_SUCCESS, errorMsg: 'no error', result: String(friend) }; } else { let pos = hwId.indexOf('.'); let str_serialMod = hwId.substr(0, pos); let str_friendModFull = this._yapi.imm_getFriendlyNameFunction('Module', str_serialMod).result; let int_friendModDot = str_friendModFull.indexOf('.'); let str_friendMod = (int_friendModDot > 0 ? str_friendModFull.substr(0, int_friendModDot) : str_friendModFull); let str_friendFunc = hwId.substr(pos + 1); name = this._nameByHwId[hwId]; if (name != undefined && name != '') { str_friendFunc = name; } return { errorType: YAPI_SUCCESS, errorMsg: 'no error', result: String(str_friendMod + '.' + str_friendFunc) }; } } /** Associates a given function object to a function id */ imm_setFunction(str_func, obj_func) { let funres = this.imm_resolve(str_func); if (funres.result) { // the function has been located on a device this._connectedFns[funres.result] = obj_func; } else { // the function is still abstract this._requestedFns[str_func] = obj_func; } } /** Retrieve a function object by hardware id, updating the indexes on the fly if needed */ imm_getFunction(str_func) { let funres = this.imm_resolve(str_func); if (funres.errorType == YAPI_SUCCESS) { // the function has been located on a device let conn_fn = this._connectedFns[funres.result]; if (conn_fn != undefined) { return conn_fn; } let req_fn = this._requestedFns[str_func]; if (req_fn != undefined) { this._connectedFns[funres.result] = req_fn; delete this._requestedFns[str_func]; } return req_fn; } else { // the function is still abstract return this._requestedFns[str_func]; } } /** Stores a function advertised value by hardware id, and tell if an event should be queued for it */ imm_setFunctionValue(str_hwid, str_pubval) { let currval = this._valueByHwId[str_hwid]; if (!(currval == undefined) && currval == str_pubval) { return false; } this._valueByHwId[str_hwid] = str_pubval; return true; } /** Retrieve a function advertised value by hardware id */ imm_getFunctionValue(str_hwid) { return this._valueByHwId[str_hwid]; } /** Return the basetype of this function class */ imm_getBaseType() { return this._baseType; } /** Test if function type is compatible with basetype */ imm_matchBaseType(baseclass) { return baseclass == YOCTO_BASETYPE_FUNCTION || baseclass == this._baseType; } /** Find the hardwareId of the first instance of a given function class */ imm_getFirstHardwareId() { let res = null; //noinspection LoopStatementThatDoesntLoopJS for (res in this._valueByHwId) { break; } return res; } /** Find the hardwareId for the next instance of a given function class */ imm_getNextHardwareId(str_hwid) { for (let iter_hwid in this._valueByHwId) { if (str_hwid == '!') { return iter_hwid; } if (str_hwid == iter_hwid) { str_hwid = '!'; } } return null; // no more instance found } } export class YHTTPBody { /** Object storing a file to upload */ constructor(str_fname, bin_data, fun_progressCb) { this.fname = str_fname; this.data = bin_data; this.progressCb = fun_progressCb; } } export class YHTTPRequest { /** Object storing the result of any HTTP Query, with status code and error message */ constructor(bin_res, int_errType = YAPI_SUCCESS, str_errMsg = 'no error') { this.devUrl = null; this.obj_result = null; this.asyncId = 0; this.acceptor = null; this.toBeSent = null; this.sendPos = 0; this.progressCb = null; this.timeoutId = null; /* actually a number | NodeJS.Timeout */ this.sendTimeoutId = null; /* actually a number | NodeJS.Timeout */ this.next = null; this._creat = ''; this._sent = ''; this.bin_result = bin_res; this.errorType = int_errType; this.errorMsg = str_errMsg; } } class YFuncRequest { /* Object storing the result of a function request, with status code and error message */ constructor(obj_res, int_errType = YAPI_SUCCESS, str_errMsg = 'no error') { this.errorType = int_errType; this.errorMsg = str_errMsg; this.obj_result = obj_res; } } //--- (generated code: YDataStream definitions) //--- (end of generated code: YDataStream definitions) //--- (generated code: YDataStream class start) /** * YDataStream Class: Unformatted data sequence * * DataStream objects represent bare recorded measure sequences, * exactly as found within the data logger present on Yoctopuce * sensors. * * In most cases, it is not necessary to use DataStream objects * directly, as the DataSet objects (returned by the * get_recordedData() method from sensors and the * get_dataSets() method from the data logger) provide * a more convenient interface. */ //--- (end of generated code: YDataStream class start) export class YDataStream { // API symbols as static members //--- (end of generated code: YDataStream attributes declaration) constructor(obj_parent, obj_dataset, encoded) { //--- (generated code: YDataStream constructor) //--- (end of generated code: YDataStream constructor) this._cal = null; this._runNo = 0; this._utcStamp = 0; this._nCols = 0; this._nRows = 0; this._startTime = 0; this._duration = 0; this._dataSamplesInterval = 0; this._firstMeasureDuration = 0; this._columnNames = []; this._functionId = ''; this._isClosed = false; this._isAvg = false; this._minVal = 0; this._avgVal = 0; this._maxVal = 0; this._values = []; this._isLoaded = false; this.DATA_INVALID = YAPI_INVALID_DOUBLE; this.DURATION_INVALID = YAPI_INVALID_DOUBLE; this._parent = obj_parent; this._yapi = this._parent._yapi; if (typeof obj_dataset != 'undefined') { this.imm_initFromDataSet(obj_dataset, encoded); } } //--- (generated code: YDataStream implementation) _parseCalibArr(iCalib) { let caltyp; let calhdl; let maxpos; let position; let calpar = []; let calraw = []; let calref = []; let fRaw; let fRef; caltyp = ((iCalib[0] / 1000) >> 0); if (caltyp < YOCTO_CALIB_TYPE_OFS) { // Unknown calibration type: calibrated value will be provided by the device this._cal = null; return YAPI_SUCCESS; } calhdl = this._yapi.imm_getCalibrationHandler(caltyp); if (!(calhdl != null)) { // Unknown calibration type: calibrated value will be provided by the device this._cal = null; return YAPI_SUCCESS; } // New 32 bits text format maxpos = iCalib.length; calpar.length = 0; position = 1; while (position < maxpos) { calpar.push(iCalib[position]); position = position + 1; } calraw.length = 0; calref.length = 0; position = 1; while (position + 1 < maxpos) { fRaw = iCalib[position]; fRaw = fRaw / 1000.0; fRef = iCalib[position + 1]; fRef = fRef / 1000.0; calraw.push(fRaw); calref.push(fRef); position = position + 2; } this._cal = { src: '', hdl: calhdl, typ: caltyp, par: calpar, raw: calraw, cal: calref }; return YAPI_SUCCESS; } imm_initFromDataSet(dataset, encoded) { let val; let ms_offset; let samplesPerHour; let caltyp; let iCalib = []; // decode sequence header to extract data this._runNo = encoded[0] + ((encoded[1] << 16)); this._utcStamp = encoded[2] + ((encoded[3] << 16)); val = encoded[4]; this._isAvg = ((val & 0x100) == 0); samplesPerHour = (val & 0xff); if ((val & 0x100) != 0) { samplesPerHour = samplesPerHour * 3600; } else { if ((val & 0x200) != 0) { samplesPerHour = samplesPerHour * 60; } } this._dataSamplesInterval = 3600.0 / samplesPerHour; ms_offset = encoded[6]; if (ms_offset < 1000) { // new encoding -> add the ms to the UTC timestamp this._startTime = this._utcStamp + (ms_offset / 1000.0); } else { // legacy encoding subtract the measure interval form the UTC timestamp this._startTime = this._utcStamp - this._dataSamplesInterval; } this._firstMeasureDuration = encoded[5]; if (!(this._isAvg)) { this._firstMeasureDuration = this._firstMeasureDuration / 1000.0; } val = encoded[7]; this._isClosed = (val != 0xffff); if (val == 0xffff) { val = 0; } this._nRows = val; if (this._nRows > 0) { if (this._firstMeasureDuration > 0) { this._duration = this._firstMeasureDuration + (this._nRows - 1) * this._dataSamplesInterval; } else { this._duration = this._nRows * this._dataSamplesInterval; } } else { this._duration = 0; } // precompute decoding parameters iCalib = dataset.imm_get_calibration(); caltyp = iCalib[0]; if (caltyp == 0) { this._cal = null; } else { this._parseCalibArr(iCalib); } // preload column names for backward-compatibility this._functionId = dataset.imm_get_functionId(); if (this._isAvg) { this._columnNames.length = 0; this._columnNames.push(this._functionId + '_min'); this._columnNames.push(this._functionId + '_avg'); this._columnNames.push(this._functionId + '_max'); this._nCols = 3; } else { this._columnNames.length = 0; this._columnNames.push(this._functionId); this._nCols = 1; } // decode min/avg/max values for the sequence if (this._nRows > 0) { this._avgVal = this.imm_decodeAvg(encoded[8] + (((encoded[9] ^ 0x8000) << 16)), 1); this._minVal = this.imm_decodeVal(encoded[10] + ((encoded[11] << 16))); this._maxVal = this.imm_decodeVal(encoded[12] + ((encoded[13] << 16))); } return 0; } imm_parseStream(sdata) { let idx; let udat = []; let dat = []; if (this._isLoaded && !(this._isClosed)) { return YAPI_SUCCESS; } if ((sdata).length == 0) { this._nRows = 0; return YAPI_SUCCESS; } udat = this._yapi.imm_decodeWords(this._parent.imm_json_get_string(sdata)); this._values.length = 0; idx = 0; if (this._isAvg) { while (idx + 3 < udat.length) { dat.length = 0; if ((udat[idx] == 65535) && (udat[idx + 1] == 65535)) { dat.push(NaN); dat.push(NaN); dat.push(NaN); } else { dat.push(this.imm_decodeVal(udat[idx + 2] + (((udat[idx + 3]) << 16)))); dat.push(this.imm_decodeAvg(udat[idx] + ((((udat[idx + 1]) ^ 0x8000) << 16)), 1)); dat.push(this.imm_decodeVal(udat[idx + 4] + (((udat[idx + 5]) << 16)))); } idx = idx + 6; this._values.push(dat.slice()); } } else { while (idx + 1 < udat.length) { dat.length = 0; if ((udat[idx] == 65535) && (udat[idx + 1] == 65535)) { dat.push(NaN); } else { dat.push(this.imm_decodeAvg(udat[idx] + ((((udat[idx + 1]) ^ 0x8000) << 16)), 1)); } this._values.push(dat.slice()); idx = idx + 2; } } this._nRows = this._values.length; this._isLoaded = true; return YAPI_SUCCESS; } imm_wasLoaded() { return this._isLoaded; } imm_get_url() { let url; url = 'logger.json?id=' + this._functionId + '&run=' + String(Math.round(this._runNo)) + '&utc=' + String(Math.round(this._utcStamp)); return url; } imm_get_baseurl() { let url; url = 'logger.json?id=' + this._functionId + '&run=' + String(Math.round(this._runNo)) + '&utc='; return url; } imm_get_urlsuffix() { let url; url = String(Math.round(this._utcStamp)); return url; } async loadStream() { return this.imm_parseStream(await this._parent._download(this.imm_get_url())); } imm_decodeVal(w) { let val; val = (w) / 1000.0; if (!(this._cal == null)) { val = this._cal.hdl(val, this._cal.typ, this._cal.par, this._cal.raw, this._cal.cal); } return val; } imm_decodeAvg(dw, count) { let val; val = (dw) / 1000.0; if (!(this._cal == null)) { val = this._cal.hdl(val, this._cal.typ, this._cal.par, this._cal.raw, this._cal.cal); } return val; } async isClosed() { return this._isClosed; } /** * Returns the run index of the data stream. A run can be made of * multiple datastreams, for different time intervals. * * @return an unsigned number corresponding to the run index. */ async get_runIndex() { return this._runNo; } /** * Returns the relative start time of the data stream, measured in seconds. * For recent firmwares, the value is relative to the present time, * which means the value is always negative. * If the device uses a firmware older than version 13000, value is * relative to the start of the time the device was powered on, and * is always positive. * If you need an absolute UTC timestamp, use get_realStartTimeUTC(). * * <b>DEPRECATED</b>: This method has been replaced by get_realStartTimeUTC(). * * @return an unsigned number corresponding to the number of seconds * between the start of the run and the beginning of this data * stream. */ async get_startTime() { return this._utcStamp - ((Date.now() / 1000) >> 0); } /** * Returns the start time of the data stream, relative to the Jan 1, 1970. * If the UTC time was not set in the datalogger at the time of the recording * of this data stream, this method returns 0. * * <b>DEPRECATED</b>: This method has been replaced by get_realStartTimeUTC(). * * @return an unsigned number corresponding to the number of seconds * between the Jan 1, 1970 and the beginning of this data * stream (i.e. Unix time representation of the absolute time). */ async get_startTimeUTC() { return Math.round(this._startTime); } /** * Returns the start time of the data stream, relative to the Jan 1, 1970. * If the UTC time was not set in the datalogger at the time of the recording * of this data stream, this method returns 0. * * @return a floating-point number corresponding to the number of seconds * between the Jan 1, 1970 and the beginning of this data * stream (i.e. Unix time representation of the absolute time). */ async get_realStartTimeUTC() { return this._startTime; } /** * Returns the number of milliseconds between two consecutive * rows of this data stream. By default, the data logger records one row * per second, but the recording frequency can be changed for * each device function * * @return an unsigned number corresponding to a number of milliseconds. */ async get_dataSamplesIntervalMs() { return Math.round(this._dataSamplesInterval * 1000); } async get_dataSamplesInterval() { return this._dataSamplesInterval; } async get_firstDataSamplesInterval() { return this._firstMeasureDuration; } /** * Returns the number of data rows present in this stream. * * If the device uses a firmware older than version 13000, * this method fetches the whole data stream from the device * if not yet done, which can cause a little delay. * * @return an unsigned number corresponding to the number of rows. * * On failure, throws an exception or returns zero. */ async get_rowCount() { if ((this._nRows != 0) && this._isClosed) { return this._nRows; } await this.loadStream(); return this._nRows; } /** * Returns the number of data columns present in this stream. * The meaning of the values present in each column can be obtained * using the method get_columnNames(). * * If the device uses a firmware older than version 13000, * this method fetches the whole data stream from the device * if not yet done, which can cause a little delay. * * @return an unsigned number corresponding to the number of columns. * * On failure, throws an exception or returns zero. */ async get_columnCount() { if (this._nCols != 0) { return this._nCols; } await this.loadStream(); return this._nCols; } /** * Returns the title (or meaning) of each data column present in this stream. * In most case, the title of the data column is the hardware identifier * of the sensor that produced the data. For streams recorded at a lower * recording rate, the dataLogger stores the min, average and max value * during each measure interval into three columns with suffixes _min, * _avg and _max respectively. * * If the device uses a firmware older than version 13000, * this method fetches the whole data stream from the device * if not yet done, which can cause a little delay. * * @return a list containing as many strings as there are columns in the * data stream. * * On failure, throws an exception or returns an empty array. */ async get_columnNames() { if (this._columnNames.length != 0) { return this._columnNames; } await this.loadStream(); return this._columnNames; } /** * Returns the smallest measure observed within this stream. * If the device uses a firmware older than version 13000, * this method will always return YDataStream.DATA_INVALID. * * @return a floating-point number corresponding to the smallest value, * or YDataStream.DATA_INVALID if the stream is not yet complete (still recording). * * On failure, throws an exception or returns YDataStream.DATA_INVALID. */ async get_minValue() { return this._minVal; } /** * Returns the average of all measures observed within this stream. * If the device uses a firmware older than version 13000, * this method will always return YDataStream.DATA_INVALID. * * @return a floating-point number corresponding to the average value, * or YDataStream.DATA_INVALID if the stream is not yet complete (still recording). * * On failure, throws an exception or returns YDataStream.DATA_INVALID. */ async get_averageValue() { return this._avgVal; } /** * Returns the largest measure observed within this stream. * If the device uses a firmware older than version 13000, * this method will always return YDataStream.DATA_INVALID. * * @return a floating-point number corresponding to the largest value, * or YDataStream.DATA_INVALID if the stream is not yet complete (still recording). * * On failure, throws an exception or returns YDataStream.DATA_INVALID. */ async get_maxValue() { return this._maxVal; } async get_realDuration() { if (this._isClosed) { return this._duration; } return ((Date.now() / 1000) >> 0) - this._utcStamp; } /** * Returns the whole data set contained in the stream, as a bidimensional * table of numbers. * The meaning of the values present in each column can be obtained * using the method get_columnNames(). * * This method fetches the whole data stream from the device, * if not yet done. * * @return a list containing as many elements as there are rows in the * data stream. Each row itself is a list of floating-point * numbers. * * On failure, throws an exception or returns an empty array. */ async get_dataRows() { if ((this._values.length == 0) || !(this._isClosed)) { await this.loadStream(); } return this._values;