UNPKG

@robotical/ricjs

Version:

Javascript/TS library for Robotical RIC

672 lines 25.8 kB
"use strict"; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // RICJS // Communications Library // // Rob Dobson & Chris Greening 2020-2022 // (C) 2020-2022 // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// Object.defineProperty(exports, "__esModule", { value: true }); const tslib_1 = require("tslib"); const RICWifiTypes_1 = require("./RICWifiTypes"); const RICLog_1 = tslib_1.__importDefault(require("./RICLog")); const RICTypes_1 = require("./RICTypes"); class RICSystem { /** * constructor * @param ricMsgHandler * @param addOnManager */ constructor(ricMsgHandler, addOnManager) { // System info this._systemInfo = null; // RIC naming this._ricFriendlyName = null; // HWElems (connected to RIC) - excluding AddOns this._hwElemsExcludingAddOns = new Array(); this._connectedAddOns = new Array(); // Calibration info this._calibInfo = null; // WiFi connection info this._ricWifiConnStatus = new RICWifiTypes_1.RICWifiConnStatus(); this._defaultWiFiHostname = "Marty"; this._maxSecsToWaitForWiFiConn = 20; this._ricMsgHandler = ricMsgHandler; this._addOnManager = addOnManager; } /** * getFriendlyName * * @returns friendly name */ getFriendlyName() { return this._ricFriendlyName; } /** * invalidate */ invalidate() { // Invalidate system info this._systemInfo = null; this._hwElemsExcludingAddOns = new Array(); this._connectedAddOns = new Array(); this._addOnManager.clear(); this._calibInfo = null; this._ricFriendlyName = null; RICLog_1.default.debug("RICSystem information invalidated"); } /** * getSystemInfo - get system info * @returns Promise<RICSystemInfo> * */ async retrieveInfo() { var _a; // Get HWElems (connected to RIC) try { await this.getHWElemList(); } catch (error) { RICLog_1.default.warn("retrieveInfo - failed to get HWElems " + error); return false; } // Get system info RICLog_1.default.debug(`RICSystem retrieveInfo getting system info`); try { await this.getRICSystemInfo(true); RICLog_1.default.debug(`retrieveInfo - RIC Version ${(_a = this._systemInfo) === null || _a === void 0 ? void 0 : _a.SystemVersion}`); } catch (error) { RICLog_1.default.warn("retrieveInfo - frailed to get version " + error); return false; } // Get RIC name try { await this.getRICName(); } catch (error) { RICLog_1.default.warn("retrieveInfo - failed to get RIC name " + error); return false; } // Get calibration info try { await this.getRICCalibInfo(true); } catch (error) { RICLog_1.default.warn("retrieveInfo - failed to get calib info " + error); return false; } // Get WiFi connected info try { await this.getWiFiConnStatus(); } catch (error) { RICLog_1.default.warn("retrieveInfo - failed to get WiFi Status " + error); return false; } // Get HWElems (connected to RIC) try { await this.getHWElemList(); } catch (error) { RICLog_1.default.warn("retrieveInfo - failed to get HWElems " + error); return false; } return true; } /** * * getRICSystemInfo * @returns Promise<RICSystemInfo> * */ async getRICSystemInfo(forceGetFromRIC = false) { if (!forceGetFromRIC && this._systemInfo) { return this._systemInfo; } try { this._systemInfo = await this._ricMsgHandler.sendRICRESTURL("v"); RICLog_1.default.debug("getRICSystemInfo returned " + JSON.stringify(this._systemInfo)); this._systemInfo.validMs = Date.now(); return this._systemInfo; } catch (error) { RICLog_1.default.debug(`getRICSystemInfo Failed to get version ${error}`); return new RICTypes_1.RICSystemInfo(); } } // Mark: Calibration ----------------------------------------------------------------------------------------- async calibrate(cmd, jointList, jointNames, servoControllers = { "LeftHip": "LeftHip", "LeftTwist": "LeftHip", "LeftKnee": "LeftHip", "RightHip": "RightHip", "RightTwist": "RightHip", "RightKnee": "RightHip", "LeftArm": "LeftArm", "RightArm": "LeftArm", "Eyes": "LeftArm" }) { let overallResult = true; if (cmd === "set") { const controllerArray = []; // Set calibration for (const jnt of jointList) { try { // Set calibration on joint const cmdUrl = "calibrate/set/" + jnt; const rsl = await this._ricMsgHandler.sendRICRESTURL(cmdUrl); // saving the calibration... (For the new servo boards it is necessary // to send a "save" command after the calibration ones or any servo // parameter changes in order to save any changes made into nonvolatile storage) // log it now, and we'll do the saveparams commands once we've updated all the joints // the saveparams function is not instant as the flash write takes a few ms if (jnt in servoControllers && !controllerArray.includes(servoControllers[jnt])) controllerArray.push(servoControllers[jnt]); if (rsl.rslt != "ok") overallResult = false; } catch (error) { console.log(`calibrate failed on joint ${jnt}`, error); } // Wait as writing to flash blocks servo access // as of v0.0.113 of firmware, the pause is no longer required //await new Promise(resolve => setTimeout(resolve, 3000)); } // on newer (batch4+) robots with stm32 servo controllers it is necessary to send a saveparams command once per servo controller for (const cID in controllerArray) { const saveCalibCmd = `elem/${controllerArray[cID]}/saveparams`; const rslt = await this._ricMsgHandler.sendRICRESTURL(saveCalibCmd); if (rslt.rslt != "ok") overallResult = false; await new Promise(resolve => setTimeout(resolve, 200)); } // ensure all joints are enabled for (const jnt in jointNames) { try { // enable joint const cmdUrl = "servo/" + jnt + "/enable/1"; const rsl = await this._ricMsgHandler.sendRICRESTURL(cmdUrl); if (rsl.rslt != "ok") overallResult = false; } catch (error) { console.log(`enable failed on joint ${jnt}`, error); } } // Result console.log("Set calibration flag to true"); const rslt = new RICTypes_1.RICOKFail(); rslt.set(overallResult); return rslt; } return false; } /** * * getRICCalibInfo * @returns Promise<RICCalibInfo> * */ async getRICCalibInfo(forceGetFromRIC = false) { if (!forceGetFromRIC && this._calibInfo) { return this._calibInfo; } try { this._calibInfo = await this._ricMsgHandler.sendRICRESTURL("calibrate"); RICLog_1.default.debug("getRICCalibInfo returned " + this._calibInfo); this._calibInfo.validMs = Date.now(); return this._calibInfo; } catch (error) { RICLog_1.default.debug(`getRICCalibInfo Failed to get version ${error}`); return new RICTypes_1.RICCalibInfo(); } } /** * * setRICName * @param newName name to refer to RIC - used for BLE advertising * @returns Promise<boolean> true if successful * */ async setRICName(newName) { try { this._ricFriendlyName = await this._ricMsgHandler.sendRICRESTURL(`friendlyname/${newName}`); if (this._ricFriendlyName) { this._ricFriendlyName.friendlyNameIsSet = false; this._ricFriendlyName.validMs = Date.now(); if (this._ricFriendlyName && this._ricFriendlyName.rslt && this._ricFriendlyName.rslt.toLowerCase() === "ok") { this._ricFriendlyName.friendlyNameIsSet = true; } RICLog_1.default.debug("setRICName returned " + JSON.stringify(this._ricFriendlyName)); return true; } return true; } catch (error) { this._ricFriendlyName = null; return false; } } /** * * getRICName * @returns Promise<RICNameResponse> (object containing rslt) * */ async getRICName() { try { this._ricFriendlyName = await this._ricMsgHandler.sendRICRESTURL("friendlyname"); if (this._ricFriendlyName && this._ricFriendlyName.rslt && this._ricFriendlyName.rslt === "ok") { this._ricFriendlyName.friendlyNameIsSet = this._ricFriendlyName .friendlyNameIsSet ? true : false; } else { this._ricFriendlyName.friendlyNameIsSet = false; } this._ricFriendlyName.validMs = Date.now(); RICLog_1.default.debug("Friendly name set is: " + JSON.stringify(this._ricFriendlyName)); return this._ricFriendlyName; } catch (error) { return new RICTypes_1.RICFriendlyName(); } } /** * * getHWElemList - get the list of hardware elements connected to the robot * - the result (if successful) is processed as follows: * = if no filter is applied then all non-add-ons found are stored in * this._hwElemsExcludingAddOns and all addons are stored in this._connectedAddOns * = if a filter is applied and this filter is RSAddOns then this._connectedAddOns is * updated with the new list of addons * = in all cases the discovered list is returned * * @returns Promise<RICHWElemList> * */ async getHWElemList(filterByType) { // Form a list of the requests to make const reqList = []; let addToNonAddOnsList = false; if (!filterByType) { reqList.push("SmartServo"); reqList.push("RSAddOn"); reqList.push("BusPixels"); reqList.push("!SmartServo,RSAddOn,BusPixels"); // not SmartServo or RSAddOn or BusPixels this._hwElemsExcludingAddOns = new Array(); addToNonAddOnsList = true; } else if (filterByType === "RSAddOn") { // we treat BusPixels as an RSAddOn // (batch 4 led eye add-ons have type BusPixels) reqList.push("RSAddOn"); reqList.push("BusPixels"); this._connectedAddOns = new Array(); } else { reqList.push(filterByType); } // Make the requests const fullListOfElems = new Array(); this._connectedAddOns = []; for (const reqType of reqList) { try { const hwElemList_Str = await this._ricMsgHandler.sendRICRESTURL(`hwstatus/strstat?filterByType=${reqType}`); // if the results of hwElem indicates that we are on an older fw version // send the old hwstatus command and don't expand() // the logic behind deciding if we are on a fw version that // supports strstat is: given that hwElemList_Str.hw === object[] // if we get back string[], then we know we are on an older version // if hw === empty array, then we don't have any hw elems in which // case we can stop at that point const hwElems = hwElemList_Str.hw; let hwElemList; if (hwElems.length) { if (typeof hwElems[0] !== "object") { // we are on an older version hwElemList = await this._ricMsgHandler.sendRICRESTURL(`hwstatus?filterByType=${reqType}`); } else { // we are on the fw version that supports strstat hwElemList = RICTypes_1.RICHWElemList_Str.expand(hwElemList_Str); } } if (hwElemList && hwElemList.rslt && hwElemList.rslt === "ok") { fullListOfElems.push(...hwElemList.hw); if (reqType === "RSAddOn") { this._connectedAddOns = hwElemList.hw; this._addOnManager.setHWElems(this._connectedAddOns); // Debug RICLog_1.default.debug(`getHWElemList: found ${hwElemList.hw.length} addons/buspixels`); } else if (reqType === "BusPixels") { // BusPixels are treated as an RSAddOn this._connectedAddOns.push(...hwElemList.hw); this._addOnManager.setHWElems(this._connectedAddOns); // Debug RICLog_1.default.debug(`getHWElemList: found ${hwElemList.hw.length} addons/buspixels`); } else if (addToNonAddOnsList) { this._hwElemsExcludingAddOns.push(...hwElemList.hw); // Debug RICLog_1.default.debug(`getHWElemList: found ${hwElemList.hw.length} elems matching ${reqType}`); } } } catch (error) { RICLog_1.default.debug(`getHWElemList failed to get ${reqType} ${error}`); return new Array(); } } // Handle any callbacks try { const reports = []; // add callback to subscribe to report messages this._ricMsgHandler.reportMsgCallbacksSet("getHWElemCB", function (report) { reports.push(report); RICLog_1.default.debug(`getHWElemCB Report callback ${JSON.stringify(report)}`); }); // run any required initialisation for the addons const initCmds = this._addOnManager.getInitCmds(); // send init commands to the robot const timeInitStart = Date.now(); for (let i = 0; i < initCmds.length; i++) { this.runCommand(initCmds[i], {}); } // wait a couple of seconds for any report messages to be received await new Promise((resolve) => setTimeout(resolve, 2000)); // pass report messages to add on manager for processing this._addOnManager.processReportMsg(reports, timeInitStart); // clean up callback this._ricMsgHandler.reportMsgCallbacksDelete("getHWElemCB"); } catch (error) { RICLog_1.default.debug(`getHWElemList failed processing callback reports ${error}`); return new Array(); } // return the full list of elements return fullListOfElems; } /** * * getAddOnConfigs - get list of add-ons configured on the robot * @returns Promise<RICConfiguredAddOns> * */ async getAddOnConfigs() { try { const addOnList = await this._ricMsgHandler.sendRICRESTURL("addon/list"); RICLog_1.default.debug("getAddOnConfigs returned " + addOnList); return addOnList; } catch (error) { RICLog_1.default.debug(`getAddOnConfigs Failed to get list of add-ons ${error}`); return new RICTypes_1.RICConfiguredAddOns(); } } /** * * getFileList - get list of files on file system * @returns Promise<RICFileList> * */ async getFileList() { try { const ricFileList = await this._ricMsgHandler.sendRICRESTURL("filelist"); RICLog_1.default.debug("getFileList returned " + ricFileList); return ricFileList; } catch (error) { RICLog_1.default.debug(`getFileList Failed to get file list ${error}`); return new RICTypes_1.RICFileList(); } } /** * * runCommand * @param commandName command API string * @param params parameters (simple name value pairs only) to parameterize trajectory * @returns Promise<RICOKFail> * */ async runCommand(commandName, params) { try { // Format the paramList as query string const paramEntries = Object.entries(params); let paramQueryStr = ""; for (const param of paramEntries) { if (paramQueryStr.length > 0) paramQueryStr += "&"; paramQueryStr += param[0] + "=" + param[1]; } // Format the url to send if (paramQueryStr.length > 0) commandName += "?" + paramQueryStr; return await this._ricMsgHandler.sendRICRESTURL(commandName); } catch (error) { RICLog_1.default.debug(`runCommand failed ${error}`); return new RICTypes_1.RICOKFail(); } } /** * * Get BLEMan sysmod info * * @returns RICSysModInfoBLEMan * */ async getSysModInfoBLEMan() { try { // Get SysMod Info const bleInfo = await this._ricMsgHandler.sendRICRESTURL("sysmodinfo/BLEMan"); // Debug RICLog_1.default.debug(`getSysModInfoBLEMan rslt ${bleInfo.rslt} isConn ${bleInfo.isConn} paused ${bleInfo.isAdv} txBPS ${bleInfo.txBPS} rxBPS ${bleInfo.rxBPS}`); // Check for test rate if ("tBPS" in bleInfo) { RICLog_1.default.debug(`getSysModInfoBLEMan testMsgs ${bleInfo.tM} testBytes ${bleInfo.tB} testRateBytesPS ${bleInfo.tBPS}`); } return bleInfo; } catch (error) { RICLog_1.default.debug(`getSysModInfoBLEMan sysmodinfo/BLEMan failed ${error}`); } return null; } /** * Get hostname of connected WiFi * * @return string - hostname of connected WiFi * */ _getHostnameFromFriendlyName() { const friendlyName = this.getFriendlyName(); if (!friendlyName) { return this._defaultWiFiHostname; } let hostname = friendlyName.friendlyName; hostname = hostname === null || hostname === void 0 ? void 0 : hostname.replace(/ /g, "-"); hostname = hostname.replace(/\W+/g, ""); return hostname; } /** * Get Wifi connection status * * @return boolean - true if connected * */ async getWiFiConnStatus() { try { // Get status const ricSysModInfoWiFi = await this._ricMsgHandler.sendRICRESTURL("sysmodinfo/NetMan"); RICLog_1.default.debug(`wifiConnStatus rslt ${ricSysModInfoWiFi.rslt} isConn ${ricSysModInfoWiFi.isConn} paused ${ricSysModInfoWiFi.isPaused}`); // Check status indicates WiFi connected if (ricSysModInfoWiFi.rslt === "ok") { this._ricWifiConnStatus.connState = ricSysModInfoWiFi.isConn !== 0 ? RICWifiTypes_1.RICWifiConnState.WIFI_CONN_CONNECTED : RICWifiTypes_1.RICWifiConnState.WIFI_CONN_NONE; this._ricWifiConnStatus.isPaused = ricSysModInfoWiFi.isPaused !== 0; this._ricWifiConnStatus.ipAddress = ricSysModInfoWiFi.IP; this._ricWifiConnStatus.hostname = ricSysModInfoWiFi.Hostname; this._ricWifiConnStatus.ssid = ricSysModInfoWiFi.SSID; this._ricWifiConnStatus.bssid = ricSysModInfoWiFi.WiFiMAC; this._ricWifiConnStatus.validMs = Date.now(); return (ricSysModInfoWiFi.isConn !== 0 || ricSysModInfoWiFi.isPaused !== 0); } } catch (error) { RICLog_1.default.debug(`[DEBUG]: wifiConnStatus sysmodinfo failed ${error}`); this._ricWifiConnStatus.validMs = 0; } this._ricWifiConnStatus.connState = RICWifiTypes_1.RICWifiConnState.WIFI_CONN_NONE; this._ricWifiConnStatus.isPaused = false; return null; } // Mark: WiFi Connection ------------------------------------------------------------------------------------ /** * pause Wifi connection * * @param boolean - true to pause, false to resume * @return boolean - true if successful * */ async pauseWifiConnection(pause) { try { if (pause) { await this._ricMsgHandler.sendRICRESTURL("wifipause/pause"); } else { await this._ricMsgHandler.sendRICRESTURL("wifipause/resume"); } } catch (error) { RICLog_1.default.debug(`wifiConnect wifi pause ${error}`); return true; } return false; } /** * Connect to WiFi * * @param string - WiFi SSID * @param string - WiFi password * @return boolean - true if successful * */ async wifiConnect(ssid, password) { RICLog_1.default.debug(`Connect to WiFi ${ssid} password ${password}`); // Issue the command to connect WiFi try { const RICRESTURL_wifiCredentials = "w/" + ssid + "/" + password + "/" + this._getHostnameFromFriendlyName(); RICLog_1.default.debug(`wifiConnect attempting to connect to wifi ${RICRESTURL_wifiCredentials}`); await this._ricMsgHandler.sendRICRESTURL(RICRESTURL_wifiCredentials); } catch (error) { RICLog_1.default.debug(`wifiConnect failed ${error}`); return false; } // Wait until connected, timed-out or failed for (let timeoutCount = 0; timeoutCount < this._maxSecsToWaitForWiFiConn; timeoutCount++) { // Wait a little before checking await new Promise((resolve) => setTimeout(resolve, 1000)); // Get status info const connStat = await this.getWiFiConnStatus(); RICLog_1.default.debug(`wifiConnect connStat ${connStat}`); if (connStat) { return true; } } return false; } /** * Disconnect WiFi * * @return boolean - true if successful * */ async wifiDisconnect() { try { RICLog_1.default.debug(`wifiDisconnect clearing wifi info`); await this._ricMsgHandler.sendRICRESTURL("wc"); this.getWiFiConnStatus(); return true; } catch (error) { RICLog_1.default.debug(`wifiDisconnect clearing unsuccessful`); } return false; } // Mark: WiFi Scan ------------------------------------------------------------------------------------ /** * WiFiScan start * * @return boolean - true if successful * */ async wifiScanStart() { try { RICLog_1.default.debug(`wifiScanStart`); await this._ricMsgHandler.sendRICRESTURL("wifiscan/start"); return true; } catch (error) { RICLog_1.default.debug(`wifiScanStart unsuccessful`); } return false; } /** * WiFiScan get results * * @return boolean - false if unsuccessful, otherwise the results of the promise * */ async wifiScanResults() { try { RICLog_1.default.debug(`wifiScanResults`); return this._ricMsgHandler.sendRICRESTURL("wifiscan/results"); } catch (error) { RICLog_1.default.debug(`wifiScanResults unsuccessful`); } return false; } getCachedSystemInfo() { return this._systemInfo; } getCachedAddOnList() { return this._connectedAddOns; } getCachedAllHWElems() { const allHWElems = new Array(); allHWElems.push(...this._connectedAddOns); allHWElems.push(...this._hwElemsExcludingAddOns); return allHWElems; } getCachedCalibInfo() { return this._calibInfo; } getCachedRICName() { return this._ricFriendlyName; } getCachedWifiStatus() { return this._ricWifiConnStatus; } } exports.default = RICSystem; //# sourceMappingURL=RICSystem.js.map