-
PING
-
- {{ roslib.latency ? Math.round(roslib.latency) + 'ms' : 'N/A' }}
-
+
+
+
+
+ PING
+
+ {{ roslib.latency ? `${Math.round(roslib.latency)} ms` : '— ms' }}
+
diff --git a/src/components/rover_velocity_display/RoverVelocityDisplay.vue b/src/components/rover_velocity_display/RoverVelocityDisplay.vue
index 6ba376e..ecab287 100644
--- a/src/components/rover_velocity_display/RoverVelocityDisplay.vue
+++ b/src/components/rover_velocity_display/RoverVelocityDisplay.vue
@@ -1,13 +1,13 @@
+
-
- |
-
- {{ telemetry.frontLeftDrive.value }}
-
-
- {{ telemetry.midLeftDrive.value }}
-
-
- {{ telemetry.backLeftDrive.value }}
- |
-
-
- |
-
-
- {{ telemetry.frontRightDrive.value }}
-
-
- {{ telemetry.midRightDrive.value }}
-
-
- {{ telemetry.backRightDrive.value }}
- |
-
+
+
+ |
+
+
+ {{ formatMotorValue(telemetry.frontLeftDrive.value) }}
+
+
+
+
+ {{ formatMotorValue(telemetry.midLeftDrive.value) }}
+
+
+
+
+ {{ formatMotorValue(telemetry.backLeftDrive.value) }}
+
+ |
+
+
+ |
+
+
+
+ {{ formatMotorValue(telemetry.frontRightDrive.value) }}
+
+
+
+
+ {{ formatMotorValue(telemetry.midRightDrive.value) }}
+
+
+
+
+ {{ formatMotorValue(telemetry.backRightDrive.value) }}
+
+ |
+
+
diff --git a/src/components/settings/ControllerConfig.vue b/src/components/settings/ControllerConfig.vue
new file mode 100644
index 0000000..e6f83e0
--- /dev/null
+++ b/src/components/settings/ControllerConfig.vue
@@ -0,0 +1,291 @@
+
+
+
+
+
+
+
+ Gamepad
+ {{ connectedGamepadName }}
+
+
+ No gamepad detected. Press any button on your controller to wake it up.
+
+
+
+
+
Controller Options
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/settings/ControllerMonitor.vue b/src/components/settings/ControllerMonitor.vue
new file mode 100644
index 0000000..20e4770
--- /dev/null
+++ b/src/components/settings/ControllerMonitor.vue
@@ -0,0 +1,704 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
LT
+
+
{{ fmt(btnValue(6)) }}
+
+
LB
+
+
+
+
RB
+
+
+
{{ fmt(btnValue(7)) }}
+
+
RT
+
+
+
+
+
+
+
+
+
+ X {{ fmt(axisValue(0)) }}
+ Y {{ fmt(axisValue(1)) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ X {{ fmt(axisValue(2)) }}
+ Y {{ fmt(axisValue(3)) }}
+
+
+
+
+
+
+
Press any button on your controller to activate it.
+
+
+
+
Active Bindings
+
+
+
+ | Input |
+ Type |
+ ROS Topic |
+ Value |
+
+
+
+
+ | {{ row.name }} |
+ {{ row.type }} |
+
+ {{ row.topic }}
+ -
+ |
+
+
+
+
+
+
+
+
+
+ {{ fmt(row.live) }}
+ |
+
+
+
+
+
+
+
+
diff --git a/src/components/telemetry/drive/DriveMotorCard.vue b/src/components/telemetry/drive/DriveMotorCard.vue
new file mode 100644
index 0000000..16059c0
--- /dev/null
+++ b/src/components/telemetry/drive/DriveMotorCard.vue
@@ -0,0 +1,515 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
Voltage
+
+ {{ fmt(voltage) }}
+ V
+ ↑{{ fmt(peakVoltage) }}
+
+
+
+
+
+
+
+
+
Current
+
+ {{ fmt(current) }}
+ A
+ ↑{{ fmt(peakCurrent) }}
+
+
+
+
+
+
+
+
+
Velocity
+
+ {{ fmt(velocity) }}
+ rev/s
+
+
+
+
+
+
+
+
+
Temp
+
+ {{
+ fmt(temperature, 0)
+ }}
+ °C
+
+ {{ tempState === 'critical' ? 'HOT' : 'WARM' }}
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/telemetry/drive/Sparkline.vue b/src/components/telemetry/drive/Sparkline.vue
new file mode 100644
index 0000000..1b86e63
--- /dev/null
+++ b/src/components/telemetry/drive/Sparkline.vue
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
diff --git a/src/components/telemetry/moteus/DropDownItem.vue b/src/components/telemetry/moteus/DropDownItem.vue
index fdcef6c..e97eda9 100644
--- a/src/components/telemetry/moteus/DropDownItem.vue
+++ b/src/components/telemetry/moteus/DropDownItem.vue
@@ -8,43 +8,40 @@ const emit = defineEmits(['callback']);
-
-
- {{ props.itemName }}
-
-
- ✓
-
+
+ {{ props.itemName }}
+ ✓
diff --git a/src/components/telemetry/moteus/GenericMotorTelemetry.vue b/src/components/telemetry/moteus/GenericMotorTelemetry.vue
index 259d963..58dc28e 100644
--- a/src/components/telemetry/moteus/GenericMotorTelemetry.vue
+++ b/src/components/telemetry/moteus/GenericMotorTelemetry.vue
@@ -25,21 +25,15 @@ const props = defineProps
();
// and a separate one for recording (recording shouldn't stop on navigate).
const telemetry = useTelemetry();
-let isRecordingData = ref(false);
+const isRecordingData = ref(false);
let csvData: SaveCSVData;
-// We use this to fill up csv data
-// Each element is another array that holds the actual data
-let showCheckbox = ref(true);
+const showCheckbox = ref(true);
/**
- * This is used to store what kind of data we will be displaying
- * and handling through the whole thing.
- *
- * It should be possible to simply add another entry to this, and everything should
- * show up, assuming that the data received from the rover also has these values
+ * Describes one data field we can display / record for a motor.
*/
-interface MoteuesDataChoice {
+interface MotorDataField {
identifier: keyof MoteusMotorState;
prettyName: string;
dataValue: string;
@@ -47,16 +41,16 @@ interface MoteuesDataChoice {
shouldRecordData: boolean;
}
-const moteusDataChoices: Ref = ref(
+const motorDataFields: Ref = ref(
createDataChoices({
- position: 'Position',
- velocity: 'Velocity',
- torque: 'Torque',
- temperature: 'Temperature',
- power: 'Power',
- input_voltage: 'Controller Voltage',
- q_current: 'Q Phase (Amps)',
- d_current: 'D Phase (Amps)',
+ position: 'Position',
+ velocity: 'Velocity',
+ torque: 'Torque',
+ temperature: 'Temperature',
+ power: 'Power',
+ input_voltage: 'Voltage',
+ q_current: 'Q Phase',
+ d_current: 'D Phase',
}),
);
@@ -66,9 +60,8 @@ function initialize() {
function createDataChoices(
idToPretty: Partial>,
-): MoteuesDataChoice[] {
- const output: MoteuesDataChoice[] = [];
-
+): MotorDataField[] {
+ const output: MotorDataField[] = [];
for (const key in idToPretty) {
output.push({
identifier: key as keyof MoteusMotorState,
@@ -78,244 +71,218 @@ function createDataChoices(
shouldRecordData: true,
});
}
-
return output;
}
-/**
- * Converts a piece of data related to one of the motors
- * into a nice string for the UI.
- * @param data - is the data value.
- */
-function moteusDataToString(data: string | number | null | undefined): string {
- if (typeof data === 'string') {
- data = parseFloat(data);
- }
-
- if (typeof data !== 'number') {
- data = null;
- }
+function motorDataToString(data: string | number | null | undefined): string {
+ if (typeof data === 'string') data = parseFloat(data);
+ if (typeof data !== 'number') data = null;
if (!Number.isNaN(data) && data != null) {
- if (Number.isInteger(data)) {
- // if int
- return data.toString();
- } else {
- return data.toFixed(5);
- }
- } else {
- return 'N/A';
+ return Number.isInteger(data) ? data.toString() : data.toFixed(5);
}
+ return 'N/A';
}
-/**
- * The callback for the data subscriber, which is responsible for reading
- * in the data and saving it.
- */
function dataCallback(result: MoteusMotorState[]) {
const motor = result.find((motor) => motor.can_id === props.dataSourceParameter);
- if (!motor) {
- return;
- }
-
- // update the data
+ if (!motor) return;
- for (const item of moteusDataChoices.value) {
- item.dataValue = moteusDataToString(motor[item.identifier]);
+ for (const item of motorDataFields.value) {
+ item.dataValue = motorDataToString(motor[item.identifier]);
}
- // Add it to the CSV file
constructRecordingEntry();
}
function constructRecordingEntry() {
- let tempDataArray: string[] = [];
-
- // Go through each possible entry
- for (const entry of moteusDataChoices.value) {
- //If we have selected that entry to be recorded
+ const row: string[] = [];
+ for (const entry of motorDataFields.value) {
if (entry.shouldRecordData) {
- if (entry.dataValue !== 'N/A') {
- tempDataArray.push(entry.dataValue);
- } else {
- tempDataArray.push(' ');
- }
+ row.push(entry.dataValue !== 'N/A' ? entry.dataValue : ' ');
}
}
-
- csvData.addDataEntry(tempDataArray);
+ csvData.addDataEntry(row);
}
-/**
- * Used for the dropdown menu
- * We use this to select if we should display
- * the entry targeted via itemName
- *
- */
function itemClicked(itemName: string) {
- let mything = getMoteusDataObjectFromIdentifier(itemName);
- if (mything !== null) {
- mything.isSelected = !mything.isSelected;
- }
+ const field = getFieldByIdentifier(itemName);
+ if (field) field.isSelected = !field.isSelected;
}
-/**
- * Handles the recording.
- *
- * After stopping the recording, it will build the csv file
- */
function recordButtonPressed() {
if (!isRecordingData.value) {
showCheckbox.value = false;
-
csvData = new SaveCSVData();
- let header: string[] = [];
-
- for (const entry of moteusDataChoices.value) {
- if (entry.shouldRecordData) {
- header.push(entry.identifier);
- }
+ const header: string[] = [];
+ for (const entry of motorDataFields.value) {
+ if (entry.shouldRecordData) header.push(entry.identifier);
}
-
csvData.setHeader(header);
-
telemetry.start(dataCallback);
} else {
showCheckbox.value = true;
-
telemetry.stop();
-
- if (props.displayName) {
- csvData.saveToFile(props.displayName);
- }
+ if (props.displayName) csvData.saveToFile(props.displayName);
}
isRecordingData.value = !isRecordingData.value;
}
-function getMoteusDataObjectFromIdentifier(itemName: string): MoteuesDataChoice | null {
- for (const entry of moteusDataChoices.value) {
- if (entry.identifier === itemName) {
- return entry;
- }
- }
-
- return null;
+function getFieldByIdentifier(itemName: string): MotorDataField | null {
+ return motorDataFields.value.find((e) => e.identifier === itemName) ?? null;
}
-/**
- * Used as a callback for the checkboxes
- * All this does it hold the logical state
- */
function checkboxClicked(name: string) {
- let dataEntry = getMoteusDataObjectFromIdentifier(name);
- if (dataEntry !== null) {
- dataEntry.shouldRecordData = !dataEntry.shouldRecordData;
- }
+ const field = getFieldByIdentifier(name);
+ if (field) field.shouldRecordData = !field.shouldRecordData;
}
defineExpose({ recordButtonPressed });
-
-
-
{{ displayName }}
-
-