UNPKG

@robotical/sensors-dashboard

Version:

A dashboard tool for depicting Marty data by Robotical

259 lines (258 loc) 11.5 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; require("core-js/modules/web.dom-collections.iterator.js"); require("core-js/modules/esnext.iterator.constructor.js"); require("core-js/modules/esnext.iterator.filter.js"); require("core-js/modules/es.number.to-fixed.js"); require("core-js/modules/es.promise.js"); var _react = require("react"); var _MartyInterface = require("../../app-bridge/MartyInterface"); var _getAddonsList = _interopRequireDefault(require("../../utils/get-addons-list")); var _decider = require("../../utils/start-end-rules/decider"); var _startEndOptions = require("../../utils/start-end-rules/start-end-options"); var _AddonsList = _interopRequireDefault(require("../AddonsList")); var _Graph = _interopRequireDefault(require("../Graph")); var _GraphControls = _interopRequireDefault(require("../GraphControls")); var _stylesModule = _interopRequireDefault(require("./styles.module.css")); var _reactCsv = require("react-csv"); var _exportCsv = require("../../utils/export-csv"); var _fa = require("react-icons/fa"); var _Tooltip = _interopRequireDefault(require("@mui/material/Tooltip")); var _raft = require("@robotical/webapp-types/dist-types/src/types/raft"); var _CogInterface = _interopRequireDefault(require("../../app-bridge/CogInterface")); var _jsxRuntime = require("react/jsx-runtime"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } function GraphArea(_ref) { let { graphId, removeGraph, mainRef, raft } = _ref; const [addons, setAddons] = (0, _react.useState)([]); const [refreshGraphArea, setRefreshGraphArea] = (0, _react.useState)(0); const [refreshAddons, setRefreshAddons] = (0, _react.useState)(0); const [refreshAddonsList, setRefreshAddonsList] = (0, _react.useState)(0); const [autoScrollEnabled, setAutoScrollEnabled] = (0, _react.useState)(false); const [csvData, setCsvData] = (0, _react.useState)([]); const graphData = (0, _react.useRef)({}); const maxDataXValue = (0, _react.useRef)(0); const isTracking = (0, _react.useRef)(false); const hasStartRuleMet = (0, _react.useRef)(false); const hasEndRuleMet = (0, _react.useRef)(false); const startSelectedOption = (0, _react.useRef)([]); const endSelectedOption = (0, _react.useRef)([]); const startOptions = (0, _react.useRef)([]); const endOptions = (0, _react.useRef)([]); const startDisplayingTime = (0, _react.useRef)(null); const [raftInterface, setRaftInterface] = (0, _react.useState)(null); (0, _react.useEffect)(() => { if (raft.type === _raft.RaftTypeE.COG) { setRaftInterface(new _CogInterface.default(raft)); } else if (raft.type === _raft.RaftTypeE.MARTY) { setRaftInterface(new _MartyInterface.MartyInterface(raft)); } return () => { raftInterface === null || raftInterface === void 0 ? void 0 : raftInterface.unsubscribeFromPublishedData(); }; }, [raft]); (0, _react.useEffect)(() => { raftInterface === null || raftInterface === void 0 ? void 0 : raftInterface.addEventListener("onAddonsChange", "", onAddonsChange); return () => { raftInterface === null || raftInterface === void 0 ? void 0 : raftInterface.removeEventListener("onAddonsChange", "", onAddonsChange); }; }, [raftInterface]); const onAddonsChange = () => { setRefreshAddons(oldV => oldV + 1); setRefreshAddonsList(oldV => oldV + 1); }; (0, _react.useEffect)(() => { const normalisedAddons = (0, _getAddonsList.default)(raft); addSelectedListener(normalisedAddons); setAddons(normalisedAddons); }, [refreshAddonsList]); (0, _react.useEffect)(() => { removeAddonsListeners(addons); addAddonsListeners(addons); const startOpts = (0, _startEndOptions.getRuleOptions)(addons, "start"); startOptions.current = startOpts; startSelectedOption.current = startOpts.filter(opt => opt[1] === "default")[0]; const endOpts = (0, _startEndOptions.getRuleOptions)(addons, "end"); endOptions.current = endOpts; endSelectedOption.current = endOpts.filter(opt => opt[1] === "default")[0]; setRefreshGraphArea(oldV => oldV + 1); return () => { removeAddonsListeners(addons); cleanupStartEndRules(); }; }, [refreshAddons]); const cleanupStartEndRules = () => { console.log("cleaning rules"); startSelectedOption.current[4] && startSelectedOption.current[4](); // cleanup rule endSelectedOption.current[4] && endSelectedOption.current[4](); // cleanup rule }; const addSelectedListener = addonsArr => { for (const addon of addonsArr) { for (const addonInput of addon.addonInputs) { addonInput.setSelectedListener(onSelectAddon); } } }; const onSelectAddon = () => { setRefreshAddons(oldVal => oldVal + 1); }; const addAddonsListeners = addns => { for (const addon of addns) { for (const addonInput of addon.addonInputs) { if (addonInput.selected) { raftInterface === null || raftInterface === void 0 ? void 0 : raftInterface.addEventListener("on".concat(addon.whoAmI + "=>" + addonInput.name, "Change"), graphId, onAddonChange); } else { raftInterface === null || raftInterface === void 0 ? void 0 : raftInterface.removeEventListener("on".concat(addon.whoAmI + "=>" + addonInput.name, "Change"), graphId, onAddonChange); } } } }; const removeAddonsListeners = addns => { for (const addon of addns) { for (const addonInput of addon.addonInputs) { raftInterface === null || raftInterface === void 0 ? void 0 : raftInterface.removeEventListener("on".concat(addon.whoAmI + "=>" + addonInput.name, "Change"), graphId, onAddonChange); } } }; const onAddonChange = evt => { /* for the following if's, order matters */ if (!isTracking.current) return; if (!startSelectedOption.current || !endSelectedOption.current) return; // decide whether or not to start based on start/end rules if (!hasStartRuleMet.current && !(0, _decider.isRuleMet)(evt, startSelectedOption.current)) return; hasStartRuleMet.current = true; if (hasEndRuleMet.current || (0, _decider.isRuleMet)(evt, endSelectedOption.current)) { hasEndRuleMet.current = true; return; } if (!startDisplayingTime.current) { startDisplayingTime.current = new Date().getTime(); } maxDataXValue.current = getMaxDataXValue(graphData.current); const traceId = "".concat(evt.whoAmI, "=>").concat(evt.addonInput); const newTimestamp = (new Date().getTime() - startDisplayingTime.current) / 1000; if (graphData.current.hasOwnProperty(traceId)) { graphData.current[traceId].x.push(+newTimestamp.toFixed(2)); graphData.current[traceId].y.push(evt.value); } else { graphData.current[traceId] = { x: [+newTimestamp.toFixed(2)], y: [evt.value] }; } setRefreshGraphArea(old => old + 1); }; const stopHandler = (0, _react.useCallback)(() => { isTracking.current = false; for (const traceKey in graphData.current) { delete graphData.current[traceKey]; } maxDataXValue.current = 0; hasStartRuleMet.current = false; cleanupStartEndRules(); hasEndRuleMet.current = false; startDisplayingTime.current = null; setRefreshGraphArea(old => old + 1); }, [addons, raftInterface]); const setIsTracking = (0, _react.useCallback)(value => { // if there are no selected addons do nothing; let isAnyAddonSelected = false; for (const addon of addons) { for (const addonInput of addon.addonInputs) { if (addonInput.selected) { isAnyAddonSelected = true; break; } } } if (!isAnyAddonSelected) return; isTracking.current = value; setRefreshGraphArea(old => old + 1); }, [addons]); const onRuleOptionChange = (0, _react.useCallback)((selectedOption, rule) => { if (isTracking.current) return; // changing rule won't affect graph if it's currently traking cleanupStartEndRules(); if (rule === "start") { startSelectedOption.current = selectedOption; } else { endSelectedOption.current = selectedOption; } setRefreshGraphArea(old => old + 1); }, []); const exportCsvHandler = (0, _react.useCallback)(async () => { // transform graph data to csv data const preparedCsvData = await (0, _exportCsv.prepareCSVData)(graphData.current); const titles = (0, _exportCsv.prepareTitles)(graphData.current, "Time (s)"); setCsvData([titles, ...preparedCsvData]); return true; }, [graphData]); const memoizedStartSelectedOption = (0, _react.useMemo)(() => startSelectedOption.current, [startSelectedOption.current]); const memoizedEndSelectedOption = (0, _react.useMemo)(() => endSelectedOption.current, [endSelectedOption.current]); const memoizedStartOptions = (0, _react.useMemo)(() => startOptions.current, [startOptions.current]); const memoizedEndOptions = (0, _react.useMemo)(() => endOptions.current, [endOptions.current]); return /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { className: _stylesModule.default.graphArea, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_AddonsList.default, { addons: addons }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_Graph.default, { mainRef: mainRef, data: graphData.current, maxDataXValue: maxDataXValue.current, autoScrollEnabled: autoScrollEnabled }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_GraphControls.default, { onClickPlay: () => setIsTracking(true), onClickPause: () => setIsTracking(false), onClickStop: stopHandler, onAutoScrollToggle: () => setAutoScrollEnabled(oldVal => !oldVal), autoScrollEnabled: autoScrollEnabled, onStartOptionChange: onRuleOptionChange, onEndOptionChange: onRuleOptionChange, startSelectedOption: memoizedStartSelectedOption, endSelectedOption: memoizedEndSelectedOption, startOptions: memoizedStartOptions, endOptions: memoizedEndOptions, isTracking: isTracking.current }), /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { className: _stylesModule.default.rightPanel, children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("div", { className: _stylesModule.default.closeGraph, onClick: () => removeGraph(graphId), children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, { title: "Close graph", children: /*#__PURE__*/(0, _jsxRuntime.jsx)("div", { children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_fa.FaTimes, {}) }) }) }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, { title: "Export data to CSV", children: /*#__PURE__*/(0, _jsxRuntime.jsx)("div", { children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactCsv.CSVLink, { data: csvData, onClick: exportCsvHandler, filename: (0, _exportCsv.getCSVTitle)(graphData.current), children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_fa.FaFileCsv, { fill: "black" }) }) }) })] })] }); } const getMaxDataXValue = data => { let mxDataXValue = 1; for (const traceKey in data) { const currLen = data[traceKey].x.length; if (data[traceKey].x[currLen - 1] > mxDataXValue) mxDataXValue = data[traceKey].x[currLen - 1]; } return mxDataXValue; }; var _default = exports.default = GraphArea;