@robotical/sensors-dashboard
Version:
A dashboard tool for depicting Marty data by Robotical
167 lines (153 loc) • 5.35 kB
text/typescript
import EXCLUDED_ADDONS from "../utils/constants/excluded-addons";
import SERVO_NAMES_MAP from "../utils/constants/servo-names";
import {
ACCELEROMETER_NAME,
ACCELEROMETER_NAME_Y,
ACCELEROMETER_NAME_Z,
ACCELEROMETER_NAME_X,
MOTOR_CURRENT_NAME,
MOTOR_POSITION_NAME,
} from "../utils/types/addon-names";
import renameValueLabel from "../utils/rename-value-label";
import whoAmINameMap from "../utils/constants/whoAmI-names";
import type MartyType from "@robotical/webapp-types/dist-types/src/application/RAFTs/Marty/Marty";
import {
type RICStateInfo,
type ROSSerialIMU,
type ROSSerialAddOnStatusList,
type ROSSerialSmartServos,
} from "@robotical/roboticaljs";
import { raftPubSubscriptionHelper } from "./pubSubHelper";
import RaftInterface from "./RaftInterface";
/*
We initialise this class in app.tsx, and making it globally
available by storing it in the window instance
*/
export class MartyInterface extends RaftInterface {
public marty: MartyType;
public addons: ROSSerialAddOnStatusList['addons'] = [];
public servos: ROSSerialSmartServos | null = null;
public accel: ROSSerialIMU | null = null;
public subObject: ReturnType<typeof raftPubSubscriptionHelper> | null = null;
constructor(marty: MartyType, options?: { autoSubscribe?: boolean }) {
super();
this.marty = marty;
if (options?.autoSubscribe !== false) {
this.subscribeToPublishedData();
}
}
subscribeToPublishedData() {
this.subObject = raftPubSubscriptionHelper(this.marty);
this.subObject.subscribe(({ stateInfo }: {stateInfo: RICStateInfo}) => {
this.setAddons(stateInfo.addOnInfo.addons);
this.setServos(stateInfo.smartServos);
this.setAccel(stateInfo.imuData);
});
}
unsubscribeFromPublishedData() {
this.subObject?.unsubscribe();
}
setAddons(addons: ROSSerialAddOnStatusList['addons']) {
try {
// rename dubplicated addons
const addonsDupl: ROSSerialAddOnStatusList['addons'] = addons;
checkForDoubleAddons(addonsDupl);
// if the number of the old addons is different from the new one, we dispatch
// an event to update the addons list in the GUI
if (this.addons.length !== addonsDupl.length) {
this.dispatchEvent({
type: "onAddonsChange",
addons: addonsDupl,
});
}
this.addons = addonsDupl;
for (const addon of this.addons) {
const whoAmI = whoAmINameMap(addon.whoAmI);
if (EXCLUDED_ADDONS.includes(whoAmI)) continue;
for (const valKey in addon.vals) {
const value = addon.vals[valKey];
// if (typeof value === "number") {
const nameOfAddonInput = renameValueLabel(valKey, addon.name);
this.dispatchEvent({
type: `on${whoAmI}=>${nameOfAddonInput}Change`,
value: value,
whoAmI: whoAmI,
addonInput: nameOfAddonInput,
});
// }
}
}
} catch (e) {
this.addons = [];
}
}
setServos(servos: ROSSerialSmartServos) {
try {
this.servos = servos;
for (const servo of this.servos!.smartServos) {
this.dispatchEvent({
type: `on${MOTOR_POSITION_NAME}=>${SERVO_NAMES_MAP[servo.id]}Change`,
value: servo.pos,
whoAmI: MOTOR_POSITION_NAME,
addonInput: SERVO_NAMES_MAP[servo.id],
});
this.dispatchEvent({
type: `on${MOTOR_CURRENT_NAME}=>${SERVO_NAMES_MAP[servo.id]}Change`,
value: servo.current,
whoAmI: MOTOR_CURRENT_NAME,
addonInput: SERVO_NAMES_MAP[servo.id],
});
}
} catch (e) {
this.servos = null;
}
}
setAccel(accel: ROSSerialIMU) {
try {
this.accel = accel;
this.dispatchEvent({
type: `on${ACCELEROMETER_NAME}=>${ACCELEROMETER_NAME_X}Change`,
value: this.accel?.accel.x,
whoAmI: ACCELEROMETER_NAME,
addonInput: ACCELEROMETER_NAME_X,
});
this.dispatchEvent({
type: `on${ACCELEROMETER_NAME}=>${ACCELEROMETER_NAME_Y}Change`,
value: this.accel?.accel.y,
whoAmI: ACCELEROMETER_NAME,
addonInput: ACCELEROMETER_NAME_Y,
});
this.dispatchEvent({
type: `on${ACCELEROMETER_NAME}=>${ACCELEROMETER_NAME_Z}Change`,
value: this.accel?.accel.z,
whoAmI: ACCELEROMETER_NAME,
addonInput: ACCELEROMETER_NAME_Z,
});
} catch (e) {
this.accel = null;
}
}
}
export default MartyInterface;
const checkForDoubleAddons = (addons: ROSSerialAddOnStatusList['addons']) => {
/**
* Checking if there exist double addons (2 IRFoot, or 2 coloursensors etc)
* and renames them in place to IRFoot1, IRFoot2 and so on
*/
const addonsMap: { [key: string]: ROSSerialAddOnStatusList['addons'] } = {};
for (const addon of addons) {
const whoAmIMapped = whoAmINameMap(addon.whoAmI);
if (EXCLUDED_ADDONS.includes(whoAmIMapped)) continue;
if (!addonsMap.hasOwnProperty(whoAmIMapped)) {
addonsMap[whoAmIMapped] = [];
}
addonsMap[whoAmIMapped].push(addon);
}
for (const addonWhoAmIKey in addonsMap) {
const addonsArr = addonsMap[addonWhoAmIKey];
if (addonsArr.length < 2) continue;
for (let i = 0; i < addonsArr.length; i++) {
addonsArr[i].whoAmI = addonsArr[i].whoAmI + i;
}
}
};