Skip to content

Commit

Permalink
Foxglove Fixes from Jan 13 Pool Test (#500)
Browse files Browse the repository at this point in the history
- Update `controls-monitor` layout to new controls system
- Allow creation of new layouts from the `foxglove.py` CLI
- Update `foxglove/README.md`
  • Loading branch information
maxwellmlin authored Jan 14, 2024
1 parent 1364135 commit 8480717
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 41 deletions.
24 changes: 24 additions & 0 deletions foxglove/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,35 @@ Running `python foxglove.py install` will automatically build all local dependen
- `defs`: Exports Foxglove datatype maps and TypeScript interfaces/enums for both ROS 1 and Duke Robotics custom message definitions
- `ros-typescript-generator`: CLI that generates TypeScript interfaces/enums from ROS definitions

## Patches
Patches to external node modules are located in the `patches` directory.

Running either `python foxglove.py <build/install>` will automatically install these patches.

- `create-foxglove-extension+0.8.6.patch`
- No longer require `README.md` or `CHANGELOG.md` when installing an extension
- Before installing an extension, only remove `dist/extension.js` (instead of cleaning the entire `dist` directory)

## Other Files
- `.eslintrc.json`: Configuration file for ESLint
- `.prettierrc.yaml`: Configuration file for Prettier
- `tsconfig.json`: Configuration file for TypeScript
- `package.json`: Configuration file for npm
- `package-lock.json`: The exact npm dependency tree of the foxglove monorepo, generated using `npm i`
- `empty-layout.json`: Default Foxglove layout JSON file used when creating new layouts from the `foxglove.py` CLI

## Contributing
### Creating a New Extension
Fork an existing Duke Robotics example extension (`call-service-panel`, `publish-topic-panel`, or `subscribe-topic-panel`) to base the new extension off of. This ensures that all of our extensions have the same code structure and use the same core set of dependencies.

### Creating a New Layout
Foxglove does not allow creating more than one layout when not signed in.
To circumvent this issue, use the `foxglove.py` CLI:
```bash
python foxglove.py --new-layout <layout-name>
```
If `<layout-name>` is not specified, the layout name will be the current time in nanoseconds.

Follow the [documentation](https://foxglove.dev/docs/studio/layouts#personal-layouts) to export your layout as a JSON file to the robosub-ros `foxglove/layouts` directory. Manually look over the JSON to ensure that the settings are correct. For example, ensure that `splitPercentage` for each panel is set to the desired amount.

### Creating a New Local Dependency
Expand Down
8 changes: 8 additions & 0 deletions foxglove/empty-layout.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"configById": {},
"globalVariables": {},
"userNodes": {},
"playbackConfig": {
"speed": 1
}
}
51 changes: 46 additions & 5 deletions foxglove/foxglove.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#!/usr/bin/env python3
"""
A CLI to automatically install/uninstall Foxglove extensions and layouts.
usage: foxglove.py {install,uninstall} [--extensions] [--layouts]
To install a specific extension, use the -e flag:
python foxglove.py install -e <extension-1> <extension-2> ...
Expand All @@ -25,6 +24,7 @@
import platform
import argparse
from typing import Sequence, Union
import time

ORGANIZATION = "dukerobotics"

Expand Down Expand Up @@ -175,7 +175,7 @@ def install_layouts(layouts_path: pathlib.Path = LAYOUTS_PATH, install_path: pat
with open(install_path / id, 'w') as f:
json.dump(packaged_layout, f)

print(f"{name}: installed")
print(f"{id}: installed")

successes += 1

Expand Down Expand Up @@ -243,10 +243,48 @@ def extension_package(name: str, extension_paths: Sequence[pathlib.Path] = EXTEN
raise argparse.ArgumentTypeError(f"{name} is not a valid extension name")


def create_new_layout(name: str, install_path: pathlib.Path = LAYOUT_INSTALL_PATH):
"""
Create a new layout in Foxglove Desktop.
Foxglove does not allow creating more than one layout when not signed in.
This script circumvents this issue.
Args:
name: Name of the new layout.
install_path: Path to install layouts to.
"""
with open("empty-layout.json") as f:
data = json.load(f)

baseline = {
"data": data,
"savedAt": datetime.datetime.now(datetime.timezone.utc).isoformat()
}

id = f"{ORGANIZATION}.{name}"
packaged_layout = {
"id": id,
"name": name,
"permission": "CREATOR_WRITE",
"baseline": baseline,
}

with open(install_path / id, 'w') as f:
json.dump(packaged_layout, f)


if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Install/Uninstall Foxglove extensions and layouts.")
parser.add_argument(
'--new-layout',
nargs='?',
const=str(time.time_ns()), # Use current time if no name is given to avoid collisions
action='store',
help="Create a new layout by name. If no name is given, use current time in nanoseconds as name."
)

subparsers = parser.add_subparsers(dest="action", required=True)
subparsers = parser.add_subparsers(dest="action")

install_parser = subparsers.add_parser(
'install',
Expand All @@ -258,7 +296,7 @@ def extension_package(name: str, extension_paths: Sequence[pathlib.Path] = EXTEN
action='extend',
nargs='*',
type=extension_package,
help="Install extension(s) by name. By default, all extensions are installed."
help="Install extension(s) by name. If no name(s) are given, all extensions are installed."
)
install_parser.add_argument(
'-l', '--layouts',
Expand All @@ -282,7 +320,7 @@ def extension_package(name: str, extension_paths: Sequence[pathlib.Path] = EXTEN
build_parser = subparsers.add_parser(
'build',
aliases=['b'],
help='Build all necessary dependencies for Foxglove.'
help='Build all necessary dependencies for Foxglove. This is automatically run when installing extensions.'
)
build_parser.add_argument(
'--skip-ci',
Expand Down Expand Up @@ -322,3 +360,6 @@ def extension_package(name: str, extension_paths: Sequence[pathlib.Path] = EXTEN
elif args.action in ("build", "b"):
check_npm()
build_deps(skip_ci=args.skip_ci)

if args.new_layout:
create_new_layout(args.new_layout)
72 changes: 36 additions & 36 deletions foxglove/layouts/controls-monitor.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"Plot!3gn30zb": {
"paths": [
{
"value": "/controls/x_pos/setpoint.data",
"value": "/controls/position_error.linear.x",
"enabled": true,
"timestampMethod": "receiveTime",
"label": "Setpoint X",
Expand All @@ -12,7 +12,7 @@
},
{
"timestampMethod": "receiveTime",
"value": "/control_effort/x.data",
"value": "/controls/position_efforts.linear.x",
"enabled": true,
"label": "Control Effort X",
"color": "#ff4000"
Expand All @@ -22,19 +22,19 @@
"showYAxisLabels": true,
"showLegend": true,
"legendDisplay": "floating",
"showPlotValuesInLegend": false,
"showPlotValuesInLegend": true,
"isSynced": true,
"xAxisVal": "timestamp",
"sidebarDimension": 240,
"foxglovePanelTitle": "X",
"minYValue": -3,
"maxYValue": 3,
"minXValue": 0
"minYValue": -1,
"maxYValue": 1,
"followingViewWidth": 15
},
"Plot!3yf78kk": {
"paths": [
{
"value": "/controls/roll_pos/setpoint.data",
"value": "/controls/position_error.angular.x",
"enabled": true,
"timestampMethod": "receiveTime",
"label": "Setpoint Roll",
Expand All @@ -43,7 +43,7 @@
},
{
"timestampMethod": "receiveTime",
"value": "/control_effort/roll.data",
"value": "/controls/position_efforts.angular.x",
"enabled": true,
"label": "Control Effort Roll",
"color": "#ff4000"
Expand All @@ -53,19 +53,19 @@
"showYAxisLabels": true,
"showLegend": true,
"legendDisplay": "floating",
"showPlotValuesInLegend": false,
"showPlotValuesInLegend": true,
"isSynced": true,
"xAxisVal": "timestamp",
"sidebarDimension": 240,
"foxglovePanelTitle": "Roll",
"minYValue": -3,
"maxYValue": 3,
"minXValue": 0
"minYValue": -1,
"maxYValue": 1,
"followingViewWidth": 15
},
"Plot!1vpzbmx": {
"paths": [
{
"value": "/controls/y_pos/setpoint.data",
"value": "/controls/position_error.linear.y",
"enabled": true,
"timestampMethod": "receiveTime",
"label": "Setpoint Y",
Expand All @@ -74,7 +74,7 @@
},
{
"timestampMethod": "receiveTime",
"value": "/control_effort/y.data",
"value": "/controls/position_efforts.linear.y",
"enabled": true,
"label": "Control Effort Y",
"color": "#ff4000"
Expand All @@ -84,19 +84,19 @@
"showYAxisLabels": true,
"showLegend": true,
"legendDisplay": "floating",
"showPlotValuesInLegend": false,
"showPlotValuesInLegend": true,
"isSynced": true,
"xAxisVal": "timestamp",
"sidebarDimension": 240,
"foxglovePanelTitle": "Y",
"minYValue": -3,
"maxYValue": 3,
"minXValue": 0
"minYValue": -1,
"maxYValue": 1,
"followingViewWidth": 15
},
"Plot!266v8jj": {
"paths": [
{
"value": "/controls/pitch_pos/setpoint.data",
"value": "/controls/position_error.angular.y",
"enabled": true,
"timestampMethod": "receiveTime",
"label": "Setpoint Pitch",
Expand All @@ -105,7 +105,7 @@
},
{
"timestampMethod": "receiveTime",
"value": "/control_effort/pitch.data",
"value": "/controls/position_efforts.angular.y",
"enabled": true,
"label": "Control Effort Pitch",
"color": "#ff4000"
Expand All @@ -115,19 +115,19 @@
"showYAxisLabels": true,
"showLegend": true,
"legendDisplay": "floating",
"showPlotValuesInLegend": false,
"showPlotValuesInLegend": true,
"isSynced": true,
"xAxisVal": "timestamp",
"sidebarDimension": 240,
"foxglovePanelTitle": "Pitch",
"minYValue": -3,
"maxYValue": 3,
"minXValue": 0
"minYValue": -1,
"maxYValue": 1,
"followingViewWidth": 15
},
"Plot!1p60d5": {
"paths": [
{
"value": "/controls/z_pos/setpoint.data",
"value": "/controls/position_error.linear.z",
"enabled": true,
"timestampMethod": "receiveTime",
"label": "Setpoint Z",
Expand All @@ -136,7 +136,7 @@
},
{
"timestampMethod": "receiveTime",
"value": "/control_effort/z.data",
"value": "/controls/position_efforts.linear.z",
"enabled": true,
"label": "Control Effort Z",
"color": "#ff4000"
Expand All @@ -146,19 +146,19 @@
"showYAxisLabels": true,
"showLegend": true,
"legendDisplay": "floating",
"showPlotValuesInLegend": false,
"showPlotValuesInLegend": true,
"isSynced": true,
"xAxisVal": "timestamp",
"sidebarDimension": 240,
"foxglovePanelTitle": "Z",
"minYValue": -3,
"maxYValue": 3,
"minXValue": 0
"minYValue": -1,
"maxYValue": 1,
"followingViewWidth": 15
},
"Plot!3y2hu8l": {
"paths": [
{
"value": "/controls/yaw_pos/setpoint.data",
"value": "/controls/position_error.angular.z",
"enabled": true,
"timestampMethod": "receiveTime",
"label": "Setpoint Yaw",
Expand All @@ -167,7 +167,7 @@
},
{
"timestampMethod": "receiveTime",
"value": "/control_effort/yaw.data",
"value": "/controls/position_efforts.angular.z",
"enabled": true,
"label": "Control Effort Yaw",
"color": "#ff4000"
Expand All @@ -177,14 +177,14 @@
"showYAxisLabels": true,
"showLegend": true,
"legendDisplay": "floating",
"showPlotValuesInLegend": false,
"showPlotValuesInLegend": true,
"isSynced": true,
"xAxisVal": "timestamp",
"sidebarDimension": 240,
"foxglovePanelTitle": "Yaw",
"minYValue": -3,
"maxYValue": 3,
"minXValue": 0
"minYValue": -1,
"maxYValue": 1,
"followingViewWidth": 15
}
},
"globalVariables": {},
Expand Down

0 comments on commit 8480717

Please sign in to comment.