@robotical/sensors-dashboard
Version:
A dashboard tool for depicting Marty data by Robotical
259 lines (258 loc) • 11.5 kB
JavaScript
;
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;