From f9218ffab946ed8db44825c449bd218d4a08465c Mon Sep 17 00:00:00 2001 From: Jonah <47046556+jwbonner@users.noreply.github.com> Date: Sat, 2 Nov 2024 13:08:54 -0400 Subject: [PATCH] Add support for tuning with AdvantageKit --- docsSite/docs/getting-started/connect-live.md | 18 +++++++++--- src/hub/Sidebar.ts | 4 ++- src/hub/dataSources/LiveDataTuner.ts | 3 ++ src/hub/dataSources/nt4/NT4Source.ts | 29 +++++++++++++------ src/hub/dataSources/nt4/NT4Tuner.ts | 24 +++++++++++++-- 5 files changed, 61 insertions(+), 17 deletions(-) diff --git a/docsSite/docs/getting-started/connect-live.md b/docsSite/docs/getting-started/connect-live.md index f7cdbe7a..09a1d312 100644 --- a/docsSite/docs/getting-started/connect-live.md +++ b/docsSite/docs/getting-started/connect-live.md @@ -68,13 +68,23 @@ Some live sources support live tuning of numeric and boolean values. For example By default, all values in AdvantageScope are read-only. To toggle tuning mode, **click the slider icon** to the right of the search bar when connected to a supported live source. When the icon is purple, tuning mode is active and field editing is enabled. -:::note -Fields published by AdvantageKit are output-only and not tunable. -::: - - To edit a **numeric field**, enter a new value using the text box to the right of the field in the sidebar. The value is published after the input is deselected or the "Enter" key is pressed. Leave the text box blank to use the robot-published value. - To toggle a **boolean field**, click the red or green circle to the right of the field in the sidebar. :::warning This feature is not intended for controlling the robot on the field. Dashboard-style inputs like choosers, trigger buttons, etc. are not supported. ::: + +### Tuning With AdvantageKit + +Fields published by AdvantageKit to the `AdvantageKit` subtable are output-only and cannot be edited. However, users can publish fields from user code that are tunable from AdvantageScope. **Any fields published to the "/Tuning" table on NetworkTables will appear under the "Tuning" table when when using the "NetworkTables 4 (AdvantageKit)" live source.** + +For example, a tunable number can be published using the [`LoggedNetworkNumber`](https://docs.advantagekit.org/recording-inputs/dashboard-inputs) class: + +```java +LoggedNetworkNumber tunableNumber = new LoggedNetworkNumber("/Tuning/MyTunableNumber"); +``` + +:::warning +The `NetworkInputs` subtable **cannot be edited**, since it is used by AdvantageKit to record network values for logging and replay. Use the `Tuning` table to interact with network inputs in real time. +::: diff --git a/src/hub/Sidebar.ts b/src/hub/Sidebar.ts index 1795740f..6cb963dd 100644 --- a/src/hub/Sidebar.ts +++ b/src/hub/Sidebar.ts @@ -30,6 +30,8 @@ export default class Sidebar { "ReplayOutputs", "SystemStats", "PowerDistribution", + "Tuning", + "NetworkInputs", "DashboardInputs", "Timestamp", "AdvantageKit", @@ -370,7 +372,7 @@ export default class Sidebar { /** Show or hide tuning button based on tuner availability. */ private updateTuningButton() { let tuningButtonVisible = !this.TUNING_BUTTON.hidden; - let tunerAvailable = window.tuner !== null; + let tunerAvailable = window.tuner !== null && window.tuner.hasTunableFields(); if (tuningButtonVisible !== tunerAvailable) { this.TUNING_BUTTON.hidden = !tunerAvailable; document.documentElement.style.setProperty("--show-tuning-button", tunerAvailable ? "1" : "0"); diff --git a/src/hub/dataSources/LiveDataTuner.ts b/src/hub/dataSources/LiveDataTuner.ts index 32a34934..b111b361 100644 --- a/src/hub/dataSources/LiveDataTuner.ts +++ b/src/hub/dataSources/LiveDataTuner.ts @@ -1,5 +1,8 @@ /** A target for live tuning values, connected to a live data source. */ export default interface LiveDataTuner { + /** Returns whether any key supports tuning. */ + hasTunableFields(): boolean; + /** Returns whether a particular key support tuning. */ isTunable(key: string): boolean; diff --git a/src/hub/dataSources/nt4/NT4Source.ts b/src/hub/dataSources/nt4/NT4Source.ts index 829f15b5..e3ca50a8 100644 --- a/src/hub/dataSources/nt4/NT4Source.ts +++ b/src/hub/dataSources/nt4/NT4Source.ts @@ -10,6 +10,7 @@ import NT4Tuner from "./NT4Tuner"; export const WPILOG_PREFIX = "NT:"; export const AKIT_PREFIX = "/AdvantageKit"; +export const AKIT_TUNING_PREFIX = "/Tuning"; export default class NT4Source extends LiveDataSource { private akitMode: boolean; @@ -54,7 +55,12 @@ export default class NT4Source extends LiveDataSource { this.lowBandwidthTopicSubscription = null; } if (this.loggingSubscription === null) { - this.loggingSubscription = this.client.subscribe([this.akitMode ? AKIT_PREFIX + "/" : ""], true, true, 0.02); + this.loggingSubscription = this.client.subscribe( + this.akitMode ? [AKIT_PREFIX + "/", AKIT_TUNING_PREFIX + "/"] : [""], + true, + true, + 0.02 + ); } } else { // Switch to low bandwidth subscribe mode @@ -64,7 +70,7 @@ export default class NT4Source extends LiveDataSource { } if (this.lowBandwidthTopicSubscription === null) { this.lowBandwidthTopicSubscription = this.client.subscribeTopicsOnly( - [this.akitMode ? AKIT_PREFIX + "/" : ""], + this.akitMode ? [AKIT_PREFIX + "/", AKIT_TUNING_PREFIX + "/"] : [""], true ); } @@ -97,7 +103,11 @@ export default class NT4Source extends LiveDataSource { } if (subscribeKey !== null) { if (this.akitMode) { - activeFields.add(AKIT_PREFIX + subscribeKey); + if (subscribeKey.startsWith(AKIT_TUNING_PREFIX)) { + activeFields.add(subscribeKey); + } else { + activeFields.add(AKIT_PREFIX + subscribeKey); + } } else { activeFields.add(subscribeKey.slice(WPILOG_PREFIX.length)); } @@ -348,20 +358,21 @@ export default class NT4Source extends LiveDataSource { } getTuner() { - if (this.akitMode) { - // Tuning is not applicable with AdvantageKit - return null; - } else if (this.client === null || this.log === null) { + if (this.client === null || this.log === null) { throw "Cannot create NT4 tuner before starting connection"; } else { - return new NT4Tuner(this.client); + return new NT4Tuner(this.client, this.akitMode); } } /** Gets the name of the topic, depending on whether we're running in AdvantageKit mode. */ private getKeyFromTopic(topic: NT4_Topic): string { if (this.akitMode) { - return topic.name.slice(AKIT_PREFIX.length); + if (topic.name.startsWith(AKIT_PREFIX)) { + return topic.name.slice(AKIT_PREFIX.length); + } else { + return topic.name; + } } else { return WPILOG_PREFIX + topic.name; } diff --git a/src/hub/dataSources/nt4/NT4Tuner.ts b/src/hub/dataSources/nt4/NT4Tuner.ts index 04628783..7124ed18 100644 --- a/src/hub/dataSources/nt4/NT4Tuner.ts +++ b/src/hub/dataSources/nt4/NT4Tuner.ts @@ -1,13 +1,23 @@ import LoggableType from "../../../shared/log/LoggableType"; import LiveDataTuner from "../LiveDataTuner"; import { NT4_Client } from "./NT4"; -import { AKIT_PREFIX, WPILOG_PREFIX } from "./NT4Source"; +import { AKIT_PREFIX, AKIT_TUNING_PREFIX, WPILOG_PREFIX } from "./NT4Source"; export default class NT4Tuner implements LiveDataTuner { private client: NT4_Client; + private akitMode: boolean; - constructor(client: NT4_Client) { + constructor(client: NT4_Client, akitMode: boolean) { this.client = client; + this.akitMode = akitMode; + } + + hasTunableFields(): boolean { + if (this.akitMode) { + return !window.log.getFieldKeys().every((key) => !key.startsWith(AKIT_TUNING_PREFIX)); + } else { + return true; + } } isTunable(key: string): boolean { @@ -45,6 +55,14 @@ export default class NT4Tuner implements LiveDataTuner { } private getRemoteKey(key: string): string { - return key.slice(WPILOG_PREFIX.length); + if (this.akitMode) { + if (key.startsWith(AKIT_TUNING_PREFIX)) { + return key; + } else { + return AKIT_PREFIX + key; + } + } else { + return key.slice(WPILOG_PREFIX.length); + } } }