From d6d2a8a9f249dff3ea3fd5f5dee06c532df6d703 Mon Sep 17 00:00:00 2001 From: JessamyT <75634662+JessamyT@users.noreply.github.com> Date: Mon, 8 Jul 2024 05:19:15 -0700 Subject: [PATCH 01/22] Update collect-sensor-data.md thumbnail (#3098) --- docs/use-cases/collect-sensor-data.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/use-cases/collect-sensor-data.md b/docs/use-cases/collect-sensor-data.md index 098b941724..e64f96a513 100644 --- a/docs/use-cases/collect-sensor-data.md +++ b/docs/use-cases/collect-sensor-data.md @@ -3,7 +3,7 @@ title: "Collect and view sensor data from any machines" linkTitle: "Collect sensor data" weight: 29 type: "docs" -images: ["/services/icons/data-query.svg"] +images: ["/services/icons/data-capture.svg"] description: "Gather sensor data, sync it to the cloud, and view it in the Viam app." modulescript: true # SME: Devin Hilly From f5774e53f2d6c34418def525b03766ad177be228 Mon Sep 17 00:00:00 2001 From: Jayahari Vavachan <10448770+jayahariv@users.noreply.github.com> Date: Mon, 8 Jul 2024 18:13:34 +0530 Subject: [PATCH 02/22] Update robot.md (#3103) --- static/include/robot/apis/generated/robot.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/include/robot/apis/generated/robot.md b/static/include/robot/apis/generated/robot.md index fb74f337e7..8ba0076f93 100644 --- a/static/include/robot/apis/generated/robot.md +++ b/static/include/robot/apis/generated/robot.md @@ -533,7 +533,7 @@ Get app-related information about the robot. **Example:** ```python {class="line-numbers linkable-line-numbers"} -metadata = machine.get_cloud_metadata() +metadata = await machine.get_cloud_metadata() print(metadata.machine_id) print(metadata.machine_part_id) print(metadata.primary_org_id) From 314a39c2e08b330da4572ccadeb604741618f49b Mon Sep 17 00:00:00 2001 From: Nicolas Menard Date: Mon, 8 Jul 2024 08:49:20 -0400 Subject: [PATCH 03/22] [DOCS-1603] Document data management service for Micro-RDK (#3105) Co-authored-by: Naomi Pentrel <5212232+npentrel@users.noreply.github.com> Co-authored-by: andf-viam <132301587+andf-viam@users.noreply.github.com> --- .../build/micro-rdk/data_management/_index.md | 182 ++++++++++++++++++ static/include/micro-rdk.md | 1 + 2 files changed, 183 insertions(+) create mode 100644 docs/build/micro-rdk/data_management/_index.md diff --git a/docs/build/micro-rdk/data_management/_index.md b/docs/build/micro-rdk/data_management/_index.md new file mode 100644 index 0000000000..4c1843c542 --- /dev/null +++ b/docs/build/micro-rdk/data_management/_index.md @@ -0,0 +1,182 @@ +--- +title: "Configure Data capture and sync" +linkTitle: "Data Capture" +weight: 30 +type: "docs" +description: "Configure data capture and sync in the micro-RDK to save data from components." +images: ["/services/icons/data-capture.svg"] +icon: true +tags: ["data management", "cloud", "sync"] +no_list: true +no_service: true +# SMEs: Gautham V. +--- + +The micro-RDK data management service captures data from one or more components in the ESP32's flash memory. +The service periodically uploads data to Viam cloud. +If the machine restarts before all data is synced, all unsynced data captured since the last sync point is lost. + +The micro-RDK data management service can capture data from multiple components at the same or different frequencies. +The micro-RDK does not impose an upper limit on the frequency of data collection. +However, in practice, high frequency data collection (> 100Hz) requires special considerations on the ESP32. + +## Add the data management service + +1. Navigate to the **CONFIGURE** tab of your machine's page in [the Viam app](https://app.viam.com). +2. Click the **+** icon next to your machine part in the left-hand menu and select **Service**. +3. Select the `data management` type, then either use the suggested name or specify a name for your data management service, for example `data-manager`. +4. Click **Create**. +5. On the panel that appears, you can manage the capturing and syncing functions and specify the sync **Interval**. + {{< alert title="Info" color="info" >}} + With micro-RDK, the `capture_dir`, `tags`, and `additional_sync_paths` attributes are ignored and should not be configured. + {{< /alert >}} +6. Click the **Save** button in the top right corner of the page. + +![data capture configuration](/tutorials/data-management/data-management-conf.png) + +{{%expand "Click to view the JSON configuration for the data management service" %}} + +```json {class="line-numbers linkable-line-numbers"} +{ + "components": [], + "services": [ + { + "attributes": { + "capture_dir": "", + "tags": [], + "additional_sync_paths": [], + "sync_interval_mins": 3 + }, + "name": "my-data-manager", + "namespace": "rdk", + "type": "data_manager" + } + ] +} +``` + +{{< alert title="Info" color="info" >}} +With micro-RDK, the `capture_dir`, `tags`, and `additional_sync_paths` attributes are ignored and should not be configured. +{{< /alert >}} + +{{% /expand%}} + +## Configure data capture for individual components + +Once you have added the data management service, you can specify the data you want to capture at a component level. + +### Supported components + +Only the following components types are currently supported with data capture: +| Type | Method | +| ----- | ----------- | +| [Sensor](/build/micro-rdk/sensor/) | [`GetReadings`](/components/sensor/#getreadings) | +| [Movement Sensor](/build/micro-rdk/movement-sensor/) | [`AngularVelocity`](/components/movement-sensor/#getangularvelocity), [`LinearAcceleration`](/components/movement-sensor/#getlinearacceleration), [`LinearVelocity`](/components/movement-sensor/#getlinearvelocity) | + +To add data capture for a component, navigate to the **CONFIGURE** tab of your machine's page in the Viam app. + +For each component you can capture data for, find the `Data capture` section in its panel. +Click `Add Method` and then select the **Method** type and the capture **Frequency**. + +{{< alert title="Caution" color="caution" >}} + +Avoid configuring data capture to higher rates than your hardware can handle, as this can lead to performance degradation. + +{{< /alert >}} + +Click the **Save** button in the top right corner of the page. + +Now your data will be captured at the configured capture frequency and synced to Viam app at the selected interval. + +{{%expand "Click to view an example JSON configuration capturing data from the GetReadings method of a temperature sensor and wifi signal sensor" %}} + +```json {class="line-numbers linkable-line-numbers"} +{ + "services": [ + { + "attributes": { + "capture_dir": "", + "tags": [], + "additional_sync_paths": [], + "sync_interval_mins": 3 + }, + "name": "dm", + "namespace": "rdk", + "type": "data_manager" + } + ], + "components": [ + { + "type": "sensor", + "model": "tmp36", + "attributes": { + "analog_reader": "temp", + "num_readings": 15 + }, + "depends_on": [], + "service_configs": [ + { + "attributes": { + "capture_methods": [ + { + "capture_frequency_hz": 0.2, + "additional_params": {}, + "method": "Readings" + } + ] + }, + "type": "data_manager" + } + ], + "name": "tmp36", + "namespace": "rdk" + }, + { + "type": "sensor", + "model": "wifi-rssi", + "attributes": {}, + "service_configs": [ + { + "type": "data_manager", + "attributes": { + "capture_methods": [ + { + "additional_params": {}, + "method": "Readings", + "capture_frequency_hz": 0.1 + } + ] + } + } + ], + "name": "my-wifi-sensor", + "namespace": "rdk" + } + ] +} +``` + +{{% /expand%}} + +- To enable or disable data capture for a configured component or method, use the `on/off` toggle on the component's configuration pane in the Viam app. +- To change the frequency of data capture for a method, enter the number of measurements you wish to capture per second in the frequency field on the component's configuration pane in the Viam app. + +After adding the configuration for the methods, click the **Save** button in the top right corner of the page. + +If you want to remove a capture method from the configuration, click the `delete` icon. + +## View captured data + +To view captured data for a machine, click on the data icon next to the **Save** button on the top right of the Viam app. + +{{}} + +To view captured data for a {{< glossary_tooltip term_id="part" text="machine part" >}}, click on the **...** menu in the top right of its card, or the menu in the machine resources list in the **Builder** menu, and select **View captured data**. + +{{}} + +To view captured data for a {{< glossary_tooltip term_id="part" text="machine part" >}}, click on the **...** menu in the top right of its card, or the menu in the machine resources list in the **Builder** menu, and select **View captured data**. + +![Resource menu with the options Rename, Duplicate, View captured data, and Delete](/services/data/resource-menu.png) + +To view all the captured data you have access to, see [View Data](/services/data/view/). diff --git a/static/include/micro-rdk.md b/static/include/micro-rdk.md index 04320eee37..c602fbb804 100644 --- a/static/include/micro-rdk.md +++ b/static/include/micro-rdk.md @@ -19,6 +19,7 @@ The only microcontroller the micro-RDK currently supports is the [ESP32](https:/ {{% card link="/build/micro-rdk/sensor/" %}} {{% card link="/build/micro-rdk/servo/" %}} {{% card link="/build/micro-rdk/generic/" %}} +{{% card link="/build/micro-rdk/data_management/" %}} {{< /cards >}} Click on each supported resource to see supported models, API methods, and configuration info. From 5d4a2abed4bd7efadc0ab51c090a21362d42c605 Mon Sep 17 00:00:00 2001 From: Nick Franczak <46872531+nfranczak@users.noreply.github.com> Date: Mon, 8 Jul 2024 09:16:33 -0400 Subject: [PATCH 04/22] RSDK-7778 - Update motion service docs to include information about bounding regions + GeoGeometries (#3106) --- docs/services/motion/_index.md | 2 ++ docs/services/navigation/_index.md | 1 + 2 files changed, 3 insertions(+) diff --git a/docs/services/motion/_index.md b/docs/services/motion/_index.md index f148fef58a..6ebae638a8 100644 --- a/docs/services/motion/_index.md +++ b/docs/services/motion/_index.md @@ -511,6 +511,7 @@ Translation in obstacles is not supported by the [navigation service](/services/ - `destination` [(GeoPoint)](https://python.viam.dev/autoapi/viam/components/movement_sensor/index.html#viam.components.movement_sensor.GeoPoint): The location of the component's destination, represented in geographic notation as a [GeoPoint](https://python.viam.dev/autoapi/viam/components/movement_sensor/index.html#viam.components.movement_sensor.GeoPoint) _(lat, lng)_. - `movement_sensor_name` [(ResourceName)](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.ResourceName): The `ResourceName` of the [movement sensor](/components/movement-sensor/) that you want to use to check the machine's location. - `obstacles` [(Optional[Sequence[GeoGeometry]])](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.GeoGeometry): Obstacles to consider when planning the motion of the component, with each represented as a `GeoGeometry`.
  • Default: `None`
+- `bounding_regions` [(Optional[Sequence[GeoGeometry]])](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.GeoGeometry): Set of bounds which the robot must remain within or in collision with while navigating, with each represented as a `GeoGeometry`. The call will only execture normally if both the robot and destination are at least partly contained by the defined bounding regions or if no bounding regions are defined. If one or more bounding regions are defined and the robot's geometry is neither encompassed by or in collision with any bounding regions the call will fail. If any bounding regions are defined and the selected destination is not within the bounding regions the call will fail.
  • Default: `None`
- `heading` [(Optional[float])](https://docs.python.org/library/typing.html#typing.Optional): The compass heading, in degrees, that the machine's movement sensor should report at the `destination` point.
  • Range: `[0-360)` 0: North, 90: East, 180: South, 270: West
  • Default: `None`
- `configuration` [(Optional[MotionConfiguration])](https://python.viam.dev/autoapi/viam/proto/service/motion/index.html#viam.proto.service.motion.MotionConfiguration): The configuration you want to set across this machine for this motion service. This parameter and each of its fields are optional. - `obstacle_detectors` [(Iterable[ObstacleDetector])](https://python.viam.dev/autoapi/viam/proto/service/motion/index.html#viam.proto.service.motion.ObstacleDetector): The names of each [vision service](/services/vision/) and [camera](/components/camera/) resource pair you want to use for transient obstacle avoidance. @@ -558,6 +559,7 @@ execution_id = await motion.move_on_globe( - `heading` [(float64)](https://pkg.go.dev/builtin#float64): The compass heading, in degrees, that the machine's movement sensor should report at the `destination` point.
  • Range: `[0-360)` 0: North, 90: East, 180: South, 270: West
  • Default: `0`
- `movementSensorName` [(resource.Name)](https://pkg.go.dev/go.viam.com/rdk/resource#Name): The `resource.Name` of the [movement sensor](/components/movement-sensor/) that you want to use to check the machine's location. - `obstacles` [([]\*spatialmath.GeoGeometry)](https://pkg.go.dev/go.viam.com/rdk/spatialmath#GeoGeometry): Obstacles to consider when planning the motion of the component, with each represented as a `GeoGeometry`.
  • Default: `nil`
+ - `boundingRegions` [([]\*spatialmath.GeoGeometry)](https://pkg.go.dev/go.viam.com/rdk/spatialmath#GeoGeometry): Set of bounds which the robot must remain within or in collision with while navigating, with each represented as a `GeoGeometry`. The call will only execture normally if both the robot and destination are at least partly contained by the defined bounding regions or if no bounding regions are defined. If one or more bounding regions are defined and the robot's geometry is neither encompassed by or in collision with any bounding regions the call will fail. If any bounding regions are defined and the selected destination is not within the bounding regions the call will fail.
  • Default: `nil`
- `motionConfig` [(\*MotionConfiguration)](https://pkg.go.dev/go.viam.com/rdk/services/motion#MotionConfiguration): The configuration you want to set across this machine for this motion service. This parameter and each of its fields are optional. - `ObstacleDetectors` [([]ObstacleDetectorName)](https://pkg.go.dev/go.viam.com/rdk/services/motion#ObstacleDetectorName): The names of each [vision service](/services/vision/) and [camera](/components/camera/) resource pair you want to use for transient obstacle avoidance. - `PositionPollingFreqHz` [(float64)](https://pkg.go.dev/builtin#float64): The frequency in hz to poll the position of the machine. diff --git a/docs/services/navigation/_index.md b/docs/services/navigation/_index.md index b41d668f78..3ac9fd8410 100644 --- a/docs/services/navigation/_index.md +++ b/docs/services/navigation/_index.md @@ -163,6 +163,7 @@ The following attributes are available for `Navigation` services: | `degs_per_sec` | float | Optional | The default angular velocity for the [base](/components/base/) in degrees per second.
Default: `20` | | `meters_per_sec` | float | Optional | The default linear velocity for the [base](/components/base/) in meters per second.
Default: `0.3` | | `obstacles` | obj | Optional | Any obstacles you wish to add to the machine's path. See the [motion service](/services/motion/) for more information. | +| `bounding_regions` | obj | Optional | Set of bounds which the robot must remain within while navigating. See the [motion service](/services/motion/) for more information. | ### Configure and calibrate the frame system service for GPS navigation From 7ac06f0152faac092a7420475127141bad9fbc5c Mon Sep 17 00:00:00 2001 From: andf-viam <132301587+andf-viam@users.noreply.github.com> Date: Mon, 8 Jul 2024 13:11:40 -0400 Subject: [PATCH 05/22] DOCS-2429: Merge autogen: data, dataset, data_sync (#3085) --- docs/appendix/apis/_index.md | 14 +- docs/appendix/apis/data-client.md | 1099 +---------------- docs/services/data/dataset.md | 9 + docs/services/data/export.md | 9 +- .../include/app/apis/generated/data-table.md | 22 + static/include/app/apis/generated/data.md | 750 +++++++++++ .../app/apis/generated/data_sync-table.md | 8 + .../include/app/apis/generated/data_sync.md | 241 ++++ .../app/apis/generated/dataset-table.md | 8 + static/include/app/apis/generated/dataset.md | 146 +++ .../python.data.binary_data_by_filter.last.md | 3 + ...python.data.tabular_data_by_filter.last.md | 3 + static/include/services/apis/data-client.md | 33 - 13 files changed, 1214 insertions(+), 1131 deletions(-) create mode 100644 static/include/app/apis/generated/data-table.md create mode 100644 static/include/app/apis/generated/data.md create mode 100644 static/include/app/apis/generated/data_sync-table.md create mode 100644 static/include/app/apis/generated/data_sync.md create mode 100644 static/include/app/apis/generated/dataset-table.md create mode 100644 static/include/app/apis/generated/dataset.md create mode 100644 static/include/app/apis/overrides/methods/python.data.binary_data_by_filter.last.md create mode 100644 static/include/app/apis/overrides/methods/python.data.tabular_data_by_filter.last.md delete mode 100644 static/include/services/apis/data-client.md diff --git a/docs/appendix/apis/_index.md b/docs/appendix/apis/_index.md index 35e8401774..4c20a64edf 100644 --- a/docs/appendix/apis/_index.md +++ b/docs/appendix/apis/_index.md @@ -36,9 +36,19 @@ The [fleet management API](/appendix/apis/fleet/) supports the following methods ### Data client API -The [data client API](/appendix/apis/data-client/) supports the following methods to upload and retrieve data like images or sensor readings directly to the [Viam app](https://app.viam.com) (among [others](https://python.viam.dev/autoapi/viam/app/data_client/index.html)): +The [data client API](/appendix/apis/data-client/) supports the following methods: -{{< readfile "/static/include/services/apis/data-client.md" >}} +Methods to upload data like images or sensor readings directly to the [Viam app](https://app.viam.com): + +{{< readfile "/static/include/app/apis/generated/data_sync-table.md" >}} + +Methods to download, filter, tag, or perform other tasks on data like images or sensor readings: + +{{< readfile "/static/include/app/apis/generated/data-table.md" >}} + +Methods to work with datasets: + +{{< readfile "/static/include/app/apis/generated/dataset-table.md" >}} ### ML training API diff --git a/docs/appendix/apis/data-client.md b/docs/appendix/apis/data-client.md index e1019e905d..bb94318c53 100644 --- a/docs/appendix/apis/data-client.md +++ b/docs/appendix/apis/data-client.md @@ -77,1104 +77,25 @@ Once you have instantiated a `DataClient`, you can run [API methods](#api) again ## API -The data client API supports the following methods (among [others](https://python.viam.dev/autoapi/viam/app/data_client/index.html)): +The data client API supports the following methods: -{{< readfile "/static/include/services/apis/data-client.md" >}} +Methods to upload data like images or sensor readings directly to the [Viam app](https://app.viam.com): -### TabularDataByFilter +{{< readfile "/static/include/app/apis/generated/data_sync-table.md" >}} -Filter and download optionally filtered tabular data from the [Viam app](https://app.viam.com). -The data will be paginated into pages of `limit` items, and the pagination ID will be included in the returned tuple. -If a destination is provided, the data will be saved to that file. -If the file is not empty, it will be overwritten. -You can also find your data under the **Sensors** subtab of the app's [**Data** tab](https://app.viam.com/data). +Methods to download, filter, tag, or perform other tasks on data like images or sensor readings: -{{< tabs >}} -{{% tab name="Python" %}} +{{< readfile "/static/include/app/apis/generated/data-table.md" >}} -**Parameters:** +Methods to work with datasets: -- `filter` [(Optional[viam.proto.app.data.Filter])](https://python.viam.dev/autoapi/viam/proto/app/data/index.html#viam.proto.app.data.Filter): Optional `Filter` specifying data to retrieve. Specify no filter to download all data. -- `limit` [(Optional[int])](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex): The maximum number of entries to include in a page. Default: `50`. -- `sort_order` (Optional[Order.ValueType]): The desired sort order of the data. -- `last` [(Optional[str])](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): Optional string indicating the ID of the last-returned data. If provided, the server will return the next data entries after the `last` ID. -- `count_only` [(bool)](https://docs.python.org/3/library/stdtypes.html): Whether to return only the total count of entries. Default: `False`. -- `include_internal_data` [(bool)](https://docs.python.org/3/library/stdtypes.html): Whether to return the internal data. Internal data is used for Viam-specific data ingestion, like cloud SLAM. Default: `False`. -- `dest` [(Optional[str])](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): Filepath to write retrieved data to. If not populated, writes to your current directory. +{{< readfile "/static/include/app/apis/generated/dataset-table.md" >}} -**Returns**: +{{< readfile "/static/include/app/apis/generated/data_sync.md" >}} -- [(List[TabularData])](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.TabularData), int, str]): The data retrieved from the [Viam app](https://app.viam.com). -- [(int)](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex): The count (number of entries). -- [(str)](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): The last-returned page ID. +{{< readfile "/static/include/app/apis/generated/data.md" >}} -```python {class="line-numbers linkable-line-numbers"} -from viam.proto.app.data import Filter - -my_filter = Filter(component_name="left_motor") -tabular_data, count, id = await data_client.tabular_data_by_filter(my_filter) -``` - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.tabular_data_by_filter). - -{{% /tab %}} -{{< /tabs >}} - -### BinaryDataByFilter - -Filter and download binary data from the [Viam app](https://app.viam.com). -The data will be paginated into pages of `limit` items, and the pagination ID will be included in the returned tuple. -If a destination is provided, the data will be saved to that file. -If the file is not empty, it will be overwritten. - -You can also find your binary data under the **Images**, **Point clouds**, or **Files** subtab of the app's [**Data** tab](https://app.viam.com/data), depending on the type of data that you have uploaded. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `filter` [(Optional[viam.proto.app.data.Filter])](https://python.viam.dev/autoapi/viam/proto/app/data/index.html#viam.proto.app.data.Filter): Optional `Filter` specifying binary data to retrieve. Specify no filter to download all binary data. -- `limit` [(Optional[int])](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex): The maximum number of entries to include in a page. Default: `50`. -- `sort_order` (Optional[Order.ValueType]): The desired sort order of the data. -- `last` [(Optional[str])](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): Optional string indicating the ID of the last-returned data. If provided, the server will return the next data entries after the `last` ID. -- `include_binary_data` [(bool)](https://docs.python.org/3/library/stdtypes.html): Boolean specifying whether to actually include the binary file data with each retrieved file. Whether to return the internal data. Internal data is used for Viam-specific data ingestion, like cloud SLAM. Default: `True` meaning both the files' data and metadata are returned. -- `count_only` [(bool)](https://docs.python.org/3/library/stdtypes.html): Whether to return only the total count of entries. Default: `False`. -- `include_internal_data` [(bool)](https://docs.python.org/3/library/stdtypes.html): Whether to return the internal data. Internal data is used for Viam-specific data ingestion, like cloud SLAM. Default: `False`. -- `dest` [(Optional[str])](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): Filepath to write retrieved data to. If not populated, writes to your current directory. - -**Returns**: - -- [(List[BinaryData])](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.BinaryData): The binary data retrieved from the [Viam app](https://app.viam.com). -- [(int)](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex): The count (number of entries). -- [(str)](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): The last-returned page ID. - -```python {class="line-numbers linkable-line-numbers"} -from viam.proto.app.data import Filter - -my_filter = Filter(component_type="camera") -binary_data, count, id = await data_client.binary_data_by_filter(my_filter) -``` - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.binary_data_by_filter). - -{{% /tab %}} -{{< /tabs >}} - -### BinaryDataByIDs - -Retrieve binary data from the [Viam app](https://app.viam.com) by [`BinaryID`](https://python.viam.dev/autoapi/viam/proto/app/data/index.html#viam.proto.app.data.BinaryID). -You can also find your binary data under the **Images**, **Point clouds**, or **Files** subtab of the app's [**Data** tab](https://app.viam.com/data), depending on the type of data that you have uploaded. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `binary_ids` [(List[viam.proto.app.data.BinaryID])](https://python.viam.dev/autoapi/viam/proto/app/data/index.html#viam.proto.app.data.BinaryID): `BinaryID` objects specifying the desired data. - Must be non-empty. -- `dest` [(Optional[str])](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): Filepath to write retrieved data to. If not populated, writes to your current directory. - -**Returns**: - -- [(List[BinaryData])](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.BinaryData): The binary data retrieved from the [Viam app](https://app.viam.com). - -```python {class="line-numbers linkable-line-numbers"} -from viam.proto.app.data import BinaryID - -binary_metadata = await data_client.binary_data_by_filter( - include_file_data=False - ) - -my_ids = [] - -for obj in binary_metadata: - my_ids.append( - BinaryID( - file_id=obj.metadata.id, - organization_id=obj.metadata.capture_metadata.organization_id, - location_id=obj.metadata.capture_metadata.location_id - ) - ) - -binary_data = await data_client.binary_data_by_ids(my_ids) -``` - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.binary_data_by_ids). - -{{% /tab %}} -{{< /tabs >}} - -### TabularDataBySQL - -Obtain unified tabular data and metadata, queried with SQL. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `organization_id` [(str)](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): The ID of the organization that owns the data. -- `sql_query` [(str)](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): The SQL query to run. - -**Returns:** - -- (List[Dict[str, ValueTypes]]): An array of data objects. - -```python {class="line-numbers linkable-line-numbers"} -tabular_data = await data_client.tabular_data_by_sql( - organization_id=organization_id, - sql_query="SELECT * FROM readings LIMIT 5" -) -``` - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.tabular_data_by_sql). - -{{% /tab %}} -{{< /tabs >}} - -### TabularDataByMQL - -Obtain unified tabular data and metadata, queried with MQL. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `organization_id` [(str)](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): The ID of the organization that owns the data. -- `mql_binary` (List[bytes]): The MQL query to run as a list of BSON documents. - -**Returns:** - -- (List[Dict[str, ValueTypes]]): An array of data objects. - -```python {class="line-numbers linkable-line-numbers"} -tabular_data = await data_client.tabular_data_by_mql( - organization_id=organization_id, - mql_query=[ - bson.dumps({'$match': {'location_id': ''}}), - bson.dumps({"$limit": 5}) - ] -) -``` - -You must `import bson` to create valid bson binary objects as input for the `tabular_data_by_mql` method. -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.tabular_data_by_mql). - -{{% /tab %}} -{{< /tabs >}} - -### DeleteTabularData - -Delete tabular data older than a specified number of days. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `organization_id` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)): ID of organization to delete data from. - You can obtain your organization id from the [organization settings page](/cloud/organizations/). -- `delete_older_than_days` ([int](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)): Delete data that was captured up to this many days ago. - For example if delete_older_than_days is `10`, this deletes any data that was captured up to 10 days ago. - If it is `0`, all existing data is deleted. - -**Returns:** - -- None. - -```python {class="line-numbers linkable-line-numbers"} -from viam.proto.app.data import Filter - -my_filter = Filter(component_name="left_motor") -days_of_data_to_delete = 10 -tabular_data = await data_client.delete_tabular_data( - "a12b3c4e-1234-1abc-ab1c-ab1c2d345abc", days_of_data_to_delete) -``` - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.delete_tabular_data). - -{{% /tab %}} -{{< /tabs >}} - -### DeleteBinaryDataByFilter - -Filter and delete binary data. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `filter` ([viam.proto.app.data.Filter](https://python.viam.dev/autoapi/viam/proto/app/data/index.html#viam.proto.app.data.Filter "viam.proto.app.data.Filter")): Optional Filter specifying binary data to delete. - Passing an empty Filter will lead to all data being deleted. - Exercise caution when using this option. - -**Returns:** - -- None. - -```python {class="line-numbers linkable-line-numbers"} -from viam.proto.app.data import Filter - -my_filter = Filter(component_name="left_motor") -res = await data_client.delete_binary_data_by_filter(my_filter) -``` - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.delete_binary_data_by_filter). - -{{% /tab %}} -{{< /tabs >}} - -### DeleteBinaryDataByIds - -Filter and delete binary data by ids. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `binary_ids` (List[[viam.proto.app.data.BinaryID](https://python.viam.dev/autoapi/viam/proto/app/data/index.html#viam.proto.app.data.BinaryID "viam.proto.app.data.BinaryID")]): BinaryID objects specifying the data to be deleted. - Must be non-empty. - -**Returns:** - -- ([int](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)): The number of items deleted. - -**Raises:** - -- `GRPCError` – This error is raised if no BinaryID objects are provided. - -```python {class="line-numbers linkable-line-numbers"} -from viam.proto.app.data import BinaryID - -binary_metadata = await data_client.binary_data_by_filter( - include_file_data=False - ) - -my_ids = [] - -for obj in binary_metadata: - my_ids.append( - BinaryID( - file_id=obj.metadata.id, - organization_id=obj.metadata.capture_metadata.organization_id, - location_id=obj.metadata.capture_metadata.location_id - ) - ) - -binary_data = await data_client.delete_binary_data_by_ids(my_ids) -``` - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.delete_binary_data_by_ids). - -{{% /tab %}} -{{< /tabs >}} - -### AddTagsToBinaryDataByIds - -Add tags to binary data by ids. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `tags` ([List[str]](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)): List of tags to add to specified binary data. - Must be non-empty. -- `binary_ids` (List[viam.app.proto.BinaryID]): List of BinaryID objects specifying binary data to tag. - Must be non-empty. - -**Returns:** - -- None. - -**Raises:** - -- `GRPCError` – This error is raised if no BinaryID objects or tags are provided. - -```python {class="line-numbers linkable-line-numbers"} -from viam.proto.app.data import BinaryID - -tags = ["tag1", "tag2"] - -binary_metadata = await data_client.binary_data_by_filter( - include_file_data=False - ) - -my_ids = [] - -for obj in binary_metadata: - my_ids.append( - BinaryID( - file_id=obj.metadata.id, - organization_id=obj.metadata.capture_metadata.organization_id, - location_id=obj.metadata.capture_metadata.location_id - ) - ) - -binary_data = await data_client.add_tags_to_binary_data_by_ids(tags, my_ids) -``` - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.add_tags_to_binary_data_by_ids). - -{{% /tab %}} -{{< /tabs >}} - -### AddTagsToBinaryDataByFilter - -Add tags to binary data by filter. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `tags` ([List[str]](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)): List of tags to add to specified binary data. - Must be non-empty. -- `filter` ([viam.proto.app.data.Filter](https://python.viam.dev/autoapi/viam/proto/app/data/index.html#viam.proto.app.data.Filter "viam.proto.app.data.Filter")): Filter specifying binary data to tag. If no Filter is provided, all data will be tagged. - -**Returns:** - -- None. - -**Raises:** - -- `GRPCError` – This error is raised if no Btags are provided. - -```python {class="line-numbers linkable-line-numbers"} -from viam.proto.app.data import Filter - -my_filter = Filter(component_name="my_camera") -tags = ["tag1", "tag2"] -res = await data_client.add_tags_to_binary_data_by_filter(tags, my_filter) -``` - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.add_tags_to_binary_data_by_filter). - -{{% /tab %}} -{{< /tabs >}} - -### RemoveTagsFromBinaryDataByIds - -Remove tags from binary by ids. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `tags` ([List[str]](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)): List of tags to remove from specified binary data. - Must be non-empty. -- `file_ids` ([List[str]](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)): List of BinaryID objects specifying binary data to untag. - Must be non-empty. - -**Returns:** - -- ([int](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)): The number of tags removed. - -**Raises:** - -- `GRPCError` – This error is raised if no BinaryID objects or tags are provided. - -```python {class="line-numbers linkable-line-numbers"} -from viam.proto.app.data import BinaryID - -tags = ["tag1", "tag2"] - -binary_metadata = await data_client.binary_data_by_filter( - include_file_data=False - ) - -my_ids = [] - -for obj in binary_metadata: - my_ids.append( - BinaryID( - file_id=obj.metadata.id, - organization_id=obj.metadata.capture_metadata.organization_id, - location_id=obj.metadata.capture_metadata.location_id - ) - ) - -binary_data = await data_client.remove_tags_from_binary_data_by_ids( - tags, my_ids) -``` - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.remove_tags_from_binary_data_by_ids). - -{{% /tab %}} -{{< /tabs >}} - -### RemoveTagsFromBinaryDataByFilter - -Remove tags from binary data by filter. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `tags` ([List[str]](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)): List of tags to remove from specified binary data. -- `filter` ([viam.proto.app.data.Filter](https://python.viam.dev/autoapi/viam/proto/app/data/index.html#viam.proto.app.data.Filter "viam.proto.app.data.Filter")): Filter specifying binary data to untag. - If no Filter is provided, all data will be untagged. - -**Returns:** - -- ([int](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)): The number of tags removed. - -**Raises:** - -- `GRPCError` – This error is raised if no tags are provided. - -```python {class="line-numbers linkable-line-numbers"} -from viam.proto.app.data import Filter - -my_filter = Filter(component_name="my_camera") -tags = ["tag1", "tag2"] -res = await data_client.remove_tags_from_binary_data_by_filter(tags, my_filter) -``` - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.remove_tags_from_binary_data_by_filter). - -{{% /tab %}} -{{< /tabs >}} - -### TagsByFilter - -Get a list of tags using a filter. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `filter` ([viam.proto.app.data.Filter](https://python.viam.dev/autoapi/viam/proto/app/data/index.html#viam.proto.app.data.Filter "viam.proto.app.data.Filter")): Filter specifying data to retrieve from. If no Filter is provided, all data tags are returned. - -**Returns:** - -- ([List[str]](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)): The list of tags. - -```python {class="line-numbers linkable-line-numbers"} -from viam.proto.app.data import Filter - -my_filter = Filter(component_name="my_camera") -tags = await data_client.tags_by_filter(my_filter) -``` - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.tags_by_filter). - -{{% /tab %}} -{{< /tabs >}} - -### BoundingBoxLabelsByFilter - -Get a list of bounding box labels using a Filter. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `filter` ([viam.proto.app.data.Filter](https://python.viam.dev/autoapi/viam/proto/app/data/index.html#viam.proto.app.data.Filter "viam.proto.app.data.Filter")): Filter specifying data to retrieve from. If no Filter is provided, all labels will return. - -**Returns:** - -- ([List[str]](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)): The list of bounding box labels. - -```python {class="line-numbers linkable-line-numbers"} -from viam.proto.app.data import Filter - -my_filter = Filter(component_name="my_camera") -bounding_box_labels = await data_client.bounding_box_labels_by_filter( - my_filter) -``` - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.bounding_box_labels_by_filter). - -{{% /tab %}} -{{< /tabs >}} - -### GetDatabaseConnection - -Get a connection to access a MongoDB Atlas Data federation instance. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `organization_id` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)): Organization to retrieve the connection for. - You can obtain your organization id from the [organization settings page](/cloud/organizations/). - -**Returns:** - -- ([`str`](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)): The hostname of the federated database. - -```python {class="line-numbers linkable-line-numbers"} -data_client.get_database_connection("a12b3c4e-1234-1abc-ab1c-ab1c2d345abc") -``` - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.get_database_connection). - -{{% /tab %}} -{{< /tabs >}} - -### BinaryDataCaptureUpload - -Upload binary data collected on your machine through a specific component and the relevant metadata to the [Viam app](https://app.viam.com). -Uploaded binary data can be found under the **Images**, **Point clouds**, or **Files** subtab of the app's [**Data** tab](https://app.viam.com/data), depending on the type of data that you upload. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `binary_data` [(bytes)](https://docs.python.org/3/library/stdtypes.html#bytes-objects): The data to be uploaded, represented in bytes. -- `part_id` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)): Part ID of the component used to capture the data. See [Find part ID](#find-part-id) for instructions on retrieving this value. -- `component_type` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)): Type of the component used to capture the data. -- `component_name` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)): Name of the component used to capture the data. -- `method_name` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)): Name of the method used to capture the data. -- `tags` ([Optional[List[str]]](https://docs.python.org/3/library/stdtypes.html#typesseq-list)): Optional list of [image tags](/services/data/dataset/#image-tags) to allow for tag-based data filtering when retrieving data. -- `data_request_times` ([Optional[Tuple[datetime.datetime, datetime.datetime]]](https://docs.python.org/3/library/stdtypes.html#tuples)): Optional tuple containing [`datetime`](https://docs.python.org/3/library/datetime.html) objects denoting the times this data was requested and received by the appropriate sensor. -- `file_extension` ([Optional[str]](https://docs.python.org/3/library/typing.html#typing.Optional)): The file extension of binary data including the period. For example, `".jpg"`, `".png"`, or `".pcd"`. Specify this to route the binary data to its corresponding mime type in storage in the [Viam app](https://app.viam.com). - -**Returns**: - -- ([`str`](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)): ID of the new file. - -```python {class="line-numbers linkable-line-numbers"} -time_requested = datetime(2023, 6, 5, 11) -time_received = datetime(2023, 6, 5, 11, 0, 3) - -file_id = await data_client.binary_data_capture_upload( - part_id="INSERT YOUR PART ID", - component_type='camera', - component_name='my_camera', - method_name='GetImages', - method_parameters=None, - tags=["tag_1", "tag_2"], - data_request_times=[time_requested, time_received], - file_extension=".jpg", - binary_data=b"Encoded image bytes" -) -``` - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.binary_data_capture_upload). - -{{% /tab %}} -{{< /tabs >}} - -### TabularDataCaptureUpload - -Upload tabular data collected on your machine through a specific [component](/components/) to the [Viam app](https://app.viam.com). -Uploaded tabular data can be found under the **Sensors** subtab of the app's [**Data** tab](https://app.viam.com/data). - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `tabular_data` [(List[Mapping[str, Any]])](https://docs.python.org/3/library/stdtypes.html#typesseq-list): List of the data to be uploaded, represented tabularly as a collection of dictionaries. -- `part_id` [(str)](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): Part ID of the component used to capture the data. See [Find part ID](#find-part-id) for instructions on retrieving this value. -- `component_type` [(str)](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): Type of the component used to capture the data. -- `component_name` [(str)](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): Name of the component used to capture the data. -- `method_name` [(str)](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): Name of the method used to capture the data. -- `tags` [(Optional[List[str]])](https://docs.python.org/3/library/stdtypes.html#typesseq-list): Optional list of [image tags](/services/data/dataset/#image-tags) to allow for tag-based data filtering when retrieving data. -- `data_request_times` [(Optional[Tuple[datetime.datetime, datetime.datetime]])](https://docs.python.org/3/library/stdtypes.html#tuples): Optional tuple containing [`datetime`](https://docs.python.org/3/library/datetime.html) objects denoting the times this data was requested and received by the appropriate sensor. - -**Returns**: - -- ([`str`](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)): ID of the new file. - -```python {class="line-numbers linkable-line-numbers"} -time_requested = datetime(2023, 6, 5, 11) -time_received = datetime(2023, 6, 5, 11, 0, 3) - -file_id = await data_client.tabular_data_capture_upload( - part_id="INSERT YOUR PART ID", - component_type='motor', - component_name='left_motor', - method_name='IsPowered', - tags=["tag_1", "tag_2"], - data_request_times=[(time_requested, time_received)], - tabular_data=[{'PowerPCT': 0, 'IsPowered': False}] -) -``` - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.tabular_data_capture_upload). - -{{% /tab %}} -{{< /tabs >}} - -### StreamingDataCaptureUpload - -Upload the contents of streaming binary data and the relevant metadata to the [Viam app](https://app.viam.com). -Uploaded streaming data can be found under the [**Data** tab](https://app.viam.com/data). - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `data` [(bytes)](https://docs.python.org/3/library/stdtypes.html#bytes-objects): Data to be uploaded, represented in bytes. -- `part_id` [(str)](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): Part ID of the resource associated with the file. -- `file_ext` [(str)](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): File extension type for the data. required for determining MIME type. -- `component_type` [(Optional[str])](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): Optional type of the component associated with the file (For example, “movement_sensor”). -- `component_name` [(Optional[str])](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): Optional name of the component associated with the file. -- `method_name` [(Optional[str])](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): Optional name of the method associated with the file. -- `method_parameters` [(Optional[str])](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): Optional dictionary of the method parameters. No longer in active use. -- `data_request_times` [(Optional[Tuple[datetime.datetime, datetime.datetime]])](https://docs.python.org/3/library/stdtypes.html#tuples): Optional tuple containing [`datetime`](https://docs.python.org/3/library/datetime.html) objects denoting the times this data was requested and received by the appropriate sensor. -- `tags` [(Optional[List[str]])](https://docs.python.org/3/library/stdtypes.html#typesseq-list): Optional list of [image tags](/services/data/dataset/#image-tags) to allow for tag-based data filtering when retrieving data. - -**Returns:** - -- [(str)](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): The `file_id` of the uploaded data. - -**Raises:** - -- `GRPCError` – If an invalid part ID is passed. - -```python {class="line-numbers linkable-line-numbers"} -time_requested = datetime(2023, 6, 5, 11) -time_received = datetime(2023, 6, 5, 11, 0, 3) - -file_id = await data_client.streaming_data_capture_upload( - data="byte-data-to-upload", - part_id="INSERT YOUR PART ID", - file_ext="png", - component_type='motor', - component_name='left_motor', - method_name='IsPowered', - data_request_times=[(time_requested, time_received)], - tags=["tag_1", "tag_2"] -) - -print(file_id) -``` - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.streaming_data_capture_upload). - -{{% /tab %}} -{{< /tabs >}} - -### FileUpload - -Upload arbitrary files stored on your machine to the [Viam app](https://app.viam.com) by file name. -If uploaded with a file extension of .jpeg/.jpg/.png, uploaded files can be found in the **Images** subtab of the app's [**Data** tab](https://app.viam.com/data). -If .pcd, the uploaded files can be found in the **Point clouds** subtab. -All other types of uploaded files can be found under the **Files** subtab of the app's [**Data** tab](https://app.viam.com/data). - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `data` [(bytes)](https://docs.python.org/3/library/stdtypes.html#bytes-objects): Bytes representing the file data to upload. -- `part_id` [(str)](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): Part ID of the component used to capture the data. See [Find part ID](#find-part-id) for instructions on retrieving this value. -- `component_type` [(Optional[str])](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): Type of the component used to capture the data. -- `component_name` [(Optional[str])](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): Name of the component used to capture the data. -- `file_name` [(Optional[str])](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): Optional name of the file. The empty string `""` will be assigned as the filename if one isn’t provided. -- `file_extension` [(Optional[str])](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): Optional file extension. The empty string `""` will be assigned as the file extension if one isn’t provided. -- `tags` [(Optional[List[str]])](https://docs.python.org/3/library/stdtypes.html#typesseq-list): Optional list of [image tags](/services/data/dataset/#image-tags) to allow for tag-based data filtering when retrieving data. - -**Returns**: - -- [(str)](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): ID of the new file. - -```python {class="line-numbers linkable-line-numbers"} -file_id = await data_client.file_upload( - data=b"Encoded image bytes", - part_id="INSERT YOUR PART ID", - tags=["tag_1", "tag_2"], - file_name="your-file", - file_extension=".txt" -) -``` - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.file_upload). - -{{% /tab %}} -{{< /tabs >}} - -### FileUploadFromPath - -Upload files stored on your machine to the [Viam app](https://app.viam.com) by filepath. -Uploaded files can be found under the **Files** subtab of the app's [**Data** tab](https://app.viam.com/data). - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `filepath` [(str)](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): The absolute filepath of the file to be uploaded. -- `part_id` [(str)](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): Part ID of the component used to capture the data. See [Find part ID](#find-part-id) for instructions on retrieving this value. -- `component_type` [(Optional[str])](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): Type of the component used to capture the data. -- `component_name` [(Optional[str])](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): Name of the component used to capture the data. -- `tags` [(Optional[List[str]])](https://docs.python.org/3/library/stdtypes.html#typesseq-list): Optional list of [image tags](/services/data/dataset/#image-tags) to allow for tag-based data filtering when retrieving data. - -**Returns**: - -- [(str)](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): ID of the new file. - -```python {class="line-numbers linkable-line-numbers"} -file_id = await data_client.file_upload_from_path( - part_id="INSERT YOUR PART ID", - tags=["tag_1", "tag_2"], - filepath="/Users///" -) -``` - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.file_upload_from_path). - -{{% /tab %}} -{{< /tabs >}} - -### AddBoundingBoxToImageById - -Add a bounding box to an image specified by its BinaryID. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `binary_id` ([viam.proto.app.data.BinaryID](https://python.viam.dev/autoapi/viam/proto/app/data/index.html#viam.proto.app.data.BinaryID)): The ID of the image to add the bounding box to. -- `label` [(str)](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): A label for the bounding box. -- `x_min_normalized` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)): Minimum X value of the bounding box normalized from `0` to `1`. -- `y_min_normalized` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)): Minimum Y value of the bounding box normalized from `0` to `1`. -- `x_max_normalized` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)): Maximum X value of the bounding box normalized from `0` to `1`. -- `y_max_normalized` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)): Maximum Y value of the bounding box normalized from `0` to `1`. - -**Returns:** - -- [(str)](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): The bounding box ID of the image. - -**Raises:** - -- `GRPCError` – If the X or Y values are outside of the [0, 1] range. - -```python {class="line-numbers linkable-line-numbers"} -from viam.proto.app.data import BinaryID - -MY_BINARY_ID = BinaryID( - file_id=your-file_id, - organization_id=your-org-id, - location_id=your-location-id -) - -bbox_label = await data_client.add_bounding_box_to_image_by_id( - binary_id=MY_BINARY_ID, - label="label", - x_min_normalized=0, - y_min_normalized=.1, - x_max_normalized=.2, - y_max_normalized=.3 -) - -print(bbox_label) -``` - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.add_bounding_box_to_image_by_id). - -{{% /tab %}} -{{< /tabs >}} - -### RemoveBoundingBoxFromImageById - -Removes a bounding box from an image specified by its BinaryID. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `bbox_id` [(str)](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): The ID of the bounding box to remove. -- `binary_id` ([viam.proto.app.data.BinaryID](https://python.viam.dev/autoapi/viam/proto/app/data/index.html#viam.proto.app.data.BinaryID)): Binary ID of the image to to remove the bounding box from. - -**Returns:** - -- None. - -```python {class="line-numbers linkable-line-numbers"} -from viam.proto.app.data import BinaryID - -MY_BINARY_ID = BinaryID( - file_id=your-file_id, - organization_id=your-org-id, - location_id=your-location-id -) - -await data_client.remove_bounding_box_from_image_by_id( - binary_id=MY_BINARY_ID, - bbox_id="your-bounding-box-id-to-delete" -) -``` - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.remove_bounding_box_from_image_by_id). - -{{% /tab %}} -{{< /tabs >}} - -### CreateDataset - -Create a new dataset. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `name` [(str)](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): The name of the dataset being created. -- `organization_id` [(str)](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): The ID of the organization where the dataset is being created. - -**Returns:** - -- [(str)](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): The dataset ID of the created dataset. - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.create_dataset). - -```python {class="line-numbers linkable-line-numbers"} -name = await data_client.create_dataset( - name="", - organization_id="" -) -print(name) -``` - -{{% /tab %}} -{{< /tabs >}} - -### ListDatasetByIds - -Get a list of datasets using their IDs. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `ids` (List[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)]): The IDs of the datasets being called for. - -**Returns:** - -- (Sequence[[Dataset](https://python.viam.dev/autoapi/viam/gen/app/dataset/v1/dataset_pb2/index.html#viam.gen.app.dataset.v1.dataset_pb2.Dataset)]): The list of datasets. - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.list_dataset_by_ids). - -```python {class="line-numbers linkable-line-numbers"} -datasets = await data_client.list_dataset_by_ids( - ids=["abcd-1234xyz-8765z-123abc"] -) -print(datasets) -``` - -{{% /tab %}} -{{< /tabs >}} - -### ListDatasetByOrganizationId - -Get the datasets in an organization. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `organization_id` [(str)](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): The ID of the organization. - -**Returns:** - -- (Sequence[[Dataset](https://python.viam.dev/autoapi/viam/gen/app/dataset/v1/dataset_pb2/index.html#viam.gen.app.dataset.v1.dataset_pb2.Dataset)]): The list of datasets in the organization. - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.list_datasets_by_organization_id). - -```python {class="line-numbers linkable-line-numbers"} -datasets = await data_client.list_datasets_by_organization_id( - organization_id="" -) -print(datasets) -``` - -{{% /tab %}} -{{< /tabs >}} - -### RenameDataset - -Rename a dataset specified by the dataset ID. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `id` [(str)](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): The ID of the dataset. To retrieve this, navigate to your dataset's page in the [Viam app](https://app.viam.com/data/datasets), click **...** in the left-hand menu, and click **Copy dataset ID**. -- `name` [(str)](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): The new name of the dataset. - -**Returns:** - -- None. - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.rename_dataset). - -```python {class="line-numbers linkable-line-numbers"} -await data_client.rename_dataset( - id="abcd-1234xyz-8765z-123abc", - name="" -) -``` - -{{% /tab %}} -{{< /tabs >}} - -### DeleteDataset - -Delete a dataset. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `id` [(str)](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): The ID of the dataset. - -**Returns:** - -- None. - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.delete_dataset). - -```python {class="line-numbers linkable-line-numbers"} -await data_client.delete_dataset( - id="abcd-1234xyz-8765z-123abc" -) -``` - -{{% /tab %}} -{{< /tabs >}} - -### AddBinaryDataToDatasetByIds - -Add the [BinaryData](https://python.viam.dev/autoapi/viam/proto/app/data/index.html#viam.proto.app.data.BinaryData) to the provided dataset. -This BinaryData will be tagged with the VIAM_DATASET\_{id} label. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `binary_ids` (List[[BinaryID](https://python.viam.dev/autoapi/viam/gen/app/data/v1/data_pb2/index.html#viam.gen.app.data.v1.data_pb2.BinaryID "viam.gen.app.data.v1.data_pb2.BinaryID")]): The IDs of binary data to add to dataset. -- `dataset_id` [(str)](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): The ID of the dataset to be added to. - -**Returns:** - -- None. - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.add_binary_data_to_dataset_by_ids). - -```python {class="line-numbers linkable-line-numbers"} -from viam.proto.app.data import BinaryID - -binary_metadata = await data_client.binary_data_by_filter( - include_file_data=False -) - -my_binary_ids = [] - -for obj in binary_metadata: - my_binary_ids.append( - BinaryID( - file_id=obj.metadata.id, - organization_id=obj.metadata.capture_metadata.organization_id, - location_id=obj.metadata.capture_metadata.location_id - ) - ) - -await data_client.add_binary_data_to_dataset_by_ids( - binary_ids=my_binary_ids, - dataset_id="abcd-1234xyz-8765z-123abc" -) -``` - -{{% /tab %}} -{{< /tabs >}} - -### RemoveBinaryDataFromDatasetByIds - -Remove the BinaryData from the provided dataset. -This BinaryData will lose the VIAM_DATASET\_{id} tag. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `binary_ids` (List[[BinaryID](https://python.viam.dev/autoapi/viam/gen/app/data/v1/data_pb2/index.html#viam.gen.app.data.v1.data_pb2.BinaryID "viam.gen.app.data.v1.data_pb2.BinaryID")]): The IDs of binary data to remove from dataset. -- `dataset_id` [(str)](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): The ID of the dataset to be removed from. - -**Returns:** - -- None. - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.remove_binary_data_from_dataset_by_ids). - -```python {class="line-numbers linkable-line-numbers"} -from viam.proto.app.data import BinaryID - -binary_metadata = await data_client.binary_data_by_filter( - include_file_data=False -) - -my_binary_ids = [] - -for obj in binary_metadata: - my_binary_ids.append( - BinaryID( - file_id=obj.metadata.id, - organization_id=obj.metadata.capture_metadata.organization_id, - location_id=obj.metadata.capture_metadata.location_id - ) - ) - -await data_client.remove_binary_data_from_dataset_by_ids( - binary_ids=my_binary_ids, - dataset_id="abcd-1234xyz-8765z-123abc" -) -``` - -{{% /tab %}} -{{< /tabs >}} - -### ConfigureDatabaseUser - -Configure a database user for the Viam organization’s MongoDB Atlas Data Federation instance. -You can also use this to reset the password of the existing database user. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `organization_id` [(str)](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): The ID of the organization. -- `password` [(str)](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): The password of the user. - -**Returns:** - -- None. - -```python {class="line-numbers linkable-line-numbers"} -await data_client.configure_database_user( - organization_id=organization_id, - password="CHANGE-ME" -) -``` - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.configure_database_user). - -{{% /tab %}} -{{< /tabs >}} +{{< readfile "/static/include/app/apis/generated/dataset.md" >}} ## Find part ID diff --git a/docs/services/data/dataset.md b/docs/services/data/dataset.md index 8acbaacd80..0012c80713 100644 --- a/docs/services/data/dataset.md +++ b/docs/services/data/dataset.md @@ -132,6 +132,15 @@ Alternatively, you can also click on the dataset and rename it from the three-do To delete a dataset, go to the [**DATASETS** subtab](https://app.viam.com/data/datasets), right click on the dataset and click on **Delete** in the menu that appears. Alternatively, you can also click on the dataset and delete it from the three-dot icon next to the dataset name. +## API + +You can also use the Dataset API, which supports the following methods: + +{{< readfile "/static/include/app/apis/generated/dataset-table.md" >}} + +The data client API supports a separate set of methods that allow you to upload and export data to and from the Viam app. +For information about that API, see [Data Client API](/appendix/apis/data-client/). + ## Next steps {{< cards >}} diff --git a/docs/services/data/export.md b/docs/services/data/export.md index bf8d6396a0..492985d5ca 100644 --- a/docs/services/data/export.md +++ b/docs/services/data/export.md @@ -47,13 +47,8 @@ You can see more information about exporting data in the [Viam CLI documentation ## Manage data with the data client API -A set of methods using the data client API for managing data, including export, batch delete, tag, and upload functions, are provided in the [Python SDK](https://python.viam.dev). - -The following methods are supported by the data client API: - -{{< readfile "/static/include/services/apis/data-client.md" >}} - -Click on the method name for more information. +You can also use the [Data Client API](/appendix/apis/data-client/) to upload and export data to and from the Viam app. +This API includes a set of methods for managing data, including export, batch delete, tag, upload, and many more. ## Next steps diff --git a/static/include/app/apis/generated/data-table.md b/static/include/app/apis/generated/data-table.md new file mode 100644 index 0000000000..af95a687f5 --- /dev/null +++ b/static/include/app/apis/generated/data-table.md @@ -0,0 +1,22 @@ + +| Method Name | Description | +| ----------- | ----------- | +| [`TabularDataByFilter`](/appendix/apis/data-client/#tabulardatabyfilter) | Retrieve optionally filtered tabular data from the Viam app. | +| [`TabularDataBySQL`](/appendix/apis/data-client/#tabulardatabysql) | Obtain unified tabular data and metadata, queried with SQL. | +| [`TabularDataByMQL`](/appendix/apis/data-client/#tabulardatabymql) | Obtain unified tabular data and metadata, queried with MQL. | +| [`BinaryDataByFilter`](/appendix/apis/data-client/#binarydatabyfilter) | Retrieve optionally filtered binary data from the Viam app. | +| [`BinaryDataByIDs`](/appendix/apis/data-client/#binarydatabyids) | Retrieve binary data from the Viam app by `BinaryID`. | +| [`DeleteTabularData`](/appendix/apis/data-client/#deletetabulardata) | Delete tabular data older than a specified number of days. | +| [`DeleteBinaryDataByFilter`](/appendix/apis/data-client/#deletebinarydatabyfilter) | Filter and delete binary data. | +| [`DeleteBinaryDataByIDs`](/appendix/apis/data-client/#deletebinarydatabyids) | Filter and delete binary data by ids. | +| [`AddTagsToBinaryDataByIDs`](/appendix/apis/data-client/#addtagstobinarydatabyids) | Add tags to binary data by ids. | +| [`AddTagsToBinaryDataByFilter`](/appendix/apis/data-client/#addtagstobinarydatabyfilter) | Add tags to binary data by filter. | +| [`RemoveTagsFromBinaryDataByIDs`](/appendix/apis/data-client/#removetagsfrombinarydatabyids) | Remove tags from binary by ids. | +| [`RemoveTagsFromBinaryDataByFilter`](/appendix/apis/data-client/#removetagsfrombinarydatabyfilter) | Remove tags from binary data by filter. | +| [`TagsByFilter`](/appendix/apis/data-client/#tagsbyfilter) | Get a list of tags using a filter. | +| [`AddBoundingBoxToImageByID`](/appendix/apis/data-client/#addboundingboxtoimagebyid) | Add a bounding box to an image specified by its BinaryID. | +| [`RemoveBoundingBoxFromImageByID`](/appendix/apis/data-client/#removeboundingboxfromimagebyid) | Removes a bounding box from an image specified by its BinaryID. | +| [`BoundingBoxLabelsByFilter`](/appendix/apis/data-client/#boundingboxlabelsbyfilter) | Get a list of bounding box labels using a Filter. | +| [`GetDatabaseConnection`](/appendix/apis/data-client/#getdatabaseconnection) | Get a connection to access a MongoDB Atlas Data federation instance. | +| [`AddBinaryDataToDatasetByIDs`](/appendix/apis/data-client/#addbinarydatatodatasetbyids) | Add the `BinaryData` to the provided dataset. | +| [`RemoveBinaryDataFromDatasetByIDs`](/appendix/apis/data-client/#removebinarydatafromdatasetbyids) | Remove the BinaryData from the provided dataset. | diff --git a/static/include/app/apis/generated/data.md b/static/include/app/apis/generated/data.md new file mode 100644 index 0000000000..71159652f8 --- /dev/null +++ b/static/include/app/apis/generated/data.md @@ -0,0 +1,750 @@ +### TabularDataByFilter + +Retrieve optionally filtered tabular data from the [Viam app](https://app.viam.com). +You can also find your tabular data under the **Sensors** subtab of the app's [**Data** tab](https://app.viam.com/data). + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `filter` ([viam.proto.app.data.Filter](https://python.viam.dev/autoapi/viam/proto/app/data/index.html#viam.proto.app.data.Filter)) (optional): Optional Filter specifying tabular data to retrieve. No Filter implies all tabular data. +- `limit` ([int](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (optional): The maximum number of entries to include in a page. Defaults to 50 if unspecified. +- `sort_order` ([viam.proto.app.data.Order.ValueType](https://python.viam.dev/autoapi/viam/proto/app/data/index.html#viam.proto.app.data.Order)) (optional): The desired sort order of the data. +- `last` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (optional): Optional string indicating the object identifier of the last-returned data. This object identifier is returned by calls to `TabularDataByFilter` as the last value. If provided, the server will return the next data entries after the last object identifier. +- `count_only` ([bool](https://docs.python.org/3/library/stdtypes.html#boolean-type-bool)) (required): Whether to return only the total count of entries. +- `include_internal_data` ([bool](https://docs.python.org/3/library/stdtypes.html#boolean-type-bool)) (required): Whether to return the internal data. Internal data is used for Viam-specific data ingestion, like cloud SLAM. Defaults to False. +- `dest` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (optional): Optional filepath for writing retrieved data. + +**Returns:** + +- (Tuple[List[TabularData], [int](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex), [str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)]): A tuple containing the following: List[TabularData]: The tabular data, int: The count (number of entries), str: The last-returned page ID. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +from viam.utils import create_filter + +my_data = [] +last = None +my_filter = create_filter(component_name="left_motor") +while True: + tabular_data, count, last = await data_client.tabular_data_by_filter(my_filter, last) + if not tabular_data: + break + my_data.extend(tabular_data) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.tabular_data_by_filter). + +{{% /tab %}} +{{< /tabs >}} + +### TabularDataBySQL + +Obtain unified tabular data and metadata, queried with SQL. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `organization_id` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): The ID of the organization that owns the data. You can obtain your organization ID from the Viam app’s organization settings page. +- `sql_query` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): The SQL query to run. + +**Returns:** + +- (List[Dict[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), viam.utils.ValueTypes]]): An array of data objects. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +data = await data_client.tabular_data_by_sql(org_id="", sql_query="SELECT * FROM readings LIMIT 5") +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.tabular_data_by_sql). + +{{% /tab %}} +{{< /tabs >}} + +### TabularDataByMQL + +Obtain unified tabular data and metadata, queried with MQL. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `organization_id` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): The ID of the organization that owns the data. You can obtain your organization ID from the Viam app’s organization settings page. +- `mql_binary` (List[[bytes](https://docs.python.org/3/library/stdtypes.html#bytes-objects)]) (required): The MQL query to run as a list of BSON queries. You can encode your bson queries using a library like pymongo or bson. + +**Returns:** + +- (List[Dict[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), viam.utils.ValueTypes]]): An array of data objects. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +# using bson +import bson +tabular_data = await data_client.tabular_data_by_mql(org_id="", mql_binary=[ + bson.dumps({ '$match': { 'location_id': '' } }), + bson.dumps({ "$limit": 5 }) +]) + +# using pymongo +import bson +tabular_data = await data_client.tabular_data_by_mql(org_id="", mql_binary=[ + bson.encode({ '$match': { 'location_id': '' } }), + bson.encode({ "$limit": 5 }) +]) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.tabular_data_by_mql). + +{{% /tab %}} +{{< /tabs >}} + +### BinaryDataByFilter + +Retrieve optionally filtered binary data from the [Viam app](https://app.viam.com). +You can also find your binary data under the **Images**, **Point clouds**, or **Files** subtab of the app's [**Data** tab](https://app.viam.com/data), depending on the type of data that you have uploaded. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `filter` ([viam.proto.app.data.Filter](https://python.viam.dev/autoapi/viam/proto/app/data/index.html#viam.proto.app.data.Filter)) (optional): Optional Filter specifying tabular data to retrieve. No Filter implies all binary data. +- `limit` ([int](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (optional): The maximum number of entries to include in a page. Defaults to 50 if unspecified. +- `sort_order` ([viam.proto.app.data.Order.ValueType](https://python.viam.dev/autoapi/viam/proto/app/data/index.html#viam.proto.app.data.Order)) (optional): The desired sort order of the data. +- `last` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (optional): Optional string indicating the object identifier of the last-returned data. This object identifier is returned by calls to `BinaryDataByFilter` as the last value. If provided, the server will return the next data entries after the last object identifier. +- `include_binary_data` ([bool](https://docs.python.org/3/library/stdtypes.html#boolean-type-bool)) (required): Boolean specifying whether to actually include the binary file data with each retrieved file. Defaults to true (that is, both the files’ data and metadata are returned). +- `count_only` ([bool](https://docs.python.org/3/library/stdtypes.html#boolean-type-bool)) (required): Whether to return only the total count of entries. +- `include_internal_data` ([bool](https://docs.python.org/3/library/stdtypes.html#boolean-type-bool)) (required): Whether to return the internal data. Internal data is used for Viam-specific data ingestion, like cloud SLAM. Defaults to False. +- `dest` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (optional): Optional filepath for writing retrieved data. + +**Returns:** + +- (Tuple[List[viam.proto.app.data.BinaryData], [int](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex), [str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)]): A tuple containing the following: List[viam.proto.app.data.BinaryData]: The binary data, int: The count (number of entries), str: The last-returned page ID. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +from viam.utils import create_filter + + +my_data = [] +last = None +my_filter = create_filter(component_name="camera") +while True: + data, count, last = await data_client.binary_data_by_filter(my_filter, last) + if not data: + break + my_data.extend(data) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.binary_data_by_filter). + +{{% /tab %}} +{{< /tabs >}} + +### BinaryDataByIDs + +Retrieve binary data from the [Viam app](https://app.viam.com) by `BinaryID`. +You can also find your binary data under the **Images**, **Point clouds**, or **Files** subtab of the app's [**Data** tab](https://app.viam.com/data), depending on the type of data that you have uploaded. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `binary_ids` ([List[viam.proto.app.data.BinaryID]](https://python.viam.dev/autoapi/viam/proto/app/data/index.html#viam.proto.app.data.BinaryID)) (required): BinaryID objects specifying the desired data. Must be non-empty. +- `dest` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (optional): Optional filepath for writing retrieved data. + +**Returns:** + +- ([List[viam.proto.app.data.BinaryData]](https://python.viam.dev/autoapi/viam/proto/app/data/index.html#viam.proto.app.data.BinaryData)): The binary data. + +**Raises:** + +- (GRPCError): If no BinaryID objects are provided. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +from viam.proto.app.data import BinaryID + +binary_metadata = await data_client.binary_data_by_filter( + include_file_data=False +) + +my_ids = [] + +for obj in binary_metadata: + my_ids.append( + BinaryID( + file_id=obj.metadata.id, + organization_id=obj.metadata.capture_metadata.organization_id, + location_id=obj.metadata.capture_metadata.location_id + ) + ) + +binary_data = await data_client.binary_data_by_ids(my_ids) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.binary_data_by_ids). + +{{% /tab %}} +{{< /tabs >}} + +### DeleteTabularData + +Delete tabular data older than a specified number of days. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `organization_id` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): ID of organization to delete data from. You can obtain your organization ID from the Viam app’s organization settings page. +- `delete_older_than_days` ([int](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (required): Delete data that was captured up to this many days ago. For example if delete_older_than_days is 10, this deletes any data that was captured up to 10 days ago. If it is 0, all existing data is deleted. + +**Returns:** + +- ([int](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)): The number of items deleted. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +from viam.utils import create_filter + +my_filter = create_filter(component_name="left_motor") +days_of_data_to_delete = 10 +tabular_data = await data_client.delete_tabular_data( + org_id="a12b3c4e-1234-1abc-ab1c-ab1c2d345abc", days_of_data_to_delete) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.delete_tabular_data). + +{{% /tab %}} +{{< /tabs >}} + +### DeleteBinaryDataByFilter + +Filter and delete binary data. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `filter` ([viam.proto.app.data.Filter](https://python.viam.dev/autoapi/viam/proto/app/data/index.html#viam.proto.app.data.Filter)) (optional): Optional Filter specifying binary data to delete. Passing an empty Filter will lead to all data being deleted. Exercise caution when using this option. + +**Returns:** + +- ([int](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)): The number of items deleted. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +from viam.utils import create_filter + +my_filter = create_filter(component_name="left_motor") +res = await data_client.delete_binary_data_by_filter(my_filter) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.delete_binary_data_by_filter). + +{{% /tab %}} +{{< /tabs >}} + +### DeleteBinaryDataByIDs + +Filter and delete binary data by ids. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `binary_ids` ([List[viam.proto.app.data.BinaryID]](https://python.viam.dev/autoapi/viam/proto/app/data/index.html#viam.proto.app.data.BinaryID)) (required): BinaryID objects specifying the data to be deleted. Must be non-empty. + +**Returns:** + +- ([int](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)): The number of items deleted. + +**Raises:** + +- (GRPCError): If no BinaryID objects are provided. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +from viam.proto.app.data import BinaryID + +binary_metadata = await data_client.binary_data_by_filter( + include_file_data=False +) + +my_ids = [] + +for obj in binary_metadata: + my_ids.append( + BinaryID( + file_id=obj.metadata.id, + organization_id=obj.metadata.capture_metadata.organization_id, + location_id=obj.metadata.capture_metadata.location_id + ) + ) + +binary_data = await data_client.delete_binary_data_by_ids(my_ids) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.delete_binary_data_by_ids). + +{{% /tab %}} +{{< /tabs >}} + +### AddTagsToBinaryDataByIDs + +Add tags to binary data by ids. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `tags` (List[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)]) (required): List of tags to add to specified binary data. Must be non-empty. +- `binary_ids` ([List[viam.proto.app.data.BinaryID]](https://python.viam.dev/autoapi/viam/proto/app/data/index.html#viam.proto.app.data.BinaryID)) (required): List of BinaryID objects specifying binary data to tag. Must be non-empty. + +**Returns:** + +- None. + +**Raises:** + +- (GRPCError): If no BinaryID objects or tags are provided. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +from viam.proto.app.data import BinaryID + +tags = ["tag1", "tag2"] + +binary_metadata = await data_client.binary_data_by_filter( + include_file_data=False +) + +my_ids = [] + +for obj in binary_metadata: + my_ids.append( + BinaryID( + file_id=obj.metadata.id, + organization_id=obj.metadata.capture_metadata.organization_id, + location_id=obj.metadata.capture_metadata.location_id + ) + ) + +binary_data = await data_client.add_tags_to_binary_data_by_ids(tags, my_ids) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.add_tags_to_binary_data_by_ids). + +{{% /tab %}} +{{< /tabs >}} + +### AddTagsToBinaryDataByFilter + +Add tags to binary data by filter. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `tags` (List[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)]) (required): List of tags to add to specified binary data. Must be non-empty. +- `filter` ([viam.proto.app.data.Filter](https://python.viam.dev/autoapi/viam/proto/app/data/index.html#viam.proto.app.data.Filter)) (optional): Filter specifying binary data to tag. If no Filter is provided, all data will be tagged. + +**Returns:** + +- None. + +**Raises:** + +- (GRPCError): If no tags are provided. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +from viam.utils import create_filter + +my_filter = create_filter(component_name="my_camera") +tags = ["tag1", "tag2"] +res = await data_client.add_tags_to_binary_data_by_filter(tags, my_filter) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.add_tags_to_binary_data_by_filter). + +{{% /tab %}} +{{< /tabs >}} + +### RemoveTagsFromBinaryDataByIDs + +Remove tags from binary by ids. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `tags` (List[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)]) (required): List of tags to remove from specified binary data. Must be non-empty. +- `binary_ids` ([List[viam.proto.app.data.BinaryID]](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.BinaryID)) (required): List of BinaryID objects specifying binary data to untag. Must be non-empty. + +**Returns:** + +- ([int](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)): The number of tags removed. + +**Raises:** + +- (GRPCError): If no binary_ids or tags are provided. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +from viam.proto.app.data import BinaryID + +tags = ["tag1", "tag2"] + +binary_metadata = await data_client.binary_data_by_filter( + include_file_data=False +) + +my_ids = [] + +for obj in binary_metadata: + my_ids.append( + BinaryID( + file_id=obj.metadata.id, + organization_id=obj.metadata.capture_metadata.organization_id, + location_id=obj.metadata.capture_metadata.location_id + ) + ) + +binary_data = await data_client.remove_tags_from_binary_data_by_ids( + tags, my_ids) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.remove_tags_from_binary_data_by_ids). + +{{% /tab %}} +{{< /tabs >}} + +### RemoveTagsFromBinaryDataByFilter + +Remove tags from binary data by filter. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `tags` (List[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)]) (required): List of tags to remove from specified binary data. +- `filter` ([viam.proto.app.data.Filter](https://python.viam.dev/autoapi/viam/proto/app/data/index.html#viam.proto.app.data.Filter)) (optional): Filter specifying binary data to untag. If no Filter is provided, all data will be untagged. + +**Returns:** + +- ([int](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)): The number of tags removed. + +**Raises:** + +- (GRPCError): If no tags are provided. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +from viam.utils import create_filter + +my_filter = create_filter(component_name="my_camera") +tags = ["tag1", "tag2"] +res = await data_client.remove_tags_from_binary_data_by_filter(tags, my_filter) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.remove_tags_from_binary_data_by_filter). + +{{% /tab %}} +{{< /tabs >}} + +### TagsByFilter + +Get a list of tags using a filter. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `filter` ([viam.proto.app.data.Filter](https://python.viam.dev/autoapi/viam/proto/app/data/index.html#viam.proto.app.data.Filter)) (optional): Filter specifying data to retrieve from. If no Filter is provided, all data tags will return. + +**Returns:** + +- (List[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)]): The list of tags. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +from viam.utils import create_filter + +my_filter = create_filter(component_name="my_camera") +tags = await data_client.tags_by_filter(my_filter) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.tags_by_filter). + +{{% /tab %}} +{{< /tabs >}} + +### AddBoundingBoxToImageByID + +Add a bounding box to an image specified by its BinaryID. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `binary_id` ([viam.proto.app.data.BinaryID](https://python.viam.dev/autoapi/viam/proto/app/data/index.html#viam.proto.app.data.BinaryID)) (required): The ID of the image to add the bounding box to. +- `label` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): A label for the bounding box. +- `x_min_normalized` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (required): Min X value of the bounding box normalized from 0 to 1. +- `y_min_normalized` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (required): Min Y value of the bounding box normalized from 0 to 1. +- `x_max_normalized` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (required): Max X value of the bounding box normalized from 0 to 1. +- `y_max_normalized` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (required): Max Y value of the bounding box normalized from 0 to 1. + +**Returns:** + +- ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)): The bounding box ID. + +**Raises:** + +- (GRPCError): If the X or Y values are outside of the [0, 1] range. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +from viam.proto.app.data import BinaryID + +MY_BINARY_ID = BinaryID( + file_id=your-file_id, + organization_id=your-org-id, + location_id=your-location-id +) + +bbox_label = await data_client.add_bounding_box_to_image_by_id( + binary_id=MY_BINARY_ID, + label="label", + x_min_normalized=0, + y_min_normalized=.1, + x_max_normalized=.2, + y_max_normalized=.3 +) + +print(bbox_label) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.add_bounding_box_to_image_by_id). + +{{% /tab %}} +{{< /tabs >}} + +### RemoveBoundingBoxFromImageByID + +Removes a bounding box from an image specified by its BinaryID. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `bbox_id` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): The ID of the bounding box to remove. +- `binary_id` ([viam.proto.app.data.BinaryID](https://python.viam.dev/autoapi/viam/proto/app/data/index.html#viam.proto.app.data.BinaryID)) (required): Binary ID of the image to to remove the bounding box from. + +**Returns:** + +- None. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +from viam.proto.app.data import BinaryID + +MY_BINARY_ID = BinaryID( + file_id=your-file_id, + organization_id=your-org-id, + location_id=your-location-id +) + +await data_client.remove_bounding_box_from_image_by_id( +binary_id=MY_BINARY_ID, +bbox_id="your-bounding-box-id-to-delete" +) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.remove_bounding_box_from_image_by_id). + +{{% /tab %}} +{{< /tabs >}} + +### BoundingBoxLabelsByFilter + +Get a list of bounding box labels using a Filter. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `filter` ([viam.proto.app.data.Filter](https://python.viam.dev/autoapi/viam/proto/app/data/index.html#viam.proto.app.data.Filter)) (optional): Filter specifying data to retrieve from. If no Filter is provided, all labels will return. + +**Returns:** + +- (List[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)]): The list of bounding box labels. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +from viam.utils import create_filter + +my_filter = create_filter(component_name="my_camera") +bounding_box_labels = await data_client.bounding_box_labels_by_filter( + my_filter) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.bounding_box_labels_by_filter). + +{{% /tab %}} +{{< /tabs >}} + +### GetDatabaseConnection + +Get a connection to access a MongoDB Atlas Data federation instance. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `organization_id` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): Organization to retrieve the connection for. You can obtain your organization ID from the Viam app’s organization settings page. + +**Returns:** + +- ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)): The hostname of the federated database. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +data_client.get_database_connection(org_id="a12b3c4e-1234-1abc-ab1c-ab1c2d345abc") +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.get_database_connection). + +{{% /tab %}} +{{< /tabs >}} + +### AddBinaryDataToDatasetByIDs + +Add the `BinaryData` to the provided dataset. +This BinaryData will be tagged with the VIAM_DATASET\_{id} label. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `binary_ids` ([List[viam.proto.app.data.BinaryID]](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.BinaryID)) (required): The IDs of binary data to add to dataset. To retrieve these IDs, navigate to your dataset’s page in the Viam app, click … in the left-hand menu, and click Copy dataset ID. +- `dataset_id` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): The ID of the dataset to be added to. + +**Returns:** + +- None. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +from viam.proto.app.data import BinaryID + +binary_metadata = await data_client.binary_data_by_filter( + include_file_data=False +) + +my_binary_ids = [] + +for obj in binary_metadata: + my_binary_ids.append( + BinaryID( + file_id=obj.metadata.id, + organization_id=obj.metadata.capture_metadata.organization_id, + location_id=obj.metadata.capture_metadata.location_id + ) + ) + +await data_client.add_binary_data_to_dataset_by_ids( + binary_ids=my_binary_ids, + dataset_id="abcd-1234xyz-8765z-123abc" +) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.add_binary_data_to_dataset_by_ids). + +{{% /tab %}} +{{< /tabs >}} + +### RemoveBinaryDataFromDatasetByIDs + +Remove the BinaryData from the provided dataset. +This BinaryData will lose the VIAM_DATASET\_{id} tag. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `binary_ids` ([List[viam.proto.app.data.BinaryID]](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.BinaryID)) (required): The IDs of binary data to remove from dataset. To retrieve these IDs, navigate to your dataset’s page in the Viam app, click … in the left-hand menu, and click Copy dataset ID. +- `dataset_id` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): The ID of the dataset to be removed from. + +**Returns:** + +- None. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +from viam.proto.app.data import BinaryID + +binary_metadata = await data_client.binary_data_by_filter( + include_file_data=False +) + +my_binary_ids = [] + +for obj in binary_metadata: + my_binary_ids.append( + BinaryID( + file_id=obj.metadata.id, + organization_id=obj.metadata.capture_metadata.organization_id, + location_id=obj.metadata.capture_metadata.location_id + ) + ) + +await data_client.remove_binary_data_from_dataset_by_ids( + binary_ids=my_binary_ids, + dataset_id="abcd-1234xyz-8765z-123abc" +) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.remove_binary_data_from_dataset_by_ids). + +{{% /tab %}} +{{< /tabs >}} diff --git a/static/include/app/apis/generated/data_sync-table.md b/static/include/app/apis/generated/data_sync-table.md new file mode 100644 index 0000000000..09267057d8 --- /dev/null +++ b/static/include/app/apis/generated/data_sync-table.md @@ -0,0 +1,8 @@ + +| Method Name | Description | +| ----------- | ----------- | +| [`BinaryDataCaptureUpload`](/appendix/apis/data-client/#binarydatacaptureupload) | Upload binary data collected on your machine through a specific component and the relevant metadata to the Viam app. | +| [`TabularDataCaptureUpload`](/appendix/apis/data-client/#tabulardatacaptureupload) | Upload tabular data collected on your machine through a specific component to the Viam app. | +| [`FileUpload`](/appendix/apis/data-client/#fileupload) | Upload arbitrary files stored on your machine to the Viam app by file name. | +| [`FileUploadFromPath`](/appendix/apis/data-client/#fileuploadfrompath) | Upload files stored on your machine to the Viam app by filepath. | +| [`StreamingDataCaptureUpload`](/appendix/apis/data-client/#streamingdatacaptureupload) | Upload the contents of streaming binary data and the relevant metadata to the Viam app. | diff --git a/static/include/app/apis/generated/data_sync.md b/static/include/app/apis/generated/data_sync.md new file mode 100644 index 0000000000..c58924755a --- /dev/null +++ b/static/include/app/apis/generated/data_sync.md @@ -0,0 +1,241 @@ +### BinaryDataCaptureUpload + +Upload binary data collected on your machine through a specific component and the relevant metadata to the [Viam app](https://app.viam.com). +Uploaded binary data can be found under the **Images**, **Point clouds**, or **Files** subtab of the app's [**Data** tab](https://app.viam.com/data), depending on the type of data that you upload. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `binary_data` ([bytes](https://docs.python.org/3/library/stdtypes.html#bytes-objects)) (required): The data to be uploaded, represented in bytes. +- `part_id` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): Part ID of the component used to capture the data. +- `component_type` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): Type of the component used to capture the data (for example, “movement_sensor”). +- `component_name` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): Name of the component used to capture the data. +- `method_name` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): Name of the method used to capture the data. +- `file_extension` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): The file extension of binary data including the period, for example .jpg, .png, .pcd. The backend will route the binary to its corresponding mime type based on this extension. Files with a .jpeg, .jpg, or .png extension will be saved to the images tab. +- `method_parameters` (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), Any]) (optional): Optional dictionary of method parameters. No longer in active use. +- `tags` (List[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)]) (optional): Optional list of tags to allow for tag-based data filtering when retrieving data. +- `data_request_times` (Tuple[[datetime.datetime](https://docs.python.org/3/library/datetime.html), [datetime.datetime](https://docs.python.org/3/library/datetime.html)]) (optional): Optional tuple containing datetime objects denoting the times this data was requested[0] by the robot and received[1] from the appropriate sensor. + +**Returns:** + +- ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)): The file_id of the uploaded data. + +**Raises:** + +- (GRPCError): If an invalid part ID is passed. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +time_requested = datetime(2023, 6, 5, 11) +time_received = datetime(2023, 6, 5, 11, 0, 3) + +file_id = await data_client.binary_data_capture_upload( + part_id="INSERT YOUR PART ID", + component_type='camera', + component_name='my_camera', + method_name='GetImages', + method_parameters=None, + tags=["tag_1", "tag_2"], + data_request_times=[time_requested, time_received], + file_extension=".jpg", + binary_data=b"Encoded image bytes" +) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.binary_data_capture_upload). + +{{% /tab %}} +{{< /tabs >}} + +### TabularDataCaptureUpload + +Upload tabular data collected on your machine through a specific [component](/components/) to the [Viam app](https://app.viam.com). +Uploaded tabular data can be found under the **Sensors** subtab of the app's [**Data** tab](https://app.viam.com/data). + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `tabular_data` (List[Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), Any]]) (required): List of the data to be uploaded, represented tabularly as a collection of dictionaries. +- `part_id` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): Part ID of the component used to capture the data. +- `component_type` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): Type of the component used to capture the data (for example, “movement_sensor”). +- `component_name` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): Name of the component used to capture the data. +- `method_name` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): Name of the method used to capture the data. +- `method_parameters` (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), Any]) (optional): Optional dictionary of method parameters. No longer in active use. +- `tags` (List[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)]) (optional): Optional list of tags to allow for tag-based data filtering when retrieving data. +- `data_request_times` (List[Tuple[[datetime.datetime](https://docs.python.org/3/library/datetime.html), [datetime.datetime](https://docs.python.org/3/library/datetime.html)]]) (optional): Optional list of tuples, each containing datetime objects denoting the times this data was requested[0] by the robot and received[1] from the appropriate sensor. Passing a list of tabular data and Timestamps with length n > 1 will result in n datapoints being uploaded, all tied to the same metadata. + +**Returns:** + +- ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)): The file_id of the uploaded data. + +**Raises:** + +- (GRPCError): If an invalid part ID is passed. +- (ValueError): If a list of Timestamp objects is provided and its length does not match the length of the list of tabular data. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +time_requested = datetime(2023, 6, 5, 11) +time_received = datetime(2023, 6, 5, 11, 0, 3) + +file_id = await data_client.tabular_data_capture_upload( + part_id="INSERT YOUR PART ID", + component_type='motor', + component_name='left_motor', + method_name='IsPowered', + tags=["tag_1", "tag_2"], + data_request_times=[(time_requested, time_received)], + tabular_data=[{'PowerPCT': 0, 'IsPowered': False}] +) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.tabular_data_capture_upload). + +{{% /tab %}} +{{< /tabs >}} + +### FileUpload + +Upload arbitrary files stored on your machine to the [Viam app](https://app.viam.com) by file name. +If uploaded with a file extension of .jpeg/.jpg/.png, uploaded files can be found in the **Images** subtab of the app's [**Data** tab](https://app.viam.com/data). +If .pcd, the uploaded files can be found in the **Point clouds** subtab. +All other types of uploaded files can be found under the **Files** subtab of the app's [**Data** tab](https://app.viam.com/data). + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `part_id` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): Part ID of the resource associated with the file. +- `data` ([bytes](https://docs.python.org/3/library/stdtypes.html#bytes-objects)) (required): Bytes representing file data to upload. +- `component_type` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (optional): Optional type of the component associated with the file (for example, “movement_sensor”). +- `component_name` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (optional): Optional name of the component associated with the file. +- `method_name` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (optional): Optional name of the method associated with the file. +- `file_name` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (optional): Optional name of the file. The empty string “” will be assigned as the file name if one isn’t provided. +- `method_parameters` (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), Any]) (optional): Optional dictionary of the method parameters. No longer in active use. +- `file_extension` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (optional): Optional file extension. The empty string “” will be assigned as the file extension if one isn’t provided. Files with a .jpeg, .jpg, or .png extension will be saved to the images tab. +- `tags` (List[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)]) (optional): Optional list of tags to allow for tag-based filtering when retrieving data. + +**Returns:** + +- ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)): ID of the new file. + +**Raises:** + +- (GRPCError): If an invalid part ID is passed. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +file_id = await data_client.file_upload( + data=b"Encoded image bytes", + part_id="INSERT YOUR PART ID", + tags=["tag_1", "tag_2"], + file_name="your-file", + file_extension=".txt" +) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.file_upload). + +{{% /tab %}} +{{< /tabs >}} + +### FileUploadFromPath + +Upload files stored on your machine to the [Viam app](https://app.viam.com) by filepath. +Uploaded files can be found under the **Files** subtab of the app's [**Data** tab](https://app.viam.com/data). + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `filepath` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): Absolute filepath of file to be uploaded. +- `part_id` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): Part ID of the component associated with the file. +- `component_type` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (optional): Optional type of the component associated with the file (for example, “movement_sensor”). +- `component_name` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (optional): Optional name of the component associated with the file. +- `method_name` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (optional): Optional name of the method associated with the file. +- `method_parameters` (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), Any]) (optional): Optional dictionary of the method parameters. No longer in active use. +- `tags` (List[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)]) (optional): Optional list of tags to allow for tag-based filtering when retrieving data. + +**Returns:** + +- ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)): ID of the new file. + +**Raises:** + +- (GRPCError): If an invalid part ID is passed. +- (FileNotFoundError): If the provided filepath is not found. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +file_id = await data_client.file_upload_from_path( + part_id="INSERT YOUR PART ID", + tags=["tag_1", "tag_2"], + filepath="/Users///" +) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.file_upload_from_path). + +{{% /tab %}} +{{< /tabs >}} + +### StreamingDataCaptureUpload + +Upload the contents of streaming binary data and the relevant metadata to the [Viam app](https://app.viam.com). +Uploaded streaming data can be found under the [**Data** tab](https://app.viam.com/data). + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `data` ([bytes](https://docs.python.org/3/library/stdtypes.html#bytes-objects)) (required): the data to be uploaded. +- `part_id` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): Part ID of the resource associated with the file. +- `file_ext` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): file extension type for the data. required for determining MIME type. +- `component_type` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (optional): Optional type of the component associated with the file (for example, “movement_sensor”). +- `component_name` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (optional): Optional name of the component associated with the file. +- `method_name` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (optional): Optional name of the method associated with the file. +- `method_parameters` (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), Any]) (optional): Optional dictionary of the method parameters. No longer in active use. +- `data_request_times` (Tuple[[datetime.datetime](https://docs.python.org/3/library/datetime.html), [datetime.datetime](https://docs.python.org/3/library/datetime.html)]) (optional): Optional tuple containing datetime objects denoting the times this data was requested[0] by the robot and received[1] from the appropriate sensor. +- `tags` (List[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)]) (optional): Optional list of tags to allow for tag-based filtering when retrieving data. + +**Returns:** + +- ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)): The file_id of the uploaded data. + +**Raises:** + +- (GRPCError): If an invalid part ID is passed. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +time_requested = datetime(2023, 6, 5, 11) +time_received = datetime(2023, 6, 5, 11, 0, 3) + +file_id = await data_client.streaming_data_capture_upload( + data="byte-data-to-upload", + part_id="INSERT YOUR PART ID", + file_ext="png", + component_type='motor', + component_name='left_motor', + method_name='IsPowered', + data_request_times=[(time_requested, time_received)], + tags=["tag_1", "tag_2"] +) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.streaming_data_capture_upload). + +{{% /tab %}} +{{< /tabs >}} diff --git a/static/include/app/apis/generated/dataset-table.md b/static/include/app/apis/generated/dataset-table.md new file mode 100644 index 0000000000..980e3859f7 --- /dev/null +++ b/static/include/app/apis/generated/dataset-table.md @@ -0,0 +1,8 @@ + +| Method Name | Description | +| ----------- | ----------- | +| [`CreateDataset`](/appendix/apis/data-client/#createdataset) | Create a new dataset. | +| [`DeleteDataset`](/appendix/apis/data-client/#deletedataset) | Delete a dataset. | +| [`RenameDataset`](/appendix/apis/data-client/#renamedataset) | Rename a dataset specified by the dataset ID. | +| [`ListDatasetsByOrganizationID`](/appendix/apis/data-client/#listdatasetsbyorganizationid) | Get the datasets in an organization. | +| [`ListDatasetsByIDs`](/appendix/apis/data-client/#listdatasetsbyids) | Get a list of datasets using their IDs. | diff --git a/static/include/app/apis/generated/dataset.md b/static/include/app/apis/generated/dataset.md new file mode 100644 index 0000000000..e89b0a7879 --- /dev/null +++ b/static/include/app/apis/generated/dataset.md @@ -0,0 +1,146 @@ +### CreateDataset + +Create a new dataset. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `name` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): The name of the dataset being created. +- `organization_id` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): The ID of the organization where the dataset is being created. You can obtain your organization ID from the Viam app’s organization settings page. + +**Returns:** + +- ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)): The dataset ID of the created dataset. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +name = await data_client.create_dataset( + name="", + organization_id="" +) +print(name) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.create_dataset). + +{{% /tab %}} +{{< /tabs >}} + +### DeleteDataset + +Delete a dataset. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `id` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): The ID of the dataset. + +**Returns:** + +- None. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +await data_client.delete_dataset( + id="abcd-1234xyz-8765z-123abc" +) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.delete_dataset). + +{{% /tab %}} +{{< /tabs >}} + +### RenameDataset + +Rename a dataset specified by the dataset ID. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `id` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): The ID of the dataset. +- `name` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): The new name of the dataset. + +**Returns:** + +- None. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +await data_client.rename_dataset( + id="abcd-1234xyz-8765z-123abc", + name="" +) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.rename_dataset). + +{{% /tab %}} +{{< /tabs >}} + +### ListDatasetsByOrganizationID + +Get the datasets in an organization. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `organization_id` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): The ID of the organization. You can obtain your organization ID from the Viam app’s organization settings page. + +**Returns:** + +- ([Sequence[viam.proto.app.dataset.Dataset]](https://python.viam.dev/autoapi/viam/proto/app/dataset/index.html#viam.proto.app.dataset.Dataset)): The list of datasets in the organization. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +datasets = await data_client.list_dataset_by_organization_id( + organization_id=[""a12b3c4e-1234-1abc-ab1c-ab1c2d345abc""] +) +print(datasets) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.list_datasets_by_organization_id). + +{{% /tab %}} +{{< /tabs >}} + +### ListDatasetsByIDs + +Get a list of datasets using their IDs. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `ids` (List[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)]) (required): The IDs of the datasets being called for. To retrieve these IDs, navigate to your dataset’s page in the Viam app, click … in the left-hand menu, and click Copy dataset ID. + +**Returns:** + +- ([Sequence[viam.proto.app.dataset.Dataset]](https://python.viam.dev/autoapi/viam/proto/app/dataset/index.html#viam.proto.app.dataset.Dataset)): The list of datasets. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +datasets = await data_client.list_dataset_by_ids( + ids=["abcd-1234xyz-8765z-123abc"] +) +print(datasets) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/app/data_client/index.html#viam.app.data_client.DataClient.list_dataset_by_ids). + +{{% /tab %}} +{{< /tabs >}} diff --git a/static/include/app/apis/overrides/methods/python.data.binary_data_by_filter.last.md b/static/include/app/apis/overrides/methods/python.data.binary_data_by_filter.last.md new file mode 100644 index 0000000000..d6df4b70e1 --- /dev/null +++ b/static/include/app/apis/overrides/methods/python.data.binary_data_by_filter.last.md @@ -0,0 +1,3 @@ +Optional string indicating the object identifier of the last-returned data. +This object identifier is returned by calls to `BinaryDataByFilter` as the last value. +If provided, the server will return the next data entries after the last object identifier. diff --git a/static/include/app/apis/overrides/methods/python.data.tabular_data_by_filter.last.md b/static/include/app/apis/overrides/methods/python.data.tabular_data_by_filter.last.md new file mode 100644 index 0000000000..8c054e85b4 --- /dev/null +++ b/static/include/app/apis/overrides/methods/python.data.tabular_data_by_filter.last.md @@ -0,0 +1,3 @@ +Optional string indicating the object identifier of the last-returned data. +This object identifier is returned by calls to `TabularDataByFilter` as the last value. +If provided, the server will return the next data entries after the last object identifier. diff --git a/static/include/services/apis/data-client.md b/static/include/services/apis/data-client.md deleted file mode 100644 index 5353686ba6..0000000000 --- a/static/include/services/apis/data-client.md +++ /dev/null @@ -1,33 +0,0 @@ - -Method Name | Description ------------ | ----------- -[`TabularDataByFilter`](/appendix/apis/data-client/#tabulardatabyfilter) | Filter and download tabular data. -[`BinaryDataByFilter`](/appendix/apis/data-client/#binarydatabyfilter) | Filter and download binary data. -[`BinaryDataByIDs`](/appendix/apis/data-client/#binarydatabyids) | Download binary data by IDs. -[`TabularDataBySQL`](/appendix/apis/data-client/#tabulardatabysql) | Obtain unified tabular data and metadata, queried with SQL. -[`TabularDataByMQL`](/appendix/apis/data-client/#tabulardatabymql) | Obtain unified tabular data and metadata, queried with MQL. -[`DeleteTabularData`](/appendix/apis/data-client/#deletetabulardata) | Delete tabular data older than a specified number of days. -[`DeleteBinaryDataByFilter`](/appendix/apis/data-client/#deletebinarydatabyfilter) | Filter and delete binary data. -[`DeleteBinaryDataByIds`](/appendix/apis/data-client/#deletebinarydatabyids) | Filter and delete binary data by ids. -[`AddTagsToBinaryDataByIds`](/appendix/apis/data-client/#addtagstobinarydatabyids) | Add tags to binary data by ids. -[`AddTagsToBinaryDataByFilter`](/appendix/apis/data-client/#addtagstobinarydatabyfilter) | Add tags to binary data by filter. -[`RemoveTagsFromBinaryDataByIds`](/appendix/apis/data-client/#removetagsfrombinarydatabyids) | Remove tags from binary data by ids. -[`RemoveTagsFromBinaryDataByFilter`](/appendix/apis/data-client/#removetagsfrombinarydatabyfilter) | Remove tags from binary data by filter. -[`TagsByFilter`](/appendix/apis/data-client/#tagsbyfilter) | Get tags from data by filter. -[`BoundingBoxLabelsByFilter`](/appendix/apis/data-client/#boundingboxlabelsbyfilter) | Get a list of bounding box labels using a Filter. -[`GetDatabaseConnection`](/appendix/apis/data-client/#getdatabaseconnection) | Get a connection to access a MongoDB Atlas Data federation instance. -[`BinaryDataCaptureUpload`](/appendix/apis/data-client/#binarydatacaptureupload) | Upload binary data collected on your machine through a specific component and the relevant metadata to the Viam app. -[`TabularDataCaptureUpload`](/appendix/apis/data-client/#tabulardatacaptureupload) | Upload tabular data collected on your machine through a specific component and the relevant metadata to the Viam app. -[`StreamingDataCaptureUpload`](/appendix/apis/data-client/#streamingdatacaptureupload) | Upload the contents of streaming binary data and the relevant metadata to the Viam app. -[`FileUpload`](/appendix/apis/data-client/#fileupload) | Upload file data stored on your machine and the relevant metadata to the Viam app. -[`FileUploadFromPath`](/appendix/apis/data-client/#fileuploadfrompath) | Upload file data stored on your machine from the specified filepath and the relevant metadata to the Viam app. -[`AddBoundingBoxToImageById`](/appendix/apis/data-client/#addboundingboxtoimagebyid) | Add a bounding box to an image specified by its BinaryID. -[`RemoveBoundingBoxFromImageById`](/appendix/apis/data-client/#removeboundingboxfromimagebyid) | Removes a bounding box from an image specified by its BinaryID. -[`CreateDataset`](/appendix/apis/data-client/#createdataset) | Create a new dataset. -[`ListDatasetByIds`](/appendix/apis/data-client/#listdatasetbyids) | Get a list of datasets using their IDs. -[`ListDatasetByOrganizationId`](/appendix/apis/data-client/#listdatasetbyorganizationid) | Get the datasets in an organization. -[`RenameDataset`](/appendix/apis/data-client/#renamedataset) | Rename a dataset specified by the dataset ID. -[`DeleteDataset`](/appendix/apis/data-client/#deletedataset) | Delete a dataset. -[`AddBinaryDataToDatasetByIds`](/appendix/apis/data-client/#addbinarydatatodatasetbyids) | Add the BinaryData to the provided dataset. This BinaryData will be tagged with the VIAM_DATASET_{id} label. -[`RemoveBinaryDataFromDatasetByIds`](/appendix/apis/data-client/#removebinarydatafromdatasetbyids) | Remove the BinaryData from the provided dataset. This BinaryData will lose the VIAM_DATASET_{id} tag. -[`ConfigureDatabaseUser`](/appendix/apis/data-client/#configuredatabaseuser) | Configure a database user for the Viam organization’s MongoDB Atlas Data Federation instance. From 2fe300f7113edb5325e09309f429fbe9d8f64464 Mon Sep 17 00:00:00 2001 From: andf-viam <132301587+andf-viam@users.noreply.github.com> Date: Mon, 8 Jul 2024 13:19:42 -0400 Subject: [PATCH 06/22] DOCS-1867: Specify SQL dialect (#3100) --- docs/use-cases/sensor-data-query.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/use-cases/sensor-data-query.md b/docs/use-cases/sensor-data-query.md index b3c39b730f..70e75a86bf 100644 --- a/docs/use-cases/sensor-data-query.md +++ b/docs/use-cases/sensor-data-query.md @@ -17,6 +17,11 @@ Then, you can follow the steps on this page to query it using {{< glossary_toolt For example, you can configure data capture for several sensors on one machine, or for several sensors across multiple machines, to report the ambient operating temperature. You can then run queries against that data to search for outliers or edge cases, to analyze how the ambient temperature affects your machines' operation. +- **SQL:** For querying captured data, Viam supports the [MongoDB Atlas SQL dialect](https://www.mongodb.com/docs/atlas/data-federation/query/sql/query-with-asql-statements/), which supports standard SQL query syntax in addition to Atlas-specific capabilities such as `FLATTEN` and `UNWIND`. + For more information, see the [MongoDB Atlas SQL language reference](https://www.mongodb.com/docs/atlas/data-federation/query/sql/language-reference/). + +- **MQL**: Viam also supports the [MongoDB Query language](https://www.mongodb.com/docs/manual/tutorial/query-documents/) for querying captured data from MQL-compatible clients such as `mongosh` or MongoDB Compass. + {{< alert title="In this page" color="tip" >}} 1. [Query data in the Viam app](#query-data-in-the-viam-app). From 7a07303d35b70178f2d1564961509e479184ecd0 Mon Sep 17 00:00:00 2001 From: andf-viam <132301587+andf-viam@users.noreply.github.com> Date: Mon, 8 Jul 2024 15:41:19 -0400 Subject: [PATCH 07/22] DOCS-2511: Merge autogen: generics (#3093) --- .github/workflows/sdk_protos_map.csv | 4 +- .github/workflows/update_sdk_methods.py | 19 +- docs/appendix/apis/_index.md | 4 +- docs/components/generic/_index.md | 182 +--------------- docs/services/generic/_index.md | 122 +---------- .../apis/generated/generic_component-table.md | 8 + .../apis/generated/generic_component.md | 204 ++++++++++++++++++ .../apis/generated/generic_service-table.md | 5 + .../apis/generated/generic_service.md | 113 ++++++++++ 9 files changed, 356 insertions(+), 305 deletions(-) create mode 100644 static/include/components/apis/generated/generic_component-table.md create mode 100644 static/include/components/apis/generated/generic_component.md create mode 100644 static/include/services/apis/generated/generic_service-table.md create mode 100644 static/include/services/apis/generated/generic_service.md diff --git a/.github/workflows/sdk_protos_map.csv b/.github/workflows/sdk_protos_map.csv index 0ed59ff0c2..8aaa479acd 100644 --- a/.github/workflows/sdk_protos_map.csv +++ b/.github/workflows/sdk_protos_map.csv @@ -102,7 +102,7 @@ gantry,Close,close,Close, ## Generic Component ## NOTED:Generic Component in Go SDK doesn't appear to implement (inherit) these: -generic_component,DoCommand,do_command,,doCommand +generic_component,DoCommand,do_command,DoCommand,doCommand generic_component,GetGeometries,get_geometries,, ## HACK: No proto for close, manually mapping: ## NOTED: Go SDK also missing Close, but we have it in our docs?: @@ -230,7 +230,7 @@ data_manager,Close,,Close, ## Generic Service ## NOTED:Generic Component in Go SDK doesn't appear to implement (inherit) these: -generic_service,DoCommand,do_command,, +generic_service,DoCommand,do_command,DoCommand, ## HACK: No proto for close, manually mapping: ## NOTED: Go SDK also missing Close, but we have it in our docs?: generic_service,Close,close,, diff --git a/.github/workflows/update_sdk_methods.py b/.github/workflows/update_sdk_methods.py index 934a69ab7e..b8b84a83b9 100755 --- a/.github/workflows/update_sdk_methods.py +++ b/.github/workflows/update_sdk_methods.py @@ -725,7 +725,10 @@ def parse(type, names): pass ## Scrape each parent method tag and all contained child tags for Go by resource: - if sdk == "go" and type != "app": + ## Skip Go: App (Go has no App client) and the generic component and service, which + ## require explicit in-script workaround (DoCommand neither inherited (from resource.Resource) + ## nor explicitly defined in-interface (not in Go docs, only in un-doc'd code): + if sdk == "go" and type != "app" and resource != "generic_component" and resource != "generic_service": soup = make_soup(url) @@ -936,6 +939,20 @@ def parse(type, names): ## in its entirety to the all_methods dictionary using "go" as the key: all_methods["go"] = go_methods + ## Assemble workaround data object for DoCommand for Go generic component and service. + ## Using code sample and method_link from resource.Resource, because these cannot be found + ## in Go docs for these resources: + elif sdk == "go" and (resource == "generic_component" or resource == "generic_service"): + + go_methods[type][resource]['DoCommand'] = {} + go_methods[type][resource]['DoCommand'] = {'proto': 'DoCommand', \ + 'description': 'DoCommand sends/receives arbitrary data.', \ + 'usage': 'DoCommand(ctx context.Context, cmd map[string]interface{}) (map[string]interface{}, error)', \ + 'method_link': 'https://pkg.go.dev/go.viam.com/rdk/resource#Resource', \ + 'code_sample': '// This example shows using DoCommand with an arm component.\nmyArm, err := arm.FromRobot(machine, "my_arm")\n\ncommand := map[string]interface{}{"cmd": "test", "data1": 500}\nresult, err := myArm.DoCommand(context.Background(), command)\n'} + + all_methods["go"] = go_methods + elif sdk == "go" and type == "app": ##Go SDK has no APP API! pass diff --git a/docs/appendix/apis/_index.md b/docs/appendix/apis/_index.md index 4c20a64edf..54848d9cd7 100644 --- a/docs/appendix/apis/_index.md +++ b/docs/appendix/apis/_index.md @@ -108,7 +108,7 @@ The [gantry component](/components/gantry/) supports the following methods: The [generic component](/components/generic/) supports the following methods: -{{< readfile "/static/include/components/apis/generic.md" >}} +{{< readfile "/static/include/components/apis/generated/generic_component-table.md" >}} ### Gripper @@ -178,7 +178,7 @@ The [data management service](/services/data/) supports the following methods: The [generic service](/services/generic/) supports the following methods: -{{< readfile "/static/include/services/apis/generic.md" >}} +{{< readfile "/static/include/services/apis/generated/generic_service-table.md" >}} ### ML Model diff --git a/docs/components/generic/_index.md b/docs/components/generic/_index.md index a95ae8219c..69af8324b7 100644 --- a/docs/components/generic/_index.md +++ b/docs/components/generic/_index.md @@ -87,187 +87,9 @@ import ( The generic component supports the following method: -{{< readfile "/static/include/components/apis/generic.md" >}} +{{< readfile "/static/include/components/apis/generated/generic_component-table.md" >}} -### GetGeometries - -Get all the geometries associated with the generic component in its current configuration, in the [frame](/services/frame-system/) of the generic component. -The [motion](/services/motion/) and [navigation](/services/navigation/) services use the relative position of inherent geometries to configured geometries representing obstacles for collision detection and obstacle avoidance while motion planning. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `extra` [(Optional\[Dict\[str, Any\]\])](https://docs.python.org/library/typing.html#typing.Optional): Extra options to pass to the underlying RPC call. -- `timeout` [(Optional\[float\])](https://docs.python.org/library/typing.html#typing.Optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. - -**Returns:** - -- [(List[Geometry])](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.Geometry): The geometries associated with the generic component, in any order. - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/generic/client/index.html#viam.components.generic.client.GenericClient.get_geometries). - -```python {class="line-numbers linkable-line-numbers"} -my_generic = Generic.from_robot(robot=robot, name="my_generic_component") - -geometries = await my_generic.get_geometries() - -if geometries: - # Get the center of the first geometry - print(f"Pose of the first geometry's centerpoint: {geometries[0].center}") -``` - -{{% /tab %}} - - - -{{< /tabs >}} - -### DoCommand - -Execute model-specific commands. -If you are implementing your own generic component and add features that have no built-in API method, you can access them with `DoCommand`. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `command` [(Dict[str, Any])](https://docs.python.org/3/library/stdtypes.html#typesmapping): The command to execute. - -**Returns:** - -- [(Dict[str, Any])](https://docs.python.org/3/library/stdtypes.html#typesmapping): Result of the executed command. - -```python {class="line-numbers linkable-line-numbers"} -my_generic = Generic.from_robot(robot=robot, name="my_generic_component") - -raw_dict = { - "command": "raw", - "raw_input": "home" -} -await my_generic.do_command(raw_dict) -``` - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/generic/client/index.html#viam.components.generic.client.GenericClient.do_command). - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `ctx` [(Context)](https://pkg.go.dev/context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. -- `cmd` [(map[string]interface{})](https://go.dev/blog/maps): The command to execute. - -**Returns:** - -- [(map[string]interface{})](https://go.dev/blog/maps): Result of the executed command. -- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. - -```go {class="line-numbers linkable-line-numbers"} -resp, err := myGeneric.DoCommand(context.Background(), map[string]interface{}{"command": "example"}) -``` - -For more information, see the [Go SDK Code](https://github.com/viamrobotics/api/blob/main/component/generic/v1/generic_grpc.pb.go). - -{{% /tab %}} -{{% tab name="C++" %}} - -**Parameters:** - -- `command` [(AttributeMap)](https://github.com/viamrobotics/viam-cpp-sdk/blob/main/src/viam/sdk/common/proto_type.hpp#L13): The command to execute. - -**Returns:** - -- [(AttributeMap)](https://github.com/viamrobotics/viam-cpp-sdk/blob/main/src/viam/sdk/common/proto_type.hpp#L13): Result of the executed command. - -```cpp {class="line-numbers linkable-line-numbers"} -auto my_generic = robot->resource_by_name("my_generic_component"); -auto example = std::make_shared(std::string("example")); -AttributeMap command = - std::make_shared>>(); -command->insert({{std::string("command"), example}}); -auto resp = my_generic->do_command(command); -``` - -For more information, see the [C++ SDK Docs](https://cpp.viam.dev/classviam_1_1sdk_1_1GenericComponent.html) - -{{% /tab %}} -{{< /tabs >}} - -### Close - -Safely shut down the resource and prevent further use. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- None - -**Returns:** - -- None - -```python {class="line-numbers linkable-line-numbers"} -my_generic = Generic.from_robot(robot, "my_generic") - -await my_generic.close() -``` - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/generic/client/index.html#viam.components.generic.client.GenericClient.close). - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `ctx` [(Context)](https://pkg.go.dev/context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. - -**Returns:** - -- [(error)](https://pkg.go.dev/builtin#error) : An error, if one occurred. Close will never return an error for a generic resource. - -```go {class="line-numbers linkable-line-numbers"} -err := myGeneric.Close(context.Background()) -``` - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#TriviallyCloseable). - -{{% /tab %}} -{{% tab name="C++" %}} - -There is no need to explicitly close a generic component's resource in C++, as resource destruction is handled automatically by the generic component's class destructor when variables exit scope. - -{{% /tab %}} -{{< /tabs >}} +{{< readfile "/static/include/components/apis/generated/generic_component.md" >}} ## Troubleshooting diff --git a/docs/services/generic/_index.md b/docs/services/generic/_index.md index 543c91ff7c..5aba73e411 100644 --- a/docs/services/generic/_index.md +++ b/docs/services/generic/_index.md @@ -85,127 +85,9 @@ import ( The generic service supports the following method: -{{< readfile "/static/include/services/apis/generic.md" >}} +{{< readfile "/static/include/services/apis/generated/generic_service-table.md" >}} -### DoCommand - -Execute model-specific commands. -If you are implementing your own generic service and add features that have no built-in API method, you can access them with `DoCommand`. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `command` [(Dict[str, Any])](https://docs.python.org/3/library/stdtypes.html#typesmapping): The command to execute. - -**Returns:** - -- [(Dict[str, Any])](https://docs.python.org/3/library/stdtypes.html#typesmapping): Result of the executed command. - -```python {class="line-numbers linkable-line-numbers"} -my_generic = Generic.from_robot(robot=robot, name="my_generic_service") - -raw_dict = { - "command": "raw", - "raw_input": "home" -} -await my_generic.do_command(raw_dict) -``` - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/services/generic/client/index.html#viam.services.generic.client.GenericClient.do_command). - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `ctx` [(Context)](https://pkg.go.dev/context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. -- `cmd` [(map[string]interface{})](https://go.dev/blog/maps): The command to execute. - -**Returns:** - -- [(map[string]interface{})](https://go.dev/blog/maps): Result of the executed command. -- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. - -```go {class="line-numbers linkable-line-numbers"} -resp, err := myGeneric.DoCommand(context.Background(), map[string]interface{}{"command": "example"}) -``` - -For more information, see the [Go SDK Code](https://github.com/viamrobotics/api/blob/main/service/generic/v1/generic_grpc.pb.go). - -{{% /tab %}} -{{% tab name="C++" %}} - -**Parameters:** - -- `command` [(AttributeMap)](https://github.com/viamrobotics/viam-cpp-sdk/blob/main/src/viam/sdk/common/proto_type.hpp#L13): The command to execute. - -**Returns:** - -- [(AttributeMap)](https://github.com/viamrobotics/viam-cpp-sdk/blob/main/src/viam/sdk/common/proto_type.hpp#L13): Result of the executed command. - -```cpp {class="line-numbers linkable-line-numbers"} -auto my_generic = robot->resource_by_name("my_generic_service"); -auto example = std::make_shared(std::string("example")); -AttributeMap command = - std::make_shared>>(); -command->insert({{std::string("command"), example}}); -auto resp = my_generic->do_command(command); -``` - -For more information, see the [C++ SDK Docs](https://cpp.viam.dev/classviam_1_1sdk_1_1GenericService.html) - -{{% /tab %}} -{{< /tabs >}} - -### Close - -Safely shut down the resource and prevent further use. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- None - -**Returns:** - -- None - -```python {class="line-numbers linkable-line-numbers"} -my_generic = Generic.from_robot(robot, "my_generic") - -await my_generic.close() -``` - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/services/generic/client/index.html#viam.services.generic.client.GenericClient.close). - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `ctx` [(Context)](https://pkg.go.dev/context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. - -**Returns:** - -- [(error)](https://pkg.go.dev/builtin#error) : An error, if one occurred. Close will never return an error for a generic resource. - -```go {class="line-numbers linkable-line-numbers"} -err := myGeneric.Close(context.Background()) -``` - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#TriviallyCloseable). - -{{% /tab %}} -{{% tab name="C++" %}} - -There is no need to explicitly close a generic service's resource in C++, as resource destruction is handled automatically by the generic service's class destructor when variables exit scope. - -{{% /tab %}} -{{< /tabs >}} +{{< readfile "/static/include/services/apis/generated/generic_service.md" >}} ## Troubleshooting diff --git a/static/include/components/apis/generated/generic_component-table.md b/static/include/components/apis/generated/generic_component-table.md new file mode 100644 index 0000000000..d5224302d8 --- /dev/null +++ b/static/include/components/apis/generated/generic_component-table.md @@ -0,0 +1,8 @@ + +| Method Name | Description | +| ----------- | ----------- | +| [`DoCommand`](/components/generic/#docommand) | Execute model-specific commands. | +| [`GetGeometries`](/components/generic/#getgeometries) | Get all the geometries associated with the generic component in its current configuration, in the frame of the generic component. | +| [`FromRobot`](/components/generic/#fromrobot) | Get the resource from the provided robot with the given name. | +| [`Name`](/components/generic/#name) | Get the `ResourceName` for this generic component with the given name. | +| [`Close`](/components/generic/#close) | Safely shut down the resource and prevent further use. | diff --git a/static/include/components/apis/generated/generic_component.md b/static/include/components/apis/generated/generic_component.md new file mode 100644 index 0000000000..d2144930cb --- /dev/null +++ b/static/include/components/apis/generated/generic_component.md @@ -0,0 +1,204 @@ +### DoCommand + +Execute model-specific commands. +If you are implementing your own generic component and add features that have no built-in API method, you can access them with `DoCommand`. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `command` (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), ValueTypes]) (required): The command to execute. +- `timeout` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. + +**Returns:** + +- (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), Any]): Result of the executed command. + +**Raises:** + +- (NotImplementedError): Raised if the Resource does not support arbitrary commands. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +command = {"cmd": "test", "data1": 500} +result = component.do(command) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/generic/client/index.html#viam.components.generic.client.GenericClient.do_command). + +{{% /tab %}} +{{% tab name="Go" %}} + +**Parameters:** + +- `ctx` [(Context)](https://pkg.go.dev/context#Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. +- `cmd` [(map[string]interface{})](https://go.dev/blog/maps): The command to execute. + +**Returns:** + +- [(map[string]interface{})](https://pkg.go.dev/builtin#string): The command response. +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +**Example:** + +```go {class="line-numbers linkable-line-numbers"} +// This example shows using DoCommand with an arm component. +myArm, err := arm.FromRobot(machine, "my_arm") + +command := map[string]interface{}{"cmd": "test", "data1": 500} +result, err := myArm.DoCommand(context.Background(), command) +``` + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Resource). + +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `command` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic> (required) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +// Example using doCommand with an arm component +const command = {'cmd': 'test', 'data1': 500}; +var result = myArm.doCommand(command); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Resource/doCommand.html). + +{{% /tab %}} +{{% tab name="C++" %}} + +**Parameters:** + +- `command` [(AttributeMap)](https://github.com/viamrobotics/viam-cpp-sdk/blob/main/src/viam/sdk/common/proto_type.hpp#L13): The command to execute. + +**Returns:** + +- [(AttributeMap)](https://github.com/viamrobotics/viam-cpp-sdk/blob/main/src/viam/sdk/common/proto_type.hpp#L13): Result of the executed command. + +```cpp {class="line-numbers linkable-line-numbers"} +auto my_generic = robot->resource_by_name("my_generic_component"); +auto example = std::make_shared(std::string("example")); +AttributeMap command = + std::make_shared>>(); +command->insert({{std::string("command"), example}}); +auto resp = my_generic->do_command(command); +``` + +For more information, see the [C++ SDK Docs](https://cpp.viam.dev/classviam_1_1sdk_1_1GenericComponent.html) + +{{% /tab %}} +{{< /tabs >}} + +### GetGeometries + +Get all the geometries associated with the generic component in its current configuration, in the [frame](/services/frame-system/) of the generic component. +The [motion](/services/motion/) and [navigation](/services/navigation/) services use the relative position of inherent geometries to configured geometries representing obstacles for collision detection and obstacle avoidance while motion planning. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `extra` (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), Any]) (optional): Extra options to pass to the underlying RPC call. +- `timeout` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. + +**Returns:** + +- ([List[viam.proto.common.Geometry]](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.Geometry)): The geometries associated with the Component. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +geometries = await component.get_geometries() + +if geometries: + # Get the center of the first geometry + print(f"Pose of the first geometry's centerpoint: {geometries[0].center}") +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/generic/client/index.html#viam.components.generic.client.GenericClient.get_geometries). + +{{% /tab %}} +{{< /tabs >}} + +### FromRobot + +Get the resource from the provided robot with the given name. + +{{< tabs >}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `robot` [RobotClient](https://flutter.viam.dev/viam_sdk/RobotClient-class.html) (required) +- `name` [String](https://api.flutter.dev/flutter/dart-core/String-class.html) (required) + +**Returns:** + +- [Generic](https://flutter.viam.dev/viam_sdk/Generic-class.html) + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Generic/fromRobot.html). + +{{% /tab %}} +{{< /tabs >}} + +### Name + +Get the `ResourceName` for this generic component with the given name. + +{{< tabs >}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `name` [String](https://api.flutter.dev/flutter/dart-core/String-class.html) (required) + +**Returns:** + +- [ResourceName](https://flutter.viam.dev/viam_sdk/ResourceName-class.html) + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Generic/getResourceName.html). + +{{% /tab %}} +{{< /tabs >}} + +### Close + +Safely shut down the resource and prevent further use. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- None. + +**Returns:** + +- None. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +await component.close() +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/generic/client/index.html#viam.components.generic.client.GenericClient.close). + +{{% /tab %}} +{{% tab name="C++" %}} + +There is no need to explicitly close a generic component's resource in C++, as resource destruction is handled automatically by the generic component's class destructor when variables exit scope. + +{{% /tab %}} +{{< /tabs >}} diff --git a/static/include/services/apis/generated/generic_service-table.md b/static/include/services/apis/generated/generic_service-table.md new file mode 100644 index 0000000000..8bf9cb01fb --- /dev/null +++ b/static/include/services/apis/generated/generic_service-table.md @@ -0,0 +1,5 @@ + +| Method Name | Description | +| ----------- | ----------- | +| [`DoCommand`](/services/generic/#docommand) | Execute model-specific commands. | +| [`Close`](/services/generic/#close) | Safely shut down the resource and prevent further use. | diff --git a/static/include/services/apis/generated/generic_service.md b/static/include/services/apis/generated/generic_service.md new file mode 100644 index 0000000000..07165e5a22 --- /dev/null +++ b/static/include/services/apis/generated/generic_service.md @@ -0,0 +1,113 @@ +### DoCommand + +Execute model-specific commands. +If you are implementing your own generic service and add features that have no built-in API method, you can access them with `DoCommand`. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `command` (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), ValueTypes]) (required): The command to execute. +- `timeout` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. + +**Returns:** + +- (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), Any]): Result of the executed command. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +motion = MotionClient.from_robot(robot, "builtin") + +my_command = { + "cmnd": "dosomething", + "someparameter": 52 +} + +# Can be used with any resource, using the motion service as an example +await motion.do_command(command=my_command) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/services/generic/client/index.html#viam.services.generic.client.GenericClient.do_command). + +{{% /tab %}} +{{% tab name="Go" %}} + +**Parameters:** + +- `ctx` [(Context)](https://pkg.go.dev/context#Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. +- `cmd` [(map[string]interface{})](https://go.dev/blog/maps): The command to execute. + +**Returns:** + +- [(map[string]interface{})](https://pkg.go.dev/builtin#string): The command response. +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +**Example:** + +```go {class="line-numbers linkable-line-numbers"} +// This example shows using DoCommand with an arm component. +myArm, err := arm.FromRobot(machine, "my_arm") + +command := map[string]interface{}{"cmd": "test", "data1": 500} +result, err := myArm.DoCommand(context.Background(), command) +``` + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Resource). + +{{% /tab %}} +{{% tab name="C++" %}} + +**Parameters:** + +- `command` [(AttributeMap)](https://github.com/viamrobotics/viam-cpp-sdk/blob/main/src/viam/sdk/common/proto_type.hpp#L13): The command to execute. + +**Returns:** + +- [(AttributeMap)](https://github.com/viamrobotics/viam-cpp-sdk/blob/main/src/viam/sdk/common/proto_type.hpp#L13): Result of the executed command. + +```cpp {class="line-numbers linkable-line-numbers"} +auto my_generic = robot->resource_by_name("my_generic_service"); +auto example = std::make_shared(std::string("example")); +AttributeMap command = + std::make_shared>>(); +command->insert({{std::string("command"), example}}); +auto resp = my_generic->do_command(command); +``` + +For more information, see the [C++ SDK Docs](https://cpp.viam.dev/classviam_1_1sdk_1_1GenericService.html) + +{{% /tab %}} +{{< /tabs >}} + +### Close + +Safely shut down the resource and prevent further use. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- None. + +**Returns:** + +- None. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +await component.close() +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/services/generic/client/index.html#viam.services.generic.client.GenericClient.close). + +{{% /tab %}} +{{% tab name="C++" %}} + +There is no need to explicitly close a generic service's resource in C++, as resource destruction is handled automatically by the generic service's class destructor when variables exit scope. + +{{% /tab %}} +{{< /tabs >}} From ba08303d93d67f6436197e89a729117a86cf6a09 Mon Sep 17 00:00:00 2001 From: andf-viam <132301587+andf-viam@users.noreply.github.com> Date: Mon, 8 Jul 2024 16:25:43 -0400 Subject: [PATCH 08/22] DOCS-2420: Merge autogen: base, board (#3089) --- .github/workflows/sdk_protos_map.csv | 8 +- .github/workflows/update_sdk_methods.py | 10 +- docs/appendix/apis/_index.md | 24 +- docs/build/micro-rdk/board/_index.md | 4 +- docs/build/micro-rdk/board/esp32.md | 2 +- docs/components/base/_index.md | 607 +------- docs/components/board/_index.md | 1204 +-------------- static/include/components/apis/analog.md | 5 - .../components/apis/digitalinterrupt.md | 4 - .../components/apis/generated/base-table.md | 16 + .../include/components/apis/generated/base.md | 846 ++++++++++ .../components/apis/generated/board-table.md | 25 + .../components/apis/generated/board.md | 1370 +++++++++++++++++ static/include/components/apis/gpiopin.md | 9 - .../go.base.MoveStraight.distanceMm.md | 3 + .../methods/go.base.MoveStraight.mmPerSec.md | 3 + .../go.base.Properties.Properties.return.md | 1 + .../methods/go.base.SetPower.angular.md | 4 + .../methods/go.base.SetPower.linear.md | 5 + .../methods/go.base.SetVelocity.angular.md | 2 + .../methods/go.base.SetVelocity.linear.md | 2 + .../methods/go.base.Spin.angleDeg.md | 2 + .../methods/go.base.Spin.degsPerSec.md | 2 + .../go.board.AnalogByName.Analog.return.md | 1 + .../methods/go.board.AnalogByName.name.md | 2 + .../go.board.AnalogNames.string.return.md | 1 + ...InterruptByName.DigitalInterrupt.return.md | 1 + .../go.board.DigitalInterruptByName.name.md | 2 + ...ard.DigitalInterruptNames.string.return.md | 1 + .../go.board.GPIOPinByName.GPIOPin.return.md | 1 + .../methods/go.board.GPIOPinByName.name.md | 2 + .../methods/go.board.Get.bool.return.md | 2 + .../methods/go.board.PWM.float64.return.md | 1 + .../methods/go.board.PWMFreq.uint.return.md | 1 + .../go.board.Read.AnalogValue.return.md | 1 + .../overrides/methods/go.board.Set.high.md | 2 + .../methods/go.board.SetPWM.dutyCyclePct.md | 1 + .../methods/go.board.SetPWMFreq.freqHz.md | 1 + .../methods/go.board.SetPowerMode.duration.md | 1 + .../methods/go.board.SetPowerMode.mode.md | 1 + .../methods/go.board.StreamTicks.ch.md | 1 + .../go.board.StreamTicks.interrupts.md | 1 + .../methods/go.board.Value.int64.return.md | 1 + .../overrides/methods/go.board.Write.value.md | 1 + .../overrides/protos/board.AnalogByName.md | 1 + .../overrides/protos/board.AnalogNames.md | 1 + .../protos/board.AnalogReaderNames.md | 1 - .../protos/board.ReadAnalogReader.md | 1 - .../apis/overrides/protos/board.Value.md | 3 - .../{board.WriteAnalog.md => board.Write.md} | 0 50 files changed, 2330 insertions(+), 1861 deletions(-) delete mode 100644 static/include/components/apis/analog.md delete mode 100644 static/include/components/apis/digitalinterrupt.md create mode 100644 static/include/components/apis/generated/base-table.md create mode 100644 static/include/components/apis/generated/base.md create mode 100644 static/include/components/apis/generated/board-table.md create mode 100644 static/include/components/apis/generated/board.md delete mode 100644 static/include/components/apis/gpiopin.md create mode 100644 static/include/components/apis/overrides/methods/go.base.MoveStraight.distanceMm.md create mode 100644 static/include/components/apis/overrides/methods/go.base.MoveStraight.mmPerSec.md create mode 100644 static/include/components/apis/overrides/methods/go.base.Properties.Properties.return.md create mode 100644 static/include/components/apis/overrides/methods/go.base.SetPower.angular.md create mode 100644 static/include/components/apis/overrides/methods/go.base.SetPower.linear.md create mode 100644 static/include/components/apis/overrides/methods/go.base.SetVelocity.angular.md create mode 100644 static/include/components/apis/overrides/methods/go.base.SetVelocity.linear.md create mode 100644 static/include/components/apis/overrides/methods/go.base.Spin.angleDeg.md create mode 100644 static/include/components/apis/overrides/methods/go.base.Spin.degsPerSec.md create mode 100644 static/include/components/apis/overrides/methods/go.board.AnalogByName.Analog.return.md create mode 100644 static/include/components/apis/overrides/methods/go.board.AnalogByName.name.md create mode 100644 static/include/components/apis/overrides/methods/go.board.AnalogNames.string.return.md create mode 100644 static/include/components/apis/overrides/methods/go.board.DigitalInterruptByName.DigitalInterrupt.return.md create mode 100644 static/include/components/apis/overrides/methods/go.board.DigitalInterruptByName.name.md create mode 100644 static/include/components/apis/overrides/methods/go.board.DigitalInterruptNames.string.return.md create mode 100644 static/include/components/apis/overrides/methods/go.board.GPIOPinByName.GPIOPin.return.md create mode 100644 static/include/components/apis/overrides/methods/go.board.GPIOPinByName.name.md create mode 100644 static/include/components/apis/overrides/methods/go.board.Get.bool.return.md create mode 100644 static/include/components/apis/overrides/methods/go.board.PWM.float64.return.md create mode 100644 static/include/components/apis/overrides/methods/go.board.PWMFreq.uint.return.md create mode 100644 static/include/components/apis/overrides/methods/go.board.Read.AnalogValue.return.md create mode 100644 static/include/components/apis/overrides/methods/go.board.Set.high.md create mode 100644 static/include/components/apis/overrides/methods/go.board.SetPWM.dutyCyclePct.md create mode 100644 static/include/components/apis/overrides/methods/go.board.SetPWMFreq.freqHz.md create mode 100644 static/include/components/apis/overrides/methods/go.board.SetPowerMode.duration.md create mode 100644 static/include/components/apis/overrides/methods/go.board.SetPowerMode.mode.md create mode 100644 static/include/components/apis/overrides/methods/go.board.StreamTicks.ch.md create mode 100644 static/include/components/apis/overrides/methods/go.board.StreamTicks.interrupts.md create mode 100644 static/include/components/apis/overrides/methods/go.board.Value.int64.return.md create mode 100644 static/include/components/apis/overrides/methods/go.board.Write.value.md create mode 100644 static/include/components/apis/overrides/protos/board.AnalogByName.md create mode 100644 static/include/components/apis/overrides/protos/board.AnalogNames.md delete mode 100644 static/include/components/apis/overrides/protos/board.AnalogReaderNames.md delete mode 100644 static/include/components/apis/overrides/protos/board.ReadAnalogReader.md rename static/include/components/apis/overrides/protos/{board.WriteAnalog.md => board.Write.md} (100%) diff --git a/.github/workflows/sdk_protos_map.csv b/.github/workflows/sdk_protos_map.csv index 8aaa479acd..2d1fa4c22d 100644 --- a/.github/workflows/sdk_protos_map.csv +++ b/.github/workflows/sdk_protos_map.csv @@ -40,12 +40,12 @@ board,GetPWM,get_pwm,PWM,pwm board,SetPWM,set_pwm,SetPWM,setPwm board,PWMFrequency,get_pwm_frequency,PWMFreq,pwmFrequency board,SetPWMFrequency,set_pwm_frequency,SetPWMFreq,setPwmFrequency -board,AnalogReaderNames,analog_names,AnalogNames, -board,ReadAnalogReader,analog_by_name,AnalogByName,analogReaderValue +board,AnalogNames,analog_names,AnalogNames, +board,AnalogByName,analog_by_name,AnalogByName,analogReaderValue ## HACK: Omitting PySDK: write_analog, currently borked: https://python.viam.dev/autoapi/viam/components/board/client/index.html#viam.components.board.client.BoardClient.write_analog -board,WriteAnalog,,Write,writeAnalog +board,Write,,Write,writeAnalog board,GetDigitalInterruptValue,digital_interrupt_by_name,DigitalInterruptByName,, -board,StreamTicks,,StreamTicks,streamTicks +board,StreamTicks,stream_ticks,StreamTicks,streamTicks board,SetPowerMode,set_power_mode,SetPowerMode,setPowerMode board,GetGeometries,get_geometries,, ## HACK: Board (python, go) provides additional helper functions, adding 5 pseudo-entries: diff --git a/.github/workflows/update_sdk_methods.py b/.github/workflows/update_sdk_methods.py index b8b84a83b9..e37ff0ec67 100755 --- a/.github/workflows/update_sdk_methods.py +++ b/.github/workflows/update_sdk_methods.py @@ -1106,7 +1106,7 @@ def parse(type, names): this_method_parameters_dict['param_usage'] = strong_tag.parent.text.replace("\n", " ") ## Some params provide data type links in Parameters section only, not initial usage. - ## Get that here if soL + ## Get that here if so: if strong_tag.parent.find('a', class_="reference internal"): param_type_link_raw = strong_tag.parent.find('a', class_="reference internal").get("href") ## Parameter type link is an anchor link: @@ -1539,8 +1539,14 @@ def format_method_usage(parsed_usage_string, go_method_name, resource, path_to_m param_or_return_description = '' ## Param override: if type_name != '': + # To handle 'ch chan' param name, similar. Use as 'ch' in override filename: + if ' ' in type_name: + type_name_short = type_name.split(' ')[0] + else: + type_name_short = type_name + ## .../overrides/methods/{sdk}.{resource}.{method_name}.{param_name}.md - param_desc_override_file = path_to_methods_override + '/go.' + resource + '.' + go_method_name + '.' + type_name + '.md' + param_desc_override_file = path_to_methods_override + '/go.' + resource + '.' + go_method_name + '.' + type_name_short + '.md' ## Return override: else: if 'map[string]interface{}' in param_type: diff --git a/docs/appendix/apis/_index.md b/docs/appendix/apis/_index.md index 54848d9cd7..86350171a4 100644 --- a/docs/appendix/apis/_index.md +++ b/docs/appendix/apis/_index.md @@ -78,13 +78,13 @@ The [arm component](/components/arm/) supports the following methods: The [base component](/components/base/) supports the following methods: -{{< readfile "/static/include/components/apis/base.md" >}} +{{< readfile "/static/include/components/apis/generated/base-table.md" >}} ### Board The [board component](/components/board/) supports the following methods: -{{< readfile "/static/include/components/apis/board.md" >}} +{{< readfile "/static/include/components/apis/generated/board-table.md" >}} ### Camera @@ -210,26 +210,6 @@ Different [vision service](/services/vision/) models support different methods: {{< readfile "/static/include/services/apis/generated/vision-table.md" >}} -## Signaling APIs - -### GPIO pins - -In addition to the [board API](#board), the [board component](/components/board/) supports the following methods for interfacing with GPIO pins on a board: - -{{< readfile "/static/include/components/apis/gpiopin.md" >}} - -### Analog-to-digital converters (ADCs) - -In addition to the [board API](#board), the [board component](/components/board/) supports the following methods for interfacing with [ADCs](/components/board/#analogs) on a board: - -{{< readfile "/static/include/components/apis/analog.md" >}} - -### Digital interrupts - -In addition to the [board API](#board), the [board component](/components/board/) supports the following methods for interfacing with [digital interrupts](/components/board/#digital_interrupts) on a board: - -{{< readfile "/static/include/components/apis/digitalinterrupt.md" >}} - ## ResourceBase methods In the Python SDK, the [`ResourceBase`](https://python.viam.dev/autoapi/viam/resource/base/index.html) class defines a basic set of API methods that all child resources should provide for users. diff --git a/docs/build/micro-rdk/board/_index.md b/docs/build/micro-rdk/board/_index.md index 743670045e..312678e285 100644 --- a/docs/build/micro-rdk/board/_index.md +++ b/docs/build/micro-rdk/board/_index.md @@ -38,8 +38,8 @@ For `GPIOPin`s: - [`GetGPIO()`](/components/board/#getgpio) - [`GetPWM()`](/components/board/#getpwm) - [`SetPWM()`](/components/board/#setpwm) -- [`PWMFreq()`](/components/board/#pwmfreq) -- [`SetPWMFreq()`](/components/board/#setpwmfreq) +- [`PWMFreq()`](/components/board/#pwmfrequency) +- [`SetPWMFreq()`](/components/board/#setpwmfrequency) See [PWM signals on `esp32` pins](/build/micro-rdk/board/esp32/#pwm-signals-on-esp32-pins) for more information on setting PWM frequencies with `esp32` boards. diff --git a/docs/build/micro-rdk/board/esp32.md b/docs/build/micro-rdk/board/esp32.md index daaa9253a9..024149cf6f 100644 --- a/docs/build/micro-rdk/board/esp32.md +++ b/docs/build/micro-rdk/board/esp32.md @@ -106,7 +106,7 @@ The following attributes are available for `esp32` boards: | `analogs` | object | Optional | Attributes of any pins that can be used as analog-to-digital converter (ADC) inputs. See [configuration info](#analogs). | | `i2cs` | object | Optional | Any Inter-Integrated Circuit (I2C) pins' bus index and name. See [configuration info](#i2cs). | | `digital_interrupts` | object | Optional | Any digital interrupts' GPIO number. See [configuration info](#digital_interrupts). | -| `pins` | object | Required | The GPIO number of any GPIO pins you wish to use as input/output with the [`GPIOPin` API](/appendix/apis/#gpio-pins). | +| `pins` | object | Required | The GPIO number of any GPIO pins you wish to use as input/output with the [board API](/components/board/#api). | Any pin not specified in either `"pins"` or `"digital_interrupts"` cannot be interacted with through the [board API](/components/board/#api). Interaction with digital interrupts is only supported with the [board API](/components/board/#api); these digital interrupts cannot be used as software interrupts in driver implementations. diff --git a/docs/components/base/_index.md b/docs/components/base/_index.md index f03769793e..f84ca1150b 100644 --- a/docs/components/base/_index.md +++ b/docs/components/base/_index.md @@ -85,612 +85,9 @@ import ( The base component supports the following methods: -{{< readfile "/static/include/components/apis/base.md" >}} +{{< readfile "/static/include/components/apis/generated/base-table.md" >}} -### MoveStraight - -Move the base in a straight line across the given distance (mm) at the given velocity (mm/sec). - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `distance` [(int)](https://docs.python.org/3/library/functions.html#int): The distance to move in millimeters. - Positive implies forwards. - Negative implies backwards. -- `velocity` [(float)](https://docs.python.org/3/library/functions.html#float): The velocity at which to move in millimeters per second. - Positive implies forwards. - Negative implies backwards. - -**Returns:** - -- None. - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/base/client/index.html#viam.components.base.client.BaseClient.move_straight). - -```python {class="line-numbers linkable-line-numbers"} -my_base = Base.from_robot(robot=robot, name="my_base") - -# Move the base 40 mm at a velocity of 90 mm/s, forward. -await my_base.move_straight(distance=40, velocity=90) - -# Move the base 40 mm at a velocity of -90 mm/s, backward. -await my_base.move_straight(distance=40, velocity=-90) -``` - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `ctx` [(Context)](https://pkg.go.dev/context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. -- `distanceMm` [(int)](https://pkg.go.dev/builtin#int): The distance to move the base in millimeters. - Positive implies forwards. - Negative implies backwards. -- `mmPerSec` [(float64)](https://pkg.go.dev/builtin#float64): The velocity at which to move the base in millimeters per second. - Positive implies forwards. - Negative implies backwards. -- `extra` [(map\[string\]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. - -**Returns:** - -- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/base#Base). - -```go {class="line-numbers linkable-line-numbers"} -myBase, err := base.FromRobot(machine, "my_base") - -// Move the base forward 40 mm at a velocity of 90 mm/s. -myBase.MoveStraight(context.Background(), 40, 90, nil) - -// Move the base backward 40 mm at a velocity of -90 mm/s. -myBase.MoveStraight(context.Background(), 40, -90, nil) -``` - -{{% /tab %}} -{{< /tabs >}} - -### Spin - -Turn the base in place, rotating it to the given angle (degrees) at the given angular velocity (degrees/sec). - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `angle` [(float)](https://docs.python.org/3/library/functions.html#float): The angle to spin in degrees. - Positive implies turning to the left. -- `velocity` [(float)](https://docs.python.org/3/library/functions.html#float): The angular velocity at which to spin in degrees per second. - Given a positive angle and a positive velocity, the base turns to the left (for built-in base models). - -**Returns:** - -- None. - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/base/client/index.html#viam.components.base.client.BaseClient.spin). - -```python {class="line-numbers linkable-line-numbers"} -my_base = Base.from_robot(robot=robot, name="my_base") - -# Spin the base 10 degrees at an angular velocity of 15 deg/sec. -await my_base.spin(angle=10, velocity=15) -``` - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `ctx` [(Context)](https://pkg.go.dev/context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. -- `angleDeg` [(float64)](https://pkg.go.dev/builtin#float64): The angle to spin in degrees. - Positive implies turning to the left. -- `degsPerSec` [(float64)](https://pkg.go.dev/builtin#float64): The angular velocity at which to spin in degrees per second. - Given a positive angle and a positive velocity, the base turns to the left (for built-in base models). -- `extra` [(map\[string\]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. - -**Returns:** - -- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/base#Base). - -```go {class="line-numbers linkable-line-numbers"} -myBase, err := base.FromRobot(machine, "my_base") - -// Spin the base 10 degrees at an angular velocity of 15 deg/sec. -myBase.Spin(context.Background(), 10, 15, nil) -``` - -{{% /tab %}} -{{< /tabs >}} - -### SetPower - -Set the linear and angular power of the base, represented as a percentage of max power for each direction in the range of [-1.0 to 1.0]. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `linear` [(Vector3)](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.Vector3): The percentage of max power of the base's linear propulsion. - In the range of -1.0 to 1.0, with 1.0 meaning 100% power. - Viam's coordinate system considers +Y to be the forward axis (+/- X right/left, +/- Z up/down), so use the Y component of this vector to move forward and backward when controlling a wheeled base. - Positive "Y" values imply moving forwards. - Negative "Y" values imply moving backwards. -- `angular` [(Vector3)](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.Vector3): The percentage of max power of the base's angular propulsion. - In the range of -1.0 to 1.0, with 1.0 meaning 100% power. - Use the Z component of this vector to spin left or right when controlling a wheeled base. - Positive "Z" values imply spinning to the left (for built-in base models). - -**Returns:** - -- None. - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/base/client/index.html#viam.components.base.client.BaseClient.set_power). - -```python {class="line-numbers linkable-line-numbers"} -my_base = Base.from_robot(robot=robot, name="my_base") - -# Make your wheeled base move forward. Set linear power to 75%. -print("move forward") -await my_base.set_power( - linear=Vector3(x=0, y=-.75, z=0), - angular=Vector3(x=0, y=0, z=0)) - -# Make your wheeled base move backward. Set linear power to -100%. -print("move backward") -await my_base.set_power( - linear=Vector3(x=0, y=-1.0, z=0), - angular=Vector3(x=0, y=0, z=0)) - -# Make your wheeled base spin left. Set angular power to 100%. -print("spin left") -await my_base.set_power( - linear=Vector3(x=0, y=0, z=0), - angular=Vector3(x=0, y=0, z=1)) - -# Make your wheeled base spin right. Set angular power to -75%. -print("spin right") -await my_base.set_power( - linear=Vector3(x=0, y=0, z=0), - angular=Vector3(x=0, y=0, z=-.75)) -``` - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `ctx` [(Context)](https://pkg.go.dev/context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. -- `linear` [(r3.Vector)](https://pkg.go.dev/github.com/golang/geo/r3#Vector): The percentage of max power of the base's linear propulsion. - In the range of -1.0 to 1.0, with 1.0 meaning 100% power. - Viam's coordinate system considers +Y to be the forward axis (+/- X right/left, +/- Z up/down), so use the Y component of this vector to move forward and backward when controlling a wheeled base. - Positive "Y" values imply moving forwards. - Negative "Y" values imply moving backwards. -- `angular` [(r3.Vector)](https://pkg.go.dev/github.com/golang/geo/r3#Vector): The percentage of max power of the base's angular propulsion. - In the range of -1.0 to 1.0, with 1.0 meaning 100% power. - Use the Z component of this vector to spin left or right when controlling a wheeled base. - Positive "Z" values imply spinning to the left (for built-in base models). -- `extra` [(map\[string\]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. - -**Returns:** - -- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/base#Base). - -```go {class="line-numbers linkable-line-numbers"} -myBase, err := base.FromRobot(machine, "my_base") - -// Make your wheeled base move forward. Set linear power to 75%. -logger.Info("move forward") -err = myBase.SetPower(context.Background(), r3.Vector{Y: .75}, r3.Vector{}, nil) - -// Make your wheeled base move backward. Set linear power to -100%. -logger.Info("move backward") -err = myBase.SetPower(context.Background(), r3.Vector{Y: -1}, r3.Vector{}, nil) - -// Make your wheeled base spin left. Set angular power to 100%. -logger.Info("spin left") -err = myBase.SetPower(context.Background(), r3.Vector{}, r3.Vector{Z: 1}, nil) - -// Make your wheeled base spin right. Set angular power to -75%. -logger.Info("spin right") -err = mybase.SetPower(context.Background(), r3.Vector{}, r3.Vector{Z: -.75}, nil) -``` - -{{% /tab %}} -{{< /tabs >}} - -### SetVelocity - -Set the linear velocity (mm/sec) and angular velocity (degrees/sec) of the base. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `linear` [(Vector3)](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.Vector3): The linear velocity in millimeters per second. - Only the Y component of the vector is used for a wheeled base, since Viam's coordinate system considers +Y to be the forward axis. -- `angular` [(Vector3)](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.Vector3): The angular velocity in degrees per second. - Only the Z component of the vector is used for a wheeled base, since Viam's coordinate system considers +Z to point up and the angular velocity to rotate around the Z axis. - -**Returns:** - -- None. - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/base/client/index.html#viam.components.base.client.BaseClient.set_velocity). - -```python {class="line-numbers linkable-line-numbers"} -my_base = Base.from_robot(robot=robot, name="my_base") - -# Set the linear velocity to 50 mm/sec and the angular velocity to -# 15 degree/sec. -await my_base.set_velocity( - linear=Vector3(x=0, y=50, z=0), angular=Vector3(x=0, y=0, z=15)) -``` - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `ctx` [(Context)](https://pkg.go.dev/context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. -- `linear` [(r3.Vector)](https://pkg.go.dev/github.com/golang/geo/r3#Vector): The linear velocity in millimeters per second. - Only the Y component of the vector is used for a wheeled base. -- `angular` [(r3.Vector)](https://pkg.go.dev/github.com/golang/geo/r3#Vector): The angular velocity in degrees per second. - Only the Z component of the vector is used for a wheeled base. -- `extra` [(map\[string\]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. - -**Returns:** - -- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/base#Base). - -```go {class="line-numbers linkable-line-numbers"} -// import "github.com/golang/geo/r3" ... - -myBase, err := base.FromRobot(machine, "my_base") - -// Set the linear velocity to 50 mm/sec and the angular velocity to 15 deg/sec. -myBase.SetVelocity(context.Background(), r3.Vector{Y: 50}, r3.Vector{Z: 15}, nil) -``` - -{{% /tab %}} -{{< /tabs >}} - -### Stop - -Stop the base from moving immediately. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- None. - -**Returns:** - -- None. - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/base/client/index.html#viam.components.base.client.BaseClient.stop). - -```python {class="line-numbers linkable-line-numbers"} -my_base = Base.from_robot(robot=robot, name="my_base") - -# Move the base forward 10 mm at a velocity of 50 mm/s. -await my_base.move_straight(distance=10, velocity=50) - -# Stop the base. -await my_base.stop() -``` - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `ctx` [(Context)](https://pkg.go.dev/context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. -- `extra` [(map\[string\]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. - -**Returns:** - -- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Actuator). - -```go {class="line-numbers linkable-line-numbers"} -myBase, err := base.FromRobot(machine, "my_base") - -// Move the base forward 10 mm at a velocity of 50 mm/s. -myBase.MoveStraight(context.Background(), 10, 50, nil) - -// Stop the base. -myBase.Stop(context.Background(), nil) -``` - -{{% /tab %}} -{{< /tabs >}} - -### IsMoving - -Returns whether the base is actively moving (or attempting to move) under its own power. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- None. - -**Returns:** - -- [(bool)](https://docs.python.org/3/library/functions.html#bool): True if the base is currently moving; false if not. - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/base/index.html#viam.components.base.Base.is_moving). - -```python -my_base = Base.from_robot(robot=robot, name="my_base") - -# Check whether the base is currently moving. -moving = await my_base.is_moving() -print('Moving: ', moving) -``` - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `ctx` [(Context)](https://pkg.go.dev/context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. - -**Returns:** - -- [(bool)](https://pkg.go.dev/builtin#bool): True if the base is currently moving. -- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#MovingCheckable). - -```go -myBase, err := base.FromRobot(machine, "my_base") - -// Check whether the base is currently moving. -moving, err := myBase.IsMoving(context.Background()) - -logger.Info("Is moving?") -logger.Info(moving) -``` - -{{% /tab %}} -{{< /tabs >}} - -### GetProperties - -Get the width and turning radius of the {{< glossary_tooltip term_id="model" text="model" >}} of base in meters. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `timeout` [(Optional\[float\])](https://docs.python.org/library/typing.html#typing.Optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. - -**Returns:** - -- [(Properties)](https://python.viam.dev/autoapi/viam/components/base/index.html#viam.components.base.Base.Properties): A [dataclass](https://docs.python.org/3/library/dataclasses.html) with three fields, `width_meters`, `turning_radius_meters`, and `wheel_circumference_meters` representing the width, turning radius, and wheel circumference of the physical base in meters _(m)_. - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/base/client/index.html#viam.components.base.client.BaseClient.get_properties). - -```python {class="line-numbers linkable-line-numbers"} -my_base = Base.from_robot(robot=robot, name="my_base") - -# Get the width and turning radius of the base -properties = await my_base.get_properties() - -# Get the width -print(f"Width of base: {properties.width_meters}") - -# Get the turning radius -print(f"Turning radius of base: {properties.turning_radius_meters}") - -# Get the wheel circumference -print(f"Wheel circumference of base: {properties.wheel_circumference_meters}") -``` - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `ctx` [(Context)](https://pkg.go.dev/context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. -- `extra` [(map\[string\]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. - -**Returns:** - -- [(Properties)](https://pkg.go.dev/go.viam.com/rdk/components/base#Properties): A structure with three fields, `WidthMeters`, `TurningRadiusMeters`, and `WheelCircumferenceMeters` representing the width, turning radius, and wheel circumference of the physical base in meters _(m)_. -- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/base#Base). - -```go {class="line-numbers linkable-line-numbers"} -myBase, err := base.FromRobot(machine, "my_base") - -// Get the width and turning radius of the base -properties, err := myBase.Properties(context.Background(), nil) - -// Get the width -myBaseWidth := properties.WidthMeters - -// Get the turning radius -myBaseTurningRadius := properties.TurningRadiusMeters - -// Get the wheel circumference -myBaseWheelCircumference := properties.WheelCircumferenceMeters -``` - -{{% /tab %}} -{{< /tabs >}} - -### GetGeometries - -Get all the geometries associated with the base in its current configuration, in the [frame](/services/frame-system/) of the base. -The [motion](/services/motion/) and [navigation](/services/navigation/) services use the relative position of inherent geometries to configured geometries representing obstacles for collision detection and obstacle avoidance while motion planning. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `extra` [(Optional\[Dict\[str, Any\]\])](https://docs.python.org/library/typing.html#typing.Optional): Extra options to pass to the underlying RPC call. -- `timeout` [(Optional\[float\])](https://docs.python.org/library/typing.html#typing.Optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. - -**Returns:** - -- [(List[Geometry])](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.Geometry): The geometries associated with the base, in any order. - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/base/client/index.html#viam.components.base.client.BaseClient.get_geometries). - -```python {class="line-numbers linkable-line-numbers"} -my_base = Base.from_robot(robot=robot, name="my_base") - -geometries = await my_base.get_geometries() - -if geometries: - # Get the center of the first geometry - print(f"Pose of the first geometry's centerpoint: {geometries[0].center}") -``` - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `ctx` [(Context)](https://pkg.go.dev/context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. - -**Returns:** - -- [`[]spatialmath.Geometry`](https://pkg.go.dev/go.viam.com/rdk/spatialmath#Geometry): The geometries associated with the base, in any order. -- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Shaped). - -```go {class="line-numbers linkable-line-numbers"} -myBase, err := base.FromRobot(machine, "my_base") - -geometries, err := myBase.Geometries(context.Background(), nil) - -if len(geometries) > 0 { - // Get the center of the first geometry - elem := geometries[0] - fmt.Println("Pose of the first geometry's center point:", elem.ToProtobuf().Center) -} -``` - -{{% /tab %}} -{{< /tabs >}} - -### DoCommand - -Execute model-specific commands that are not otherwise defined by the component API. -For built-in models, model-specific commands are covered with each model's documentation. -If you are implementing your own base and add features that have no built-in API method, you can access them with `DoCommand`. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `command` [(Dict[str, Any])](https://docs.python.org/3/library/stdtypes.html#typesmapping): The command to execute. - -**Returns:** - -- [(Dict[str, Any])](https://docs.python.org/3/library/stdtypes.html#typesmapping): Result of the executed command. - -```python {class="line-numbers linkable-line-numbers"} -my_base = Base.from_robot(robot, "my_base") - -command = {"cmd": "test", "data1": 500} -result = my_base.do(command) -``` - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/base/client/index.html#viam.components.base.client.BaseClient.do_command). - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `ctx` [(Context)](https://pkg.go.dev/context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. -- `cmd` [(map[string]interface{})](https://go.dev/blog/maps): The command to execute. - -**Returns:** - -- [(map[string]interface{})](https://go.dev/blog/maps): Result of the executed command. -- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. - -```go {class="line-numbers linkable-line-numbers"} -myBase, err := base.FromRobot(machine, "my_base") - -command := map[string]interface{}{"cmd": "test", "data1": 500} -result, err := myBase.DoCommand(context.Background(), command) -``` - -For more information, see the [Go SDK Code](https://github.com/viamrobotics/rdk/blob/main/resource/resource.go). - -{{% /tab %}} -{{< /tabs >}} - -### Close - -Safely shut down the resource and prevent further use. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- None - -**Returns:** - -- None - -```python {class="line-numbers linkable-line-numbers"} -my_base = Base.from_robot(robot, "my_base") - -await my_base.close() -``` - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/base/client/index.html#viam.components.base.client.BaseClient.close). - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `ctx` [(Context)](https://pkg.go.dev/context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. - -**Returns:** - -- [(error)](https://pkg.go.dev/builtin#error) : An error, if one occurred. - -```go {class="line-numbers linkable-line-numbers"} -myBase, err := base.FromRobot(machine, "my_base") - -err = myBase.Close(ctx) -``` - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Resource). - -{{% /tab %}} -{{< /tabs >}} +{{< readfile "/static/include/components/apis/generated/base.md" >}} ## Troubleshooting diff --git a/docs/components/board/_index.md b/docs/components/board/_index.md index d16ed1d047..fb1a80e327 100644 --- a/docs/components/board/_index.md +++ b/docs/components/board/_index.md @@ -99,1209 +99,9 @@ import ( The board component supports the following methods: -{{< readfile "/static/include/components/apis/board.md" >}} +{{< readfile "/static/include/components/apis/generated/board-table.md" >}} -Additionally, the nested `GPIOPin`, `Analog`, and `DigitalInterrupt` interfaces support the following methods: - -[`GPIOPin`](#gpiopin-api) API: - -{{< readfile "/static/include/components/apis/gpiopin.md" >}} - -
- -[`Analog`](#analog-api) API: - -{{< readfile "/static/include/components/apis/analog.md" >}} - -
- -[`DigitalInterrupt`](#digitalinterrupt-api) API: - -{{< readfile "/static/include/components/apis/digitalinterrupt.md" >}} - -### AnalogByName - -Get an [`Analog`](#analogs) pin by `name`. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `name` [(str)](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): Name of the analog pin you want to retrieve. - -**Returns:** - -- [(Analog)](https://python.viam.dev/autoapi/viam/components/board/index.html#viam.components.board.Board.AnalogReader): An interface representing an analog pin configured and residing on the board. - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/board/index.html#viam.components.board.Board.analog_reader_by_name). - -```python -my_board = Board.from_robot(robot=robot, name="my_board") - -# Get the Analog "my_example_analog_pin". -analog = await my_board.analog_by_name(name="my_example_analog") -``` - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `name` [(string)](https://pkg.go.dev/builtin#string): Name of the analog pin you want to retrieve. Set as the `"name"` property [in configuration](/components/board/#digital_interrupts). - -**Returns:** - -- [(Analog)](https://pkg.go.dev/go.viam.com/rdk/components/board#AnalogReader): An interface representing an analog pin configured and residing on the board. -- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/board#Board). - -```go -myBoard, err := board.FromRobot(robot, "my_board") - -// Get the Analog pin "my_example_analog". -analog, err := myBoard.AnalogByName("my_example_analog") -``` - -{{% /tab %}} -{{< /tabs >}} - -### GetDigitalInterruptValue - -Get a [`DigitalInterrupt`](#digital_interrupts) by `name`. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `name` [(str)](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): Name of the digital interrupt you want to retrieve. Set as the `"name"` property [in configuration](/components/board/#digital_interrupts). - -**Returns:** - -- [(DigitalInterrupt)](https://python.viam.dev/_modules/viam/components/board/board.html#Board.DigitalInterrupt): An interface representing a configured interrupt on the board. - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/board/index.html#viam.components.board.Board.digital_interrupt_by_name). - -```python -my_board = Board.from_robot(robot=robot, name="my_board") - -# Get the DigitalInterrupt "my_example_digital_interrupt". -interrupt = await my_board.digital_interrupt_by_name( - name="my_example_digital_interrupt") -``` - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `name` [(string)](https://pkg.go.dev/builtin#string): Name of the digital interrupt you want to retrieve. Set as the `"name"` property [in configuration](/components/board/#digital_interrupts). - -**Returns:** - -- [(DigitalInterrupt)](https://pkg.go.dev/go.viam.com/rdk/components/board#DigitalInterrupt): An interface representing a configured interrupt on the board. -- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/board#Board). - -```go -myBoard, err := board.FromRobot(robot, "my_board") - -// Get the DigitalInterrupt "my_example_digital_interrupt". -interrupt, err := myBoard.DigitalInterruptByName("my_example_digital_interrupt") -``` - -{{% /tab %}} -{{< /tabs >}} - -### GPIOPinByName - -Get a `GPIOPin` by {{< glossary_tooltip term_id="pin-number" text="pin number" >}}. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `name` [(str)](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): Pin number of the GPIO pin you want to retrieve as a `GPIOPin` interface. - Refer to the pinout diagram and data sheet of your [board model](#supported-models) for {{< glossary_tooltip term_id="pin-number" text="pin numbers" >}}. - -**Returns:** - -- [(GPIOPin)](https://python.viam.dev/_modules/viam/components/board/board.html#Board.GPIOPin): An interface representing an individual GPIO pin on the board. - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/board/index.html#viam.components.board.Board.gpio_pin_by_name). - -```python -my_board = Board.from_robot(robot=robot, name="my_board") - -# Get the GPIOPin with pin number 15. -pin = await my_board.gpio_pin_by_name(name="15") -``` - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `name` [(string)](https://pkg.go.dev/builtin#string): {{< glossary_tooltip term_id="pin-number" text="pin number" >}} of the GPIO pin you want to retrieve as a `GPIOPin` interface. - Refer to the pinout diagram and data sheet of your [board model](#supported-models) for {{< glossary_tooltip term_id="pin-number" text="pin numbers" >}}. - -**Returns:** - -- [(GPIOPin)](https://pkg.go.dev/go.viam.com/rdk/components/board#GPIOPin): An interface representing an individual GPIO pin on the board. -- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/board#Board). - -```go -myBoard, err := board.FromRobot(robot, "my_board") - -// Get the GPIOPin with pin number 15. -pin, err := myBoard.GPIOPinByName("15") -``` - -{{% /tab %}} -{{< /tabs >}} - -### AnalogNames - -Get the name of every [`Analog`](#analogs) pin configured and residing on the board or implemented in the board driver. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- None - -**Returns:** - -- [(List\[str\])](https://docs.python.org/3/library/stdtypes.html#typesseq-list): A list containing the `"name"` of every analog pin [configured](#supported-models) on the board. - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/board/index.html#viam.components.board.Board.analog_reader_names). - -```python -my_board = Board.from_robot(robot=robot, name="my_board") - -# Get the name of every Analog configured on the board. -names = await my_board.analog_names() -``` - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- None - -**Returns:** - -- [([]string)](https://go.dev/tour/moretypes/7): A slice containing the `"name"` of every analog pin [configured](#supported-models) on the board. - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/board#Board). - -```go -myBoard, err := board.FromRobot(robot, "my_board") - -// Get the name of every Analog pin configured on the board. -names := myBoard.AnalogNames() -``` - -{{% /tab %}} -{{< /tabs >}} - -### DigitalInterruptNames - -Get the name of every [`DigitalInterrupt`](#digital_interrupts) configured on the board. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- None - -**Returns:** - -- [(List\[str\])](https://docs.python.org/3/library/stdtypes.html#typesseq-list): A list containing the `"name"` of every interrupt [configured](#supported-models) on the board. - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/board/index.html#viam.components.board.Board.digital_interrupt_names). - -```python -my_board = Board.from_robot(robot=robot, name="my_board") - -# Get the name of every DigitalInterrupt configured on the board. -names = await my_board.digital_interrupt_names() -``` - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- None - -**Returns:** - -- [([]string)](https://go.dev/tour/moretypes/7): A slice containing the `"name"` of every interrupt [configured](#supported-models) on the board. - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/board#Board). - -```go -myBoard, err := board.FromRobot(robot, "my_board") - -// Get the name of every DigitalInterrupt configured on the board. -names := myBoard.DigitalInterruptNames() -``` - -{{% /tab %}} -{{< /tabs >}} - -### SetPowerMode - -Set the board to the indicated `PowerMode`. - -{{% alert title="Info" color="info" %}} - -This method may not receive a successful response from gRPC when you set the board to the offline power mode `PowerMode.POWER_MODE_OFFLINE_DEEP`. - -When this is the case for your board model, the call is returned with an error specifying that the remote procedure call timed out or that the endpoint is no longer available. -This is expected: the board has been successfully powered down and can no longer respond to messages. - -{{% /alert %}} - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `mode` [(PowerMode)](https://python.viam.dev/autoapi/viam/proto/component/board/index.html#viam.proto.component.board.PowerMode): Options to specify power usage of the board: `PowerMode.POWER_MODE_UNSPECIFIED`, `PowerMode.POWER_MODE_NORMAL`, and `PowerMode.POWER_MODE_OFFLINE_DEEP`. -- `duration` [(Optional\[datetime.timedelta\])](https://docs.python.org/3/library/typing.html#typing.Optional): If provided, the board will exit the given power mode after the specified duration. -- `timeout` [(Optional\[float\])](https://docs.python.org/library/typing.html#typing.Optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. - -**Returns:** - -- None - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/board/index.html#viam.components.board.Board.set_power_mode). - -```python -my_board = Board.from_robot(robot=robot, name="my_board") - -# Set the power mode of the board to OFFLINE_DEEP. -await my_board.set_power_mode(mode=PowerMode.POWER_MODE_OFFLINE_DEEP) -``` - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `ctx` [(Context)](https://pkg.go.dev/context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. -- `mode` [(PowerMode)](https://pkg.go.dev/go.viam.com/api/component/board/v1#PowerMode): Options to specify power usage of the board: `boardpb.PowerMode_POWER_MODE_UNSPECIFIED`, `boardpb.PowerMode_POWER_MODE_NORMAL`, and `boardpb.PowerMode_POWER_MODE_OFFLINE_DEEP`. -- `duration` [(\*time.Duration)](https://pkg.go.dev/time#Duration): If provided, the board will exit the given power mode after the specified duration. - -**Returns:** - -- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/board#Board). - -```go -myBoard, err := board.FromRobot(robot, "my_board") - -// Set the power mode of the board to OFFLINE_DEEP. -myBoard.SetPowerMode(context.Background(), boardpb.PowerMode_POWER_MODE_OFFLINE_DEEP, nil) -``` - -{{% /tab %}} -{{< /tabs >}} - -### StreamTicks - -Start a stream of [`DigitalInterrupt`](/components/board/#digital_interrupts) ticks. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `interrupts` (List[[DigitalInterrupt](https://python.viam.dev/autoapi/viam/components/board/board/index.html#viam.components.board.board.Board.DigitalInterrupt)]): List of digital interrupts to receive ticks from. - -**Returns:** - -- [(`TickStream`)](https://python.viam.dev/autoapi/viam/components/board/board/index.html#viam.components.board.board.TickStream): [Stream](https://python.viam.dev/autoapi/viam/streams/index.html#viam.streams.Stream) of [Ticks](https://python.viam.dev/autoapi/viam/proto/component/board/index.html#viam.proto.component.board.StreamTicksResponse), objects containing `pin_name`, `time`, and `high` fields. - -```python {class="line-numbers linkable-line-numbers"} -my_board = Board.from_robot(robot=robot, name="my_board") - -di8 = await my_board.digital_interrupt_by_name(name="8") -di11 = await my_board.digital_interrupt_by_name(name="11") - -# Stream ticks from the listed digital interrupts on pins 8 and 11. -ticks = await my_board.stream_ticks([di8, di11]) -``` - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/board/client/index.html#viam.components.board.client.BoardClient.stream_ticks). - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `ctx` [(Context)](https://pkg.go.dev/context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. -- `interrupts` [([]DigitalInterrupt)](https://pkg.go.dev/go.viam.com/rdk/components/board#DigitalInterrupt): Slice of digital interrupts to receive ticks from. -- `ch` ([chan](https://go.dev/tour/concurrency/2) [Tick](https://pkg.go.dev/go.viam.com/rdk/components/board#Tick)): The channel to stream Ticks, structs containing `Name`, `High`, and `TimestampNanosec` fields. -- `extra` [(map\[string\]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. - -**Returns:** - -- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/board#Board). - -```go -myBoard, err := board.FromRobot(robot, "my_board") - -// Make a channel to stream ticks -ticksChan := make(chan board.Tick) - -interrupts := []*DigitalInterrupt{} - -if di8, err := myBoard.DigitalInterruptByName("8"); err == nil { - interrupts = append(interrupts, di8) -} -if di11, err := myBoard.DigitalInterruptByName("11"); err == nil { - interrupts = append(interrupts, di11) -} - -// Stream ticks on ticksChan from the listed digital interrupts on pins 8 and 11. -err = myBoard.StreamTicks(context.Background(), interrupts, ticksChan, nil) -``` - -{{% /tab %}} -{{< /tabs >}} - -### GetGeometries - -Get all the geometries associated with the board in its current configuration, in the [frame](/services/frame-system/) of the board. -The [motion](/services/motion/) and [navigation](/services/navigation/) services use the relative position of inherent geometries to configured geometries representing obstacles for collision detection and obstacle avoidance while motion planning. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `extra` [(Optional\[Dict\[str, Any\]\])](https://docs.python.org/library/typing.html#typing.Optional): Extra options to pass to the underlying RPC call. -- `timeout` [(Optional\[float\])](https://docs.python.org/library/typing.html#typing.Optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. - -**Returns:** - -- [(List[Geometry])](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.Geometry): The geometries associated with the board, in any order. - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/board/client/index.html#viam.components.board.client.BoardClient.get_geometries). - -```python {class="line-numbers linkable-line-numbers"} -my_board = Board.from_robot(robot=robot, name="my_board") - -geometries = await my_board.get_geometries() - -if geometries: - # Get the center of the first geometry - print(f"Pose of the first geometry's centerpoint: {geometries[0].center}") -``` - -{{% /tab %}} - - - -{{< /tabs >}} - -### DoCommand - -Execute model-specific commands that are not otherwise defined by the component API. -For built-in models, model-specific commands are covered with each model's documentation. -If you are implementing your own board and add features that have no built-in API method, you can access them with `DoCommand`. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `command` [(Dict[str, Any])](https://docs.python.org/3/library/stdtypes.html#typesmapping): The command to execute. -- `timeout` [(Optional\[float\])](https://docs.python.org/library/typing.html#typing.Optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. - -**Returns:** - -- [(Dict[str, Any])](https://docs.python.org/3/library/stdtypes.html#typesmapping): Result of the executed command. - -```python {class="line-numbers linkable-line-numbers"} -my_board = Board.from_robot(robot=robot, name="my_board") - -my_command = { - "command": "dosomething", - "someparameter": 52 -} - -await my_board.do_command(my_command) -``` - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/board/index.html#viam.components.board.Board.do_command). - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `ctx` [(Context)](https://pkg.go.dev/context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. -- `cmd` [(map\[string\]interface{})](https://go.dev/blog/maps): The command to execute. - -**Returns:** - -- [(map\[string\]interface{})](https://go.dev/blog/maps): Result of the executed command. -- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. - -```go {class="line-numbers linkable-line-numbers"} -myBoard, err := board.FromRobot(robot, "my_board") - -resp, err := myBoard.DoCommand(ctx, map[string]interface{}{"command": "dosomething", "someparameter": 52}) -``` - -For more information, see the [Go SDK Code](https://github.com/viamrobotics/rdk/blob/main/resource/resource.go). - -{{% /tab %}} -{{< /tabs >}} - -### Close - -Safely shut down the resource and prevent further use. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- None - -**Returns:** - -- None - -```python {class="line-numbers linkable-line-numbers"} -my_board = Board.from_robot(robot, "my_board") - -await my_board.close() -``` - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/board/client/index.html#viam.components.board.client.BoardClient.close). - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `ctx` [(Context)](https://pkg.go.dev/context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. - -**Returns:** - -- [(error)](https://pkg.go.dev/builtin#error) : An error, if one occurred. - -```go {class="line-numbers linkable-line-numbers"} -myBoard, err := board.FromRobot(robot, "my_board") - -err := myBoard.Close(ctx) -``` - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Resource). - -{{% /tab %}} -{{< /tabs >}} - -## `GPIOPin` API - -### SetGPIO - -Set the digital signal output of this pin to low (0V) or high (active, >0V). - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `high` [(bool)](https://docs.python.org/3/library/stdtypes.html#bltin-boolean-values): If `true`, set the state of the pin to high. - If `false`, set the state of the pin to low. -- `extra` [(Optional\[Dict\[str, Any\]\])](https://docs.python.org/library/typing.html#typing.Optional): Extra options to pass to the underlying RPC call. -- `timeout` [(Optional\[float\])](https://docs.python.org/library/typing.html#typing.Optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. - -**Returns:** - -- None - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/board/index.html#viam.components.board.Board.GPIOPin.set). - -```python -my_board = Board.from_robot(robot=robot, name="my_board") - -# Get the GPIOPin with pin number 15. -pin = await my_board.gpio_pin_by_name(name="15") - -# Set the pin to high. -await pin.set(high="true") -``` - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `ctx` [(Context)](https://pkg.go.dev/context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. -- `high` [(bool)](https://pkg.go.dev/builtin#bool): If `true`, set the state of the pin to high. - If `false`, set the state of the pin to low. -- `extra` [(map\[string\]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. - -**Returns:** - -- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/board#GPIOPin). - -```go -myBoard, err := board.FromRobot(robot, "my_board") - -// Get the GPIOPin with pin number 15. -pin, err := myBoard.GPIOPinByName("15") - -// Set the pin to high. -err := pin.Set(context.Background(), "true", nil) -``` - -{{% /tab %}} -{{< /tabs >}} - -### GetGPIO - -Get if the digital signal output of this pin is high (active, >0V). - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `extra` [(Optional\[Dict\[str, Any\]\])](https://docs.python.org/library/typing.html#typing.Optional): Extra options to pass to the underlying RPC call. -- `timeout` [(Optional\[float\])](https://docs.python.org/library/typing.html#typing.Optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. - -**Returns:** - -- [(bool)](https://docs.python.org/3/library/stdtypes.html#bltin-boolean-values): If `true`, the state of the pin is high. - If `false`, the state of the pin is low. - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/board/index.html#viam.components.board.Board.GPIOPin.get). - -```python -my_board = Board.from_robot(robot=robot, name="my_board") - -# Get the GPIOPin with pin number 15. -pin = await my_board.gpio_pin_by_name(name="15") - -# Get if it is true or false that the state of the pin is high. -high = await pin.get() -``` - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `ctx` [(Context)](https://pkg.go.dev/context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. -- `extra` [(map\[string\]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. - -**Returns:** - -- [(bool)](https://pkg.go.dev/builtin#bool): If `true`, the state of the pin is high. - If `false`, the state of the pin is low. -- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/board#GPIOPin). - -```go -myBoard, err := board.FromRobot(robot, "my_board") - -// Get the GPIOPin with pin number 15. -pin, err := myBoard.GPIOPinByName("15") - -// Get if it is true or false that the state of the pin is high. -high := pin.Get(context.Background(), nil) -``` - -{{% /tab %}} -{{< /tabs >}} - -### GetPWM - -{{% alert title="Info" color="info" %}} - -[Pulse-width modulation (PWM)](https://www.digikey.com/en/blog/pulse-width-modulation) is a method where of transmitting a digital signal in the form of pulses to control analog circuits. -With PWM on a _board_, the continuous digital signal output by a GPIO pin is sampled at regular intervals and transmitted to any [hardware components](/components/) wired to the pin that read analog signals. -This enables the board to communicate with these components. - -{{% /alert %}} - -Get the pin's [pulse-width modulation (PWM) duty cycle](https://learn.sparkfun.com/tutorials/pulse-width-modulation/duty-cycle): a float [`0.0`, `1.0`] representing the percentage of time the digital signal output by this pin is in the high state (active, >0V) relative to the interval period of the PWM signal [(interval period being the mathematical inverse of the PWM frequency)](https://learn.adafruit.com/improve-brushed-dc-motor-performance/pwm-frequency). - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `extra` [(Optional\[Dict\[str, Any\]\])](https://docs.python.org/library/typing.html#typing.Optional): Extra options to pass to the underlying RPC call. -- `timeout` [(Optional\[float\])](https://docs.python.org/library/typing.html#typing.Optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. - -**Returns:** - -- [(float)](https://docs.python.org/3/library/functions.html#float): A float [`0.0`, `1.0`] representing the percentage of time the digital signal output by this pin is in the high state relative to the interval period of the PWM signal. - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/board/index.html#viam.components.board.Board.GPIOPin.get_pwm). - -```python -my_board = Board.from_robot(robot=robot, name="my_board") - -# Get the GPIOPin with pin number 15. -pin = await my_board.gpio_pin_by_name(name="15") - -# Get if it is true or false that the state of the pin is high. -duty_cycle = await pin.get_pwm() -``` - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `ctx` [(Context)](https://pkg.go.dev/context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. -- `extra` [(map\[string\]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. - -**Returns:** - -- [(float64)](https://pkg.go.dev/builtin#float64): A float [`0.0`, `1.0`] representing the percentage of time the digital signal output by this pin is in the high state relative to the interval period of the PWM signal. -- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/board#GPIOPin). - -```go -myBoard, err := board.FromRobot(robot, "my_board") - -// Get the GPIOPin with pin number 15. -pin, err := myBoard.GPIOPinByName("15") - -// Get if it is true or false that the state of the pin is high. -duty_cycle := pin.PWM(context.Background(), nil) -``` - -{{% /tab %}} -{{< /tabs >}} - -### SetPWM - -Set the pin's [Pulse-width modulation (PWM) duty cycle](https://learn.sparkfun.com/tutorials/pulse-width-modulation/duty-cycle): a float [`0.0`, `1.0`] indicating the percentage of time the digital signal output of this pin is in the high state (active, >0V) relative to the interval period of the PWM signal [(interval period being the mathematical inverse of the PWM frequency)](https://learn.adafruit.com/improve-brushed-dc-motor-performance/pwm-frequency). - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `cycle` [(float64)](https://pkg.go.dev/builtin#float64): A float [`0.0`, `1.0`] representing the percentage of time the digital signal output by this pin is in the high state relative to the interval period of the PWM signal. -- `extra` [(Optional\[Dict\[str, Any\]\])](https://docs.python.org/library/typing.html#typing.Optional): Extra options to pass to the underlying RPC call. -- `timeout` [(Optional\[float\])](https://docs.python.org/library/typing.html#typing.Optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. - -**Returns:** - -- [(bool)](https://docs.python.org/3/library/stdtypes.html#bltin-boolean-values): If `true`, the state of the pin is high. - If `false`, the state of the pin is low. - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/board/index.html#viam.components.board.Board.GPIOPin.set_pwm). - -```python -my_board = Board.from_robot(robot=robot, name="my_board") - -# Get the GPIOPin with pin number 15. -pin = await my_board.gpio_pin_by_name(name="15") - -# Set the duty cycle to .6, meaning that this pin will be in the high state for -# 60% of the duration of the PWM interval period. -await pin.set_pwm(cycle=.6) -``` - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `ctx` [(Context)](https://pkg.go.dev/context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. -- `cycle` [(float64)](https://pkg.go.dev/builtin#float64): A float [`0.0`, `1.0`] representing the percentage of time the digital signal output by this pin is in the high state relative to the interval period of the PWM signal. -- `extra` [(map\[string\]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. - -**Returns:** - -- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/board#GPIOPin). - -```go -myBoard, err := board.FromRobot(robot, "my_board") - -// Get the GPIOPin with pin number 15. -pin, err := myBoard.GPIOPinByName("15") - -// Set the duty cycle to .6, meaning that this pin will be in the high state for 60% of the duration of the PWM interval period. -err := pin.SetPWM(context.Background(), .6, nil) -``` - -{{% /tab %}} -{{< /tabs >}} - -### PWMFreq - -Get the [Pulse-width modulation (PWM) frequency](https://learn.adafruit.com/improve-brushed-dc-motor-performance/pwm-frequency) in Hertz (Hz) of this pin, the count of PWM interval periods per second. -The optimal value for PWM Frequency depends on the type and model of [component](/components/) you control with the signal output by this pin. -Refer to your device's data sheet for PWM Frequency specifications. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `extra` [(Optional\[Dict\[str, Any\]\])](https://docs.python.org/library/typing.html#typing.Optional): Extra options to pass to the underlying RPC call. -- `timeout` [(Optional\[float\])](https://docs.python.org/library/typing.html#typing.Optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. - -**Returns:** - -- [(int)](https://docs.python.org/3/library/functions.html#int): The PWM Frequency in Hertz (Hz) (the count of PWM interval periods per second) the digital signal output by this pin is set to. - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/board/index.html#viam.components.board.Board.GPIOPin.get_pwm_frequency). - -```python -my_board = Board.from_robot(robot=robot, name="my_board") - -# Get the GPIOPin with pin number 15. -pin = await my_board.gpio_pin_by_name(name="15") - -# Get the PWM frequency of this pin. -freq = await pin.get_pwm_frequency() -``` - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `ctx` [(Context)](https://pkg.go.dev/context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. -- `extra` [(map\[string\]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. - -**Returns:** - -- [(unit)](https://pkg.go.dev/builtin#uint): The PWM Frequency in Hertz (Hz) (the count of PWM interval periods per second) the digital signal output by this pin is set to. -- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/board#GPIOPin). - -```go -myBoard, err := board.FromRobot(robot, "my_board") - -// Get the GPIOPin with pin number 15. -pin, err := myBoard.GPIOPinByName("15") - -// Get the PWM frequency of this pin. -freqHz, err := pin.PWMFreq(context.Background(), nil) -``` - -{{% /tab %}} -{{< /tabs >}} - -### SetPWMFreq - -Set the [Pulse-width modulation (PWM) frequency](https://learn.adafruit.com/improve-brushed-dc-motor-performance/pwm-frequency) in Hertz (Hz) of this pin, the count of PWM interval periods per second. -The optimal value for PWM Frequency depends on the type and model of [component](/components/) you control with the PWM signal output by this pin. -Refer to your device's data sheet for PWM Frequency specifications. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `extra` [(Optional\[Dict\[str, Any\]\])](https://docs.python.org/library/typing.html#typing.Optional): Extra options to pass to the underlying RPC call. -- `timeout` [(Optional\[float\])](https://docs.python.org/library/typing.html#typing.Optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. - -**Returns:** - -- [(int)](https://docs.python.org/3/library/functions.html#int): The PWM Frequency in Hertz (Hz), the count of PWM interval periods per second, to set the digital signal output by this pin to. - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/board/index.html#viam.components.board.Board.GPIOPin.set_pwm_frequency). - -```python -my_board = Board.from_robot(robot=robot, name="my_board") - -# Get the GPIOPin with pin number 15. -pin = await my_board.gpio_pin_by_name(name="15") - -# Set the PWM frequency of this pin to 1600 Hz. -high = await pin.set_pwm_frequency(frequency=1600) -``` - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `ctx` [(Context)](https://pkg.go.dev/context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. -- `freqHz` [(unit)](https://pkg.go.dev/builtin#uint): The PWM Frequency in Hertz (Hz), the count of PWM interval periods per second, to set the digital signal output by this pin to. -- `extra` [(map\[string\]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. - -**Returns:** - -- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/board#GPIOPin). - -```go -myBoard, err := board.FromRobot(robot, "my_board") - -// Get the GPIOPin with pin number 15. -pin, err := myBoard.GPIOPinByName("15") - -// Set the PWM frequency of this pin to 1600 Hz. -high := pin.SetPWMFreq(context.Background(), 1600, nil) -``` - -{{% /tab %}} -{{< /tabs >}} - -### Close - -Safely shut down the resource and prevent further use. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- None - -**Returns:** - -- None - -```python {class="line-numbers linkable-line-numbers"} -my_board = Board.from_robot(robot=robot, name="my_board") - -# Get the GPIOPin with pin number 15. -pin = await my_board.gpio_pin_by_name(name="15") - -# Close the pin. -await pin.close() -``` - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/board/client/index.html#viam.components.board.client.GPIOPinClient.close). - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `ctx` [(Context)](https://pkg.go.dev/context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. - -**Returns:** - -- [(error)](https://pkg.go.dev/builtin#error) : An error, if one occurred. - -```go {class="line-numbers linkable-line-numbers"} -myBoard, err := board.FromRobot(robot, "my_board") - -// Get the GPIOPin with pin number 15. -pin, err := myBoard.GPIOPinByName("15") - -// Close the pin. -err := pin.Close(ctx) -``` - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Resource). - -{{% /tab %}} -{{< /tabs >}} - -## `Analog` API - -### Read - -Read the current value from an analog pin or analog reader capable of reading analog values. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `extra` [(Optional\[Dict\[str, Any\]\])](https://docs.python.org/library/typing.html#typing.Optional): Extra options to pass to the underlying RPC call. -- `timeout` [(Optional\[float\])](https://docs.python.org/library/typing.html#typing.Optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. - -**Returns:** - -- [(Analog.Value)](https://python.viam.dev/autoapi/viam/components/board/index.html#viam.components.board.Board.Analog.Value): The result of reading an analog reader. - It contains the raw data read as `value`, the reader’s minimum and maximum possible values as `min_range` and `max_range`, and its `step_size` (the minimum possible change between values it can read). - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/board/index.html#viam.components.board.Board.Analog.read). - -```python -my_board = Board.from_robot(robot=robot, name="my_board") - -# Get the GPIOPin with pin number 15. -pin = await my_board.gpio_pin_by_name(name="15") - -# Get if it is true or false that the pin is set to high. -duty_cycle = await pin.get_pwm() - -# Get the Analog pin "my_example_analog". -analog = await my_board.analog_by_name( - name="my_example_analog") - -# Get the value of the digital signal "my_example_analog" has most -# recently measured. -reading = analog.read() -reading_value = reading.value -``` - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `ctx` [(Context)](https://pkg.go.dev/context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. -- `extra` [(map\[string\]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. - -**Returns:** - -- [(AnalogValue)](https://pkg.go.dev/go.viam.com/rdk/components/board#AnalogValue): The current value, including the integer `Value` of the digital signal output by the analog pin and the `Min`, `Max`, and `StepSize` of the reader. -- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/board#Analog). - -```go -myBoard, err := board.FromRobot(robot, "my_board") - -// Get the analog pin "my_example_analog". -analog, err := myBoard.AnalogByName("my_example_analog") - -// Get the value of the digital signal "my_example_analog" has most recently measured. -reading, err := analog.Read(context.Background(), nil) -readingValue := reading.Value -stepSize := reading.StepSize -``` - -{{% /tab %}} -{{< /tabs >}} - -### Write - -Write an analog value to a pin or analog output on a board capable of doing so. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `value` [(int)](https://docs.python.org/3/library/functions.html#int): Value to write to the pin. -- `extra` [(Optional\[Dict\[str, Any\]\])](https://docs.python.org/library/typing.html#typing.Optional): Extra options to pass to the underlying RPC call. -- `timeout` [(Optional\[float\])](https://docs.python.org/library/typing.html#typing.Optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. - -**Returns:** - -- None - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/board/client/index.html#viam.components.board.client.AnalogClient.write). - -```python {class="line-numbers linkable-line-numbers"} -my_board = Board.from_robot(robot=robot, name="my_board") - -# Get the AnalogWriter “my_example_analog_writer”. -writer = await my_board.analog_by_name(name="my_example_analog_writer") - -await writer.write(42) -``` - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `ctx` [(Context)](https://pkg.go.dev/context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. -- `value` [(int)](https://pkg.go.dev/builtin#int32): Value to write to the pin. -- `extra` [(map\[string\]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. - -**Returns:** - -- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/board#Board). - -```go -myBoard, err := board.FromRobot(robot, "my_board") - -// Get the Analog pin "my_example_analog". -analog, err := myBoard.AnalogByName("my_example_analog") - -// Set the pin to value 48. -err := analog.Write(context.Background(), 48, nil) -``` - -{{% /tab %}} -{{< /tabs >}} - -## `DigitalInterrupt` API - -### Value - -Get the current value of this interrupt. - -Calculation of value differs between the `"type"` of interrupt [configured](#digital_interrupts): - -{{< tabs >}} -{{% tab name="Basic" %}} -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `extra` [(Optional\[Dict\[str, Any\]\])](https://docs.python.org/library/typing.html#typing.Optional): Extra options to pass to the underlying RPC call. -- `timeout` [(Optional\[float\])](https://docs.python.org/library/typing.html#typing.Optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. - -**Returns:** - -- [(int)](https://docs.python.org/3/library/functions.html#int): The amount of ticks that have occurred. - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/board/index.html#viam.components.board.Board.DigitalInterrupt.value). - -```python -my_board = Board.from_robot(robot=robot, name="my_board") - -# Get the DigitalInterrupt "my_example_digital_interrupt". -interrupt = await my_board.digital_interrupt_by_name( - name="my_example_digital_interrupt") - -# Get the amount of times this DigitalInterrupt has been interrupted with a -# tick. -count = await interrupt.value() -``` - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `ctx` [(Context)](https://pkg.go.dev/context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. -- `extra` [(map\[string\]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. - -**Returns:** - -- [(int64)](https://pkg.go.dev/builtin#int64): The amount of ticks that have occurred. -- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/board#BasicDigitalInterrupt). - -```go -myBoard, err := board.FromRobot(robot, "my_board") - -// Get the DigitalInterrupt "my_example_digital_interrupt". -interrupt, err := myBoard.DigitalInterruptByName("my_example_digital_interrupt") - -// Get the amount of times this DigitalInterrupt has been interrupted with a tick. -count, err := interrupt.Value(context.Background(), nil) -``` - -{{% /tab %}} -{{< /tabs >}} -{{% /tab %}} -{{% tab name="Servo" %}} -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `extra` [(Optional\[Dict\[str, Any\]\])](https://docs.python.org/library/typing.html#typing.Optional): Extra options to pass to the underlying RPC call. -- `timeout` [(Optional\[float\])](https://docs.python.org/library/typing.html#typing.Optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. - -**Returns:** - -- [(int)](https://docs.python.org/3/library/functions.html#int): The [RollingAverage](https://pkg.go.dev/go.viam.com/rdk/utils#RollingAverage) of the time in nanoseconds between two successive low signals (pulse width) recorded by the digital interrupt's ticks, computed over a window of size `10`. - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/board/index.html#viam.components.board.Board.DigitalInterrupt.value). - -```python -my_board = Board.from_robot(robot=robot, name="my_board") - -# Get the DigitalInterrupt "my_example_digital_interrupt". -interrupt = await my_board.digital_interrupt_by_name( - name="my_example_digital_interrupt") - -# Get the rolling average of the pulse width across each time the -# DigitalInterrupt is interrupted with a tick. -rolling_avg = await interrupt.value() -``` - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `ctx` [(Context)](https://pkg.go.dev/context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. -- `extra` [(map\[string\]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. - -**Returns:** - -- [(int64)](https://pkg.go.dev/builtin#int64): The [RollingAverage](https://pkg.go.dev/go.viam.com/rdk/utils#RollingAverage) of the time in nanoseconds between two successive low signals (pulse width) recorded by the digital interrupt's ticks, computed over a window of size `10`. -- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/board#ServoDigitalInterrupt). - -```go -myBoard, err := board.FromRobot(robot, "my_board") - -// Get the DigitalInterrupt "my_example_digital_interrupt". -interrupt, err := myBoard.DigitalInterruptByName("my_example_digital_interrupt") - -// Get the rolling average of the pulse width across each time the DigitalInterrupt is interrupted with a tick. -rolling_avg, err := interrupt.Value(context.Background(), nil) -``` - -{{% /tab %}} -{{< /tabs >}} -{{% /tab %}} -{{< /tabs >}} +{{< readfile "/static/include/components/apis/generated/board.md" >}} ## Troubleshooting diff --git a/static/include/components/apis/analog.md b/static/include/components/apis/analog.md deleted file mode 100644 index 232352f74d..0000000000 --- a/static/include/components/apis/analog.md +++ /dev/null @@ -1,5 +0,0 @@ - -| Method Name | Description | -| ----------- | ----------- | -| [`Read`](/components/board/#read) | Read the current value of the digital signal output by an analog pin or ADC. | -| [`Write`](/components/board/#write) | Write an analog value to a pin on an SBC. | diff --git a/static/include/components/apis/digitalinterrupt.md b/static/include/components/apis/digitalinterrupt.md deleted file mode 100644 index 327678ecc5..0000000000 --- a/static/include/components/apis/digitalinterrupt.md +++ /dev/null @@ -1,4 +0,0 @@ - -| Method Name | Description | -| ----------- | ----------- | -| [`Value`](/components/board/#value) | Get the current value of this interrupt. | diff --git a/static/include/components/apis/generated/base-table.md b/static/include/components/apis/generated/base-table.md new file mode 100644 index 0000000000..1aa8485091 --- /dev/null +++ b/static/include/components/apis/generated/base-table.md @@ -0,0 +1,16 @@ + +| Method Name | Description | +| ----------- | ----------- | +| [`MoveStraight`](/components/base/#movestraight) | Move the base in a straight line across the given distance (mm) at the given velocity (mm/sec). | +| [`Spin`](/components/base/#spin) | Turn the base in place, rotating it to the given angle (degrees) at the given angular velocity (degrees/sec). | +| [`SetPower`](/components/base/#setpower) | Set the linear and angular power of the base, represented as a percentage of max power for each direction in the range of [-1.0 to 1.0]. | +| [`SetVelocity`](/components/base/#setvelocity) | Set the linear velocity (mm/sec) and angular velocity (degrees/sec) of the base. | +| [`GetProperties`](/components/base/#getproperties) | Get the width and turning radius of the {{< glossary_tooltip term_id="model" text="model" >}} of base in meters. | +| [`IsMoving`](/components/base/#ismoving) | Returns whether the base is actively moving (or attempting to move) under its own power. | +| [`Stop`](/components/base/#stop) | Stop the base from moving immediately. | +| [`GetGeometries`](/components/base/#getgeometries) | Get all the geometries associated with the base in its current configuration, in the frame of the base. | +| [`Reconfigure`](/components/base/#reconfigure) | Reconfigure this resource. | +| [`DoCommand`](/components/base/#docommand) | Execute model-specific commands that are not otherwise defined by the component API. | +| [`FromRobot`](/components/base/#fromrobot) | Get the resource from the provided robot with the given name. | +| [`Name`](/components/base/#name) | Get the `ResourceName` for this base with the given name. | +| [`Close`](/components/base/#close) | Safely shut down the resource and prevent further use. | diff --git a/static/include/components/apis/generated/base.md b/static/include/components/apis/generated/base.md new file mode 100644 index 0000000000..bc0ca69ed6 --- /dev/null +++ b/static/include/components/apis/generated/base.md @@ -0,0 +1,846 @@ +### MoveStraight + +Move the base in a straight line across the given distance (mm) at the given velocity (mm/sec). + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `distance` ([int](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (required): The distance (in millimeters) to move. Negative implies backwards. +- `velocity` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (required): The velocity (in millimeters per second) to move. Negative implies backwards. +- `extra` (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), Any]) (optional): Extra options to pass to the underlying RPC call. +- `timeout` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. + +**Returns:** + +- None. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +my_base = Base.from_robot(robot=robot, name="my_base") + +# Move the base 40 mm at a velocity of 90 mm/s, forward. +await my_base.move_straight(distance=40, velocity=90) + +# Move the base 40 mm at a velocity of -90 mm/s, backward. +await my_base.move_straight(distance=40, velocity=-90) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/base/client/index.html#viam.components.base.client.BaseClient.move_straight). + +{{% /tab %}} +{{% tab name="Go" %}} + +**Parameters:** + +- `ctx` [(Context)](https://pkg.go.dev/context#Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. +- `distanceMm` [(int)](https://pkg.go.dev/builtin#int): The distance to move the base in millimeters. Positive implies forwards. Negative implies backwards. +- `mmPerSec` [(float64)](https://pkg.go.dev/builtin#float64): The velocity at which to move the base in millimeters per second. Positive implies forwards. Negative implies backwards. +- `extra` [(map[string]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. + +**Returns:** + +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +**Example:** + +```go {class="line-numbers linkable-line-numbers"} +myBase, err := base.FromRobot(machine, "my_base") +// Move the base forward 40 mm at a velocity of 90 mm/s. +myBase.MoveStraight(context.Background(), 40, 90, nil) + +// Move the base backward 40 mm at a velocity of -90 mm/s. +myBase.MoveStraight(context.Background(), 40, -90, nil) +``` + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/base#Base). + +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `distance` [int](https://api.flutter.dev/flutter/dart-core/int-class.html) (required) +- `velocity` [double](https://api.flutter.dev/flutter/dart-core/double-class.html) (required) +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html) + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +// Move the base 40mm forward at 90 mm/s +await myBase.moveStraight(40, 90); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Base/moveStraight.html). + +{{% /tab %}} +{{< /tabs >}} + +### Spin + +Turn the base in place, rotating it to the given angle (degrees) at the given angular velocity (degrees/sec). + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `angle` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (required): The angle (in degrees) to spin. +- `velocity` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (required): The angular velocity (in degrees per second) to spin. Given a positive angle and a positive velocity, the base will turn to the left. +- `extra` (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), Any]) (optional): Extra options to pass to the underlying RPC call. +- `timeout` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. + +**Returns:** + +- None. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +my_base = Base.from_robot(robot=robot, name="my_base") + +# Spin the base 10 degrees at an angular velocity of 15 deg/sec. +await my_base.spin(angle=10, velocity=15) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/base/client/index.html#viam.components.base.client.BaseClient.spin). + +{{% /tab %}} +{{% tab name="Go" %}} + +**Parameters:** + +- `ctx` [(Context)](https://pkg.go.dev/context#Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. +- `angleDeg` [(float64)](https://pkg.go.dev/builtin#float64): The angle to spin in degrees. Positive implies turning to the left. +- `degsPerSec` [(float64)](https://pkg.go.dev/builtin#float64): The angular velocity at which to spin in degrees per second. Given a positive angle and a positive velocity, the base turns to the left (for built-in base models). +- `extra` [(map[string]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. + +**Returns:** + +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +**Example:** + +```go {class="line-numbers linkable-line-numbers"} +myBase, err := base.FromRobot(machine, "my_base") + +// Spin the base 10 degrees at an angular velocity of 15 deg/sec. +myBase.Spin(context.Background(), 10, 15, nil) +``` + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/base#Base). + +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `angle` [double](https://api.flutter.dev/flutter/dart-core/double-class.html) (required) +- `velocity` [double](https://api.flutter.dev/flutter/dart-core/double-class.html) (required) +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html) + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +// Spin the base 10 degrees at 15 deg/s +await myBase.spin(10, 15); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Base/spin.html). + +{{% /tab %}} +{{< /tabs >}} + +### SetPower + +Set the linear and angular power of the base, represented as a percentage of max power for each direction in the range of [-1.0 to 1.0]. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `linear` ([viam.components.base.Vector3](https://python.viam.dev/autoapi/viam/components/base/base/index.html#viam.components.base.base.Vector3)) (required): The linear component. Only the Y component is used for wheeled base. Positive implies forwards. +- `angular` ([viam.components.base.Vector3](https://python.viam.dev/autoapi/viam/components/base/base/index.html#viam.components.base.base.Vector3)) (required): The angular component. Only the Z component is used for wheeled base. Positive turns left; negative turns right. +- `extra` (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), Any]) (optional): Extra options to pass to the underlying RPC call. +- `timeout` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. + +**Returns:** + +- None. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +my_base = Base.from_robot(robot=robot, name="my_base") + +# Make your wheeled base move forward. Set linear power to 75%. +print("move forward") +await my_base.set_power( + linear=Vector3(x=0, y=-.75, z=0), + angular=Vector3(x=0, y=0, z=0)) + +# Make your wheeled base move backward. Set linear power to -100%. +print("move backward") +await my_base.set_power( + linear=Vector3(x=0, y=-1.0, z=0), + angular=Vector3(x=0, y=0, z=0)) + +# Make your wheeled base spin left. Set angular power to 100%. +print("spin left") +await my_base.set_power( + linear=Vector3(x=0, y=0, z=0), + angular=Vector3(x=0, y=0, z=1)) + +# Make your wheeled base spin right. Set angular power to -75%. +print("spin right") +await my_base.set_power( + linear=Vector3(x=0, y=0, z=0), + angular=Vector3(x=0, y=0, z=-.75)) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/base/client/index.html#viam.components.base.client.BaseClient.set_power). + +{{% /tab %}} +{{% tab name="Go" %}} + +**Parameters:** + +- `ctx` [(Context)](https://pkg.go.dev/context#Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. +- `linear` [(r3.Vector)](https://pkg.go.dev/github.com/golang/geo/r3#Vector): The percentage of max power of the base's linear propulsion. In the range of -1.0 to 1.0, with 1.0 meaning 100% power. Viam's coordinate system considers +Y to be the forward axis (+/- X right/left, +/- Z up/down), so use the Y component of this vector to move forward and backward when controlling a wheeled base. Positive "Y" values imply moving forwards. Negative "Y" values imply moving backwards. +- `angular` [(r3.Vector)](https://pkg.go.dev/github.com/golang/geo/r3#Vector): The percentage of max power of the base's angular propulsion. In the range of -1.0 to 1.0, with 1.0 meaning 100% power. Use the Z component of this vector to spin left or right when controlling a wheeled base. Positive "Z" values imply spinning to the left (for built-in base models). +- `extra` [(map[string]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. + +**Returns:** + +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +**Example:** + +```go {class="line-numbers linkable-line-numbers"} +myBase, err := base.FromRobot(machine, "my_base") + +// Make your wheeled base move forward. Set linear power to 75%. +logger.Info("move forward") +err = myBase.SetPower(context.Background(), r3.Vector{Y: .75}, r3.Vector{}, nil) + +// Make your wheeled base move backward. Set linear power to -100%. +logger.Info("move backward") +err = myBase.SetPower(context.Background(), r3.Vector{Y: -1}, r3.Vector{}, nil) + +// Make your wheeled base spin left. Set angular power to 100%. +logger.Info("spin left") +err = myBase.SetPower(context.Background(), r3.Vector{}, r3.Vector{Z: 1}, nil) + +// Make your wheeled base spin right. Set angular power to -75%. +logger.Info("spin right") +err = mybase.SetPower(context.Background(), r3.Vector{}, r3.Vector{Z: -.75}, nil) +``` + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/base#Base). + +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `linear` [Vector3](https://flutter.viam.dev/viam_sdk/Vector3-class.html) (required) +- `angular` [Vector3](https://flutter.viam.dev/viam_sdk/Vector3-class.html) (required) +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html) + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +// Move the base straight forward at 75% power: +await myBase.setPower(Vector3(0, 0.75, 0), Vector3()); + +// Move the base straight backward at 100% power: +await myBase.setPower(Vector3(0, -1, 0), Vector3()); + +// Turn the base to the left at 50% power: +await myBase.setPower(Vector3(), Vector3(0, 0, 0.5)); + +// Turn the base to the right at 60% power: +await myBase.setPower(Vector3(), Vector3(0, 0, -0.6)); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Base/setPower.html). + +{{% /tab %}} +{{< /tabs >}} + +### SetVelocity + +Set the linear velocity (mm/sec) and angular velocity (degrees/sec) of the base. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `linear` ([viam.components.base.Vector3](https://python.viam.dev/autoapi/viam/components/base/base/index.html#viam.components.base.base.Vector3)) (required): Velocity in mm/sec. +- `angular` ([viam.components.base.Vector3](https://python.viam.dev/autoapi/viam/components/base/base/index.html#viam.components.base.base.Vector3)) (required): Velocity in deg/sec. +- `extra` (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), Any]) (optional): Extra options to pass to the underlying RPC call. +- `timeout` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. + +**Returns:** + +- None. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +my_base = Base.from_robot(robot=robot, name="my_base") + +# Set the linear velocity to 50 mm/sec and the angular velocity to +# 15 degree/sec. +await my_base.set_velocity( + linear=Vector3(x=0, y=50, z=0), angular=Vector3(x=0, y=0, z=15)) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/base/client/index.html#viam.components.base.client.BaseClient.set_velocity). + +{{% /tab %}} +{{% tab name="Go" %}} + +**Parameters:** + +- `ctx` [(Context)](https://pkg.go.dev/context#Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. +- `linear` [(r3.Vector)](https://pkg.go.dev/github.com/golang/geo/r3#Vector): The linear velocity in millimeters per second. Only the Y component of the vector is used for a wheeled base. +- `angular` [(r3.Vector)](https://pkg.go.dev/github.com/golang/geo/r3#Vector): The angular velocity in degrees per second. Only the Z component of the vector is used for a wheeled base. +- `extra` [(map[string]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. + +**Returns:** + +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +**Example:** + +```go {class="line-numbers linkable-line-numbers"} +myBase, err := base.FromRobot(machine, "my_base") + +// Set the linear velocity to 50 mm/sec and the angular velocity to 15 deg/sec. +myBase.SetVelocity(context.Background(), r3.Vector{Y: 50}, r3.Vector{Z: 15}, nil) +``` + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/base#Base). + +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `linear` [Vector3](https://flutter.viam.dev/viam_sdk/Vector3-class.html) (required) +- `angular` [Vector3](https://flutter.viam.dev/viam_sdk/Vector3-class.html) (required) +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html) + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +// Set the linear velocity to 50mm/s forward, and the angular velocity +to 15 deg/s counterclockwise +// +await myBase.setVelocity(Vector3(0, 50, 0), Vector3(0, 0, 15)); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Base/setVelocity.html). + +{{% /tab %}} +{{< /tabs >}} + +### GetProperties + +Get the width and turning radius of the {{< glossary_tooltip term_id="model" text="model" >}} of base in meters. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `extra` (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), Any]) (optional): Extra options to pass to the underlying RPC call. +- `timeout` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. + +**Returns:** + +- (viam.components.base.Base.Properties): The properties of the base. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +my_base = Base.from_robot(robot=robot, name="my_base") + +# Get the width and turning radius of the base +properties = await my_base.get_properties() + +# Get the width +print(f"Width of base: {properties.width_meters}") + +# Get the turning radius +print(f"Turning radius of base: {properties.turning_radius_meters}") + +# Get the wheel circumference +print(f"Wheel circumference of base: {properties.wheel_circumference_meters}") +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/base/client/index.html#viam.components.base.client.BaseClient.get_properties). + +{{% /tab %}} +{{% tab name="Go" %}} + +**Parameters:** + +- `ctx` [(Context)](https://pkg.go.dev/context#Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. +- `extra` [(map[string]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. + +**Returns:** + +- [(Properties)](https://pkg.go.dev/go.viam.com/rdk/components/base#Properties): A structure with three fields, `WidthMeters`, `TurningRadiusMeters`, and `WheelCircumferenceMeters` representing the width, turning radius, and wheel circumference of the physical base in meters _(m)_. +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +**Example:** + +```go {class="line-numbers linkable-line-numbers"} +myBase, err := base.FromRobot(machine, "my_base") + +// Get the width and turning radius of the base +properties, err := myBase.Properties(context.Background(), nil) + +// Get the width +myBaseWidth := properties.WidthMeters + +// Get the turning radius +myBaseTurningRadius := properties.TurningRadiusMeters + +// Get the wheel circumference +myBaseWheelCircumference := properties.WheelCircumferenceMeters +``` + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/base#Base). + +{{% /tab %}} +{{< /tabs >}} + +### IsMoving + +Returns whether the base is actively moving (or attempting to move) under its own power. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `timeout` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. + +**Returns:** + +- ([bool](https://docs.python.org/3/library/stdtypes.html#boolean-type-bool)): Whether the base is moving. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +my_base = Base.from_robot(robot=robot, name="my_base") + +# Check whether the base is currently moving. +moving = await my_base.is_moving() +print('Moving: ', moving) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/base/client/index.html#viam.components.base.client.BaseClient.is_moving). + +{{% /tab %}} +{{% tab name="Go" %}} + +**Parameters:** + +- `ctx` [(Context)](https://pkg.go.dev/context#Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. + +**Returns:** + +- [(bool)](https://pkg.go.dev/builtin#bool): Whether this resource is moving (`true`) or not (`false`). +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +**Example:** + +```go {class="line-numbers linkable-line-numbers"} +// This example shows using IsMoving with an arm component. +myArm, err := arm.FromRobot(machine, "my_arm") + +// Stop all motion of the arm. It is assumed that the arm stops immediately. +myArm.Stop(context.Background(), nil) + +// Log if the arm is currently moving. +is_moving, err := myArm.IsMoving(context.Background()) +logger.Info(is_moving) +``` + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Actuator). + +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- None. + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[bool](https://api.flutter.dev/flutter/dart-core/bool-class.html)> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +bool baseIsMoving = await myBase.isMoving(); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Base/isMoving.html). + +{{% /tab %}} +{{< /tabs >}} + +### Stop + +Stop the base from moving immediately. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `extra` (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), Any]) (optional): Extra options to pass to the underlying RPC call. +- `timeout` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. + +**Returns:** + +- None. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +my_base = Base.from_robot(robot=robot, name="my_base") + +# Move the base forward 10 mm at a velocity of 50 mm/s. +await my_base.move_straight(distance=10, velocity=50) + +# Stop the base. +await my_base.stop() +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/base/client/index.html#viam.components.base.client.BaseClient.stop). + +{{% /tab %}} +{{% tab name="Go" %}} + +**Parameters:** + +- `ctx` [(Context)](https://pkg.go.dev/context#Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. +- `extra` [(map[string]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. + +**Returns:** + +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +**Example:** + +```go {class="line-numbers linkable-line-numbers"} +// This example shows using Stop with an arm component. +myArm, err := arm.FromRobot(machine, "my_arm") + +// Stop all motion of the arm. It is assumed that the arm stops immediately. +err = myArm.Stop(context.Background(), nil) +``` + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Actuator). + +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html) + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +await myBase.stop(); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Base/stop.html). + +{{% /tab %}} +{{< /tabs >}} + +### GetGeometries + +Get all the geometries associated with the base in its current configuration, in the [frame](/services/frame-system/) of the base. +The [motion](/services/motion/) and [navigation](/services/navigation/) services use the relative position of inherent geometries to configured geometries representing obstacles for collision detection and obstacle avoidance while motion planning. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `extra` (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), Any]) (optional): Extra options to pass to the underlying RPC call. +- `timeout` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. + +**Returns:** + +- ([List[viam.proto.common.Geometry]](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.Geometry)): The geometries associated with the Component. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +geometries = await component.get_geometries() + +if geometries: + # Get the center of the first geometry + print(f"Pose of the first geometry's centerpoint: {geometries[0].center}") +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/base/client/index.html#viam.components.base.client.BaseClient.get_geometries). + +{{% /tab %}} +{{% tab name="Go" %}} + +**Parameters:** + +- `ctx` [(Context)](https://pkg.go.dev/context#Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. +- `extra` [(map[string]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. + +**Returns:** + +- [([]spatialmath.Geometry)](https://pkg.go.dev/go.viam.com/rdk/spatialmath#Geometry): The geometries associated with this resource, in any order. +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +**Example:** + +```go {class="line-numbers linkable-line-numbers"} +// This example shows using Geometries with an arm component. +myArm, err := arm.FromRobot(machine, "my_arm") + +geometries, err := myArm.Geometries(context.Background(), nil) + +if len(geometries) > 0 { + // Get the center of the first geometry + elem := geometries[0] + fmt.Println("Pose of the first geometry's center point:", elem.center) +} +``` + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Shaped). + +{{% /tab %}} +{{< /tabs >}} + +### Reconfigure + +Reconfigure this resource. +Reconfigure must reconfigure the resource atomically and in place. + +{{< tabs >}} +{{% tab name="Go" %}} + +**Parameters:** + +- `ctx` [(Context)](https://pkg.go.dev/context#Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. +- `deps` [(Dependencies)](https://pkg.go.dev/go.viam.com/rdk/resource#Dependencies): The resource dependencies. +- `conf` [(Config)](https://pkg.go.dev/go.viam.com/rdk/resource#Config): The resource configuration. + +**Returns:** + +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Resource). + +{{% /tab %}} +{{< /tabs >}} + +### DoCommand + +Execute model-specific commands that are not otherwise defined by the component API. +For built-in models, model-specific commands are covered with each model's documentation. +If you are implementing your own base and add features that have no built-in API method, you can access them with `DoCommand`. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `command` (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), ValueTypes]) (required): The command to execute. +- `timeout` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. + +**Returns:** + +- (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), viam.utils.ValueTypes]): Result of the executed command. + +**Raises:** + +- (NotImplementedError): Raised if the Resource does not support arbitrary commands. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +command = {"cmd": "test", "data1": 500} +result = component.do(command) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/base/client/index.html#viam.components.base.client.BaseClient.do_command). + +{{% /tab %}} +{{% tab name="Go" %}} + +**Parameters:** + +- `ctx` [(Context)](https://pkg.go.dev/context#Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. +- `cmd` [(map[string]interface{})](https://go.dev/blog/maps): The command to execute. + +**Returns:** + +- [(map[string]interface{})](https://pkg.go.dev/builtin#string): The command response. +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +**Example:** + +```go {class="line-numbers linkable-line-numbers"} +// This example shows using DoCommand with an arm component. +myArm, err := arm.FromRobot(machine, "my_arm") + +command := map[string]interface{}{"cmd": "test", "data1": 500} +result, err := myArm.DoCommand(context.Background(), command) +``` + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Resource). + +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `command` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic> (required) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +// Example using doCommand with an arm component +const command = {'cmd': 'test', 'data1': 500}; +var result = myArm.doCommand(command); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Resource/doCommand.html). + +{{% /tab %}} +{{< /tabs >}} + +### FromRobot + +Get the resource from the provided robot with the given name. + +{{< tabs >}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `robot` [RobotClient](https://flutter.viam.dev/viam_sdk/RobotClient-class.html) (required) +- `name` [String](https://api.flutter.dev/flutter/dart-core/String-class.html) (required) + +**Returns:** + +- [Base](https://flutter.viam.dev/viam_sdk/Base-class.html) + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Base/fromRobot.html). + +{{% /tab %}} +{{< /tabs >}} + +### Name + +Get the `ResourceName` for this base with the given name. + +{{< tabs >}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `name` [String](https://api.flutter.dev/flutter/dart-core/String-class.html) (required) + +**Returns:** + +- [ResourceName](https://flutter.viam.dev/viam_sdk/ResourceName-class.html) + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Base/getResourceName.html). + +{{% /tab %}} +{{< /tabs >}} + +### Close + +Safely shut down the resource and prevent further use. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- None. + +**Returns:** + +- None. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +await component.close() +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/base/client/index.html#viam.components.base.client.BaseClient.close). + +{{% /tab %}} +{{% tab name="Go" %}} + +**Parameters:** + +- `ctx` [(Context)](https://pkg.go.dev/context#Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. + +**Returns:** + +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +**Example:** + +```go {class="line-numbers linkable-line-numbers"} +// This example shows using Close with an arm component. +myArm, err := arm.FromRobot(machine, "my_arm") + +err = myArm.Close(ctx) +``` + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Resource). + +{{% /tab %}} +{{< /tabs >}} diff --git a/static/include/components/apis/generated/board-table.md b/static/include/components/apis/generated/board-table.md new file mode 100644 index 0000000000..4a6fd1165c --- /dev/null +++ b/static/include/components/apis/generated/board-table.md @@ -0,0 +1,25 @@ + +| Method Name | Description | +| ----------- | ----------- | +| [`SetGPIO`](/components/board/#setgpio) | Set the digital signal output of this pin to low (0V) or high (active, >0V). | +| [`GetGPIO`](/components/board/#getgpio) | Get if the digital signal output of this pin is high (active, >0V). | +| [`GetPWM`](/components/board/#getpwm) | Get the pin's pulse-width modulation (PWM) duty cycle: a float [`0.0`, `1.0`] representing the percentage of time the digital signal output by this pin is in the high state (active, >0V) relative to the interval period of the PWM signal (interval period being the mathematical inverse of the PWM frequency). | +| [`SetPWM`](/components/board/#setpwm) | Set the pin's Pulse-width modulation (PWM) duty cycle: a float [`0.0`, `1.0`] indicating the percentage of time the digital signal output of this pin is in the high state (active, >0V) relative to the interval period of the PWM signal (interval period being the mathematical inverse of the PWM frequency). | +| [`PWMFrequency`](/components/board/#pwmfrequency) | Get the PWM frequency of the GPIO pin. | +| [`SetPWMFrequency`](/components/board/#setpwmfrequency) | Set the pin to the given PWM `frequency` (in Hz). When `frequency` is 0, it will use the board’s default PWM frequency. | +| [`AnalogNames`](/components/board/#analognames) | Get the name of every `Analog` configured and residing on the board. | +| [`AnalogByName`](/components/board/#analogbyname) | Get an `Analog` by `name`. | +| [`Write`](/components/board/#write) | Write an analog value to a pin on the board. | +| [`GetDigitalInterruptValue`](/components/board/#getdigitalinterruptvalue) | Get an `DigitalInterrupt` by `name`. | +| [`StreamTicks`](/components/board/#streamticks) | Start a stream of `DigitalInterrupt` ticks. | +| [`SetPowerMode`](/components/board/#setpowermode) | Set the board to the indicated `PowerMode`. | +| [`GetGeometries`](/components/board/#getgeometries) | Get all the geometries associated with the board in its current configuration, in the frame of the board. | +| [`Read`](/components/board/#read) | Read the current integer value of the digital signal output by the ADC. | +| [`Value`](/components/board/#value) | Get the current value of this interrupt. | +| [`DigitalInterruptNames`](/components/board/#digitalinterruptnames) | Get the name of every `DigitalInterrupt` configured on the board. | +| [`GPIOPinByName`](/components/board/#gpiopinbyname) | Get a `GPIOPin` by {{< glossary_tooltip term_id="pin-number" text="pin number" >}}. | +| [`Reconfigure`](/components/board/#reconfigure) | Reconfigure this resource. | +| [`DoCommand`](/components/board/#docommand) | Execute model-specific commands that are not otherwise defined by the component API. | +| [`FromRobot`](/components/board/#fromrobot) | Get the resource from the provided robot with the given name. | +| [`Name`](/components/board/#name) | Get the `ResourceName` for this board with the given name. | +| [`Close`](/components/board/#close) | Safely shut down the resource and prevent further use. | diff --git a/static/include/components/apis/generated/board.md b/static/include/components/apis/generated/board.md new file mode 100644 index 0000000000..345fd5b9c0 --- /dev/null +++ b/static/include/components/apis/generated/board.md @@ -0,0 +1,1370 @@ +### SetGPIO + +Set the digital signal output of this pin to low (0V) or high (active, >0V). + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `high` ([bool](https://docs.python.org/3/library/stdtypes.html#boolean-type-bool)) (required): When true, sets the pin to high. When false, sets the pin to low. +- `extra` (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), Any]) (optional): Extra options to pass to the underlying RPC call. +- `timeout` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. + +**Returns:** + +- None. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +my_board = Board.from_robot(robot=robot, name="my_board") + +# Get the GPIOPin with pin number 15. +pin = await my_board.gpio_pin_by_name(name="15") + +# Set the pin to high. +await pin.set(high="true") +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/board/client/index.html#viam.components.board.client.GPIOPinClient.set). + +{{% /tab %}} +{{% tab name="Go" %}} + +**Parameters:** + +- `ctx` [(Context)](https://pkg.go.dev/context#Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. +- `high` [(bool)](https://pkg.go.dev/builtin#bool): If `true`, set the state of the pin to high. If `false`, set the state of the pin to low. +- `extra` [(map[string]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. + +**Returns:** + +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +**Example:** + +```go {class="line-numbers linkable-line-numbers"} +myBoard, err := board.FromRobot(robot, "my_board") + +// Get the GPIOPin with pin number 15. +pin, err := myBoard.GPIOPinByName("15") + +// Set the pin to high. +err := pin.Set(context.Background(), "true", nil) +``` + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/board#GPIOPin). + +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `pin` [String](https://api.flutter.dev/flutter/dart-core/String-class.html) (required) +- `high` [bool](https://api.flutter.dev/flutter/dart-core/bool-class.html) (required) +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html) + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +// Set pin 15 to high +await myBoard.setGpioState('15', true); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Board/setGpioState.html). + +{{% /tab %}} +{{< /tabs >}} + +### GetGPIO + +Get if the digital signal output of this pin is high (active, >0V). + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `extra` (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), Any]) (optional): Extra options to pass to the underlying RPC call. +- `timeout` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. + +**Returns:** + +- ([bool](https://docs.python.org/3/library/stdtypes.html#boolean-type-bool)): Indicates if the state of the pin is high. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +my_board = Board.from_robot(robot=robot, name="my_board") + +# Get the GPIOPin with pin number 15. +pin = await my_board.gpio_pin_by_name(name="15") + +# Get if it is true or false that the state of the pin is high. +high = await pin.get() +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/board/client/index.html#viam.components.board.client.GPIOPinClient.get). + +{{% /tab %}} +{{% tab name="Go" %}} + +**Parameters:** + +- `ctx` [(Context)](https://pkg.go.dev/context#Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. +- `extra` [(map[string]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. + +**Returns:** + +- [(bool)](https://pkg.go.dev/builtin#bool): If `true`, the state of the pin is high. If `false`, the state of the pin is low. +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +**Example:** + +```go {class="line-numbers linkable-line-numbers"} +myBoard, err := board.FromRobot(robot, "my_board") + +// Get the GPIOPin with pin number 15. +pin, err := myBoard.GPIOPinByName("15") + +// Get if it is true or false that the state of the pin is high. +high := pin.Get(context.Background(), nil) +``` + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/board#GPIOPin). + +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `pin` [String](https://api.flutter.dev/flutter/dart-core/String-class.html) (required) +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[bool](https://api.flutter.dev/flutter/dart-core/bool-class.html)> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +// Whether the state of pin 15 is currently high +bool pinStateIsHigh = await myBoard.gpio('15'); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Board/gpio.html). + +{{% /tab %}} +{{< /tabs >}} + +### GetPWM + +{{% alert title="Info" color="info" %}} + +[Pulse-width modulation (PWM)](https://www.digikey.com/en/blog/pulse-width-modulation) is a method where of transmitting a digital signal in the form of pulses to control analog circuits. +With PWM on a _board_, the continuous digital signal output by a GPIO pin is sampled at regular intervals and transmitted to any [hardware components](/components/) wired to the pin that read analog signals. +This enables the board to communicate with these components. + +{{% /alert %}} + +Get the pin's [pulse-width modulation (PWM) duty cycle](https://learn.sparkfun.com/tutorials/pulse-width-modulation/duty-cycle): a float [`0.0`, `1.0`] representing the percentage of time the digital signal output by this pin is in the high state (active, >0V) relative to the interval period of the PWM signal [(interval period being the mathematical inverse of the PWM frequency)](https://learn.adafruit.com/improve-brushed-dc-motor-performance/pwm-frequency). + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `extra` (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), Any]) (optional): Extra options to pass to the underlying RPC call. +- `timeout` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. + +**Returns:** + +- ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)): The duty cycle. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +my_board = Board.from_robot(robot=robot, name="my_board") + +# Get the GPIOPin with pin number 15. +pin = await my_board.gpio_pin_by_name(name="15") + +# Get if it is true or false that the state of the pin is high. +duty_cycle = await pin.get_pwm() +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/board/client/index.html#viam.components.board.client.GPIOPinClient.get_pwm). + +{{% /tab %}} +{{% tab name="Go" %}} + +**Parameters:** + +- `ctx` [(Context)](https://pkg.go.dev/context#Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. +- `extra` [(map[string]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. + +**Returns:** + +- [(float64)](https://pkg.go.dev/builtin#float64): A float [`0.0`, `1.0`] representing the percentage of time the digital signal output by this pin is in the high state relative to the interval period of the PWM signal. +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +**Example:** + +```go {class="line-numbers linkable-line-numbers"} +myBoard, err := board.FromRobot(robot, "my_board") + +// Get the GPIOPin with pin number 15. +pin, err := myBoard.GPIOPinByName("15") + +// Returns the duty cycle. +duty_cycle := pin.PWM(context.Background(), nil) +``` + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/board#GPIOPin). + +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `pin` [String](https://api.flutter.dev/flutter/dart-core/String-class.html) (required) +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[double](https://api.flutter.dev/flutter/dart-core/double-class.html)> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +// Get the PWM duty cycle of pin 15 +var dutyCycle = await myBoard.pwm('15'); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Board/pwm.html). + +{{% /tab %}} +{{< /tabs >}} + +### SetPWM + +Set the pin's [Pulse-width modulation (PWM) duty cycle](https://learn.sparkfun.com/tutorials/pulse-width-modulation/duty-cycle): a float [`0.0`, `1.0`] indicating the percentage of time the digital signal output of this pin is in the high state (active, >0V) relative to the interval period of the PWM signal [(interval period being the mathematical inverse of the PWM frequency)](https://learn.adafruit.com/improve-brushed-dc-motor-performance/pwm-frequency). + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `duty_cycle` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (required): The duty cycle. +- `extra` (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), Any]) (optional): Extra options to pass to the underlying RPC call. +- `timeout` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. + +**Returns:** + +- None. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +my_board = Board.from_robot(robot=robot, name="my_board") + +# Get the GPIOPin with pin number 15. +pin = await my_board.gpio_pin_by_name(name="15") + +# Set the duty cycle to .6, meaning that this pin will be in the high state for +# 60% of the duration of the PWM interval period. +await pin.set_pwm(cycle=.6) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/board/client/index.html#viam.components.board.client.GPIOPinClient.set_pwm). + +{{% /tab %}} +{{% tab name="Go" %}} + +**Parameters:** + +- `ctx` [(Context)](https://pkg.go.dev/context#Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. +- `dutyCyclePct` [(float64)](https://pkg.go.dev/builtin#float64): A float [`0.0`, `1.0`] representing the percentage of time the digital signal output by this pin is in the high state relative to the interval period of the PWM signal. +- `extra` [(map[string]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. + +**Returns:** + +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +**Example:** + +```go {class="line-numbers linkable-line-numbers"} +myBoard, err := board.FromRobot(robot, "my_board") + +// Get the GPIOPin with pin number 15. +pin, err := myBoard.GPIOPinByName("15") + +// Set the duty cycle to .6, meaning that this pin will be in the high state for 60% of the duration of the PWM interval period. +err := pin.SetPWM(context.Background(), .6, nil) +``` + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/board#GPIOPin). + +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `pin` [String](https://api.flutter.dev/flutter/dart-core/String-class.html) (required) +- `dutyCyclePct` [double](https://api.flutter.dev/flutter/dart-core/double-class.html) (required) +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html) + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +// Set the PWM duty cycle of pin 13 +await myBoard.setPwm('13', 0.6); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Board/setPwm.html). + +{{% /tab %}} +{{< /tabs >}} + +### PWMFrequency + +Get the PWM frequency of the GPIO pin. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `extra` (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), Any]) (optional): Extra options to pass to the underlying RPC call. +- `timeout` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. + +**Returns:** + +- ([int](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)): The PWM frequency. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +my_board = Board.from_robot(robot=robot, name="my_board") + +# Get the GPIOPin with pin number 15. +pin = await my_board.gpio_pin_by_name(name="15") + +# Get the PWM frequency of this pin. +freq = await pin.get_pwm_frequency() +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/board/client/index.html#viam.components.board.client.GPIOPinClient.get_pwm_frequency). + +{{% /tab %}} +{{% tab name="Go" %}} + +**Parameters:** + +- `ctx` [(Context)](https://pkg.go.dev/context#Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. +- `extra` [(map[string]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. + +**Returns:** + +- [(uint)](https://pkg.go.dev/builtin#uint): The PWM Frequency in Hertz (Hz) (the count of PWM interval periods per second) the digital signal output by this pin is set to. +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +**Example:** + +```go {class="line-numbers linkable-line-numbers"} +myBoard, err := board.FromRobot(robot, "my_board") + +// Get the GPIOPin with pin number 15. +pin, err := myBoard.GPIOPinByName("15") + +// Get the PWM frequency of this pin. +freqHz, err := pin.PWMFreq(context.Background(), nil) +``` + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/board#GPIOPin). + +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `pin` [String](https://api.flutter.dev/flutter/dart-core/String-class.html) (required) +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[int](https://api.flutter.dev/flutter/dart-core/int-class.html)> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +// Get the PWM frequency of pin 11 +var frequency = await myBoard.pwmFrequency('11'); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Board/pwmFrequency.html). + +{{% /tab %}} +{{< /tabs >}} + +### SetPWMFrequency + +Set the pin to the given PWM `frequency` (in Hz). When `frequency` is 0, it will use the board’s default PWM frequency. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `frequency` ([int](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (required): The frequency, in Hz. +- `extra` (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), Any]) (optional): Extra options to pass to the underlying RPC call. +- `timeout` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. + +**Returns:** + +- None. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +my_board = Board.from_robot(robot=robot, name="my_board") + +# Get the GPIOPin with pin number 15. +pin = await my_board.gpio_pin_by_name(name="15") + +# Set the PWM frequency of this pin to 1600 Hz. +high = await pin.set_pwm_frequency(frequency=1600) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/board/client/index.html#viam.components.board.client.GPIOPinClient.set_pwm_frequency). + +{{% /tab %}} +{{% tab name="Go" %}} + +**Parameters:** + +- `ctx` [(Context)](https://pkg.go.dev/context#Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. +- `freqHz` [(uint)](https://pkg.go.dev/builtin#uint): The PWM Frequency in Hertz (Hz), the count of PWM interval periods per second, to set the digital signal output by this pin to. +- `extra` [(map[string]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. + +**Returns:** + +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +**Example:** + +```go {class="line-numbers linkable-line-numbers"} +myBoard, err := board.FromRobot(robot, "my_board") + +// Get the GPIOPin with pin number 15. +pin, err := myBoard.GPIOPinByName("15") + +// Set the PWM frequency of this pin to 1600 Hz. +high := pin.SetPWMFreq(context.Background(), 1600, nil) +``` + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/board#GPIOPin). + +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `pin` [String](https://api.flutter.dev/flutter/dart-core/String-class.html) (required) +- `frequencyHz` [int](https://api.flutter.dev/flutter/dart-core/int-class.html) (required) +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html) + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +// Set the PWM frequency of pin 15 to 1600 Hz +await myBoard.setPwmFrequency('15', 1600); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Board/setPwmFrequency.html). + +{{% /tab %}} +{{< /tabs >}} + +### AnalogNames + +Get the name of every [`Analog`](#analogs) configured and residing on the board. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- None. + +**Returns:** + +- (List[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)]): The list of names of all known analog readers/writers. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +my_board = Board.from_robot(robot=robot, name="my_board") + +# Get the name of every Analog configured on the board. +names = await my_board.analog_names() +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/board/client/index.html#viam.components.board.client.BoardClient.analog_names). + +{{% /tab %}} +{{% tab name="Go" %}} + +**Parameters:** + +- None. + +**Returns:** + +- [([]string)](https://pkg.go.dev/builtin#string): A slice containing the `"name"` of every analog pin [configured](#supported-models) on the board. + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/board#Board). + +{{% /tab %}} +{{< /tabs >}} + +### AnalogByName + +Get an [`Analog`](#analogs) by `name`. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `name` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): Name of the analog reader to be retrieved. + +**Returns:** + +- ([viam.components.board.board.Board.Analog](https://python.viam.dev/autoapi/viam/components/board/board/index.html#viam.components.board.board.Board.Analog)): The analog reader or writer. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +my_board = Board.from_robot(robot=robot, name="my_board") + +# Get the Analog "my_example_analog_reader". +reader = await my_board.analog_by_name(name="my_example_analog_reader") +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/board/client/index.html#viam.components.board.client.BoardClient.analog_by_name). + +{{% /tab %}} +{{% tab name="Go" %}} + +**Parameters:** + +- `name` [(string)](https://pkg.go.dev/builtin#string): Name of the analog pin you want to retrieve. Set as the `"name"` property [in configuration](/components/board/#digital_interrupts). + +**Returns:** + +- [(Analog)](https://pkg.go.dev/go.viam.com/rdk/components/board#Analog): An interface representing an analog pin configured and residing on the board. +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +**Example:** + +```go {class="line-numbers linkable-line-numbers"} +myBoard, err := board.FromRobot(robot, "my_board") + +// Get the Analog pin "my_example_analog". +analog, err := myBoard.AnalogByName("my_example_analog") +``` + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/board#Board). + +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `analogReaderName` [String](https://api.flutter.dev/flutter/dart-core/String-class.html) (required) +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[AnalogValue](https://flutter.viam.dev/viam_sdk/AnalogValue.html)> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +// Get the current value of an analog reader named "my_example_analog" +var analogVal = await myBoard.analogReaderValue('my_example_analog'); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Board/analogReaderValue.html). + +{{% /tab %}} +{{< /tabs >}} + +### Write + +Write an analog value to a pin on the board. + +{{< tabs >}} +{{% tab name="Go" %}} + +**Parameters:** + +- `ctx` [(Context)](https://pkg.go.dev/context#Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. +- `value` [(int)](https://pkg.go.dev/builtin#int): Value to write to the pin. +- `extra` [(map[string]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. + +**Returns:** + +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +**Example:** + +```go {class="line-numbers linkable-line-numbers"} +myBoard, err := board.FromRobot(robot, "my_board") + +// Get the Analog pin "my_example_analog". +analog, err := myBoard.AnalogByName("my_example_analog") + +// Set the pin to value 48. +err := analog.Write(context.Background(), 48, nil) +``` + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/board#Analog). + +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `pin` [String](https://api.flutter.dev/flutter/dart-core/String-class.html) (required) +- `value` [int](https://api.flutter.dev/flutter/dart-core/int-class.html) (required) +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html) + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +// Set pin 11 to value 48 +await myBoard.writeAnalog('11', 48); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Board/writeAnalog.html). + +{{% /tab %}} +{{< /tabs >}} + +### GetDigitalInterruptValue + +Get an [`DigitalInterrupt`](#digital_interrupts) by `name`. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `name` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): Name of the digital interrupt. + +**Returns:** + +- ([viam.components.board.board.Board.DigitalInterrupt](https://python.viam.dev/autoapi/viam/components/board/board/index.html#viam.components.board.board.Board.DigitalInterrupt)): The digital interrupt. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +my_board = Board.from_robot(robot=robot, name="my_board") + +# Get the DigitalInterrupt "my_example_digital_interrupt". +interrupt = await my_board.digital_interrupt_by_name( + name="my_example_digital_interrupt") +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/board/client/index.html#viam.components.board.client.BoardClient.digital_interrupt_by_name). + +{{% /tab %}} +{{% tab name="Go" %}} + +**Parameters:** + +- `name` [(string)](https://pkg.go.dev/builtin#string): Name of the digital interrupt you want to retrieve. Set as the `"name"` property [in configuration](/components/board/#digital_interrupts). + +**Returns:** + +- [(DigitalInterrupt)](https://pkg.go.dev/go.viam.com/rdk/components/board#DigitalInterrupt): An interface representing a configured interrupt on the board. +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +**Example:** + +```go {class="line-numbers linkable-line-numbers"} +myBoard, err := board.FromRobot(robot, "my_board") + +// Get the DigitalInterrupt "my_example_digital_interrupt". +interrupt, err := myBoard.DigitalInterruptByName("my_example_digital_interrupt") +``` + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/board#Board). + +{{% /tab %}} +{{< /tabs >}} + +### StreamTicks + +Start a stream of [`DigitalInterrupt`](/components/board/#digital_interrupts) ticks. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `interrupts` ([List[viam.components.board.board.Board.DigitalInterrupt]](https://python.viam.dev/autoapi/viam/components/board/board/index.html#viam.components.board.board.Board.DigitalInterrupt)) (required): list of digital interrupts to receive ticks from. +- `extra` (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), Any]) (optional): Extra options to pass to the underlying RPC call. + +**Returns:** + +- (viam.components.board.board.TickStream): stream of ticks. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +my_board = Board.from_robot(robot=robot, name="my_board") +di8 = await my_board.digital_interrupt_by_name(name="8")) +di11 = await my_board.digital_interrupt_by_name(name="11")) + +# Iterate over stream of ticks from pins 8 and 11. +async for tick in my_board.stream_ticks([di8, di11]): + print(f"Pin {tick.pin_name} changed to {'high' if tick.high else 'low'} at {tick.time}") +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/board/client/index.html#viam.components.board.client.BoardClient.stream_ticks). + +{{% /tab %}} +{{% tab name="Go" %}} + +**Parameters:** + +- `ctx` [(Context)](https://pkg.go.dev/context#Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. +- `interrupts` [([]DigitalInterrupt)](https://pkg.go.dev/go.viam.com/rdk/components/board#DigitalInterrupt): Slice of digital interrupts to receive ticks from. +- `ch chan` [(Tick)](https://pkg.go.dev/go.viam.com/rdk/components/board#Tick): The channel to stream Ticks, structs containing `Name`, `High`, and `TimestampNanosec` fields. +- `extra` [(map[string]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. + +**Returns:** + +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +**Example:** + +```go {class="line-numbers linkable-line-numbers"} +myBoard, err := board.FromRobot(robot, "my_board") + +// Make a channel to stream ticks +ticksChan := make(chan board.Tick) + +interrupts := []*DigitalInterrupt{} + +if di8, err := myBoard.DigitalInterruptByName("8"); err == nil { + interrupts = append(interrupts, di8) +} + +if di11, err := myBoard.DigitalInterruptByName("11"); err == nil { + interrupts = append(interrupts, di11) +} + +err = myBoard.StreamTicks(context.Background(), interrupts, ticksChan, nil) +``` + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/board#Board). + +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `interrupts` [List](https://api.flutter.dev/flutter/dart-core/List-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html)> (required) +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Stream](https://api.flutter.dev/flutter/dart-async/Stream-class.html)<[Tick](https://flutter.viam.dev/viam_sdk/Tick.html)> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +// Stream ticks from digital interrupts on pins 8 and 11 +var interrupts = ['8', '11']; +Stream tickStream = await myBoard.streamTicks(interrupts); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Board/streamTicks.html). + +{{% /tab %}} +{{< /tabs >}} + +### SetPowerMode + +Set the board to the indicated `PowerMode`. + +{{% alert title="Info" color="info" %}} + +This method may not receive a successful response from gRPC when you set the board to the offline power mode `PowerMode.POWER_MODE_OFFLINE_DEEP`. + +When this is the case for your board model, the call is returned with an error specifying that the remote procedure call timed out or that the endpoint is no longer available. +This is expected: the board has been successfully powered down and can no longer respond to messages. + +{{% /alert %}} + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `mode` ([viam.proto.component.board.PowerMode.ValueType](https://python.viam.dev/autoapi/viam/components/board/client/index.html#viam.components.board.client.PowerMode)) (required): The desired power mode. +- `duration` ([datetime.timedelta](https://docs.python.org/3/library/datetime.html#timedelta-objects)) (optional): Requested duration to stay in power mode. +- `timeout` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. + +**Returns:** + +- None. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +my_board = Board.from_robot(robot=robot, name="my_board") + +# Set the power mode of the board to OFFLINE_DEEP. +status = await my_board.set_power_mode(mode=PowerMode.POWER_MODE_OFFLINE_DEEP) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/board/client/index.html#viam.components.board.client.BoardClient.set_power_mode). + +{{% /tab %}} +{{% tab name="Go" %}} + +**Parameters:** + +- `ctx` [(Context)](https://pkg.go.dev/context#Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. +- `mode` [(pb.PowerMode)](https://pkg.go.dev/go.viam.com/api/component/board/v1#PowerMode): Options to specify power usage of the board: `boardpb.PowerMode_POWER_MODE_UNSPECIFIED`, `boardpb.PowerMode_POWER_MODE_NORMAL`, and `boardpb.PowerMode_POWER_MODE_OFFLINE_DEEP`. +- `duration` [(*time.Duration)](https://pkg.go.dev/time#Duration): If provided, the board will exit the given power mode after the specified duration. + +**Returns:** + +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +**Example:** + +```go {class="line-numbers linkable-line-numbers"} +myBoard, err := board.FromRobot(robot, "my_board") + +Set the power mode of the board to OFFLINE_DEEP. +myBoard.SetPowerMode(context.Background(), boardpb.PowerMode_POWER_MODE_OFFLINE_DEEP, nil) +``` + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/board#Board). + +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `powerMode` [PowerMode](https://flutter.viam.dev/viam_protos.component.board/PowerMode-class.html) (required) +- `seconds` [int](https://api.flutter.dev/flutter/dart-core/int-class.html) (required) +- `nanos` [int](https://api.flutter.dev/flutter/dart-core/int-class.html) (required) +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html) + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +// Set the power mode of the board to offline deep for 60 seconds +// Requires importing 'package:viam_sdk/protos/component/board.dart' +const powerMode = PowerMode.POWER_MODE_OFFLINE_DEEP; +await myBoard.setPowerMode(powerMode, 60, 0); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Board/setPowerMode.html). + +{{% /tab %}} +{{< /tabs >}} + +### GetGeometries + +Get all the geometries associated with the board in its current configuration, in the [frame](/services/frame-system/) of the board. +The [motion](/services/motion/) and [navigation](/services/navigation/) services use the relative position of inherent geometries to configured geometries representing obstacles for collision detection and obstacle avoidance while motion planning. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `extra` (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), Any]) (optional): Extra options to pass to the underlying RPC call. +- `timeout` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. + +**Returns:** + +- ([List[viam.proto.common.Geometry]](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.Geometry)): The geometries associated with the Component. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +geometries = await component.get_geometries() + +if geometries: + # Get the center of the first geometry + print(f"Pose of the first geometry's centerpoint: {geometries[0].center}") +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/board/client/index.html#viam.components.board.client.BoardClient.get_geometries). + +{{% /tab %}} +{{< /tabs >}} + +### Read + +Read the current integer value of the digital signal output by the [ADC](#analogs). + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `extra` (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), Any]) (optional): Extra options to pass to the underlying RPC call. +- `timeout` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. + +**Returns:** + +- (viam.components.board.board.Board.Analog.Value): The current value, including the min, max, and step_size of the reader. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +my_board = Board.from_robot(robot=robot, name="my_board") + +# Get the Analog "my_example_analog_reader". +reader = await my_board.analog_reader_by_name( + name="my_example_analog_reader") + +# Get the value of the digital signal "my_example_analog_reader" has most +# recently measured. +reading = await reader.read() +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/board/client/index.html#viam.components.board.client.AnalogClient.read). + +{{% /tab %}} +{{% tab name="Go" %}} + +**Parameters:** + +- `ctx` [(Context)](https://pkg.go.dev/context#Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. +- `extra` [(map[string]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. + +**Returns:** + +- [(AnalogValue)](https://pkg.go.dev/go.viam.com/rdk/components/board#AnalogValue): The current value, including the integer `Value` of the digital signal output by the analog pin and the `Min`, `Max`, and `StepSize` of the reader. +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +**Example:** + +```go {class="line-numbers linkable-line-numbers"} +myBoard, err := board.FromRobot(robot, "my_board") + +// Get the analog pin "my_example_analog". +analog, err := myBoard.AnalogByName("my_example_analog") + +// Get the value of the analog signal "my_example_analog" has most recently measured. +reading, err := analog.Read(context.Background(), nil) +readingValue := reading.Value +stepSize := reading.StepSize +``` + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/board#Analog). + +{{% /tab %}} +{{< /tabs >}} + +### Value + +Get the current value of this interrupt. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `extra` (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), Any]) (optional): Extra options to pass to the underlying RPC call. +- `timeout` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. + +**Returns:** + +- ([int](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)): The current value. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +my_board = Board.from_robot(robot=robot, name="my_board") + +# Get the DigitalInterrupt "my_example_digital_interrupt". +interrupt = await my_board.digital_interrupt_by_name( + name="my_example_digital_interrupt") + +# Get the amount of times this DigitalInterrupt has been interrupted with a +# tick. +count = await interrupt.value() +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/board/client/index.html#viam.components.board.client.DigitalInterruptClient.value). + +{{% /tab %}} +{{% tab name="Go" %}} + +**Parameters:** + +- `ctx` [(Context)](https://pkg.go.dev/context#Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. +- `extra` [(map[string]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. + +**Returns:** + +- [(int64)](https://pkg.go.dev/builtin#int64): The amount of ticks that have occurred. +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +**Example:** + +```go {class="line-numbers linkable-line-numbers"} +myBoard, err := board.FromRobot(robot, "my_board") + +// Get the DigitalInterrupt "my_example_digital_interrupt". +interrupt, err := myBoard.DigitalInterruptByName("my_example_digital_interrupt") + +// Get the amount of times this DigitalInterrupt has ticked. +count, err := interrupt.Value(context.Background(), nil) +``` + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/board#DigitalInterrupt). + +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `digitalInterruptName` [String](https://api.flutter.dev/flutter/dart-core/String-class.html) (required) +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[int](https://api.flutter.dev/flutter/dart-core/int-class.html)> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +// Get the current value of a digital interrupt named "my_example_digital_interrupt" +var interruptVal = await myBoard.digitalInterruptValue('my_example_digital_interrupt'); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Board/digitalInterruptValue.html). + +{{% /tab %}} +{{< /tabs >}} + +### DigitalInterruptNames + +Get the name of every [`DigitalInterrupt`](#digital_interrupts) configured on the board. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- None. + +**Returns:** + +- (List[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)]): The names of the digital interrupts. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +my_board = Board.from_robot(robot=robot, name="my_board") + +# Get the name of every DigitalInterrupt configured on the board. +names = await my_board.digital_interrupt_names() +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/board/client/index.html#viam.components.board.client.BoardClient.digital_interrupt_names). + +{{% /tab %}} +{{% tab name="Go" %}} + +**Parameters:** + +- None. + +**Returns:** + +- [([]string)](https://pkg.go.dev/builtin#string): A slice containing the `"name"` of every interrupt [configured](#supported-models) on the board. + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/board#Board). + +{{% /tab %}} +{{< /tabs >}} + +### GPIOPinByName + +Get a `GPIOPin` by {{< glossary_tooltip term_id="pin-number" text="pin number" >}}. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `name` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): Name of the GPIO pin. + +**Returns:** + +- ([viam.components.board.board.Board.GPIOPin](https://python.viam.dev/autoapi/viam/components/board/board/index.html#viam.components.board.board.Board.GPIOPin)): The pin. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +my_board = Board.from_robot(robot=robot, name="my_board") + +# Get the GPIOPin with pin number 15. +pin = await my_board.gpio_pin_by_name(name="15") +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/board/client/index.html#viam.components.board.client.BoardClient.gpio_pin_by_name). + +{{% /tab %}} +{{% tab name="Go" %}} + +**Parameters:** + +- `name` [(string)](https://pkg.go.dev/builtin#string): Pin number of the GPIO pin you want to retrieve as a `GPIOPin` interface. Refer to the pinout diagram and data sheet of your [board model](#supported-models) for {{< glossary_tooltip term_id="pin-number" text="pin numbers" >}}. + +**Returns:** + +- [(GPIOPin)](https://pkg.go.dev/go.viam.com/rdk/components/board#GPIOPin): An interface representing an individual GPIO pin on the board. +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +**Example:** + +```go {class="line-numbers linkable-line-numbers"} +myBoard, err := board.FromRobot(robot, "my_board") + +// Get the GPIOPin with pin number 15. +pin, err := myBoard.GPIOPinByName("15") +``` + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/board#Board). + +{{% /tab %}} +{{< /tabs >}} + +### Reconfigure + +Reconfigure this resource. +Reconfigure must reconfigure the resource atomically and in place. + +{{< tabs >}} +{{% tab name="Go" %}} + +**Parameters:** + +- `ctx` [(Context)](https://pkg.go.dev/context#Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. +- `deps` [(Dependencies)](https://pkg.go.dev/go.viam.com/rdk/resource#Dependencies): The resource dependencies. +- `conf` [(Config)](https://pkg.go.dev/go.viam.com/rdk/resource#Config): The resource configuration. + +**Returns:** + +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Resource). + +{{% /tab %}} +{{< /tabs >}} + +### DoCommand + +Execute model-specific commands that are not otherwise defined by the component API. +For built-in models, model-specific commands are covered with each model's documentation. +If you are implementing your own board and add features that have no built-in API method, you can access them with `DoCommand`. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `command` (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), ValueTypes]) (required): The command to execute. +- `timeout` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. + +**Returns:** + +- (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), viam.utils.ValueTypes]): Result of the executed command. + +**Raises:** + +- (NotImplementedError): Raised if the Resource does not support arbitrary commands. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +command = {"cmd": "test", "data1": 500} +result = component.do(command) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/board/client/index.html#viam.components.board.client.BoardClient.do_command). + +{{% /tab %}} +{{% tab name="Go" %}} + +**Parameters:** + +- `ctx` [(Context)](https://pkg.go.dev/context#Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. +- `cmd` [(map[string]interface{})](https://go.dev/blog/maps): The command to execute. + +**Returns:** + +- [(map[string]interface{})](https://pkg.go.dev/builtin#string): The command response. +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +**Example:** + +```go {class="line-numbers linkable-line-numbers"} +// This example shows using DoCommand with an arm component. +myArm, err := arm.FromRobot(machine, "my_arm") + +command := map[string]interface{}{"cmd": "test", "data1": 500} +result, err := myArm.DoCommand(context.Background(), command) +``` + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Resource). + +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `command` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic> (required) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +// Example using doCommand with an arm component +const command = {'cmd': 'test', 'data1': 500}; +var result = myArm.doCommand(command); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Resource/doCommand.html). + +{{% /tab %}} +{{< /tabs >}} + +### FromRobot + +Get the resource from the provided robot with the given name. + +{{< tabs >}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `robot` [RobotClient](https://flutter.viam.dev/viam_sdk/RobotClient-class.html) (required) +- `name` [String](https://api.flutter.dev/flutter/dart-core/String-class.html) (required) + +**Returns:** + +- [Board](https://flutter.viam.dev/viam_sdk/Board-class.html) + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Board/fromRobot.html). + +{{% /tab %}} +{{< /tabs >}} + +### Name + +Get the `ResourceName` for this board with the given name. + +{{< tabs >}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `name` [String](https://api.flutter.dev/flutter/dart-core/String-class.html) (required) + +**Returns:** + +- [ResourceName](https://flutter.viam.dev/viam_sdk/ResourceName-class.html) + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Board/getResourceName.html). + +{{% /tab %}} +{{< /tabs >}} + +### Close + +Safely shut down the resource and prevent further use. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- None. + +**Returns:** + +- None. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +await component.close() +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/board/client/index.html#viam.components.board.client.BoardClient.close). + +{{% /tab %}} +{{% tab name="Go" %}} + +**Parameters:** + +- `ctx` [(Context)](https://pkg.go.dev/context#Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. + +**Returns:** + +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +**Example:** + +```go {class="line-numbers linkable-line-numbers"} +// This example shows using Close with an arm component. +myArm, err := arm.FromRobot(machine, "my_arm") + +err = myArm.Close(ctx) +``` + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Resource). + +{{% /tab %}} +{{< /tabs >}} diff --git a/static/include/components/apis/gpiopin.md b/static/include/components/apis/gpiopin.md deleted file mode 100644 index 492fe999b6..0000000000 --- a/static/include/components/apis/gpiopin.md +++ /dev/null @@ -1,9 +0,0 @@ -| Method Name | Description | -| --------------------------------------------- | ------------------------------------------------------ | -| [`SetGPIO`](/components/board/#setgpio) | Set the output of this pin to high/low. | -| [`GetGPIO`](/components/board/#getgpio) | Get if this pin is active (high). | -| [`GetPWM`](/components/board/#getpwm) | Get the pin’s pulse-width modulation duty cycle. | -| [`SetPWM`](/components/board/#pwmfreq) | Set the pin’s pulse-width modulation duty cycle. | -| [`PWMFreq`](/components/board/#pwmfreq) | Get the pulse-width modulation frequency of this pin. | -| [`SetPWMFreq`](/components/board/#setpwmfreq) | Set the pulse-width modulation frequency of this pin. | -| [`Close`](/components/board/#close) | Safely shut down the resource and prevent further use. | diff --git a/static/include/components/apis/overrides/methods/go.base.MoveStraight.distanceMm.md b/static/include/components/apis/overrides/methods/go.base.MoveStraight.distanceMm.md new file mode 100644 index 0000000000..8dad755739 --- /dev/null +++ b/static/include/components/apis/overrides/methods/go.base.MoveStraight.distanceMm.md @@ -0,0 +1,3 @@ +The distance to move the base in millimeters. +Positive implies forwards. +Negative implies backwards. diff --git a/static/include/components/apis/overrides/methods/go.base.MoveStraight.mmPerSec.md b/static/include/components/apis/overrides/methods/go.base.MoveStraight.mmPerSec.md new file mode 100644 index 0000000000..e6b33fd028 --- /dev/null +++ b/static/include/components/apis/overrides/methods/go.base.MoveStraight.mmPerSec.md @@ -0,0 +1,3 @@ +The velocity at which to move the base in millimeters per second. +Positive implies forwards. +Negative implies backwards. diff --git a/static/include/components/apis/overrides/methods/go.base.Properties.Properties.return.md b/static/include/components/apis/overrides/methods/go.base.Properties.Properties.return.md new file mode 100644 index 0000000000..c7c5b12dff --- /dev/null +++ b/static/include/components/apis/overrides/methods/go.base.Properties.Properties.return.md @@ -0,0 +1 @@ +A structure with three fields, `WidthMeters`, `TurningRadiusMeters`, and `WheelCircumferenceMeters` representing the width, turning radius, and wheel circumference of the physical base in meters _(m)_. diff --git a/static/include/components/apis/overrides/methods/go.base.SetPower.angular.md b/static/include/components/apis/overrides/methods/go.base.SetPower.angular.md new file mode 100644 index 0000000000..f35bb99242 --- /dev/null +++ b/static/include/components/apis/overrides/methods/go.base.SetPower.angular.md @@ -0,0 +1,4 @@ +The percentage of max power of the base's angular propulsion. +In the range of -1.0 to 1.0, with 1.0 meaning 100% power. +Use the Z component of this vector to spin left or right when controlling a wheeled base. +Positive "Z" values imply spinning to the left (for built-in base models). diff --git a/static/include/components/apis/overrides/methods/go.base.SetPower.linear.md b/static/include/components/apis/overrides/methods/go.base.SetPower.linear.md new file mode 100644 index 0000000000..2ac10cf636 --- /dev/null +++ b/static/include/components/apis/overrides/methods/go.base.SetPower.linear.md @@ -0,0 +1,5 @@ +The percentage of max power of the base's linear propulsion. +In the range of -1.0 to 1.0, with 1.0 meaning 100% power. +Viam's coordinate system considers +Y to be the forward axis (+/- X right/left, +/- Z up/down), so use the Y component of this vector to move forward and backward when controlling a wheeled base. +Positive "Y" values imply moving forwards. +Negative "Y" values imply moving backwards. diff --git a/static/include/components/apis/overrides/methods/go.base.SetVelocity.angular.md b/static/include/components/apis/overrides/methods/go.base.SetVelocity.angular.md new file mode 100644 index 0000000000..0519282d6e --- /dev/null +++ b/static/include/components/apis/overrides/methods/go.base.SetVelocity.angular.md @@ -0,0 +1,2 @@ +The angular velocity in degrees per second. +Only the Z component of the vector is used for a wheeled base. diff --git a/static/include/components/apis/overrides/methods/go.base.SetVelocity.linear.md b/static/include/components/apis/overrides/methods/go.base.SetVelocity.linear.md new file mode 100644 index 0000000000..e974f2521b --- /dev/null +++ b/static/include/components/apis/overrides/methods/go.base.SetVelocity.linear.md @@ -0,0 +1,2 @@ +The linear velocity in millimeters per second. +Only the Y component of the vector is used for a wheeled base. diff --git a/static/include/components/apis/overrides/methods/go.base.Spin.angleDeg.md b/static/include/components/apis/overrides/methods/go.base.Spin.angleDeg.md new file mode 100644 index 0000000000..a9b2badbb2 --- /dev/null +++ b/static/include/components/apis/overrides/methods/go.base.Spin.angleDeg.md @@ -0,0 +1,2 @@ +The angle to spin in degrees. +Positive implies turning to the left. diff --git a/static/include/components/apis/overrides/methods/go.base.Spin.degsPerSec.md b/static/include/components/apis/overrides/methods/go.base.Spin.degsPerSec.md new file mode 100644 index 0000000000..e487262d61 --- /dev/null +++ b/static/include/components/apis/overrides/methods/go.base.Spin.degsPerSec.md @@ -0,0 +1,2 @@ +The angular velocity at which to spin in degrees per second. +Given a positive angle and a positive velocity, the base turns to the left (for built-in base models). diff --git a/static/include/components/apis/overrides/methods/go.board.AnalogByName.Analog.return.md b/static/include/components/apis/overrides/methods/go.board.AnalogByName.Analog.return.md new file mode 100644 index 0000000000..7c0a6a7c8e --- /dev/null +++ b/static/include/components/apis/overrides/methods/go.board.AnalogByName.Analog.return.md @@ -0,0 +1 @@ +An interface representing an analog pin configured and residing on the board. diff --git a/static/include/components/apis/overrides/methods/go.board.AnalogByName.name.md b/static/include/components/apis/overrides/methods/go.board.AnalogByName.name.md new file mode 100644 index 0000000000..c8bf221e2e --- /dev/null +++ b/static/include/components/apis/overrides/methods/go.board.AnalogByName.name.md @@ -0,0 +1,2 @@ +Name of the analog pin you want to retrieve. +Set as the `"name"` property [in configuration](/components/board/#digital_interrupts). diff --git a/static/include/components/apis/overrides/methods/go.board.AnalogNames.string.return.md b/static/include/components/apis/overrides/methods/go.board.AnalogNames.string.return.md new file mode 100644 index 0000000000..262b9d27a2 --- /dev/null +++ b/static/include/components/apis/overrides/methods/go.board.AnalogNames.string.return.md @@ -0,0 +1 @@ +A slice containing the `"name"` of every analog pin [configured](#supported-models) on the board. diff --git a/static/include/components/apis/overrides/methods/go.board.DigitalInterruptByName.DigitalInterrupt.return.md b/static/include/components/apis/overrides/methods/go.board.DigitalInterruptByName.DigitalInterrupt.return.md new file mode 100644 index 0000000000..0dd1df98e8 --- /dev/null +++ b/static/include/components/apis/overrides/methods/go.board.DigitalInterruptByName.DigitalInterrupt.return.md @@ -0,0 +1 @@ +An interface representing a configured interrupt on the board. diff --git a/static/include/components/apis/overrides/methods/go.board.DigitalInterruptByName.name.md b/static/include/components/apis/overrides/methods/go.board.DigitalInterruptByName.name.md new file mode 100644 index 0000000000..54b28f7a0d --- /dev/null +++ b/static/include/components/apis/overrides/methods/go.board.DigitalInterruptByName.name.md @@ -0,0 +1,2 @@ +Name of the digital interrupt you want to retrieve. +Set as the `"name"` property [in configuration](/components/board/#digital_interrupts). diff --git a/static/include/components/apis/overrides/methods/go.board.DigitalInterruptNames.string.return.md b/static/include/components/apis/overrides/methods/go.board.DigitalInterruptNames.string.return.md new file mode 100644 index 0000000000..29ca019781 --- /dev/null +++ b/static/include/components/apis/overrides/methods/go.board.DigitalInterruptNames.string.return.md @@ -0,0 +1 @@ +A slice containing the `"name"` of every interrupt [configured](#supported-models) on the board. diff --git a/static/include/components/apis/overrides/methods/go.board.GPIOPinByName.GPIOPin.return.md b/static/include/components/apis/overrides/methods/go.board.GPIOPinByName.GPIOPin.return.md new file mode 100644 index 0000000000..efffd99d08 --- /dev/null +++ b/static/include/components/apis/overrides/methods/go.board.GPIOPinByName.GPIOPin.return.md @@ -0,0 +1 @@ +An interface representing an individual GPIO pin on the board. diff --git a/static/include/components/apis/overrides/methods/go.board.GPIOPinByName.name.md b/static/include/components/apis/overrides/methods/go.board.GPIOPinByName.name.md new file mode 100644 index 0000000000..228e0b2fa6 --- /dev/null +++ b/static/include/components/apis/overrides/methods/go.board.GPIOPinByName.name.md @@ -0,0 +1,2 @@ +Pin number of the GPIO pin you want to retrieve as a `GPIOPin` interface. +Refer to the pinout diagram and data sheet of your [board model](#supported-models) for {{< glossary_tooltip term_id="pin-number" text="pin numbers" >}}. diff --git a/static/include/components/apis/overrides/methods/go.board.Get.bool.return.md b/static/include/components/apis/overrides/methods/go.board.Get.bool.return.md new file mode 100644 index 0000000000..5d32c1e278 --- /dev/null +++ b/static/include/components/apis/overrides/methods/go.board.Get.bool.return.md @@ -0,0 +1,2 @@ +If `true`, the state of the pin is high. +If `false`, the state of the pin is low. diff --git a/static/include/components/apis/overrides/methods/go.board.PWM.float64.return.md b/static/include/components/apis/overrides/methods/go.board.PWM.float64.return.md new file mode 100644 index 0000000000..377a80116b --- /dev/null +++ b/static/include/components/apis/overrides/methods/go.board.PWM.float64.return.md @@ -0,0 +1 @@ +A float [`0.0`, `1.0`] representing the percentage of time the digital signal output by this pin is in the high state relative to the interval period of the PWM signal. diff --git a/static/include/components/apis/overrides/methods/go.board.PWMFreq.uint.return.md b/static/include/components/apis/overrides/methods/go.board.PWMFreq.uint.return.md new file mode 100644 index 0000000000..d8bdb6ab00 --- /dev/null +++ b/static/include/components/apis/overrides/methods/go.board.PWMFreq.uint.return.md @@ -0,0 +1 @@ +The PWM Frequency in Hertz (Hz) (the count of PWM interval periods per second) the digital signal output by this pin is set to. diff --git a/static/include/components/apis/overrides/methods/go.board.Read.AnalogValue.return.md b/static/include/components/apis/overrides/methods/go.board.Read.AnalogValue.return.md new file mode 100644 index 0000000000..56bd4336ae --- /dev/null +++ b/static/include/components/apis/overrides/methods/go.board.Read.AnalogValue.return.md @@ -0,0 +1 @@ +The current value, including the integer `Value` of the digital signal output by the analog pin and the `Min`, `Max`, and `StepSize` of the reader. diff --git a/static/include/components/apis/overrides/methods/go.board.Set.high.md b/static/include/components/apis/overrides/methods/go.board.Set.high.md new file mode 100644 index 0000000000..8e4c72a215 --- /dev/null +++ b/static/include/components/apis/overrides/methods/go.board.Set.high.md @@ -0,0 +1,2 @@ +If `true`, set the state of the pin to high. +If `false`, set the state of the pin to low. diff --git a/static/include/components/apis/overrides/methods/go.board.SetPWM.dutyCyclePct.md b/static/include/components/apis/overrides/methods/go.board.SetPWM.dutyCyclePct.md new file mode 100644 index 0000000000..377a80116b --- /dev/null +++ b/static/include/components/apis/overrides/methods/go.board.SetPWM.dutyCyclePct.md @@ -0,0 +1 @@ +A float [`0.0`, `1.0`] representing the percentage of time the digital signal output by this pin is in the high state relative to the interval period of the PWM signal. diff --git a/static/include/components/apis/overrides/methods/go.board.SetPWMFreq.freqHz.md b/static/include/components/apis/overrides/methods/go.board.SetPWMFreq.freqHz.md new file mode 100644 index 0000000000..cf1edc74ee --- /dev/null +++ b/static/include/components/apis/overrides/methods/go.board.SetPWMFreq.freqHz.md @@ -0,0 +1 @@ +The PWM Frequency in Hertz (Hz), the count of PWM interval periods per second, to set the digital signal output by this pin to. diff --git a/static/include/components/apis/overrides/methods/go.board.SetPowerMode.duration.md b/static/include/components/apis/overrides/methods/go.board.SetPowerMode.duration.md new file mode 100644 index 0000000000..b2e9452c11 --- /dev/null +++ b/static/include/components/apis/overrides/methods/go.board.SetPowerMode.duration.md @@ -0,0 +1 @@ +If provided, the board will exit the given power mode after the specified duration. diff --git a/static/include/components/apis/overrides/methods/go.board.SetPowerMode.mode.md b/static/include/components/apis/overrides/methods/go.board.SetPowerMode.mode.md new file mode 100644 index 0000000000..6fdbb64652 --- /dev/null +++ b/static/include/components/apis/overrides/methods/go.board.SetPowerMode.mode.md @@ -0,0 +1 @@ +Options to specify power usage of the board: `boardpb.PowerMode_POWER_MODE_UNSPECIFIED`, `boardpb.PowerMode_POWER_MODE_NORMAL`, and `boardpb.PowerMode_POWER_MODE_OFFLINE_DEEP`. diff --git a/static/include/components/apis/overrides/methods/go.board.StreamTicks.ch.md b/static/include/components/apis/overrides/methods/go.board.StreamTicks.ch.md new file mode 100644 index 0000000000..cc539a8d36 --- /dev/null +++ b/static/include/components/apis/overrides/methods/go.board.StreamTicks.ch.md @@ -0,0 +1 @@ +The channel to stream Ticks, structs containing `Name`, `High`, and `TimestampNanosec` fields. diff --git a/static/include/components/apis/overrides/methods/go.board.StreamTicks.interrupts.md b/static/include/components/apis/overrides/methods/go.board.StreamTicks.interrupts.md new file mode 100644 index 0000000000..63113a53ca --- /dev/null +++ b/static/include/components/apis/overrides/methods/go.board.StreamTicks.interrupts.md @@ -0,0 +1 @@ +Slice of digital interrupts to receive ticks from. diff --git a/static/include/components/apis/overrides/methods/go.board.Value.int64.return.md b/static/include/components/apis/overrides/methods/go.board.Value.int64.return.md new file mode 100644 index 0000000000..7925c30ad1 --- /dev/null +++ b/static/include/components/apis/overrides/methods/go.board.Value.int64.return.md @@ -0,0 +1 @@ +The amount of ticks that have occurred. diff --git a/static/include/components/apis/overrides/methods/go.board.Write.value.md b/static/include/components/apis/overrides/methods/go.board.Write.value.md new file mode 100644 index 0000000000..0bbf58860d --- /dev/null +++ b/static/include/components/apis/overrides/methods/go.board.Write.value.md @@ -0,0 +1 @@ +Value to write to the pin. diff --git a/static/include/components/apis/overrides/protos/board.AnalogByName.md b/static/include/components/apis/overrides/protos/board.AnalogByName.md new file mode 100644 index 0000000000..94928ff12d --- /dev/null +++ b/static/include/components/apis/overrides/protos/board.AnalogByName.md @@ -0,0 +1 @@ +Get an [`Analog`](#analogs) by `name`. diff --git a/static/include/components/apis/overrides/protos/board.AnalogNames.md b/static/include/components/apis/overrides/protos/board.AnalogNames.md new file mode 100644 index 0000000000..dcbf7bdd27 --- /dev/null +++ b/static/include/components/apis/overrides/protos/board.AnalogNames.md @@ -0,0 +1 @@ +Get the name of every [`Analog`](#analogs) configured and residing on the board. diff --git a/static/include/components/apis/overrides/protos/board.AnalogReaderNames.md b/static/include/components/apis/overrides/protos/board.AnalogReaderNames.md deleted file mode 100644 index 4e002d6b80..0000000000 --- a/static/include/components/apis/overrides/protos/board.AnalogReaderNames.md +++ /dev/null @@ -1 +0,0 @@ -Get the name of every [`AnalogReader`](#analogs) configured and residing on the board. diff --git a/static/include/components/apis/overrides/protos/board.ReadAnalogReader.md b/static/include/components/apis/overrides/protos/board.ReadAnalogReader.md deleted file mode 100644 index 2a3649fb0f..0000000000 --- a/static/include/components/apis/overrides/protos/board.ReadAnalogReader.md +++ /dev/null @@ -1 +0,0 @@ -Get an [`AnalogReader`](#analogs) by `name`. diff --git a/static/include/components/apis/overrides/protos/board.Value.md b/static/include/components/apis/overrides/protos/board.Value.md index 9a307a3f50..a10439dc3d 100644 --- a/static/include/components/apis/overrides/protos/board.Value.md +++ b/static/include/components/apis/overrides/protos/board.Value.md @@ -1,4 +1 @@ Get the current value of this interrupt. -If a post processor function has been added with [`AddPostProcessor()`](#addpostprocessor), it will be applied to this value before it is returned. - -Calculation of value differs between the `"type"` of interrupt [configured](#digital_interrupts): diff --git a/static/include/components/apis/overrides/protos/board.WriteAnalog.md b/static/include/components/apis/overrides/protos/board.Write.md similarity index 100% rename from static/include/components/apis/overrides/protos/board.WriteAnalog.md rename to static/include/components/apis/overrides/protos/board.Write.md From 51f3e398290535ba0e2195401be3025d460bb1aa Mon Sep 17 00:00:00 2001 From: andf-viam <132301587+andf-viam@users.noreply.github.com> Date: Mon, 8 Jul 2024 16:26:30 -0400 Subject: [PATCH 09/22] DOCS-2425: Merge autogen: mlmodel, motion (#3091) --- .github/workflows/update_sdk_methods.py | 32 +- docs/appendix/apis/_index.md | 4 +- docs/services/ml/deploy/_index.md | 199 +--- docs/services/motion/_index.md | 839 +--------------- .../services/apis/generated/mlmodel-table.md | 8 + .../services/apis/generated/mlmodel.md | 237 +++++ .../services/apis/generated/motion-table.md | 13 + .../include/services/apis/generated/motion.md | 896 ++++++++++++++++++ static/include/services/apis/ml.md | 7 - static/include/services/apis/motion.md | 12 - .../go.mlmodel.Infer.Tensors.return.md | 1 + .../methods/go.mlmodel.Infer.tensors.md | 1 + .../go.mlmodel.Metadata.MLMetadata.return.md | 1 + .../go.motion.GetPose.PoseInFrame.return.md | 1 + .../go.motion.GetPose.componentName.md | 1 + .../go.motion.GetPose.destinationFrame.md | 1 + ...o.motion.GetPose.supplementalTransforms.md | 10 + ...istPlanStatuses.PlanStatusWithID.return.md | 1 + .../methods/go.motion.ListPlanStatuses.req.md | 4 + .../methods/go.motion.Move.bool.return.md | 1 + .../methods/go.motion.Move.componentName.md | 3 + .../methods/go.motion.Move.constraints.md | 2 + .../methods/go.motion.Move.destination.md | 4 + .../methods/go.motion.Move.worldState.md | 21 + ...o.motion.MoveOnGlobe.ExecutionID.return.md | 1 + .../methods/go.motion.MoveOnGlobe.req.md | 14 + .../go.motion.MoveOnMap.ExecutionID.return.md | 1 + .../methods/go.motion.MoveOnMap.req.md | 14 + ...otion.PlanHistory.PlanWithStatus.return.md | 1 + .../methods/go.motion.PlanHistory.req.md | 6 + .../methods/go.motion.StopPlan.req.md | 4 + .../python.motion.get_pose.component_name.md | 1 + ...ython.motion.get_pose.destination_frame.md | 1 + ...motion.get_pose.supplemental_transforms.md | 12 + .../python.motion.move.component_name.md | 3 + .../methods/python.motion.move.constraints.md | 2 + .../methods/python.motion.move.destination.md | 2 + .../methods/python.motion.move.return.md | 1 + .../methods/python.motion.move.world_state.md | 21 + ...hon.motion.move_on_globe.component_name.md | 1 + ...thon.motion.move_on_globe.configuration.md | 8 + ...python.motion.move_on_globe.destination.md | 1 + .../python.motion.move_on_globe.heading.md | 1 + ...tion.move_on_globe.movement_sensor_name.md | 1 + .../python.motion.move_on_globe.obstacles.md | 1 + .../python.motion.move_on_globe.return.md | 1 + ...ython.motion.move_on_map.component_name.md | 1 + ...python.motion.move_on_map.configuration.md | 8 + .../python.motion.move_on_map.destination.md | 1 + .../python.motion.move_on_map.obstacles.md | 1 + .../python.motion.move_on_map.return.md | 1 + ...on.motion.move_on_map.slam_service_name.md | 1 + .../python.motion.stop_plan.component_name.md | 3 + 53 files changed, 1346 insertions(+), 1067 deletions(-) create mode 100644 static/include/services/apis/generated/mlmodel-table.md create mode 100644 static/include/services/apis/generated/mlmodel.md create mode 100644 static/include/services/apis/generated/motion-table.md create mode 100644 static/include/services/apis/generated/motion.md delete mode 100644 static/include/services/apis/ml.md delete mode 100644 static/include/services/apis/motion.md create mode 100644 static/include/services/apis/overrides/methods/go.mlmodel.Infer.Tensors.return.md create mode 100644 static/include/services/apis/overrides/methods/go.mlmodel.Infer.tensors.md create mode 100644 static/include/services/apis/overrides/methods/go.mlmodel.Metadata.MLMetadata.return.md create mode 100644 static/include/services/apis/overrides/methods/go.motion.GetPose.PoseInFrame.return.md create mode 100644 static/include/services/apis/overrides/methods/go.motion.GetPose.componentName.md create mode 100644 static/include/services/apis/overrides/methods/go.motion.GetPose.destinationFrame.md create mode 100644 static/include/services/apis/overrides/methods/go.motion.GetPose.supplementalTransforms.md create mode 100644 static/include/services/apis/overrides/methods/go.motion.ListPlanStatuses.PlanStatusWithID.return.md create mode 100644 static/include/services/apis/overrides/methods/go.motion.ListPlanStatuses.req.md create mode 100644 static/include/services/apis/overrides/methods/go.motion.Move.bool.return.md create mode 100644 static/include/services/apis/overrides/methods/go.motion.Move.componentName.md create mode 100644 static/include/services/apis/overrides/methods/go.motion.Move.constraints.md create mode 100644 static/include/services/apis/overrides/methods/go.motion.Move.destination.md create mode 100644 static/include/services/apis/overrides/methods/go.motion.Move.worldState.md create mode 100644 static/include/services/apis/overrides/methods/go.motion.MoveOnGlobe.ExecutionID.return.md create mode 100644 static/include/services/apis/overrides/methods/go.motion.MoveOnGlobe.req.md create mode 100644 static/include/services/apis/overrides/methods/go.motion.MoveOnMap.ExecutionID.return.md create mode 100644 static/include/services/apis/overrides/methods/go.motion.MoveOnMap.req.md create mode 100644 static/include/services/apis/overrides/methods/go.motion.PlanHistory.PlanWithStatus.return.md create mode 100644 static/include/services/apis/overrides/methods/go.motion.PlanHistory.req.md create mode 100644 static/include/services/apis/overrides/methods/go.motion.StopPlan.req.md create mode 100644 static/include/services/apis/overrides/methods/python.motion.get_pose.component_name.md create mode 100644 static/include/services/apis/overrides/methods/python.motion.get_pose.destination_frame.md create mode 100644 static/include/services/apis/overrides/methods/python.motion.get_pose.supplemental_transforms.md create mode 100644 static/include/services/apis/overrides/methods/python.motion.move.component_name.md create mode 100644 static/include/services/apis/overrides/methods/python.motion.move.constraints.md create mode 100644 static/include/services/apis/overrides/methods/python.motion.move.destination.md create mode 100644 static/include/services/apis/overrides/methods/python.motion.move.return.md create mode 100644 static/include/services/apis/overrides/methods/python.motion.move.world_state.md create mode 100644 static/include/services/apis/overrides/methods/python.motion.move_on_globe.component_name.md create mode 100644 static/include/services/apis/overrides/methods/python.motion.move_on_globe.configuration.md create mode 100644 static/include/services/apis/overrides/methods/python.motion.move_on_globe.destination.md create mode 100644 static/include/services/apis/overrides/methods/python.motion.move_on_globe.heading.md create mode 100644 static/include/services/apis/overrides/methods/python.motion.move_on_globe.movement_sensor_name.md create mode 100644 static/include/services/apis/overrides/methods/python.motion.move_on_globe.obstacles.md create mode 100644 static/include/services/apis/overrides/methods/python.motion.move_on_globe.return.md create mode 100644 static/include/services/apis/overrides/methods/python.motion.move_on_map.component_name.md create mode 100644 static/include/services/apis/overrides/methods/python.motion.move_on_map.configuration.md create mode 100644 static/include/services/apis/overrides/methods/python.motion.move_on_map.destination.md create mode 100644 static/include/services/apis/overrides/methods/python.motion.move_on_map.obstacles.md create mode 100644 static/include/services/apis/overrides/methods/python.motion.move_on_map.return.md create mode 100644 static/include/services/apis/overrides/methods/python.motion.move_on_map.slam_service_name.md create mode 100644 static/include/services/apis/overrides/methods/python.motion.stop_plan.component_name.md diff --git a/.github/workflows/update_sdk_methods.py b/.github/workflows/update_sdk_methods.py index e37ff0ec67..b8874a2408 100755 --- a/.github/workflows/update_sdk_methods.py +++ b/.github/workflows/update_sdk_methods.py @@ -412,6 +412,8 @@ "bool": "https://docs.python.org/3/library/stdtypes.html#boolean-type-bool", "datetime.datetime": "https://docs.python.org/3/library/datetime.html", "datetime.timedelta": "https://docs.python.org/3/library/datetime.html#timedelta-objects", + ## Third-party data types: + "numpy.typing.NDArray": "https://numpy.org/doc/stable/reference/typing.html#numpy.typing.NDArray", ## Viam-specific data types: "viam.proto.app.OrganizationMember": "https://python.viam.dev/autoapi/viam/proto/app/index.html#viam.proto.app.OrganizationMember", "viam.proto.app.OrganizationInvite": "https://python.viam.dev/autoapi/viam/proto/app/index.html#viam.proto.app.OrganizationInvite", @@ -1580,8 +1582,9 @@ def format_method_usage(parsed_usage_string, go_method_name, resource, path_to_m ## If we have a param description override, use that. If not, skip: if param_or_return_description != '': - ## Add a trailing period if it is missing, either from upstream or from override file: - if not param_or_return_description.endswith('.'): + ## Add a trailing period if it is missing, either from upstream or from override file, + ## but skip doing so if the copy instead ends with an HTML tag (like a closing '' tag): + if not param_or_return_description.endswith('.') and not param_or_return_description.endswith('>'): param_or_return_description = param_or_return_description + '.' ## Format returns: @@ -1816,8 +1819,9 @@ def write_markdown(type, names, methods): if param_description: - ## Add a trailing period if it is missing, either from upstream or from override file: - if not param_description.endswith('.'): + ## Add a trailing period if it is missing, either from upstream or from override file, + ## but skip doing so if the copy instead ends with an HTML tag (like a closing '' tag): + if not param_description.endswith('.') and not param_description.endswith('>'): param_description = param_description + '.' output_file.write(f": {param_description}") @@ -1861,8 +1865,9 @@ def write_markdown(type, names, methods): if return_description: - ## Add a trailing period if it is missing, either from upstream or from override file: - if not return_description.endswith('.'): + ## Add a trailing period if it is missing, either from upstream or from override file, + ## but skip doing so if the copy instead ends with an HTML tag (like a closing '' tag): + if not return_description.endswith('.') and not return_description.endswith('>'): return_description = return_description + '.' output_file.write(f": {return_description}\n") @@ -1881,8 +1886,9 @@ def write_markdown(type, names, methods): output_file.write(f"- ({raises_type})") if "raises_description" in raises_object[raises_type]: raises_description= raises_object[raises_type]["raises_description"] - ## Add a trailing period if it is missing: - if not raises_description.endswith('.'): + ## Add a trailing period if it is missing, either from upstream or from override file, + ## but skip doing so if the copy instead ends with an HTML tag (like a closing '' tag): + if not raises_description.endswith('.') and not raises_description.endswith('>'): raises_description = raises_description + '.' output_file.write(f": {raises_description}\n") @@ -2062,8 +2068,9 @@ def write_markdown(type, names, methods): if param_description: - ## Add a trailing period if it is missing, either from upstream or from override file: - if not param_description.endswith('.'): + ## Add a trailing period if it is missing, either from upstream or from override file, + ## but skip doing so if the copy instead ends with an HTML tag (like a closing '' tag): + if not param_description.endswith('.') and not param_description.endswith('>'): param_description = param_description + '.' output_file.write(f": {param_description}") @@ -2115,8 +2122,9 @@ def write_markdown(type, names, methods): if return_description: - ## Add a trailing period if it is missing, either from upstream or from override file: - if not return_description.endswith('.'): + ## Add a trailing period if it is missing, either from upstream or from override file, + ## but skip doing so if the copy instead ends with an HTML tag (like a closing '' tag): + if not return_description.endswith('.') and not return_description.endswith('>'): return_description = return_description + '.' output_file.write(f": {return_description}\n") diff --git a/docs/appendix/apis/_index.md b/docs/appendix/apis/_index.md index 86350171a4..97cd5004fa 100644 --- a/docs/appendix/apis/_index.md +++ b/docs/appendix/apis/_index.md @@ -184,13 +184,13 @@ The [generic service](/services/generic/) supports the following methods: The [ML model service](/services/ml/) supports the following methods: -{{< readfile "/static/include/services/apis/ml.md" >}} +{{< readfile "/static/include/services/apis/generated/mlmodel-table.md" >}} ### Motion The [motion service](/services/motion/) supports the following methods: -{{< readfile "/static/include/services/apis/motion.md" >}} +{{< readfile "/static/include/services/apis/generated/motion-table.md" >}} ### Navigation diff --git a/docs/services/ml/deploy/_index.md b/docs/services/ml/deploy/_index.md index 929b5d7a9d..b79c2f4a56 100644 --- a/docs/services/ml/deploy/_index.md +++ b/docs/services/ml/deploy/_index.md @@ -92,7 +92,7 @@ The model package config looks like this: The MLModel service supports the following methods: -{{< readfile "/static/include/services/apis/ml.md" >}} +{{< readfile "/static/include/services/apis/generated/mlmodel-table.md" >}} {{% alert title="Tip" color="tip" %}} @@ -101,202 +101,7 @@ Go to your machine's **CONNECT** tab's **Code sample** page on the [Viam app](ht {{% /alert %}} -### Infer - -Take an already ordered input tensor as an array, make an inference on the model, and return an output tensor map. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `input_tensors` [(Dict[str, NDArray])](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html): A dictionary of input flat tensors, as specified in the metadata. -- `timeout` [(Optional\[float\])](https://docs.python.org/library/typing.html#typing.Optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. - -**Returns:** - -- [(`Dict[str, NDArray]`)](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.html): A dictionary of output flat tensors as specified in the metadata, after being run through an inference engine. - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/services/mlmodel/client/index.html#viam.services.mlmodel.client.MLModelClient.infer). - -```python {class="line-numbers linkable-line-numbers"} -import numpy as np - -my_mlmodel = MLModelClient.from_robot(robot=robot, name="my_mlmodel_service") - -nd_array = np.array([1, 2, 3], dtype=np.float64) -input_tensors = {"0": nd_array} - -output_tensors = await my_mlmodel.infer(input_tensors) -``` - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `ctx` [(Context)](https://pkg.go.dev/context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. -- `tensors` [(ml.Tensors)](https://pkg.go.dev/go.viam.com/rdk@v0.11.1/ml#Tensors): The input map of tensors, as specified in the metadata. - -**Returns:** - -- [(ml.Tensors)](https://pkg.go.dev/go.viam.com/rdk@v0.11.1/ml#Tensors): The output map of tensors, as specified in the metadata, after being run through an inference engine. -- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/services/mlmodel#Service). - -```go {class="line-numbers linkable-line-numbers"} -input_tensors := ml.Tensors{"0": tensor.New(tensor.WithShape(1, 2, 3), tensor.WithBacking(6))} - -output_tensors, err := myMLModel.Infer(context.Background(), input_tensors) -``` - -{{% /tab %}} -{{< /tabs >}} - -### Metadata - -Get the metadata: name, data type, expected tensor/array shape, inputs, and outputs associated with the ML model. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `timeout` [(Optional\[float\])](https://docs.python.org/library/typing.html#typing.Optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. - -**Returns:** - -- [(`Metadata`)](https://python.viam.dev/autoapi/viam/gen/service/mlmodel/v1/mlmodel_pb2/index.html#viam.gen.service.mlmodel.v1.mlmodel_pb2.Metadata): Name, type, expected tensor/array shape, inputs, and outputs associated with the ML model. - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/services/mlmodel/client/index.html#viam.services.mlmodel.client.MLModelClient.metadata). - -```python {class="line-numbers linkable-line-numbers"} -my_mlmodel = MLModelClient.from_robot(robot=robot, name="my_mlmodel_service") - -metadata = await my_mlmodel.metadata() -``` - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `ctx` [(Context)](https://pkg.go.dev/context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. - -**Returns:** - -- [(MLMetadata)](https://pkg.go.dev/go.viam.com/rdk@v0.11.1/services/mlmodel#MLMetadata): Struct containing the metadata of the model file, such as the name of the model, what kind of model it is, and the expected tensor/array shape and types of the inputs and outputs of the model. -- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/services/mlmodel#Service). - -```go {class="line-numbers linkable-line-numbers"} -metadata, err := myMLModel.Metadata(context.Background()) -``` - -{{% /tab %}} -{{< /tabs >}} - -### DoCommand - -Execute model-specific commands that are not otherwise defined by the service API. -For built-in service models, any model-specific commands available are covered with each model's documentation. -If you are implementing your own navigation service and add features that have no built-in API method, you can access them with `DoCommand`. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `command` [(Mapping[str, ValueTypes])](https://docs.python.org/3/library/stdtypes.html#typesmapping): The command to execute. -- `timeout` [(Optional\[float\])](https://docs.python.org/library/typing.html#typing.Optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. - -**Returns:** - -- [(Mapping[str, ValueTypes])](https://docs.python.org/3/library/stdtypes.html#typesmapping): Result of the executed command. - -**Raises:** - -- `NotImplementedError`: Raised if the Resource does not support arbitrary commands. - -```python {class="line-numbers linkable-line-numbers"} -my_mlmodel = MLModelClient.from_robot(robot=robot, name="my_mlmodel_service") - -my_command = { - "command": "dosomething", - "someparameter": 52 -} - -await my_mlmodel.do_command(my_command) -``` - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/services/mlmodel/client/index.html#viam.services.mlmodel.client.MLModelClient.do_command). - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `ctx` [(Context)](https://pkg.go.dev/context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. -- `cmd` [(map\[string\]interface{})](https://go.dev/blog/maps): The command to execute. - -**Returns:** - -- [(map\[string\]interface{})](https://go.dev/blog/maps): Result of the executed command. -- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. - -```go {class="line-numbers linkable-line-numbers"} -resp, err := myMLModel.DoCommand(context.Background(), map[string]interface{}{"command": "dosomething", "someparameter": 52}) -``` - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Resource). - -{{% /tab %}} -{{< /tabs >}} - -### Close - -Safely shut down the resource and prevent further use. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- None - -**Returns:** - -- None - -```python {class="line-numbers linkable-line-numbers"} -my_mlmodel = MLModelClient.from_robot(robot, "my_mlmodel_service") - -await my_mlmodel.close() -``` - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/services/mlmodel/client/index.html#viam.services.mlmodel.client.MLModelClient.close). - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `ctx` [(Context)](https://pkg.go.dev/context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. - -**Returns:** - -- [(error)](https://pkg.go.dev/builtin#error) : An error, if one occurred. - -```go {class="line-numbers linkable-line-numbers"} -err := myMLModel.Close(context.Background()) -``` - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Resource). - -{{% /tab %}} -{{< /tabs >}} +{{< readfile "/static/include/services/apis/generated/mlmodel.md" >}} ## Use the ML model service with the Viam Python SDK diff --git a/docs/services/motion/_index.md b/docs/services/motion/_index.md index 6ebae638a8..f377d0a968 100644 --- a/docs/services/motion/_index.md +++ b/docs/services/motion/_index.md @@ -56,7 +56,7 @@ Use the name `"builtin"` to access the built-in motion service in your code with The motion service supports the following methods: -{{< readfile "/static/include/services/apis/motion.md" >}} +{{< readfile "/static/include/services/apis/generated/motion-table.md" >}} {{% alert title="Tip" color="tip" %}} @@ -73,842 +73,7 @@ For examples showing how to construct the `ResourceName`, see the code samples b {{% /alert %}} -### Move - -The `Move` method is the primary way to move multiple components, or to move any object to any other location. -Given a destination pose and a component to move to that destination, `Move` will: - -1. Construct a full kinematic chain from goal to destination including all movable components in between. -2. Solve that chain to move the specified component frame to the destination while adhering to any constraints. -3. Execute that movement to move the actual machine. -4. Return whether or not this process succeeded. - -The motion service takes the volumes associated with all configured machine components (local and remote) into account for each request to ensure that the machine does not collide with itself or other known objects. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `component_name` ([ResourceName](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.ResourceName)): The `ResourceName` of the piece of the robot that should arrive at the destination. - Note that `move` moves the distal end of the component to the destination. - For example, when moving a robotic arm, the piece that will arrive at the destination is the end effector attachment point, not the base of the arm. - -- `destination` ([PoseInFrame](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.PoseInFrame)): - Describes where the `component_name` frame should be moved to. - Can be any pose, from the perspective of any component whose location is configured as a [`frame`](../frame-system/). - - Note that the destination pose is relative to the distal end of the specified frame. - This means that if the `destination` is the same as the `component_name` frame, for example an arm's frame, then a pose of {X: 10, Y: 0, Z: 0} will move that arm’s end effector by 10 mm in the local X direction. - -- `world_state` ([WorldState](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.WorldState)) (_optional_): Data structure specifying information about the world around the machine. - Used to augment the motion solving process. - `world_state` includes obstacles and transforms: - - - **Obstacles**: Geometries located at a pose relative to some frame. - When solving a motion plan with movable frames that contain inherent geometries, the solved path is constrained such that none of those inherent geometries intersect with the obstacles. - Important considerations: - - If a motion begins with a component already in collision with an obstacle, collisions between that specific component and that obstacle will not be checked. - - The motion service assumes that obstacles are static. - If a worldstate obstacle is physically attached to a part of the robot such that it will move with the robot, specify it with _transforms_. - - Obstacles are defined by a pose and a [geometry](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.Geometry) with dimensions. - The pose location is the point at the center of the geometry. - - Obstacle locations are defined with respect to the _origin_ of the specified frame. - Their poses are relative to the _origin_ of the specified frame. - An obstacle associated with the frame of an arm with a pose of {X: 0, Y: 0, Z: -10} is interpreted as being 10mm below the base of the arm, not 10mm below the end effector. - This is different from `destination` and `component_name`, where poses are relative to the distal end of a frame. - - **Transforms**: A list of `PoseInFrame` messages that specify other transformations to temporarily add to the frame system at solve time. - Transforms can be used to account for geometries that are attached to the robot but not configured as robot components. - For example, you could use a transform to represent the volume of a marker held in your machine's gripper. - Transforms are not added to the config or carried into later processes. - -- `constraints` ([Constraints](https://python.viam.dev/autoapi/viam/proto/service/motion/index.html#viam.proto.service.motion.Constraints)) (_optional_): Pass in [motion constraints](./constraints/). - By default, motion is unconstrained with the exception of obstacle avoidance. - -- `extra` (Mapping[str, Any]) (_optional_): A generic struct, containing extra options to pass to the underlying RPC call. - -**Returns:** - -- [(bool)](https://docs.python.org/3/library/stdtypes.html#bltin-boolean-values): Whether the move was successful. - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/services/motion/index.html#viam.services.motion.MotionClient.move). - -```python {class="line-numbers linkable-line-numbers"} -motion = MotionClient.from_robot(robot=robot, name="builtin") - -# Assumes a gripper configured with name "my_gripper" on the machine -gripper_name = Gripper.get_resource_name("my_gripper") -my_frame = "my_gripper_offset" - -goal_pose = Pose(x=0, y=0, z=300, o_x=0, o_y=0, o_z=1, theta=0) - -# Move the gripper -moved = await motion.move(component_name=gripper_name, - destination=PoseInFrame(reference_frame="myFrame", - pose=goal_pose), - world_state=worldState, - constraints={}, - extra={}) -``` - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `ctx` [(Context)](https://pkg.go.dev/context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. - -- `componentName` ([resource.Name](https://pkg.go.dev/go.viam.com/rdk/resource#Name)): The `resource.Name` of the piece of the robot that should arrive at the destination. - Note that `Move` moves the distal end of the component to the destination. - For example, when moving a robotic arm, the piece that will arrive at the destination is the end effector attachment point, not the base of the arm. - -- `destination` ([PoseInFrame](https://pkg.go.dev/go.viam.com/rdk/referenceframe#PoseInFrame)): - Describes where the `component_name` should end up. - Can be any pose, from the perspective of any component whose location is configured as a [`frame`](../frame-system/). - - Note that the destination pose is relative to the distal end of the specified frame. - This means that if the `destination` is the same as the `component_name` frame, for example an arm's frame, then a pose of {X: 10, Y: 0, Z: 0} will move that arm’s end effector by 10 mm in the local X direction. - -- `worldState` ([WorldState](https://pkg.go.dev/go.viam.com/rdk/referenceframe#WorldState)): Data structure specifying information about the world around the machine. - Used to augment the motion solving process. - `worldState` includes obstacles and transforms: - - - **Obstacles**: Geometries located at a pose relative to some frame. - When solving a motion plan with movable frames that contain inherent geometries, the solved path is constrained such that none of those inherent geometries intersect with the obstacles. - Important considerations: - - If a motion begins with a component already in collision with an obstacle, collisions between that specific component and that obstacle will not be checked. - - The motion service assumes that obstacles are static. - If a worldstate obstacle is physically attached to a part of the robot such that it will move with the robot, specify it with _transforms_. - - Obstacles are defined by a [(pose)](https://pkg.go.dev/go.viam.com/rdk/spatialmath#Pose) and a [(geometry)](https://pkg.go.dev/go.viam.com/rdk/spatialmath#Geometry) with dimensions. - The pose location is the point at the center of the geometry. - - Obstacle locations are defined with respect to the _origin_ of the specified frame. - Their poses are relative to the _origin_ of the specified frame. - An obstacle associated with the frame of an arm with a pose of {X: 0, Y: 0, Z: -10} is interpreted as being 10mm below the base of the arm, not 10mm below the end effector. - This is different from `destination` and `componentName`, where poses are relative to the distal end of a frame. - - **Transforms**: A list of `PoseInFrame` messages that specify other transformations to temporarily add to the frame system at solve time. - Transforms can be used to account for geometries that are attached to the robot but not configured as robot components. - For example, you could use a transform to represent the volume of a marker held in your machine's gripper. - Transforms are not added to the config or carried into later processes. - -- `constraints` ([Constraints](https://pkg.go.dev/go.viam.com/api/service/motion/v1#Constraints)): Pass in optional [motion constraints](./constraints/). - By default, motion is unconstrained with the exception of obstacle avoidance. - -- `extra` [(map\[string\]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. - -**Returns:** - -- [(bool)](https://pkg.go.dev/builtin#bool): Whether the move was successful. -- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/services/motion#Service). - -```go {class="line-numbers linkable-line-numbers"} -motionService, err := motion.FromRobot(robot, "builtin") - -// Assumes a gripper configured with name "my_gripper" on the machine -gripperName := gripper.Named("my_gripper") -myFrame := "my_gripper_offset" - -goalPose := referenceframe.PoseInFrame(0, 0, 300, 0, 0, 1, 0) - -// Move the gripper -moved, err := motionService.Move(context.Background(), gripperName, goalPose, worldState, nil, nil) -``` - -{{% /tab %}} -{{< /tabs >}} - -### GetPose - -`GetPose` gets the location and orientation of a component within the [frame system](../frame-system/). -The return type of this function is a `PoseInFrame` describing the pose of the specified component with respect to the specified destination frame. -You can use the `supplemental_transforms` argument to augment the machine's existing frame system with supplemental frames. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `component_name` ([ResourceName](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.ResourceName)): The `ResourceName` of the piece of the machine whose pose is returned. - -- `destination_frame` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)): - The name of the frame with respect to which the component's pose is reported. - -- `supplemental_transforms` ([Optional\[List\[Transforms\]\]](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.Transform)) (_optional_): A list of `Transform` objects. - A `Transform` represents an additional frame which is added to the machine's frame system. - It consists of the following fields: - - - `pose_in_observer_frame`: Provides the relationship between the frame being added and another frame. - - `physical_object`: An optional `Geometry` can be added to the frame being added. - - `reference_frame`: Specifies the name of the frame which will be added to the frame system. - - When `supplemental_transforms` are provided, a frame system is created within the context of the `GetPose` function. - This new frame system builds off the machine's frame system and incorporates the `Transform`s provided. - If the result of adding the `Transform`s results in a disconnected frame system, an error is thrown. - -- `extra` (Mapping[str, Any]) (_optional_): A generic struct, containing extra options to pass to the underlying RPC call. - -**Returns:** - -- [(PoseInFrame)](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.PoseInFrame): The pose of the component. - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/services/motion/index.html#viam.services.motion.MotionClient.get_pose). - -The following code example gets the pose of the tip of a [gripper](/components/gripper/) named `my_gripper` which is attached to the end of an arm, in the "world" `reference_frame`: - -```python {class="line-numbers linkable-line-numbers"} -from viam.components.gripper import Gripper -from viam.services.motion import MotionClient - -# Assume that the connect function is written and will return a valid machine. -robot = await connect() - -motion = MotionClient.from_robot(robot=robot, name="builtin") -gripperName = Gripper.get_resource_name("my_gripper") -gripperPoseInWorld = await motion.get_pose(component_name=gripperName, - destination_frame="world") -``` - -For a more complicated example, take the same scenario and get the pose of the same gripper with respect to an object situated at a location (100, 200, 0) relative to the "world" frame: - -```python {class="line-numbers linkable-line-numbers"} -from viam.components.gripper import Gripper -from viam.services.motion import MotionClient -from viam.proto.common import Transform, PoseInFrame, Pose - -# Assume that the connect function is written and will return a valid machine. -robot = await connect() - -motion = MotionClient.from_robot(robot=robot, name="builtin") -objectPose = Pose(x=100, y=200, z=0, o_x=0, o_y=0, o_z=1, theta=0) -objectPoseInFrame = PoseInFrame(reference_frame="world", pose=objectPose) -objectTransform = Transform(reference_frame="object", - pose_in_observer_frame=objectPoseInFrame) -gripperName = Gripper.get_resource_name("my_gripper") -gripperPoseInObjectFrame = await motion.get_pose( - component_name=gripperName, - destination_frame="world", - supplemental_transforms=objectTransform -) -``` - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `ctx` [(Context)](https://pkg.go.dev/context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. - -- `componentName` ([resource.Name](https://pkg.go.dev/go.viam.com/rdk/resource#Name)): The `resource.Name` of the piece of the machine whose pose is returned. - -- `destinationFrame` ([string](https://pkg.go.dev/builtin#string)): - The name of the frame with respect to which the component's pose is reported. - -- `supplementalTransforms` ([LinkInFrame](https://pkg.go.dev/go.viam.com/rdk/referenceframe#LinkInFrame)): An optional list of `LinkInFrame`s. - A `LinkInFrame` represents an additional frame which is added to the machine's frame system. - It consists of: - - - a `PoseInFrame`: Provides the relationship between the frame being added and another frame. - - `Geometry`: An optional `Geometry` can be added to the frame being added. - When `supplementalTransforms` are provided, a frame system is created within the context of the `GetPose` function. - This new frame system builds off the machine's frame system and incorporates the `LinkInFrame`s provided. - If the result of adding the `LinkInFrame`s results in a disconnected frame system, an error is thrown. - -- `extra` [(map\[string\]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. - -**Returns:** - -- [(PoseInFrame)](https://pkg.go.dev/go.viam.com/rdk/referenceframe#PoseInFrame): The pose of the component. -- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/services/motion#Service). - -```go {class="line-numbers linkable-line-numbers"} -// Insert code to connect to your machine. -// (see CONNECT tab of your machine's page in the Viam app) - -// Assumes a gripper configured with name "my_gripper" on the machine -gripperName := gripper.Named("my_gripper") -myFrame := "my_gripper_offset" - - // Access the motion service -motionService, err := motion.FromRobot(robot, "builtin") -if err != nil { - logger.Fatal(err) -} - -myArmMotionPose, err := motionService.GetPose(context.Background(), my_gripper, referenceframe.World, nil, nil) -if err != nil { - logger.Fatal(err) -} -logger.Info("Position of myArm from the motion service:", myArmMotionPose.Pose().Point()) -logger.Info("Orientation of myArm from the motion service:", myArmMotionPose.Pose().Orientation()) -``` - -{{% /tab %}} -{{< /tabs >}} - -### MoveOnMap - -Move a [base](/components/base/) component to a destination [`Pose`](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.Pose) on a {{< glossary_tooltip term_id="slam" text="SLAM" >}} map. - -`MoveOnMap()` is non blocking, meaning the motion service will move the component to the destination [`Pose`](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.Pose) after `MoveOnMap()` returns. - -Each successful `MoveOnMap()` call returns a unique `ExecutionID` which you can use to identify all plans generated during the `MoveOnMap()` call. - -{{< alert title="Info" color="info" >}} -If you specify a goal pose and the robot's current position is already within the set `PlanDeviationM`, then `MoveOnMap` returns an error. -{{< /alert >}} - -You can monitor the progress of the `MoveOnMap()` call by querying `GetPlan()` and `ListPlanStatuses()`. - -Use the machine's position reported by the {{< glossary_tooltip term_id="slam" text="SLAM" >}} service to check the location of the machine. - -`MoveOnMap()` is intended for use with the [navigation service](/services/navigation/), providing autonomous indoor navigation for rover [bases](/components/base/). - -{{< alert title="Requirements" color="info" >}} -To use `MoveOnMap()`, your [SLAM service](/services/slam/) must implement `GetPointCloudMap()` and `GetPosition()` - -Make sure the [SLAM service](/services/slam/) you use alongside the this motion service supports the following methods in its {{< glossary_tooltip term_id="model" text="model's" >}} implementation of the [SLAM service API](/services/slam/#api): - -- It must support `GetPointCloudMap()` to report the SLAM map as a pointcloud. -- It must support `GetPosition()` to report the machine's current location on the SLAM map. - {{< /alert >}} - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `component_name` ([ResourceName](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.ResourceName)): The `ResourceName` of the base to move. -- `destination` ([Pose](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.Pose)): The destination, which can be any [Pose](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.Pose) with respect to the SLAM map's origin. -- `slam_service_name` ([ResourceName](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.ResourceName)): The `ResourceName` of the [SLAM service](/services/slam/) from which the SLAM map is requested. -- `configuration` [(Optional[MotionConfiguration])](https://python.viam.dev/autoapi/viam/proto/service/motion/index.html#viam.proto.service.motion.MotionConfiguration): The configuration you want to set across this machine for this motion service. This parameter and each of its fields are optional. - - `obstacle_detectors` [(Iterable[ObstacleDetector])](https://python.viam.dev/autoapi/viam/proto/service/motion/index.html#viam.proto.service.motion.ObstacleDetector): The names of each [vision service](/services/vision/) and [camera](/components/camera/) resource pair you want to use for transient obstacle avoidance. - - `position_polling_frequency_hz` [(float)](https://docs.python.org/3/library/functions.html#float): The frequency in hz to poll the position of the machine. - - `obstacle_polling_frequency_hz` [(float)](https://docs.python.org/3/library/functions.html#float): The frequency in hz to poll the vision service for new obstacles. - - `plan_deviation_m` [(float)](https://docs.python.org/3/library/functions.html#float): The distance in meters that the machine can deviate from the motion plan. By default this is set to 2.6 m which is an appropriate value for outdoor usage. When you use the `MoveOnMap()` method from the **CONTROL** tab, the default is overwritten to 0.5 m for testing. - - `linear_m_per_sec` [(float)](https://docs.python.org/3/library/functions.html#float): Linear velocity this machine should target when moving. - - `angular_degs_per_sec` [(float)](https://docs.python.org/3/library/functions.html#float): Angular velocity this machine should target when turning. -- `obstacles` [Optional\[Iterable\[Geometry\]\]](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.Geometry): Obstacles, specified in the SLAM frame coordinate system, to be considered when planning the motion of the component. -- `extra` [(Optional\[Dict\[str, Any\]\])](https://docs.python.org/library/typing.html#typing.Optional): Extra options to pass to the underlying RPC call. -- `timeout` [(Optional\[float\])](https://docs.python.org/library/typing.html#typing.Optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. - -**Returns:** - -- [(str)](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): ExecutionID of the `MoveOnMap` call. - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/services/motion/index.html#viam.services.motion.MotionClient.move_on_map). - -```python {class="line-numbers linkable-line-numbers"} -motion = MotionClient.from_robot(robot=robot, name="builtin") - -# Get the ResourceNames of the base and SLAM service -my_base_resource_name = Base.get_resource_name("my_base") -my_slam_service_name = SLAMClient.get_resource_name("my_slam_service") - -# Define a destination pose with respect to the origin of the map from the SLAM -# service "my_slam_service" -my_pose = Pose(y=10) - -# Move the base component to the destination pose of Y=10, a location of -# (0, 10, 0) in respect to the origin of the map -execution_id = await motion.move_on_map(component_name=my_base_resource_name, - destination=my_pose, - slam_service_name=my_slam_service_name) -``` - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `ctx` [(Context)](https://pkg.go.dev/context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. -- `req` [MoveOnMapReq](https://pkg.go.dev/go.viam.com/rdk/services/motion#MoveOnMapReq): A `MoveOnMapReq` which contains the following values: - - `ComponentName` [(resource.Name)](https://pkg.go.dev/go.viam.com/rdk/resource#Name): The `resource.Name` of the base to move. - - `Destination` [(spatialmath.Pose)](https://pkg.go.dev/go.viam.com/rdk/spatialmath#Pose): The destination, which can be any [Pose](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.Pose) with respect to the SLAM map's origin. - - `SlamName` [(resource.Name)](https://pkg.go.dev/go.viam.com/rdk/resource#Name): The `resource.Name` of the [SLAM service](/services/slam/) from which the SLAM map is requested. - - `MotionConfig` [(\*MotionConfiguration)](https://pkg.go.dev/go.viam.com/rdk/services/motion#MotionConfiguration): The configuration you want to set across this machine for this motion service. This parameter and each of its fields are optional. - - `ObstacleDetectors` [([]ObstacleDetectorName)](https://pkg.go.dev/go.viam.com/rdk/services/motion#ObstacleDetectorName): The names of each [vision service](/services/vision/) and [camera](/components/camera/) resource pair you want to use for transient obstacle avoidance. - - `PositionPollingFreqHz` [(float64)](https://pkg.go.dev/builtin#float64): The frequency in hz to poll the position of the machine. - - `ObstaclePollingFreqHz` [(float64)](https://pkg.go.dev/builtin#float64): The frequency in hz to poll the vision service for new obstacles. - - `PlanDeviationM` [(float64)](https://pkg.go.dev/builtin#float64): The distance in meters that the machine can deviate from the motion plan. By default this is set to 2.6 m which is an appropriate value for outdoor usage. When you use the the **CONTROL** tab, the underlying calls to `MoveOnMap()` use 0.5 m instead. - - `LinearMPerSec` [(float64)](https://pkg.go.dev/builtin#float64): Linear velocity this machine should target when moving. - - `AngularDegsPerSec` [(float64)](https://pkg.go.dev/builtin#float64): Angular velocity this machine should target when turning. - - `Obstacles` [(\[\]spatialmath.Geometry)](https://pkg.go.dev/go.viam.com/rdk/spatialmath#Geometry): Obstacles, specified in the SLAM frame coordinate system, to be considered when planning the motion of the component. - - `Extra` [(map\[string\]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. - -**Returns:** - -- [(ExecutionID)](https://pkg.go.dev/go.viam.com/rdk/services/motion#ExecutionID): ExecutionID of the `MoveOnMap` call. -- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/services/motion#Service). - -```go {class="line-numbers linkable-line-numbers"} -motionService, err := motion.FromRobot(robot, "builtin") - -// Get the resource.Names of the base and of the SLAM service -myBaseResourceName := base.Named("myBase") -mySLAMServiceResourceName := slam.Named("my_slam_service") -// Define a destination Pose with respect to the origin of the map from the SLAM service "my_slam_service" -myPose := spatialmath.NewPoseFromPoint(r3.Vector{Y: 10}) - -// Move the base component to the destination pose of Y=10, a location of (0, 10, 0) in respect to the origin of the map -executionID, err := motionService.MoveOnMap(context.Background(), motion.MoveOnMapReq{ - ComponentName: myBaseResourceName, - Destination: myPose, - SlamName: mySLAMServiceResourceName, -}) -``` - -{{% /tab %}} -{{< /tabs >}} - -### MoveOnGlobe - -Move a [base](/components/base/) component to a destination GPS point, represented in geographic notation _(latitude, longitude)_. -Use a [movement sensor](/components/movement-sensor/) to check the location of the machine. - -`MoveOnGlobe()` is non blocking, meaning the motion service will move the component to the destination GPS point after `MoveOnGlobe()` returns. - -Each successful `MoveOnGlobe()` call returns a unique `ExecutionID` which you can use to identify all plans generated during the `MoveOnGlobe()`. - -{{< alert title="Info" color="info" >}} -If you specify a goal pose and the robot's current position is already within the set `PlanDeviationM`, `MoveOnGlobe` returns an error. -{{< /alert >}} - -You can monitor the progress of the `MoveOnGlobe()` call by querying `GetPlan()` and `ListPlanStatuses()`. - -`MoveOnGlobe()` is intended for use with the [navigation service](/services/navigation/), providing autonomous GPS navigation for rover [bases](/components/base/). - -{{< alert title="Requirements" color="info" >}} -To use `MoveOnGlobe()`, your movement sensor must be able to measure the GPS location and orientation of the machine. - -Make sure the [movement sensor](/components/movement-sensor/) you use supports usage of the following methods in its {{< glossary_tooltip term_id="model" text="model's" >}} implementation of the [movement sensor API](/components/movement-sensor/#api). - -- It must support `GetPosition()` to report the machine's current GPS location. -- It must **also** support **either** `GetCompassHeading()` or `GetOrientation()` to report which way the machine is facing. -- If your movement sensor provides multiple methods, your machine will default to using the values returned by `GetCompassHeading()`. - {{< /alert >}} - -{{< alert title="Stability Notice" color="alert" >}} - -The `heading` parameter is experimental. -Specifying `heading` in a request to `MoveOnGlobe` is not currently recommended if the minimum turning radius of your component is greater than zero, as this combination may cause high latency in the [motion planning algorithms](/services/motion/algorithms/). - -Specifying `obstacles` in a request to `MoveOnGlobe()` will cause an error if you configure a `"translation"` in the `"geometries"` of any of the `GeoGeometry` objects. -Translation in obstacles is not supported by the [navigation service](/services/navigation/). - -{{< /alert >}} - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `component_name` [(ResourceName)](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.ResourceName): The `ResourceName` of the base to move. -- `destination` [(GeoPoint)](https://python.viam.dev/autoapi/viam/components/movement_sensor/index.html#viam.components.movement_sensor.GeoPoint): The location of the component's destination, represented in geographic notation as a [GeoPoint](https://python.viam.dev/autoapi/viam/components/movement_sensor/index.html#viam.components.movement_sensor.GeoPoint) _(lat, lng)_. -- `movement_sensor_name` [(ResourceName)](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.ResourceName): The `ResourceName` of the [movement sensor](/components/movement-sensor/) that you want to use to check the machine's location. -- `obstacles` [(Optional[Sequence[GeoGeometry]])](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.GeoGeometry): Obstacles to consider when planning the motion of the component, with each represented as a `GeoGeometry`.
  • Default: `None`
-- `bounding_regions` [(Optional[Sequence[GeoGeometry]])](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.GeoGeometry): Set of bounds which the robot must remain within or in collision with while navigating, with each represented as a `GeoGeometry`. The call will only execture normally if both the robot and destination are at least partly contained by the defined bounding regions or if no bounding regions are defined. If one or more bounding regions are defined and the robot's geometry is neither encompassed by or in collision with any bounding regions the call will fail. If any bounding regions are defined and the selected destination is not within the bounding regions the call will fail.
  • Default: `None`
-- `heading` [(Optional[float])](https://docs.python.org/library/typing.html#typing.Optional): The compass heading, in degrees, that the machine's movement sensor should report at the `destination` point.
  • Range: `[0-360)` 0: North, 90: East, 180: South, 270: West
  • Default: `None`
-- `configuration` [(Optional[MotionConfiguration])](https://python.viam.dev/autoapi/viam/proto/service/motion/index.html#viam.proto.service.motion.MotionConfiguration): The configuration you want to set across this machine for this motion service. This parameter and each of its fields are optional. - - `obstacle_detectors` [(Iterable[ObstacleDetector])](https://python.viam.dev/autoapi/viam/proto/service/motion/index.html#viam.proto.service.motion.ObstacleDetector): The names of each [vision service](/services/vision/) and [camera](/components/camera/) resource pair you want to use for transient obstacle avoidance. - - `position_polling_frequency_hz` [(float)](https://docs.python.org/3/library/functions.html#float): The frequency in hz to poll the position of the machine. - - `obstacle_polling_frequency_hz` [(float)](https://docs.python.org/3/library/functions.html#float): The frequency in hz to poll the vision service for new obstacles. - - `plan_deviation_m` [(float)](https://docs.python.org/3/library/functions.html#float): The distance in meters that the machine can deviate from the motion plan. - - `linear_m_per_sec` [(float)](https://docs.python.org/3/library/functions.html#float): Linear velocity this machine should target when moving. - - `angular_degs_per_sec` [(float)](https://docs.python.org/3/library/functions.html#float): Angular velocity this machine should target when turning. -- `extra` [(Optional\[Dict\[str, Any\]\])](https://docs.python.org/library/typing.html#typing.Optional): Extra options to pass to the underlying RPC call. -- `timeout` [(Optional\[float\])](https://docs.python.org/library/typing.html#typing.Optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. - -**Returns:** - -- [(str)](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str): ExecutionID of the `MoveOnGlobe` call. - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/services/motion/index.html#viam.services.motion.MotionClient.move_on_globe). - -```python {class="line-numbers linkable-line-numbers"} -motion = MotionClient.from_robot(robot=robot, name="builtin") - -# Get the ResourceNames of the base and movement sensor -my_base_resource_name = Base.get_resource_name("my_base") -mvmnt_sensor_resource_name = MovementSensor.get_resource_name( - "my_movement_sensor") -# Define a destination GeoPoint at the GPS coordinates [0, 0] -my_destination = movement_sensor.GeoPoint(latitude=0, longitude=0) - -# Move the base component to the designated geographic location, as reported by -# the movement sensor -execution_id = await motion.move_on_globe( - component_name=my_base_resource_name, - destination=my_destination, - movement_sensor_name=mvmnt_sensor_resource_name) -``` - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `ctx` [(Context)](https://pkg.go.dev/context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. -- `req` [MoveOnGlobeReq](https://pkg.go.dev/go.viam.com/rdk/services/motion#MoveOnGlobeReq): A `MoveOnGlobeReq` which contains the following values: - - `componentName` [(resource.Name)](https://pkg.go.dev/go.viam.com/rdk/resource#Name): The `resource.Name` of the base to move. - - `destination` [(\*geo.Point)](https://pkg.go.dev/github.com/kellydunn/golang-geo#Point): The location of the component's destination, represented in geographic notation as a [Point](https://pkg.go.dev/github.com/kellydunn/golang-geo#Point) _(lat, lng)_. - - `heading` [(float64)](https://pkg.go.dev/builtin#float64): The compass heading, in degrees, that the machine's movement sensor should report at the `destination` point.
  • Range: `[0-360)` 0: North, 90: East, 180: South, 270: West
  • Default: `0`
- - `movementSensorName` [(resource.Name)](https://pkg.go.dev/go.viam.com/rdk/resource#Name): The `resource.Name` of the [movement sensor](/components/movement-sensor/) that you want to use to check the machine's location. - - `obstacles` [([]\*spatialmath.GeoGeometry)](https://pkg.go.dev/go.viam.com/rdk/spatialmath#GeoGeometry): Obstacles to consider when planning the motion of the component, with each represented as a `GeoGeometry`.
  • Default: `nil`
- - `boundingRegions` [([]\*spatialmath.GeoGeometry)](https://pkg.go.dev/go.viam.com/rdk/spatialmath#GeoGeometry): Set of bounds which the robot must remain within or in collision with while navigating, with each represented as a `GeoGeometry`. The call will only execture normally if both the robot and destination are at least partly contained by the defined bounding regions or if no bounding regions are defined. If one or more bounding regions are defined and the robot's geometry is neither encompassed by or in collision with any bounding regions the call will fail. If any bounding regions are defined and the selected destination is not within the bounding regions the call will fail.
  • Default: `nil`
- - `motionConfig` [(\*MotionConfiguration)](https://pkg.go.dev/go.viam.com/rdk/services/motion#MotionConfiguration): The configuration you want to set across this machine for this motion service. This parameter and each of its fields are optional. - - `ObstacleDetectors` [([]ObstacleDetectorName)](https://pkg.go.dev/go.viam.com/rdk/services/motion#ObstacleDetectorName): The names of each [vision service](/services/vision/) and [camera](/components/camera/) resource pair you want to use for transient obstacle avoidance. - - `PositionPollingFreqHz` [(float64)](https://pkg.go.dev/builtin#float64): The frequency in hz to poll the position of the machine. - - `ObstaclePollingFreqHz` [(float64)](https://pkg.go.dev/builtin#float64): The frequency in hz to poll the vision service for new obstacles. - - `PlanDeviationM` [(float64)](https://pkg.go.dev/builtin#float64): The distance in meters that the machine can deviate from the motion plan. - - `LinearMPerSec` [(float64)](https://pkg.go.dev/builtin#float64): Linear velocity this machine should target when moving. - - `AngularDegsPerSec` [(float64)](https://pkg.go.dev/builtin#float64): Angular velocity this machine should target when turning. - - `extra` [(map\[string\]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. - -**Returns:** - -- [(ExecutionID)](https://pkg.go.dev/go.viam.com/rdk/services/motion#ExecutionID): ExecutionID of the `MoveOnGlobe` call. -- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/services/motion#Service). - -```go {class="line-numbers linkable-line-numbers"} -motionService, err := motion.FromRobot(robot, "builtin") - -// Get the resource.Names of the base and movement sensor -myBaseResourceName := base.Named("myBase") -myMvmntSensorResourceName := movement_sensor.Named("my_movement_sensor") -// Define a destination Point at the GPS coordinates [0, 0] -myDestination := geo.NewPoint(0, 0) - -// Move the base component to the designated geographic location, as reported by the movement sensor -ctx := context.Background() -executionID, err := motionService.MoveOnGlobe(ctx, motion.MoveOnGlobeReq{ - ComponentName: myBaseResourceName, - Destination: myDestination, - MovementSensorName: myMvmntSensorResourceName, -}) -``` - -{{% /tab %}} -{{< /tabs >}} - -### StopPlan - -Stop a [base](/components/base/) component being moved by an in progress [`MoveOnGlobe`](/services/motion/#moveonglobe) or [`MoveOnMap`](/services/motion/#moveonmap) call. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `component_name` [(ResourceName)](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.ResourceName): The `ResourceName` of the base to stop. -- `extra` [(Optional\[Dict\[str, Any\]\])](https://docs.python.org/library/typing.html#typing.Optional): Extra options to pass to the underlying RPC call. -- `timeout` [(Optional\[float\])](https://docs.python.org/library/typing.html#typing.Optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. - -**Returns:** - -- [(None)](https://docs.python.org/3/library/stdtypes.html#the-null-object) - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/services/motion/index.html#viam.services.motion.MotionClient.stop_plan). - -```python {class="line-numbers linkable-line-numbers"} -motion = MotionClient.from_robot(robot=robot, name="builtin") -my_base_resource_name = Base.get_resource_name("my_base") - -# Assuming a move_on_globe started started the execution -# mvmnt_sensor = MovementSensor.get_resource_name("my_movement_sensor") -# my_destination = movement_sensor.GeoPoint(latitude=0, longitude=0) -# execution_id = await motion.move_on_globe( -# component_name=my_base_resource_name, -# destination=my_destination, -# movement_sensor_name=mvmnt_sensor_resource_name) - -# Stop the base component which was instructed to move by `MoveOnGlobe()` -# or `MoveOnMap()` -my_base_resource_name = Base.get_resource_name("my_base") -await motion.stop_plan(component_name=mvmnt_sensor) -``` - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `ctx` [(Context)](https://pkg.go.dev/context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. -- `req` [StopPlanReq](https://pkg.go.dev/go.viam.com/rdk/services/motion#StopPlanReq): A `StopPlanReq` which contains the following values: - - `componentName` [(resource.Name)](https://pkg.go.dev/go.viam.com/rdk/resource#Name): The `resource.Name` of the base to stop. - - `extra` [(map\[string\]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. - -**Returns:** - -- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/services/motion#Service). - -```go {class="line-numbers linkable-line-numbers"} -motionService, err := motion.FromRobot(robot, "builtin") -myBaseResourceName := base.Named("myBase") -ctx := context.Background() - -// Assuming a move_on_globe started started the execution -// myMvmntSensorResourceName := movement_sensor.Named("my_movement_sensor") -// myDestination := geo.NewPoint(0, 0) -// executionID, err := motionService.MoveOnGlobe(ctx, motion.MoveOnGlobeReq{ -// ComponentName: myBaseResourceName, -// Destination: myDestination, -// MovementSensorName: myMvmntSensorResourceName, -// }) - -// Stop the base component which was instructed to move by `MoveOnGlobe()` or `MoveOnMap()` -err := motionService.StopPlan(context.Background(), motion.StopPlanReq{ - ComponentName: s.req.ComponentName, -}) -``` - -{{% /tab %}} -{{< /tabs >}} - -### GetPlan - -By default, returns the plan history of the most recent [`MoveOnGlobe`](/services/motion/#moveonglobe) or [`MoveOnMap`](/services/motion/#moveonmap) call to move a [base](/components/base/) component. - -The plan history for executions before the most recent can be requested by providing an `ExecutionID` in the request. - -Returns a result if both of the following conditions are met: - -- the execution (call to `MoveOnGlobe` or `MoveOnMap`) is still executing **or** changed state within the last 24 hours -- the machine has not reinitialized - -Plans never change. - -Replans always create new plans. - -Replans share the `ExecutionID` of the previously executing plan. - -All repeated fields are in chronological order. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `component_name` [(ResourceName)](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.ResourceName): The `ResourceName` of the base to stop. -- `lastPlanOnly` [(Optional\[bool\])](https://docs.python.org/library/typing.html#typing.Optional): If `true`, the response will only return the the last plan for the component. If the `executionID` parameter is non empty then the last plan for the component & `executionID` is returned. -- `executionID` [(Optional\[str\])](https://docs.python.org/library/typing.html#typing.Optional): If non empty, the response will return the plans of the provided execution & component. Useful for retrieving plans from executions before the current execution. -- `extra` [(Optional\[Dict\[str, Any\]\])](https://docs.python.org/library/typing.html#typing.Optional): Extra options to pass to the underlying RPC call. -- `timeout` [(Optional\[float\])](https://docs.python.org/library/typing.html#typing.Optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. - -**Returns:** - -- [(GetPlanResponse)](https://python.viam.dev/autoapi/viam/proto/service/motion/index.html#viam.proto.service.motion.GetPlanResponse) - - [current_plan_with_status](https://python.viam.dev/autoapi/viam/proto/service/motion/index.html#viam.proto.service.motion.GetPlanResponse.current_plan_with_status): The current plan and status that matches the request query - - [replan_history](https://python.viam.dev/autoapi/viam/proto/service/motion/index.html#viam.proto.service.motion.GetPlanResponse.replan_history): The history of all previous plans that were generated in ascending order. This field will be empty if the motion service did not need to re-plan. - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/services/motion/index.html#viam.services.motion.MotionClient.get_plan). - -```python {class="line-numbers linkable-line-numbers"} -motion = MotionClient.from_robot(robot=robot, name="builtin") -my_base_resource_name = Base.get_resource_name("my_base") -# Get the plan(s) of the base component which was instructed -# to move by `MoveOnGlobe()` or `MoveOnMap()` -resp = await motion.get_plan(component_name=my_base_resource_name) -``` - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `ctx` [(Context)](https://pkg.go.dev/context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. -- `req` [PlanHistoryReq](https://pkg.go.dev/go.viam.com/rdk/services/motion#PlanHistoryReq): A `PlanHistoryReq` which contains the following values: - - `componentName` [(resource.Name)](https://pkg.go.dev/go.viam.com/rdk/resource#Name): The `resource.Name` of the base to stop. - - `lastPlanOnly` [(bool)](https://pkg.go.dev/builtin#bool): If `true`, the response will only return the the last plan for the component / execution - - `executionID` [(ExecutionID)](https://pkg.go.dev/go.viam.com/rdk/services/motion#PlanHistoryReq): If non empty, the response will return the plans of the provided execution & component. Useful for retrieving plans from executions before the current execution. - - `extra` [(map\[string\]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. - -**Returns:** - -- [([]PlanWithStatus)](https://pkg.go.dev/go.viam.com/rdk/services/motion#PlanWithStatus): PlanWithStatus contains a plan, its current status, and all state changes that came prior sorted by ascending timestamp. -- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/services/motion#Service). - -```go {class="line-numbers linkable-line-numbers"} -motionService, err := motion.FromRobot(robot, "builtin") -// Get the plan(s) of the base component's most recent execution i.e. `MoveOnGlobe()` or `MoveOnMap()` call. -ctx := context.Background() -planHistory, err := motionService.PlanHistory(ctx, motion.PlanHistoryReq{ - ComponentName: s.req.ComponentName, -}) -``` - -{{% /tab %}} -{{< /tabs >}} - -### ListPlanStatuses - -Returns the statuses of plans created by [`MoveOnGlobe`](/services/motion/#moveonglobe) or [`MoveOnMap`](/services/motion/#moveonmap) calls that meet at least one of the following conditions since the motion service initialized: - -- the plan's status is in progress -- the plan's status changed state within the last 24 hours - -All repeated fields are in chronological order. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `onlyActivePlans` [(Optional\[bool\])](https://docs.python.org/library/typing.html#typing.Optional): If `true`, the response will only return plans which are executing. -- `extra` [(Optional\[Dict\[str, Any\]\])](https://docs.python.org/library/typing.html#typing.Optional): Extra options to pass to the underlying RPC call. -- `timeout` [(Optional\[float\])](https://docs.python.org/library/typing.html#typing.Optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. - -**Returns:** - -- [(ListPlanStatusesResponse)](https://python.viam.dev/autoapi/viam/services/motion/index.html#viam.services.motion.ListPlanStatusesResponse) - - [(plan_statuses_with_ids)](https://python.viam.dev/autoapi/viam/proto/service/motion/index.html#viam.proto.service.motion.ListPlanStatusesResponse.plan_statuses_with_ids): List of plan statuses along with associated `planId`, `componentName` and `executionID` that match the request. - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/services/motion/index.html#viam.services.motion.MotionClient.list_plan_statuses). - -```python {class="line-numbers linkable-line-numbers"} -motion = MotionClient.from_robot(robot=robot, name="builtin") -# List the plan statuses of the motion service within the TTL -resp = await motion.list_plan_statuses() -``` - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `ctx` [(Context)](https://pkg.go.dev/context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. -- `req` [ListPlanStatusesReq](https://pkg.go.dev/go.viam.com/rdk/services/motion#ListPlanStatusesReq): A `ListPlanStatusesReq` which contains the following values: - - `onlyActivePlans` [(bool)](https://pkg.go.dev/builtin#bool): If `true`, the response will only return plans which are executing. - - `extra` [(map\[string\]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. - -**Returns:** - -- [([]PlanStatusWithID)](https://pkg.go.dev/go.viam.com/rdk/services/motion#PlanStatusWithID): `PlanStatusWithID` describes the state of a given plan at a point in time plus the `PlanId`, `ComponentName` and `ExecutionID` the status is associated with. -- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/services/motion#Service). - -```go {class="line-numbers linkable-line-numbers"} -motionService, err := motion.FromRobot(robot, "builtin") -// Get the plan(s) of the base component's most recent execution i.e. `MoveOnGlobe()` or `MoveOnMap()` call. -ctx := context.Background() -planStatuses, err := motionService.ListPlanStatuses(ctx, motion.ListPlanStatusesReq{}) -``` - -{{% /tab %}} -{{< /tabs >}} - -### DoCommand - -Execute model-specific commands that are not otherwise defined by the service API. -For built-in service models, any model-specific commands available are covered with each model's documentation. -If you are implementing your own motion service and add features that have no built-in API method, you can access them with `DoCommand`. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- `command` [(Mapping[str, ValueTypes])](https://docs.python.org/3/library/stdtypes.html#typesmapping): The command to execute. -- `timeout` [(Optional\[float\])](https://docs.python.org/library/typing.html#typing.Optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. - -**Returns:** - -- [(Mapping[str, ValueTypes])](https://docs.python.org/3/library/stdtypes.html#typesmapping): Result of the executed command. - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/services/motion/client/index.html#viam.services.motion.client.MotionClient.do_command). - -```python {class="line-numbers linkable-line-numbers"} -# Access the motion service -motion = MotionClient.from_robot(robot=robot, name="builtin") - -my_command = { - "command": "dosomething", - "someparameter": 52 -} - -await motion.do_command(my_command) -``` - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `ctx` [(Context)](https://pkg.go.dev/context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. -- `cmd` [(map\[string\]interface{})](https://go.dev/blog/maps): The command to execute. - -**Returns:** - -- [(map\[string\]interface{})](https://go.dev/blog/maps): Result of the executed command. -- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. - -```go {class="line-numbers linkable-line-numbers"} -// Access the motion service -motionService, err := motion.FromRobot(robot, "builtin") - -resp, err := motionService.DoCommand(ctx, map[string]interface{}{"command": "dosomething", "someparameter": 52}) -``` - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Resource). - -{{% /tab %}} -{{< /tabs >}} - -### Close - -Safely shut down the resource and prevent further use. - -{{< tabs >}} -{{% tab name="Python" %}} - -**Parameters:** - -- None - -**Returns:** - -- None - -```python {class="line-numbers linkable-line-numbers"} -motion = MotionClient.from_robot(robot, "builtin") - -await motion.close() -``` - -For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/services/motion/client/index.html#viam.services.motion.client.MotionClient.close). - -{{% /tab %}} -{{% tab name="Go" %}} - -**Parameters:** - -- `ctx` [(Context)](https://pkg.go.dev/context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. - -**Returns:** - -- [(error)](https://pkg.go.dev/builtin#error) : An error, if one occurred. - -```go {class="line-numbers linkable-line-numbers"} -motionService, err := motion.FromRobot(robot, "builtin") - -err := motionService.Close(ctx) -``` - -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Resource). - -{{% /tab %}} -{{< /tabs >}} +{{< readfile "/static/include/services/apis/generated/motion.md" >}} ## Test the motion service diff --git a/static/include/services/apis/generated/mlmodel-table.md b/static/include/services/apis/generated/mlmodel-table.md new file mode 100644 index 0000000000..0b6033b926 --- /dev/null +++ b/static/include/services/apis/generated/mlmodel-table.md @@ -0,0 +1,8 @@ + +| Method Name | Description | +| ----------- | ----------- | +| [`Infer`](/services/ml/deploy/#infer) | Take an already ordered input tensor as an array, make an inference on the model, and return an output tensor map. | +| [`Metadata`](/services/ml/deploy/#metadata) | Get the metadata: name, data type, expected tensor/array shape, inputs, and outputs associated with the ML model. | +| [`Reconfigure`](/services/ml/deploy/#reconfigure) | Reconfigure this resource. | +| [`DoCommand`](/services/ml/deploy/#docommand) | Execute model-specific commands that are not otherwise defined by the service API. | +| [`Close`](/services/ml/deploy/#close) | Safely shut down the resource and prevent further use. | diff --git a/static/include/services/apis/generated/mlmodel.md b/static/include/services/apis/generated/mlmodel.md new file mode 100644 index 0000000000..e2a6b48050 --- /dev/null +++ b/static/include/services/apis/generated/mlmodel.md @@ -0,0 +1,237 @@ +### Infer + +Take an already ordered input tensor as an array, make an inference on the model, and return an output tensor map. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `input_tensors` (Dict[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), [typing.NDArray](https://numpy.org/doc/stable/reference/typing.html#numpy.typing.NDArray)]) (required): A dictionary of input flat tensors as specified in the metadata. +- `timeout` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. + +**Returns:** + +- (Dict[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), [typing.NDArray](https://numpy.org/doc/stable/reference/typing.html#numpy.typing.NDArray)]): A dictionary of output flat tensors as specified in the metadata. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +import numpy as np + +my_mlmodel = MLModelClient.from_robot(robot=robot, name="my_mlmodel_service") + +nd_array = np.array([1, 2, 3], dtype=np.float64) +input_tensors = {"0": nd_array} + +output_tensors = await my_mlmodel.infer(input_tensors) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/services/mlmodel/client/index.html#viam.services.mlmodel.client.MLModelClient.infer). + +{{% /tab %}} +{{% tab name="Go" %}} + +**Parameters:** + +- `ctx` [(Context)](https://pkg.go.dev/context#Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. +- `tensors` [(ml.Tensors)](https://pkg.go.dev/go.viam.com/rdk/ml#Tensors): The input map of tensors, as specified in the metadata. + +**Returns:** + +- [(ml.Tensors)](https://pkg.go.dev/go.viam.com/rdk/ml#Tensors): The output map of tensors, as specified in the metadata, after being run through an inference engine. +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +**Example:** + +```go {class="line-numbers linkable-line-numbers"} +input_tensors := ml.Tensors{"0": tensor.New(tensor.WithShape(1, 2, 3), tensor.WithBacking([]int{1, 2, 3, 4, 5, 6}))} + +output_tensors, err := myMLModel.Infer(context.Background(), input_tensors) +``` + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/services/mlmodel#Service). + +{{% /tab %}} +{{< /tabs >}} + +### Metadata + +Get the metadata: name, data type, expected tensor/array shape, inputs, and outputs associated with the ML model. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `timeout` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. + +**Returns:** + +- ([viam.services.mlmodel.mlmodel.Metadata](https://python.viam.dev/autoapi/viam/services/mlmodel/mlmodel/index.html#viam.services.mlmodel.mlmodel.Metadata)): The metadata. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +my_mlmodel = MLModelClient.from_robot(robot=robot, name="my_mlmodel_service") + +metadata = await my_mlmodel.metadata() +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/services/mlmodel/client/index.html#viam.services.mlmodel.client.MLModelClient.metadata). + +{{% /tab %}} +{{% tab name="Go" %}} + +**Parameters:** + +- `ctx` [(Context)](https://pkg.go.dev/context#Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. + +**Returns:** + +- [(MLMetadata)](https://pkg.go.dev/go.viam.com/rdk/services/mlmodel#MLMetadata): Name, type, expected tensor/array shape, inputs, and outputs associated with the ML model. +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +**Example:** + +```go {class="line-numbers linkable-line-numbers"} +metadata, err := myMLModel.Metadata(context.Background()) +``` + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/services/mlmodel#Service). + +{{% /tab %}} +{{< /tabs >}} + +### Reconfigure + +Reconfigure this resource. +Reconfigure must reconfigure the resource atomically and in place. + +{{< tabs >}} +{{% tab name="Go" %}} + +**Parameters:** + +- `ctx` [(Context)](https://pkg.go.dev/context#Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. +- `deps` [(Dependencies)](https://pkg.go.dev/go.viam.com/rdk/resource#Dependencies): The resource dependencies. +- `conf` [(Config)](https://pkg.go.dev/go.viam.com/rdk/resource#Config): The resource configuration. + +**Returns:** + +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Resource). + +{{% /tab %}} +{{< /tabs >}} + +### DoCommand + +Execute model-specific commands that are not otherwise defined by the service API. +For built-in service models, any model-specific commands available are covered with each model's documentation. +If you are implementing your own ML model service and add features that have no built-in API method, you can access them with `DoCommand`. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `command` (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), ValueTypes]) (required): The command to execute. +- `timeout` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. + +**Returns:** + +- (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), viam.utils.ValueTypes]) + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +motion = MotionClient.from_robot(robot, "builtin") + +my_command = { + "cmnd": "dosomething", + "someparameter": 52 +} + +# Can be used with any resource, using the motion service as an example +await motion.do_command(command=my_command) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/services/mlmodel/client/index.html#viam.services.mlmodel.client.MLModelClient.do_command). + +{{% /tab %}} +{{% tab name="Go" %}} + +**Parameters:** + +- `ctx` [(Context)](https://pkg.go.dev/context#Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. +- `cmd` [(map[string]interface{})](https://go.dev/blog/maps): The command to execute. + +**Returns:** + +- [(map[string]interface{})](https://pkg.go.dev/builtin#string): The command response. +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +**Example:** + +```go {class="line-numbers linkable-line-numbers"} +// This example shows using DoCommand with an arm component. +myArm, err := arm.FromRobot(machine, "my_arm") + +command := map[string]interface{}{"cmd": "test", "data1": 500} +result, err := myArm.DoCommand(context.Background(), command) +``` + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Resource). + +{{% /tab %}} +{{< /tabs >}} + +### Close + +Safely shut down the resource and prevent further use. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- None. + +**Returns:** + +- None. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +await component.close() +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/services/mlmodel/client/index.html#viam.services.mlmodel.client.MLModelClient.close). + +{{% /tab %}} +{{% tab name="Go" %}} + +**Parameters:** + +- `ctx` [(Context)](https://pkg.go.dev/context#Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. + +**Returns:** + +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +**Example:** + +```go {class="line-numbers linkable-line-numbers"} +// This example shows using Close with an arm component. +myArm, err := arm.FromRobot(machine, "my_arm") + +err = myArm.Close(ctx) +``` + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Resource). + +{{% /tab %}} +{{< /tabs >}} diff --git a/static/include/services/apis/generated/motion-table.md b/static/include/services/apis/generated/motion-table.md new file mode 100644 index 0000000000..d3874d7510 --- /dev/null +++ b/static/include/services/apis/generated/motion-table.md @@ -0,0 +1,13 @@ + +| Method Name | Description | +| ----------- | ----------- | +| [`Move`](/services/motion/#move) | The `Move` method is the primary way to move multiple components, or to move any object to any other location. | +| [`MoveOnMap`](/services/motion/#moveonmap) | Move a base component to a destination pose on a {{< glossary_tooltip term_id="slam" text="SLAM" >}} map. | +| [`MoveOnGlobe`](/services/motion/#moveonglobe) | Move a base component to a destination GPS point, represented in geographic notation _(latitude, longitude)_. | +| [`GetPose`](/services/motion/#getpose) | `GetPose` gets the location and orientation of a component within the frame system. | +| [`StopPlan`](/services/motion/#stopplan) | Stop a base component being moved by an in progress `MoveOnGlobe` or `MoveOnMap` call. | +| [`ListPlanStatuses`](/services/motion/#listplanstatuses) | Returns the statuses of plans created by `MoveOnGlobe` or `MoveOnMap` calls that meet at least one of the following conditions since the motion service initialized: - the plan's status is in progress - the plan's status changed state within the last 24 hours All repeated fields are in chronological order. | +| [`GetPlan`](/services/motion/#getplan) | By default, returns the plan history of the most recent `MoveOnGlobe` or `MoveOnMap` call to move a base component. | +| [`Reconfigure`](/services/motion/#reconfigure) | Reconfigure this resource. | +| [`DoCommand`](/services/motion/#docommand) | Execute model-specific commands that are not otherwise defined by the service API. | +| [`Close`](/services/motion/#close) | Safely shut down the resource and prevent further use. | diff --git a/static/include/services/apis/generated/motion.md b/static/include/services/apis/generated/motion.md new file mode 100644 index 0000000000..7d559a7d87 --- /dev/null +++ b/static/include/services/apis/generated/motion.md @@ -0,0 +1,896 @@ +### Move + +The `Move` method is the primary way to move multiple components, or to move any object to any other location. +Given a destination pose and a component to move to that destination, `Move` will: + +1. Construct a full kinematic chain from goal to destination including all movable components in between. +2. Solve that chain to move the specified component frame to the destination while adhering to any constraints. +3. Execute that movement to move the actual machine. +4. Return whether or not this process succeeded. + +The motion service takes the volumes associated with all configured machine components (local and remote) into account for each request to ensure that the machine does not collide with itself or other known objects. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `component_name` ([viam.proto.common.ResourceName](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.ResourceName)) (required): The `ResourceName` of the piece of the robot that should arrive at the destination. Note that `move` moves the distal end of the component to the destination. For example, when moving a robotic arm, the piece that will arrive at the destination is the end effector attachment point, not the base of the arm. +- `destination` ([viam.proto.common.PoseInFrame](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.PoseInFrame)) (required): Describes where the `component_name` frame should be moved to. Can be any pose, from the perspective of any component whose location is configured as a [`frame`](../frame-system/). +- `world_state` ([viam.proto.common.WorldState](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.WorldState)) (optional): Data structure specifying information about the world around the machine. + Used to augment the motion solving process. + `world_state` includes obstacles and transforms: + + - **Obstacles**: Geometries located at a pose relative to some frame. + When solving a motion plan with movable frames that contain inherent geometries, the solved path is constrained such that none of those inherent geometries intersect with the obstacles. + Important considerations: + - If a motion begins with a component already in collision with an obstacle, collisions between that specific component and that obstacle will not be checked. + - The motion service assumes that obstacles are static. + If a worldstate obstacle is physically attached to a part of the robot such that it will move with the robot, specify it with _transforms_. + - Obstacles are defined by a pose and a [geometry](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.Geometry) with dimensions. + The pose location is the point at the center of the geometry. + - Obstacle locations are defined with respect to the _origin_ of the specified frame. + Their poses are relative to the _origin_ of the specified frame. + An obstacle associated with the frame of an arm with a pose of {X: 0, Y: 0, Z: -10} is interpreted as being 10mm below the base of the arm, not 10mm below the end effector. + This is different from `destination` and `component_name`, where poses are relative to the distal end of a frame. + - **Transforms**: A list of `PoseInFrame` messages that specify other transformations to temporarily add to the frame system at solve time. + Transforms can be used to account for geometries that are attached to the robot but not configured as robot components. + For example, you could use a transform to represent the volume of a marker held in your machine's gripper. + Transforms are not added to the config or carried into later processes. +- `constraints` ([viam.proto.service.motion.Constraints](https://python.viam.dev/autoapi/viam/proto/service/motion/index.html#viam.proto.service.motion.Constraints)) (optional): Pass in [motion constraints](./constraints/). By default, motion is unconstrained with the exception of obstacle avoidance. +- `extra` (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), Any]) (optional): Extra options to pass to the underlying RPC call. +- `timeout` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. + +**Returns:** + +- ([bool](https://docs.python.org/3/library/stdtypes.html#boolean-type-bool)): Whether the move was successful (`true`) or unsuccessful (`false`). + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +motion = MotionClient.from_robot(robot=robot, name="builtin") + +# Assumes a gripper configured with name "my_gripper" on the machine +gripper_name = Gripper.get_resource_name("my_gripper") +my_frame = "my_gripper_offset" + +goal_pose = Pose(x=0, y=0, z=300, o_x=0, o_y=0, o_z=1, theta=0) + +# Move the gripper +moved = await motion.move(component_name=gripper_name, + destination=PoseInFrame(reference_frame="myFrame", + pose=goal_pose), + world_state=worldState, + constraints={}, + extra={}) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/services/motion/client/index.html#viam.services.motion.client.MotionClient.move). + +{{% /tab %}} +{{% tab name="Go" %}} + +**Parameters:** + +- `ctx` [(Context)](https://pkg.go.dev/context#Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. +- `componentName` [(resource.Name)](https://pkg.go.dev/go.viam.com/rdk/resource#Name): The `resource.Name` of the piece of the robot that should arrive at the destination. Note that `Move` moves the distal end of the component to the destination. For example, when moving a robotic arm, the piece that will arrive at the destination is the end effector attachment point, not the base of the arm. +- `destination` [(*referenceframe.PoseInFrame)](https://pkg.go.dev/go.viam.com/rdk/referenceframe#PoseInFrame): Describes where the `component_name` should end up. Can be any pose, from the perspective of any component whose location is configured as a [`frame`](../frame-system/). Note that the destination pose is relative to the distal end of the specified frame. This means that if the `destination` is the same as the `component_name` frame, for example an arm's frame, then a pose of `{X: 10, Y: 0, Z: 0}` will move that arm’s end effector by 10 mm in the local `X` direction. +- `worldState` [(*referenceframe.WorldState)](https://pkg.go.dev/go.viam.com/rdk/referenceframe#WorldState): Data structure specifying information about the world around the machine. + Used to augment the motion solving process. + `worldState` includes obstacles and transforms: + + - **Obstacles**: Geometries located at a pose relative to some frame. + When solving a motion plan with movable frames that contain inherent geometries, the solved path is constrained such that none of those inherent geometries intersect with the obstacles. + Important considerations: + - If a motion begins with a component already in collision with an obstacle, collisions between that specific component and that obstacle will not be checked. + - The motion service assumes that obstacles are static. + If a worldstate obstacle is physically attached to a part of the robot such that it will move with the robot, specify it with _transforms_. + - Obstacles are defined by a [(pose)](https://pkg.go.dev/go.viam.com/rdk/spatialmath#Pose) and a [(geometry)](https://pkg.go.dev/go.viam.com/rdk/spatialmath#Geometry) with dimensions. + The pose location is the point at the center of the geometry. + - Obstacle locations are defined with respect to the _origin_ of the specified frame. + Their poses are relative to the _origin_ of the specified frame. + An obstacle associated with the frame of an arm with a pose of {X: 0, Y: 0, Z: -10} is interpreted as being 10mm below the base of the arm, not 10mm below the end effector. + This is different from `destination` and `componentName`, where poses are relative to the distal end of a frame. + - **Transforms**: A list of `PoseInFrame` messages that specify other transformations to temporarily add to the frame system at solve time. + Transforms can be used to account for geometries that are attached to the robot but not configured as robot components. + For example, you could use a transform to represent the volume of a marker held in your machine's gripper. + Transforms are not added to the config or carried into later processes. +- `constraints` [(*motionplan.Constraints)](https://pkg.go.dev/go.viam.com/rdk/motionplan#Constraints): Pass in optional [motion constraints](./constraints/). By default, motion is unconstrained with the exception of obstacle avoidance. +- `extra` [(map[string]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. + +**Returns:** + +- [(bool)](https://pkg.go.dev/builtin#bool): Whether the move was successful (`true`) or unsuccessful (`false`). +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +**Example:** + +```go {class="line-numbers linkable-line-numbers"} +motionService, err := motion.FromRobot(machine, "builtin") + +// Assumes a gripper configured with name "my_gripper" on the machine +gripperName := gripper.Named("my_gripper") + +// Define a destination Pose +destination := referenceframe.NewPoseInFrame("world", spatialmath.NewPoseFromPoint(r3.Vector{X: 0.1, Y: 0.0, Z: 0.0})) + +// Create obstacles +boxPose := spatialmath.NewPoseFromPoint(r3.Vector{X: 0.0, Y: 0.0, Z: 0.0}) +boxDims := r3.Vector{X: 0.2, Y: 0.2, Z: 0.2} // 20cm x 20cm x 20cm box +obstacle, _ := spatialmath.NewBox(boxPose, boxDims, "obstacle_1") + +geometryInFrame := referenceframe.NewGeometriesInFrame("base", []spatialmath.Geometry{obstacle}) +obstacles := []*referenceframe.GeometriesInFrame{geometryInFrame} + +// Create transforms +transform := referenceframe.NewLinkInFrame("gripper", + spatialmath.NewPoseFromPoint(r3.Vector{X: 0.1, Y: 0.0, Z: 0.1}), "transform_1", nil +) +transforms := []*referenceframe.LinkInFrame{transform} + +// Create WorldState +worldState, err := referenceframe.NewWorldState(obstacles, transforms) + +// Move gripper component +moved, err := motionService.Move(context.Background(), gripperName, destination, worldState, nil, nil) +``` + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/services/motion#Service). + +{{% /tab %}} +{{< /tabs >}} + +### MoveOnMap + +Move a [base](/components/base/) component to a destination [pose](/internals/orientation-vector/) on a {{< glossary_tooltip term_id="slam" text="SLAM" >}} map. + +`MoveOnMap()` is non blocking, meaning the motion service will move the component to the destination [pose](/internals/orientation-vector/) after `MoveOnMap()` returns. + +Each successful `MoveOnMap()` call returns a unique `ExecutionID` which you can use to identify all plans generated during the `MoveOnMap()` call. + +{{< alert title="Info" color="info" >}} +If you specify a goal pose and the robot's current position is already within the set `PlanDeviationM`, then `MoveOnMap` returns an error. +{{< /alert >}} + +You can monitor the progress of the `MoveOnMap()` call by querying `GetPlan()` and `ListPlanStatuses()`. + +Use the machine's position reported by the {{< glossary_tooltip term_id="slam" text="SLAM" >}} service to check the location of the machine. + +`MoveOnMap()` is intended for use with the [navigation service](/services/navigation/), providing autonomous indoor navigation for rover [bases](/components/base/). + +{{< alert title="Requirements" color="info" >}} +To use `MoveOnMap()`, your [SLAM service](/services/slam/) must implement `GetPointCloudMap()` and `GetPosition()` + +Make sure the [SLAM service](/services/slam/) you use alongside the this motion service supports the following methods in its {{< glossary_tooltip term_id="model" text="model's" >}} implementation of the [SLAM service API](/services/slam/#api): + +- It must support `GetPointCloudMap()` to report the SLAM map as a pointcloud. +- It must support `GetPosition()` to report the machine's current location on the SLAM map. + {{< /alert >}} + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `component_name` ([viam.proto.common.ResourceName](https://python.viam.dev/autoapi/viam/services/motion/client/index.html#viam.services.motion.client.ResourceName)) (required): The `ResourceName` of the base to move. +- `destination` ([viam.proto.common.Pose](https://python.viam.dev/autoapi/viam/services/motion/client/index.html#viam.services.motion.client.Pose)) (required): The destination, which can be any [Pose](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.Pose) with respect to the SLAM map's origin. +- `slam_service_name` ([viam.proto.common.ResourceName](https://python.viam.dev/autoapi/viam/services/motion/client/index.html#viam.services.motion.client.ResourceName)) (required): The `ResourceName` of the [SLAM service](/services/slam/) from which the SLAM map is requested. +- `configuration` ([viam.proto.service.motion.MotionConfiguration](https://python.viam.dev/autoapi/viam/services/motion/client/index.html#viam.services.motion.client.MotionConfiguration)) (optional): The configuration you want to set across this machine for this motion service. This parameter and each of its fields are optional. + - `obstacle_detectors` [(Iterable[ObstacleDetector])](https://python.viam.dev/autoapi/viam/proto/service/motion/index.html#viam.proto.service.motion.ObstacleDetector): The names of each [vision service](/services/vision/) and [camera](/components/camera/) resource pair you want to use for transient obstacle avoidance. + - `position_polling_frequency_hz` [(float)](https://docs.python.org/3/library/functions.html#float): The frequency in hz to poll the position of the machine. + - `obstacle_polling_frequency_hz` [(float)](https://docs.python.org/3/library/functions.html#float): The frequency in hz to poll the vision service for new obstacles. + - `plan_deviation_m` [(float)](https://docs.python.org/3/library/functions.html#float): The distance in meters that the machine can deviate from the motion plan. By default this is set to 2.6 m which is an appropriate value for outdoor usage. When you use the `MoveOnMap()` method from the **CONTROL** tab, the default is overwritten to 0.5 m for testing. + - `linear_m_per_sec` [(float)](https://docs.python.org/3/library/functions.html#float): Linear velocity this machine should target when moving. + - `angular_degs_per_sec` [(float)](https://docs.python.org/3/library/functions.html#float): Angular velocity this machine should target when turning. +- `obstacles` ([Iterable[viam.proto.common.Geometry]](https://python.viam.dev/autoapi/viam/services/motion/client/index.html#viam.services.motion.client.Geometry)) (optional): Obstacles, specified in the SLAM frame coordinate system, to be considered when planning the motion of the component. +- `extra` (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), Any]) (optional): Extra options to pass to the underlying RPC call. +- `timeout` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. + +**Returns:** + +- ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)): ExecutionID of the `MoveOnMap` call. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +motion = MotionClient.from_robot(robot=robot, name="builtin") + +# Get the ResourceNames of the base component and SLAM service +my_base_resource_name = Base.get_resource_name("my_base") +my_slam_service_name = SLAMClient.get_resource_name("my_slam_service") + +# Define a destination pose with respect to the origin of the map from the SLAM service "my_slam_service" +my_pose = Pose(y=10) + +# Move the base component to the destination pose of Y=10, a location of +# (0, 10, 0) in respect to the origin of the map +execution_id = await motion.move_on_map(component_name=my_base_resource_name, + destination=my_pose, + slam_service_name=my_slam_service_name) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/services/motion/client/index.html#viam.services.motion.client.MotionClient.move_on_map). + +{{% /tab %}} +{{% tab name="Go" %}} + +**Parameters:** + +- `ctx` [(Context)](https://pkg.go.dev/context#Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. +- `req` [(MoveOnMapReq)](https://pkg.go.dev/go.viam.com/rdk/services/motion#MoveOnMapReq): A `MoveOnMapReq` which contains the following values: + - `ComponentName` [(resource.Name)](https://pkg.go.dev/go.viam.com/rdk/resource#Name): The `resource.Name` of the base to move. + - `Destination` [(spatialmath.Pose)](https://pkg.go.dev/go.viam.com/rdk/spatialmath#Pose): The destination, which can be any [Pose](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.Pose) with respect to the SLAM map's origin. + - `SlamName` [(resource.Name)](https://pkg.go.dev/go.viam.com/rdk/resource#Name): The `resource.Name` of the [SLAM service](/services/slam/) from which the SLAM map is requested. + - `MotionConfig` [(\*MotionConfiguration)](https://pkg.go.dev/go.viam.com/rdk/services/motion#MotionConfiguration): The configuration you want to set across this machine for this motion service. This parameter and each of its fields are optional. + - `ObstacleDetectors` [([]ObstacleDetectorName)](https://pkg.go.dev/go.viam.com/rdk/services/motion#ObstacleDetectorName): The names of each [vision service](/services/vision/) and [camera](/components/camera/) resource pair you want to use for transient obstacle avoidance. + - `PositionPollingFreqHz` [(float64)](https://pkg.go.dev/builtin#float64): The frequency in hz to poll the position of the machine. + - `ObstaclePollingFreqHz` [(float64)](https://pkg.go.dev/builtin#float64): The frequency in hz to poll the vision service for new obstacles. + - `PlanDeviationM` [(float64)](https://pkg.go.dev/builtin#float64): The distance in meters that the machine can deviate from the motion plan. By default this is set to 2.6 m which is an appropriate value for outdoor usage. When you use the the **CONTROL** tab, the underlying calls to `MoveOnMap()` use 0.5 m instead. + - `LinearMPerSec` [(float64)](https://pkg.go.dev/builtin#float64): Linear velocity this machine should target when moving. + - `AngularDegsPerSec` [(float64)](https://pkg.go.dev/builtin#float64): Angular velocity this machine should target when turning. + - `Obstacles` [(\[\]spatialmath.Geometry)](https://pkg.go.dev/go.viam.com/rdk/spatialmath#Geometry): Obstacles, specified in the SLAM frame coordinate system, to be considered when planning the motion of the component. + - `Extra` [(map\[string\]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. + +**Returns:** + +- [(ExecutionID)](https://pkg.go.dev/go.viam.com/rdk/services/motion#ExecutionID): ExecutionID of the `MoveOnMap` call. +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +**Example:** + +```go {class="line-numbers linkable-line-numbers"} +// Assumes a base with the name "my_base" is configured on the machine +myBaseResourceName := base.Named("my_base") +mySLAMServiceResourceName := slam.Named("my_slam_service") + +// Define a destination Pose +myPose := spatialmath.NewPoseFromPoint(r3.Vector{Y: 10}) + +// Move the base component to the destination pose +executionID, err := motionService.MoveOnMap(context.Background(), motion.MoveOnMapReq{ + ComponentName: myBaseResourceName, + Destination: myPose, + SlamName: mySLAMServiceResourceName, +}) + +// MoveOnMap is a non-blocking method and this line can optionally be added to block until the movement is done +err = motion.PollHistoryUntilSuccessOrError( + context.Background(), + motionService, + time.Duration(time.Second), + motion.PlanHistoryReq{ + ComponentName: myBaseResourceName, + ExecutionID: executionID, + }, +) +``` + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/services/motion#Service). + +{{% /tab %}} +{{< /tabs >}} + +### MoveOnGlobe + +Move a [base](/components/base/) component to a destination GPS point, represented in geographic notation _(latitude, longitude)_. +Use a [movement sensor](/components/movement-sensor/) to check the location of the machine. + +`MoveOnGlobe()` is non blocking, meaning the motion service will move the component to the destination GPS point after `MoveOnGlobe()` returns. + +Each successful `MoveOnGlobe()` call returns a unique `ExecutionID` which you can use to identify all plans generated during the `MoveOnGlobe()`. + +{{< alert title="Info" color="info" >}} +If you specify a goal pose and the robot's current position is already within the set `PlanDeviationM`, `MoveOnGlobe` returns an error. +{{< /alert >}} + +You can monitor the progress of the `MoveOnGlobe()` call by querying `GetPlan()` and `ListPlanStatuses()`. + +`MoveOnGlobe()` is intended for use with the [navigation service](/services/navigation/), providing autonomous GPS navigation for rover [bases](/components/base/). + +{{< alert title="Requirements" color="info" >}} +To use `MoveOnGlobe()`, your movement sensor must be able to measure the GPS location and orientation of the machine. + +Make sure the [movement sensor](/components/movement-sensor/) you use supports usage of the following methods in its {{< glossary_tooltip term_id="model" text="model's" >}} implementation of the [movement sensor API](/components/movement-sensor/#api). + +- It must support `GetPosition()` to report the machine's current GPS location. +- It must **also** support **either** `GetCompassHeading()` or `GetOrientation()` to report which way the machine is facing. +- If your movement sensor provides multiple methods, your machine will default to using the values returned by `GetCompassHeading()`. + {{< /alert >}} + +{{< alert title="Stability Notice" color="alert" >}} + +The `heading` parameter is experimental. +Specifying `heading` in a request to `MoveOnGlobe` is not currently recommended if the minimum turning radius of your component is greater than zero, as this combination may cause high latency in the [motion planning algorithms](/services/motion/algorithms/). + +Specifying `obstacles` in a request to `MoveOnGlobe()` will cause an error if you configure a `"translation"` in the `"geometries"` of any of the `GeoGeometry` objects. +Translation in obstacles is not supported by the [navigation service](/services/navigation/). + +{{< /alert >}} + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `component_name` ([viam.proto.common.ResourceName](https://python.viam.dev/autoapi/viam/services/motion/client/index.html#viam.services.motion.client.ResourceName)) (required): The `ResourceName` of the base to move. +- `destination` ([viam.proto.common.GeoPoint](https://python.viam.dev/autoapi/viam/services/motion/client/index.html#viam.services.motion.client.GeoPoint)) (required): The location of the component's destination, represented in geographic notation as a [GeoPoint](https://python.viam.dev/autoapi/viam/components/movement_sensor/index.html#viam.components.movement_sensor.GeoPoint) _(lat, lng)_. +- `movement_sensor_name` ([viam.proto.common.ResourceName](https://python.viam.dev/autoapi/viam/services/motion/client/index.html#viam.services.motion.client.ResourceName)) (required): The `ResourceName` of the [movement sensor](/components/movement-sensor/) that you want to use to check the machine's location. +- `obstacles` ([Sequence[viam.proto.common.GeoGeometry]](https://python.viam.dev/autoapi/viam/services/motion/client/index.html#viam.services.motion.client.GeoGeometry)) (optional): Obstacles to consider when planning the motion of the component, with each represented as a `GeoGeometry`.
  • Default: `None`
+- `heading` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (optional): The compass heading, in degrees, that the machine's movement sensor should report at the `destination` point.
  • Range: `[0-360)` `0`: North, `90`: East, `180`: South, `270`: West
  • Default: `None`
+- `configuration` ([viam.proto.service.motion.MotionConfiguration](https://python.viam.dev/autoapi/viam/services/motion/client/index.html#viam.services.motion.client.MotionConfiguration)) (optional): The configuration you want to set across this machine for this motion service. This parameter and each of its fields are optional. + - `obstacle_detectors` [(Iterable[ObstacleDetector])](https://python.viam.dev/autoapi/viam/proto/service/motion/index.html#viam.proto.service.motion.ObstacleDetector): The names of each [vision service](/services/vision/) and [camera](/components/camera/) resource pair you want to use for transient obstacle avoidance. + - `position_polling_frequency_hz` [(float)](https://docs.python.org/3/library/functions.html#float): The frequency in hz to poll the position of the machine. + - `obstacle_polling_frequency_hz` [(float)](https://docs.python.org/3/library/functions.html#float): The frequency in hz to poll the vision service for new obstacles. + - `plan_deviation_m` [(float)](https://docs.python.org/3/library/functions.html#float): The distance in meters that the machine can deviate from the motion plan. + - `linear_m_per_sec` [(float)](https://docs.python.org/3/library/functions.html#float): Linear velocity this machine should target when moving. + - `angular_degs_per_sec` [(float)](https://docs.python.org/3/library/functions.html#float): Angular velocity this machine should target when turning. +- `bounding_regions` ([Sequence[viam.proto.common.GeoGeometry]](https://python.viam.dev/autoapi/viam/services/motion/client/index.html#viam.services.motion.client.GeoGeometry)) (optional): Set of obstacles which the robot must remain within while navigating. +- `extra` (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), Any]) (optional): Extra options to pass to the underlying RPC call. +- `timeout` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. + +**Returns:** + +- ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)): ExecutionID of the `MoveOnGlobe` call. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +motion = MotionClient.from_robot(robot=robot, name="builtin") + +# Get the ResourceNames of the base and movement sensor +my_base_resource_name = Base.get_resource_name("my_base") +mvmnt_sensor_resource_name = MovementSensor.get_resource_name( + "my_movement_sensor") +# Define a destination GeoPoint at the GPS coordinates [0, 0] +my_destination = movement_sensor.GeoPoint(latitude=0, longitude=0) + +# Move the base component to the designated geographic location, as reported by the movement sensor +execution_id = await motion.move_on_globe( + component_name=my_base_resource_name, + destination=my_destination, + movement_sensor_name=mvmnt_sensor_resource_name) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/services/motion/client/index.html#viam.services.motion.client.MotionClient.move_on_globe). + +{{% /tab %}} +{{% tab name="Go" %}} + +**Parameters:** + +- `ctx` [(Context)](https://pkg.go.dev/context#Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. +- `req` [(MoveOnGlobeReq)](https://pkg.go.dev/go.viam.com/rdk/services/motion#MoveOnGlobeReq): A `MoveOnGlobeReq` which contains the following values: + - `componentName` [(resource.Name)](https://pkg.go.dev/go.viam.com/rdk/resource#Name): The `resource.Name` of the base to move. + - `destination` [(\*geo.Point)](https://pkg.go.dev/github.com/kellydunn/golang-geo#Point): The location of the component's destination, represented in geographic notation as a [Point](https://pkg.go.dev/github.com/kellydunn/golang-geo#Point) _(lat, lng)_. + - `heading` [(float64)](https://pkg.go.dev/builtin#float64): The compass heading, in degrees, that the machine's movement sensor should report at the `destination` point.
  • Range: `[0-360)` 0: North, 90: East, 180: South, 270: West
  • Default: `0`
+ - `movementSensorName` [(resource.Name)](https://pkg.go.dev/go.viam.com/rdk/resource#Name): The `resource.Name` of the [movement sensor](/components/movement-sensor/) that you want to use to check the machine's location. + - `obstacles` [([]\*spatialmath.GeoGeometry)](https://pkg.go.dev/go.viam.com/rdk/spatialmath#GeoGeometry): Obstacles to consider when planning the motion of the component, with each represented as a `GeoGeometry`.
  • Default: `nil`
+ - `motionConfig` [(\*MotionConfiguration)](https://pkg.go.dev/go.viam.com/rdk/services/motion#MotionConfiguration): The configuration you want to set across this machine for this motion service. This parameter and each of its fields are optional. + - `ObstacleDetectors` [([]ObstacleDetectorName)](https://pkg.go.dev/go.viam.com/rdk/services/motion#ObstacleDetectorName): The names of each [vision service](/services/vision/) and [camera](/components/camera/) resource pair you want to use for transient obstacle avoidance. + - `PositionPollingFreqHz` [(float64)](https://pkg.go.dev/builtin#float64): The frequency in hz to poll the position of the machine. + - `ObstaclePollingFreqHz` [(float64)](https://pkg.go.dev/builtin#float64): The frequency in hz to poll the vision service for new obstacles. + - `PlanDeviationM` [(float64)](https://pkg.go.dev/builtin#float64): The distance in meters that the machine can deviate from the motion plan. + - `LinearMPerSec` [(float64)](https://pkg.go.dev/builtin#float64): Linear velocity this machine should target when moving. + - `AngularDegsPerSec` [(float64)](https://pkg.go.dev/builtin#float64): Angular velocity this machine should target when turning. + +**Returns:** + +- [(ExecutionID)](https://pkg.go.dev/go.viam.com/rdk/services/motion#ExecutionID): ExecutionID of the `MoveOnGlobe` call. +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +**Example:** + +```go {class="line-numbers linkable-line-numbers"} +// Assumes a base with the name "myBase" is configured on the machine +// Get the resource names of the base and movement sensor +myBaseResourceName := base.Named("myBase") +myMvmntSensorResourceName := movementsensor.Named("my_movement_sensor") + +// Define a destination Point at the GPS coordinates [0, 0] +myDestination := geo.NewPoint(0, 0) + +// Move the base component to the designated geographic location, as reported by the movement sensor +executionID, err := motionService.MoveOnGlobe(context.Background(), motion.MoveOnGlobeReq{ + ComponentName: myBaseResourceName, + Destination: myDestination, + MovementSensorName: myMvmntSensorResourceName, +}) + +// Assumes there is an active MoveOnMap() or MoveonGlobe() in progress for myBase +// MoveOnGlobe is a non-blocking method and this line can optionally be added to block until the movement is done +err = motion.PollHistoryUntilSuccessOrError( + context.Background(), + motionService, + time.Duration(time.Second), + motion.PlanHistoryReq{ + ComponentName: myBaseResourceName, + ExecutionID: executionID, + }, +) +``` + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/services/motion#Service). + +{{% /tab %}} +{{< /tabs >}} + +### GetPose + +`GetPose` gets the location and orientation of a component within the [frame system](../frame-system/). +The return type of this function is a `PoseInFrame` describing the pose of the specified component with respect to the specified destination frame. +You can use the `supplemental_transforms` argument to augment the machine's existing frame system with supplemental frames. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `component_name` ([viam.proto.common.ResourceName](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.ResourceName)) (required): The `ResourceName` of the piece of the machine whose pose is returned. +- `destination_frame` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): The name of the frame with respect to which the component's pose is reported. +- `supplemental_transforms` ([List[viam.proto.common.Transform]](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.Transform)) (optional): A list of `Transform` objects. + A `Transform` represents an additional frame which is added to the machine's frame system. + It consists of the following fields: + + - `pose_in_observer_frame`: Provides the relationship between the frame being added and another frame. + - `physical_object`: An optional `Geometry` can be added to the frame being added. + - `reference_frame`: Specifies the name of the frame which will be added to the frame system. + + When `supplemental_transforms` are provided, a frame system is created within the context of the `GetPose` function. + This new frame system builds off the machine's frame system and incorporates the `Transform`s provided. + If the result of adding the `Transform`s results in a disconnected frame system, an error is thrown. +- `extra` (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), Any]) (optional): Extra options to pass to the underlying RPC call. +- `timeout` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. + +**Returns:** + +- ([viam.proto.common.PoseInFrame](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.PoseInFrame)): Pose of the given component and the frame in which it was observed. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +# Note that the example uses the ``Arm`` class, but any component class that inherits from ``ComponentBase`` will work +# (``Base``, ``Gripper``, etc). + +# Create a `component_name`: +component_name = Arm.get_resource_name("arm") + +from viam.components.gripper import Gripper +from viam.services.motion import MotionClient + +# Assume that the connect function is written and will return a valid machine. +robot = await connect() + +motion = MotionClient.from_robot(robot=robot, name="builtin") +gripperName = Gripper.get_resource_name("my_gripper") +gripperPoseInWorld = await motion.get_pose(component_name=gripperName, + destination_frame="world") +``` + +The following code example gets the pose of the tip of a [gripper](/components/gripper/) named `my_gripper` which is attached to the end of an arm, in the "world" `reference_frame`: + +```python {class="line-numbers linkable-line-numbers"} +from viam.components.gripper import Gripper +from viam.services.motion import MotionClient + +# Assume that the connect function is written and will return a valid machine. +robot = await connect() + +motion = MotionClient.from_robot(robot=robot, name="builtin") +gripperName = Gripper.get_resource_name("my_gripper") +gripperPoseInWorld = await motion.get_pose(component_name=gripperName, + destination_frame="world") +``` + +For a more complicated example, take the same scenario and get the pose of the same gripper with respect to an object situated at a location (100, 200, 0) relative to the "world" frame: + +```python {class="line-numbers linkable-line-numbers"} +from viam.components.gripper import Gripper +from viam.services.motion import MotionClient +from viam.proto.common import Transform, PoseInFrame, Pose + +# Assume that the connect function is written and will return a valid machine. +robot = await connect() + +motion = MotionClient.from_robot(robot=robot, name="builtin") +objectPose = Pose(x=100, y=200, z=0, o_x=0, o_y=0, o_z=1, theta=0) +objectPoseInFrame = PoseInFrame(reference_frame="world", pose=objectPose) +objectTransform = Transform(reference_frame="object", + pose_in_observer_frame=objectPoseInFrame) +gripperName = Gripper.get_resource_name("my_gripper") +gripperPoseInObjectFrame = await motion.get_pose( + component_name=gripperName, + destination_frame="world", + supplemental_transforms=objectTransform +) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/services/motion/client/index.html#viam.services.motion.client.MotionClient.get_pose). + +{{% /tab %}} +{{% tab name="Go" %}} + +**Parameters:** + +- `ctx` [(Context)](https://pkg.go.dev/context#Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. +- `componentName` [(resource.Name)](https://pkg.go.dev/go.viam.com/rdk/resource#Name): The `resource.Name` of the piece of the machine whose pose is returned. +- `destinationFrame` [(string)](https://pkg.go.dev/builtin#string): The name of the frame with respect to which the component's pose is reported. +- `supplementalTransforms` [([]*referenceframe.LinkInFrame)](https://pkg.go.dev/go.viam.com/rdk/referenceframe#LinkInFrame): An optional list of `LinkInFrame`s. + A `LinkInFrame` represents an additional frame which is added to the machine's frame system. + It consists of: + + - a `PoseInFrame`: Provides the relationship between the frame being added and another frame. + - `Geometry`: An optional `Geometry` can be added to the frame being added. + When `supplementalTransforms` are provided, a frame system is created within the context of the `GetPose` function. + This new frame system builds off the machine's frame system and incorporates the `LinkInFrame`s provided. + If the result of adding the `LinkInFrame`s results in a disconnected frame system, an error is thrown. +- `extra` [(map[string]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. + +**Returns:** + +- [(*referenceframe.PoseInFrame)](https://pkg.go.dev/go.viam.com/rdk/referenceframe#PoseInFrame): The pose of the component. +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +**Example:** + +```go {class="line-numbers linkable-line-numbers"} +// Insert code to connect to your machine. +// (see CONNECT tab of your machine's page in the Viam app) + +// Assumes a gripper configured with name "my_gripper" on the machine +gripperName := gripper.Named("my_gripper") + +// Access the motion service +motionService, err := motion.FromRobot(machine, "builtin") +if err != nil { + logger.Fatal(err) +} + +myArmMotionPose, err := motionService.GetPose(context.Background(), my_gripper, referenceframe.World, nil, nil) +if err != nil { + logger.Fatal(err) +} +logger.Info("Position of myArm from the motion service:", myArmMotionPose.Pose().Point()) +logger.Info("Orientation of myArm from the motion service:", myArmMotionPose.Pose().Orientation()) +``` + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/services/motion#Service). + +{{% /tab %}} +{{< /tabs >}} + +### StopPlan + +Stop a [base](/components/base/) component being moved by an in progress [`MoveOnGlobe`](/services/motion/#moveonglobe) or [`MoveOnMap`](/services/motion/#moveonmap) call. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `component_name` ([viam.proto.common.ResourceName](https://python.viam.dev/autoapi/viam/services/motion/client/index.html#viam.services.motion.client.ResourceName)) (required): The `ResourceName` of the piece of the robot that should arrive at the destination. Note that `move` moves the distal end of the component to the destination. For example, when moving a robotic arm, the piece that will arrive at the destination is the end effector attachment point, not the base of the arm. +- `extra` (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), Any]) (optional): Extra options to pass to the underlying RPC call. +- `timeout` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. + +**Returns:** + +- None. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +# Assuming a `move_on_globe()` started the execution +# Stop the base component which was instructed to move by `move_on_globe()` +# or `move_on_map()` +my_base_resource_name = Base.get_resource_name("my_base") +await motion.stop_plan(component_name=mvmnt_sensor) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/services/motion/client/index.html#viam.services.motion.client.MotionClient.stop_plan). + +{{% /tab %}} +{{% tab name="Go" %}} + +**Parameters:** + +- `ctx` [(Context)](https://pkg.go.dev/context#Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. +- `req` [(StopPlanReq)](https://pkg.go.dev/go.viam.com/rdk/services/motion#StopPlanReq): A `StopPlanReq` which contains the following values: + - `componentName` [(resource.Name)](https://pkg.go.dev/go.viam.com/rdk/resource#Name): The `resource.Name` of the base to stop. + - `extra` [(map\[string\]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. + +**Returns:** + +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +**Example:** + +```go {class="line-numbers linkable-line-numbers"} +motionService, err := motion.FromRobot(machine, "builtin") +myBaseResourceName := base.Named("myBase") + +myMvmntSensorResourceName := movement_sensor.Named("my_movement_sensor") +myDestination := geo.NewPoint(0, 0) + +// Assuming a `MoveOnGlobe()`` started the execution +// Stop the base component which was instructed to move by `MoveOnGlobe()` or `MoveOnMap()` +err := motionService.StopPlan(context.Background(), motion.StopPlanReq{ + ComponentName: s.req.ComponentName, +}) +``` + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/services/motion#Service). + +{{% /tab %}} +{{< /tabs >}} + +### ListPlanStatuses + +Returns the statuses of plans created by [`MoveOnGlobe`](/services/motion/#moveonglobe) or [`MoveOnMap`](/services/motion/#moveonmap) calls that meet at least one of the following conditions since the motion service initialized: + +- the plan's status is in progress +- the plan's status changed state within the last 24 hours + +All repeated fields are in chronological order. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `only_active_plans` ([bool](https://docs.python.org/3/library/stdtypes.html#boolean-type-bool)) (required): If supplied, the response will filter out any plans that are not executing. +- `extra` (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), Any]) (optional): Extra options to pass to the underlying RPC call. +- `timeout` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. + +**Returns:** + +- ([viam.proto.service.motion.ListPlanStatusesResponse](https://python.viam.dev/autoapi/viam/proto/service/motion/index.html#viam.proto.service.motion.ListPlanStatusesResponse)): List of last known statuses with the associated IDs of all plans within the TTL ordered by timestamp in ascending order. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +motion = MotionClient.from_robot(robot=robot, name="builtin") +# List the plan statuses of the motion service within the TTL +resp = await motion.list_plan_statuses() +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/services/motion/client/index.html#viam.services.motion.client.MotionClient.list_plan_statuses). + +{{% /tab %}} +{{% tab name="Go" %}} + +**Parameters:** + +- `ctx` [(Context)](https://pkg.go.dev/context#Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. +- `req` [(ListPlanStatusesReq)](https://pkg.go.dev/go.viam.com/rdk/services/motion#ListPlanStatusesReq): A `ListPlanStatusesReq` which contains the following values: + - `onlyActivePlans` [(bool)](https://pkg.go.dev/builtin#bool): If `true`, the response will only return plans which are executing. + - `extra` [(map\[string\]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. + +**Returns:** + +- [([]PlanStatusWithID)](https://pkg.go.dev/go.viam.com/rdk/services/motion#PlanStatusWithID): The state of a given plan at a point in time plus the `PlanId`, `ComponentName` and `ExecutionID` the status is associated with. +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +**Example:** + +```go {class="line-numbers linkable-line-numbers"} +motionService, err := motion.FromRobot(machine, "builtin") + +// Get the plan(s) of the base component's most recent execution i.e. `MoveOnGlobe()` or `MoveOnMap()` call. +planStatuses, err := motionService.ListPlanStatuses(context.Background(), motion.ListPlanStatusesReq{}) +``` + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/services/motion#Service). + +{{% /tab %}} +{{< /tabs >}} + +### GetPlan + +By default, returns the plan history of the most recent [`MoveOnGlobe`](/services/motion/#moveonglobe) or [`MoveOnMap`](/services/motion/#moveonmap) call to move a [base](/components/base/) component. + +The plan history for executions before the most recent can be requested by providing an `ExecutionID` in the request. + +Returns a result if both of the following conditions are met: + +- the execution (call to `MoveOnGlobe` or `MoveOnMap`) is still executing **or** changed state within the last 24 hours +- the machine has not reinitialized + +Plans never change. + +Replans always create new plans. + +Replans share the `ExecutionID` of the previously executing plan. + +All repeated fields are in chronological order. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `component_name` ([viam.proto.common.ResourceName](https://python.viam.dev/autoapi/viam/services/motion/client/index.html#viam.services.motion.client.ResourceName)) (required): The component to stop. +- `last_plan_only` ([bool](https://docs.python.org/3/library/stdtypes.html#boolean-type-bool)) (required): If supplied, the response will only return the last plan for the component / execution. +- `execution_id` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (optional): If supplied, the response will only return plans with the provided execution_id. +- `extra` (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), Any]) (optional): Extra options to pass to the underlying RPC call. +- `timeout` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. + +**Returns:** + +- ([viam.proto.service.motion.GetPlanResponse](https://python.viam.dev/autoapi/viam/proto/service/motion/index.html#viam.proto.service.motion.GetPlanResponse)): The current PlanWithStatus & replan history which matches the request. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +motion = MotionClient.from_robot(robot=robot, name="builtin") +my_base_resource_name = Base.get_resource_name("my_base") +# Get the plan(s) of the base component which was instructed to move by `MoveOnGlobe()` or `MoveOnMap()` +resp = await motion.get_plan(component_name=my_base_resource_name) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/services/motion/client/index.html#viam.services.motion.client.MotionClient.get_plan). + +{{% /tab %}} +{{% tab name="Go" %}} + +**Parameters:** + +- `ctx` [(Context)](https://pkg.go.dev/context#Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. +- `req` [(PlanHistoryReq)](https://pkg.go.dev/go.viam.com/rdk/services/motion#PlanHistoryReq): A `PlanHistoryReq` which contains the following values: + - `componentName` [(resource.Name)](https://pkg.go.dev/go.viam.com/rdk/resource#Name): The `resource.Name` of the base to stop. + - `lastPlanOnly` [(bool)](https://pkg.go.dev/builtin#bool): If `true`, the response will only return the the last plan for the component / execution + - `executionID` [(ExecutionID)](https://pkg.go.dev/go.viam.com/rdk/services/motion#PlanHistoryReq): If non empty, the response will return the plans of the provided execution & component. Useful for retrieving plans from executions before the current execution. + - `extra` [(map\[string\]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. + +**Returns:** + +- [([]PlanWithStatus)](https://pkg.go.dev/go.viam.com/rdk/services/motion#PlanWithStatus): PlanWithStatus contains a plan, its current status, and all state changes that came prior sorted by ascending timestamp. +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +**Example:** + +```go {class="line-numbers linkable-line-numbers"} +// Get the resource name of the base component +myBaseResourceName := base.Named("myBase") + +// Get the plan history of the base component's most recent execution (e.g., MoveOnGlobe or MoveOnMap call) +planHistory, err := motionService.PlanHistory(context.Background(), motion.PlanHistoryReq{ + ComponentName: myBaseResourceName, +}) +``` + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/services/motion#Service). + +{{% /tab %}} +{{< /tabs >}} + +### Reconfigure + +Reconfigure this resource. +Reconfigure must reconfigure the resource atomically and in place. + +{{< tabs >}} +{{% tab name="Go" %}} + +**Parameters:** + +- `ctx` [(Context)](https://pkg.go.dev/context#Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. +- `deps` [(Dependencies)](https://pkg.go.dev/go.viam.com/rdk/resource#Dependencies): The resource dependencies. +- `conf` [(Config)](https://pkg.go.dev/go.viam.com/rdk/resource#Config): The resource configuration. + +**Returns:** + +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Resource). + +{{% /tab %}} +{{< /tabs >}} + +### DoCommand + +Execute model-specific commands that are not otherwise defined by the service API. +For built-in service models, any model-specific commands available are covered with each model's documentation. +If you are implementing your own motion service and add features that have no built-in API method, you can access them with `DoCommand`. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- `command` (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), ValueTypes]) (required): The command to execute. +- `timeout` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. + +**Returns:** + +- (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), viam.utils.ValueTypes]): Result of the executed command. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +# Access the motion service +motion = MotionClient.from_robot(robot=robot, name="builtin") + +my_command = { + "command": "dosomething", + "someparameter": 52 +} + +await motion.do_command(my_command) +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/services/motion/client/index.html#viam.services.motion.client.MotionClient.do_command). + +{{% /tab %}} +{{% tab name="Go" %}} + +**Parameters:** + +- `ctx` [(Context)](https://pkg.go.dev/context#Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. +- `cmd` [(map[string]interface{})](https://go.dev/blog/maps): The command to execute. + +**Returns:** + +- [(map[string]interface{})](https://pkg.go.dev/builtin#string): The command response. +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +**Example:** + +```go {class="line-numbers linkable-line-numbers"} +// This example shows using DoCommand with an arm component. +myArm, err := arm.FromRobot(machine, "my_arm") + +command := map[string]interface{}{"cmd": "test", "data1": 500} +result, err := myArm.DoCommand(context.Background(), command) +``` + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Resource). + +{{% /tab %}} +{{< /tabs >}} + +### Close + +Safely shut down the resource and prevent further use. + +{{< tabs >}} +{{% tab name="Python" %}} + +**Parameters:** + +- None. + +**Returns:** + +- None. + +**Example:** + +```python {class="line-numbers linkable-line-numbers"} +await component.close() +``` + +For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/services/motion/client/index.html#viam.services.motion.client.MotionClient.close). + +{{% /tab %}} +{{% tab name="Go" %}} + +**Parameters:** + +- `ctx` [(Context)](https://pkg.go.dev/context#Context): A Context carries a deadline, a cancellation signal, and other values across API boundaries. + +**Returns:** + +- [(error)](https://pkg.go.dev/builtin#error): An error, if one occurred. + +**Example:** + +```go {class="line-numbers linkable-line-numbers"} +// This example shows using Close with an arm component. +myArm, err := arm.FromRobot(machine, "my_arm") + +err = myArm.Close(ctx) +``` + +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Resource). + +{{% /tab %}} +{{< /tabs >}} diff --git a/static/include/services/apis/ml.md b/static/include/services/apis/ml.md deleted file mode 100644 index 868b504512..0000000000 --- a/static/include/services/apis/ml.md +++ /dev/null @@ -1,7 +0,0 @@ - -Method Name | Description ------------ | ----------- -[`Infer`](/services/ml/deploy/#infer) | Take an already ordered input tensor as an array, make an inference on the model, and return an output tensor map. -[`Metadata`](/services/ml/deploy/#metadata) | Get the metadata (such as name, type, expected tensor/array shape, inputs, and outputs) associated with the ML model. -[`DoCommand`](/services/ml/deploy/#docommand) | Send arbitrary commands to the resource. -[`Close`](/services/ml/deploy/#close) | Safely shut down the resource and prevent further use. diff --git a/static/include/services/apis/motion.md b/static/include/services/apis/motion.md deleted file mode 100644 index e97049532e..0000000000 --- a/static/include/services/apis/motion.md +++ /dev/null @@ -1,12 +0,0 @@ - -Method Name | Description ------------ | ----------- -[`Move`](/services/motion/#move) | Move multiple components in a coordinated way to achieve a desired motion. -[`GetPose`](/services/motion/#getpose) | Get the current location and orientation of a component as a `Pose`. -[`MoveOnMap`](/services/motion/#moveonmap) | Move a [base](/components/base/) component to a `Pose` in respect to the origin of a {{< glossary_tooltip term_id="slam" text="SLAM" >}} map. Use the machine's position reported by the [SLAM service](/services/slam/) to check the location of the machine. -[`MoveOnGlobe`](/services/motion/#moveonglobe) | Move a [base](/components/base/) component to a destination GPS point. Use a [Movement Sensor](/components/movement-sensor/) to measure the machine's GPS coordinates. -[`StopPlan`](/services/motion/#stopplan) | Stop a [base](/components/base/) component being moved by an in progress [`MoveOnGlobe`](/services/motion/#moveonglobe) or [`MoveOnMap`](/services/motion/#moveonmap) call. -[`GetPlan`](/services/motion/#getplan) | Returns the plan history of the most recent [`MoveOnGlobe`](/services/motion/#moveonglobe) or [`MoveOnMap`](/services/motion/#moveonmap) call to move a [base](/components/base/) component. -[`ListPlanStatuses`](/services/motion/#listplanstatuses) | Returns the plan statuses created by [`MoveOnGlobe`](/services/motion/#moveonglobe) or [`MoveOnMap`](/services/motion/#moveonmap) calls. -[`DoCommand`](/services/motion/#docommand) | Send arbitrary commands to the resource. -[`Close`](/services/motion/#close) | Safely shut down the resource and prevent further use. diff --git a/static/include/services/apis/overrides/methods/go.mlmodel.Infer.Tensors.return.md b/static/include/services/apis/overrides/methods/go.mlmodel.Infer.Tensors.return.md new file mode 100644 index 0000000000..83eadc025c --- /dev/null +++ b/static/include/services/apis/overrides/methods/go.mlmodel.Infer.Tensors.return.md @@ -0,0 +1 @@ +The output map of tensors, as specified in the metadata, after being run through an inference engine. diff --git a/static/include/services/apis/overrides/methods/go.mlmodel.Infer.tensors.md b/static/include/services/apis/overrides/methods/go.mlmodel.Infer.tensors.md new file mode 100644 index 0000000000..b9004553d4 --- /dev/null +++ b/static/include/services/apis/overrides/methods/go.mlmodel.Infer.tensors.md @@ -0,0 +1 @@ +The input map of tensors, as specified in the metadata. diff --git a/static/include/services/apis/overrides/methods/go.mlmodel.Metadata.MLMetadata.return.md b/static/include/services/apis/overrides/methods/go.mlmodel.Metadata.MLMetadata.return.md new file mode 100644 index 0000000000..01ade19587 --- /dev/null +++ b/static/include/services/apis/overrides/methods/go.mlmodel.Metadata.MLMetadata.return.md @@ -0,0 +1 @@ +Name, type, expected tensor/array shape, inputs, and outputs associated with the ML model. diff --git a/static/include/services/apis/overrides/methods/go.motion.GetPose.PoseInFrame.return.md b/static/include/services/apis/overrides/methods/go.motion.GetPose.PoseInFrame.return.md new file mode 100644 index 0000000000..e61ea9b809 --- /dev/null +++ b/static/include/services/apis/overrides/methods/go.motion.GetPose.PoseInFrame.return.md @@ -0,0 +1 @@ +The pose of the component. diff --git a/static/include/services/apis/overrides/methods/go.motion.GetPose.componentName.md b/static/include/services/apis/overrides/methods/go.motion.GetPose.componentName.md new file mode 100644 index 0000000000..9866922cf7 --- /dev/null +++ b/static/include/services/apis/overrides/methods/go.motion.GetPose.componentName.md @@ -0,0 +1 @@ +The `resource.Name` of the piece of the machine whose pose is returned. diff --git a/static/include/services/apis/overrides/methods/go.motion.GetPose.destinationFrame.md b/static/include/services/apis/overrides/methods/go.motion.GetPose.destinationFrame.md new file mode 100644 index 0000000000..37f902f29c --- /dev/null +++ b/static/include/services/apis/overrides/methods/go.motion.GetPose.destinationFrame.md @@ -0,0 +1 @@ +The name of the frame with respect to which the component's pose is reported. diff --git a/static/include/services/apis/overrides/methods/go.motion.GetPose.supplementalTransforms.md b/static/include/services/apis/overrides/methods/go.motion.GetPose.supplementalTransforms.md new file mode 100644 index 0000000000..1008172cfe --- /dev/null +++ b/static/include/services/apis/overrides/methods/go.motion.GetPose.supplementalTransforms.md @@ -0,0 +1,10 @@ + +An optional list of `LinkInFrame`s. + A `LinkInFrame` represents an additional frame which is added to the machine's frame system. + It consists of: + + - a `PoseInFrame`: Provides the relationship between the frame being added and another frame. + - `Geometry`: An optional `Geometry` can be added to the frame being added. + When `supplementalTransforms` are provided, a frame system is created within the context of the `GetPose` function. + This new frame system builds off the machine's frame system and incorporates the `LinkInFrame`s provided. + If the result of adding the `LinkInFrame`s results in a disconnected frame system, an error is thrown. diff --git a/static/include/services/apis/overrides/methods/go.motion.ListPlanStatuses.PlanStatusWithID.return.md b/static/include/services/apis/overrides/methods/go.motion.ListPlanStatuses.PlanStatusWithID.return.md new file mode 100644 index 0000000000..54c6b79039 --- /dev/null +++ b/static/include/services/apis/overrides/methods/go.motion.ListPlanStatuses.PlanStatusWithID.return.md @@ -0,0 +1 @@ +The state of a given plan at a point in time plus the `PlanId`, `ComponentName` and `ExecutionID` the status is associated with. diff --git a/static/include/services/apis/overrides/methods/go.motion.ListPlanStatuses.req.md b/static/include/services/apis/overrides/methods/go.motion.ListPlanStatuses.req.md new file mode 100644 index 0000000000..380eb3846c --- /dev/null +++ b/static/include/services/apis/overrides/methods/go.motion.ListPlanStatuses.req.md @@ -0,0 +1,4 @@ + +A `ListPlanStatusesReq` which contains the following values: + - `onlyActivePlans` [(bool)](https://pkg.go.dev/builtin#bool): If `true`, the response will only return plans which are executing. + - `extra` [(map\[string\]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. diff --git a/static/include/services/apis/overrides/methods/go.motion.Move.bool.return.md b/static/include/services/apis/overrides/methods/go.motion.Move.bool.return.md new file mode 100644 index 0000000000..ad2b553727 --- /dev/null +++ b/static/include/services/apis/overrides/methods/go.motion.Move.bool.return.md @@ -0,0 +1 @@ +Whether the move was successful (`true`) or unsuccessful (`false`). diff --git a/static/include/services/apis/overrides/methods/go.motion.Move.componentName.md b/static/include/services/apis/overrides/methods/go.motion.Move.componentName.md new file mode 100644 index 0000000000..71aecae6a1 --- /dev/null +++ b/static/include/services/apis/overrides/methods/go.motion.Move.componentName.md @@ -0,0 +1,3 @@ +The `resource.Name` of the piece of the robot that should arrive at the destination. +Note that `Move` moves the distal end of the component to the destination. +For example, when moving a robotic arm, the piece that will arrive at the destination is the end effector attachment point, not the base of the arm. diff --git a/static/include/services/apis/overrides/methods/go.motion.Move.constraints.md b/static/include/services/apis/overrides/methods/go.motion.Move.constraints.md new file mode 100644 index 0000000000..f382f5cf49 --- /dev/null +++ b/static/include/services/apis/overrides/methods/go.motion.Move.constraints.md @@ -0,0 +1,2 @@ +Pass in optional [motion constraints](./constraints/). +By default, motion is unconstrained with the exception of obstacle avoidance. diff --git a/static/include/services/apis/overrides/methods/go.motion.Move.destination.md b/static/include/services/apis/overrides/methods/go.motion.Move.destination.md new file mode 100644 index 0000000000..311341cc9c --- /dev/null +++ b/static/include/services/apis/overrides/methods/go.motion.Move.destination.md @@ -0,0 +1,4 @@ +Describes where the `component_name` should end up. +Can be any pose, from the perspective of any component whose location is configured as a [`frame`](../frame-system/). +Note that the destination pose is relative to the distal end of the specified frame. +This means that if the `destination` is the same as the `component_name` frame, for example an arm's frame, then a pose of `{X: 10, Y: 0, Z: 0}` will move that arm’s end effector by 10 mm in the local `X` direction. diff --git a/static/include/services/apis/overrides/methods/go.motion.Move.worldState.md b/static/include/services/apis/overrides/methods/go.motion.Move.worldState.md new file mode 100644 index 0000000000..e5abd332d4 --- /dev/null +++ b/static/include/services/apis/overrides/methods/go.motion.Move.worldState.md @@ -0,0 +1,21 @@ + +Data structure specifying information about the world around the machine. + Used to augment the motion solving process. + `worldState` includes obstacles and transforms: + + - **Obstacles**: Geometries located at a pose relative to some frame. + When solving a motion plan with movable frames that contain inherent geometries, the solved path is constrained such that none of those inherent geometries intersect with the obstacles. + Important considerations: + - If a motion begins with a component already in collision with an obstacle, collisions between that specific component and that obstacle will not be checked. + - The motion service assumes that obstacles are static. + If a worldstate obstacle is physically attached to a part of the robot such that it will move with the robot, specify it with _transforms_. + - Obstacles are defined by a [(pose)](https://pkg.go.dev/go.viam.com/rdk/spatialmath#Pose) and a [(geometry)](https://pkg.go.dev/go.viam.com/rdk/spatialmath#Geometry) with dimensions. + The pose location is the point at the center of the geometry. + - Obstacle locations are defined with respect to the _origin_ of the specified frame. + Their poses are relative to the _origin_ of the specified frame. + An obstacle associated with the frame of an arm with a pose of {X: 0, Y: 0, Z: -10} is interpreted as being 10mm below the base of the arm, not 10mm below the end effector. + This is different from `destination` and `componentName`, where poses are relative to the distal end of a frame. + - **Transforms**: A list of `PoseInFrame` messages that specify other transformations to temporarily add to the frame system at solve time. + Transforms can be used to account for geometries that are attached to the robot but not configured as robot components. + For example, you could use a transform to represent the volume of a marker held in your machine's gripper. + Transforms are not added to the config or carried into later processes. diff --git a/static/include/services/apis/overrides/methods/go.motion.MoveOnGlobe.ExecutionID.return.md b/static/include/services/apis/overrides/methods/go.motion.MoveOnGlobe.ExecutionID.return.md new file mode 100644 index 0000000000..2744731541 --- /dev/null +++ b/static/include/services/apis/overrides/methods/go.motion.MoveOnGlobe.ExecutionID.return.md @@ -0,0 +1 @@ +ExecutionID of the `MoveOnGlobe` call. diff --git a/static/include/services/apis/overrides/methods/go.motion.MoveOnGlobe.req.md b/static/include/services/apis/overrides/methods/go.motion.MoveOnGlobe.req.md new file mode 100644 index 0000000000..3776f22cf5 --- /dev/null +++ b/static/include/services/apis/overrides/methods/go.motion.MoveOnGlobe.req.md @@ -0,0 +1,14 @@ + +A `MoveOnGlobeReq` which contains the following values: + - `componentName` [(resource.Name)](https://pkg.go.dev/go.viam.com/rdk/resource#Name): The `resource.Name` of the base to move. + - `destination` [(\*geo.Point)](https://pkg.go.dev/github.com/kellydunn/golang-geo#Point): The location of the component's destination, represented in geographic notation as a [Point](https://pkg.go.dev/github.com/kellydunn/golang-geo#Point) _(lat, lng)_. + - `heading` [(float64)](https://pkg.go.dev/builtin#float64): The compass heading, in degrees, that the machine's movement sensor should report at the `destination` point.
  • Range: `[0-360)` 0: North, 90: East, 180: South, 270: West
  • Default: `0`
+ - `movementSensorName` [(resource.Name)](https://pkg.go.dev/go.viam.com/rdk/resource#Name): The `resource.Name` of the [movement sensor](/components/movement-sensor/) that you want to use to check the machine's location. + - `obstacles` [([]\*spatialmath.GeoGeometry)](https://pkg.go.dev/go.viam.com/rdk/spatialmath#GeoGeometry): Obstacles to consider when planning the motion of the component, with each represented as a `GeoGeometry`.
  • Default: `nil`
+ - `motionConfig` [(\*MotionConfiguration)](https://pkg.go.dev/go.viam.com/rdk/services/motion#MotionConfiguration): The configuration you want to set across this machine for this motion service. This parameter and each of its fields are optional. + - `ObstacleDetectors` [([]ObstacleDetectorName)](https://pkg.go.dev/go.viam.com/rdk/services/motion#ObstacleDetectorName): The names of each [vision service](/services/vision/) and [camera](/components/camera/) resource pair you want to use for transient obstacle avoidance. + - `PositionPollingFreqHz` [(float64)](https://pkg.go.dev/builtin#float64): The frequency in hz to poll the position of the machine. + - `ObstaclePollingFreqHz` [(float64)](https://pkg.go.dev/builtin#float64): The frequency in hz to poll the vision service for new obstacles. + - `PlanDeviationM` [(float64)](https://pkg.go.dev/builtin#float64): The distance in meters that the machine can deviate from the motion plan. + - `LinearMPerSec` [(float64)](https://pkg.go.dev/builtin#float64): Linear velocity this machine should target when moving. + - `AngularDegsPerSec` [(float64)](https://pkg.go.dev/builtin#float64): Angular velocity this machine should target when turning. diff --git a/static/include/services/apis/overrides/methods/go.motion.MoveOnMap.ExecutionID.return.md b/static/include/services/apis/overrides/methods/go.motion.MoveOnMap.ExecutionID.return.md new file mode 100644 index 0000000000..dd2b901859 --- /dev/null +++ b/static/include/services/apis/overrides/methods/go.motion.MoveOnMap.ExecutionID.return.md @@ -0,0 +1 @@ +ExecutionID of the `MoveOnMap` call. diff --git a/static/include/services/apis/overrides/methods/go.motion.MoveOnMap.req.md b/static/include/services/apis/overrides/methods/go.motion.MoveOnMap.req.md new file mode 100644 index 0000000000..c5c231514d --- /dev/null +++ b/static/include/services/apis/overrides/methods/go.motion.MoveOnMap.req.md @@ -0,0 +1,14 @@ + +A `MoveOnMapReq` which contains the following values: + - `ComponentName` [(resource.Name)](https://pkg.go.dev/go.viam.com/rdk/resource#Name): The `resource.Name` of the base to move. + - `Destination` [(spatialmath.Pose)](https://pkg.go.dev/go.viam.com/rdk/spatialmath#Pose): The destination, which can be any [Pose](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.Pose) with respect to the SLAM map's origin. + - `SlamName` [(resource.Name)](https://pkg.go.dev/go.viam.com/rdk/resource#Name): The `resource.Name` of the [SLAM service](/services/slam/) from which the SLAM map is requested. + - `MotionConfig` [(\*MotionConfiguration)](https://pkg.go.dev/go.viam.com/rdk/services/motion#MotionConfiguration): The configuration you want to set across this machine for this motion service. This parameter and each of its fields are optional. + - `ObstacleDetectors` [([]ObstacleDetectorName)](https://pkg.go.dev/go.viam.com/rdk/services/motion#ObstacleDetectorName): The names of each [vision service](/services/vision/) and [camera](/components/camera/) resource pair you want to use for transient obstacle avoidance. + - `PositionPollingFreqHz` [(float64)](https://pkg.go.dev/builtin#float64): The frequency in hz to poll the position of the machine. + - `ObstaclePollingFreqHz` [(float64)](https://pkg.go.dev/builtin#float64): The frequency in hz to poll the vision service for new obstacles. + - `PlanDeviationM` [(float64)](https://pkg.go.dev/builtin#float64): The distance in meters that the machine can deviate from the motion plan. By default this is set to 2.6 m which is an appropriate value for outdoor usage. When you use the the **CONTROL** tab, the underlying calls to `MoveOnMap()` use 0.5 m instead. + - `LinearMPerSec` [(float64)](https://pkg.go.dev/builtin#float64): Linear velocity this machine should target when moving. + - `AngularDegsPerSec` [(float64)](https://pkg.go.dev/builtin#float64): Angular velocity this machine should target when turning. + - `Obstacles` [(\[\]spatialmath.Geometry)](https://pkg.go.dev/go.viam.com/rdk/spatialmath#Geometry): Obstacles, specified in the SLAM frame coordinate system, to be considered when planning the motion of the component. + - `Extra` [(map\[string\]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. diff --git a/static/include/services/apis/overrides/methods/go.motion.PlanHistory.PlanWithStatus.return.md b/static/include/services/apis/overrides/methods/go.motion.PlanHistory.PlanWithStatus.return.md new file mode 100644 index 0000000000..95c83d0238 --- /dev/null +++ b/static/include/services/apis/overrides/methods/go.motion.PlanHistory.PlanWithStatus.return.md @@ -0,0 +1 @@ +PlanWithStatus contains a plan, its current status, and all state changes that came prior sorted by ascending timestamp. diff --git a/static/include/services/apis/overrides/methods/go.motion.PlanHistory.req.md b/static/include/services/apis/overrides/methods/go.motion.PlanHistory.req.md new file mode 100644 index 0000000000..546ac67393 --- /dev/null +++ b/static/include/services/apis/overrides/methods/go.motion.PlanHistory.req.md @@ -0,0 +1,6 @@ + +A `PlanHistoryReq` which contains the following values: + - `componentName` [(resource.Name)](https://pkg.go.dev/go.viam.com/rdk/resource#Name): The `resource.Name` of the base to stop. + - `lastPlanOnly` [(bool)](https://pkg.go.dev/builtin#bool): If `true`, the response will only return the the last plan for the component / execution + - `executionID` [(ExecutionID)](https://pkg.go.dev/go.viam.com/rdk/services/motion#PlanHistoryReq): If non empty, the response will return the plans of the provided execution & component. Useful for retrieving plans from executions before the current execution. + - `extra` [(map\[string\]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. diff --git a/static/include/services/apis/overrides/methods/go.motion.StopPlan.req.md b/static/include/services/apis/overrides/methods/go.motion.StopPlan.req.md new file mode 100644 index 0000000000..2597ef94c5 --- /dev/null +++ b/static/include/services/apis/overrides/methods/go.motion.StopPlan.req.md @@ -0,0 +1,4 @@ + +A `StopPlanReq` which contains the following values: + - `componentName` [(resource.Name)](https://pkg.go.dev/go.viam.com/rdk/resource#Name): The `resource.Name` of the base to stop. + - `extra` [(map\[string\]interface{})](https://go.dev/blog/maps): Extra options to pass to the underlying RPC call. diff --git a/static/include/services/apis/overrides/methods/python.motion.get_pose.component_name.md b/static/include/services/apis/overrides/methods/python.motion.get_pose.component_name.md new file mode 100644 index 0000000000..5b2fce69aa --- /dev/null +++ b/static/include/services/apis/overrides/methods/python.motion.get_pose.component_name.md @@ -0,0 +1 @@ +The `ResourceName` of the piece of the machine whose pose is returned. diff --git a/static/include/services/apis/overrides/methods/python.motion.get_pose.destination_frame.md b/static/include/services/apis/overrides/methods/python.motion.get_pose.destination_frame.md new file mode 100644 index 0000000000..37f902f29c --- /dev/null +++ b/static/include/services/apis/overrides/methods/python.motion.get_pose.destination_frame.md @@ -0,0 +1 @@ +The name of the frame with respect to which the component's pose is reported. diff --git a/static/include/services/apis/overrides/methods/python.motion.get_pose.supplemental_transforms.md b/static/include/services/apis/overrides/methods/python.motion.get_pose.supplemental_transforms.md new file mode 100644 index 0000000000..e3625492ff --- /dev/null +++ b/static/include/services/apis/overrides/methods/python.motion.get_pose.supplemental_transforms.md @@ -0,0 +1,12 @@ + +A list of `Transform` objects. + A `Transform` represents an additional frame which is added to the machine's frame system. + It consists of the following fields: + + - `pose_in_observer_frame`: Provides the relationship between the frame being added and another frame. + - `physical_object`: An optional `Geometry` can be added to the frame being added. + - `reference_frame`: Specifies the name of the frame which will be added to the frame system. + + When `supplemental_transforms` are provided, a frame system is created within the context of the `GetPose` function. + This new frame system builds off the machine's frame system and incorporates the `Transform`s provided. + If the result of adding the `Transform`s results in a disconnected frame system, an error is thrown. diff --git a/static/include/services/apis/overrides/methods/python.motion.move.component_name.md b/static/include/services/apis/overrides/methods/python.motion.move.component_name.md new file mode 100644 index 0000000000..494064755a --- /dev/null +++ b/static/include/services/apis/overrides/methods/python.motion.move.component_name.md @@ -0,0 +1,3 @@ +The `ResourceName` of the piece of the robot that should arrive at the destination. +Note that `move` moves the distal end of the component to the destination. +For example, when moving a robotic arm, the piece that will arrive at the destination is the end effector attachment point, not the base of the arm. diff --git a/static/include/services/apis/overrides/methods/python.motion.move.constraints.md b/static/include/services/apis/overrides/methods/python.motion.move.constraints.md new file mode 100644 index 0000000000..551d0ba379 --- /dev/null +++ b/static/include/services/apis/overrides/methods/python.motion.move.constraints.md @@ -0,0 +1,2 @@ +Pass in [motion constraints](./constraints/). +By default, motion is unconstrained with the exception of obstacle avoidance. diff --git a/static/include/services/apis/overrides/methods/python.motion.move.destination.md b/static/include/services/apis/overrides/methods/python.motion.move.destination.md new file mode 100644 index 0000000000..eed8bce1ea --- /dev/null +++ b/static/include/services/apis/overrides/methods/python.motion.move.destination.md @@ -0,0 +1,2 @@ +Describes where the `component_name` frame should be moved to. +Can be any pose, from the perspective of any component whose location is configured as a [`frame`](../frame-system/). diff --git a/static/include/services/apis/overrides/methods/python.motion.move.return.md b/static/include/services/apis/overrides/methods/python.motion.move.return.md new file mode 100644 index 0000000000..ad2b553727 --- /dev/null +++ b/static/include/services/apis/overrides/methods/python.motion.move.return.md @@ -0,0 +1 @@ +Whether the move was successful (`true`) or unsuccessful (`false`). diff --git a/static/include/services/apis/overrides/methods/python.motion.move.world_state.md b/static/include/services/apis/overrides/methods/python.motion.move.world_state.md new file mode 100644 index 0000000000..19cc07c247 --- /dev/null +++ b/static/include/services/apis/overrides/methods/python.motion.move.world_state.md @@ -0,0 +1,21 @@ + +Data structure specifying information about the world around the machine. + Used to augment the motion solving process. + `world_state` includes obstacles and transforms: + + - **Obstacles**: Geometries located at a pose relative to some frame. + When solving a motion plan with movable frames that contain inherent geometries, the solved path is constrained such that none of those inherent geometries intersect with the obstacles. + Important considerations: + - If a motion begins with a component already in collision with an obstacle, collisions between that specific component and that obstacle will not be checked. + - The motion service assumes that obstacles are static. + If a worldstate obstacle is physically attached to a part of the robot such that it will move with the robot, specify it with _transforms_. + - Obstacles are defined by a pose and a [geometry](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.Geometry) with dimensions. + The pose location is the point at the center of the geometry. + - Obstacle locations are defined with respect to the _origin_ of the specified frame. + Their poses are relative to the _origin_ of the specified frame. + An obstacle associated with the frame of an arm with a pose of {X: 0, Y: 0, Z: -10} is interpreted as being 10mm below the base of the arm, not 10mm below the end effector. + This is different from `destination` and `component_name`, where poses are relative to the distal end of a frame. + - **Transforms**: A list of `PoseInFrame` messages that specify other transformations to temporarily add to the frame system at solve time. + Transforms can be used to account for geometries that are attached to the robot but not configured as robot components. + For example, you could use a transform to represent the volume of a marker held in your machine's gripper. + Transforms are not added to the config or carried into later processes. diff --git a/static/include/services/apis/overrides/methods/python.motion.move_on_globe.component_name.md b/static/include/services/apis/overrides/methods/python.motion.move_on_globe.component_name.md new file mode 100644 index 0000000000..ba562a8cf5 --- /dev/null +++ b/static/include/services/apis/overrides/methods/python.motion.move_on_globe.component_name.md @@ -0,0 +1 @@ +The `ResourceName` of the base to move. diff --git a/static/include/services/apis/overrides/methods/python.motion.move_on_globe.configuration.md b/static/include/services/apis/overrides/methods/python.motion.move_on_globe.configuration.md new file mode 100644 index 0000000000..c993a5b9be --- /dev/null +++ b/static/include/services/apis/overrides/methods/python.motion.move_on_globe.configuration.md @@ -0,0 +1,8 @@ + +The configuration you want to set across this machine for this motion service. This parameter and each of its fields are optional. + - `obstacle_detectors` [(Iterable[ObstacleDetector])](https://python.viam.dev/autoapi/viam/proto/service/motion/index.html#viam.proto.service.motion.ObstacleDetector): The names of each [vision service](/services/vision/) and [camera](/components/camera/) resource pair you want to use for transient obstacle avoidance. + - `position_polling_frequency_hz` [(float)](https://docs.python.org/3/library/functions.html#float): The frequency in hz to poll the position of the machine. + - `obstacle_polling_frequency_hz` [(float)](https://docs.python.org/3/library/functions.html#float): The frequency in hz to poll the vision service for new obstacles. + - `plan_deviation_m` [(float)](https://docs.python.org/3/library/functions.html#float): The distance in meters that the machine can deviate from the motion plan. + - `linear_m_per_sec` [(float)](https://docs.python.org/3/library/functions.html#float): Linear velocity this machine should target when moving. + - `angular_degs_per_sec` [(float)](https://docs.python.org/3/library/functions.html#float): Angular velocity this machine should target when turning. diff --git a/static/include/services/apis/overrides/methods/python.motion.move_on_globe.destination.md b/static/include/services/apis/overrides/methods/python.motion.move_on_globe.destination.md new file mode 100644 index 0000000000..32c19c9f57 --- /dev/null +++ b/static/include/services/apis/overrides/methods/python.motion.move_on_globe.destination.md @@ -0,0 +1 @@ +The location of the component's destination, represented in geographic notation as a [GeoPoint](https://python.viam.dev/autoapi/viam/components/movement_sensor/index.html#viam.components.movement_sensor.GeoPoint) _(lat, lng)_. diff --git a/static/include/services/apis/overrides/methods/python.motion.move_on_globe.heading.md b/static/include/services/apis/overrides/methods/python.motion.move_on_globe.heading.md new file mode 100644 index 0000000000..07119e8631 --- /dev/null +++ b/static/include/services/apis/overrides/methods/python.motion.move_on_globe.heading.md @@ -0,0 +1 @@ +The compass heading, in degrees, that the machine's movement sensor should report at the `destination` point.
  • Range: `[0-360)` `0`: North, `90`: East, `180`: South, `270`: West
  • Default: `None`
diff --git a/static/include/services/apis/overrides/methods/python.motion.move_on_globe.movement_sensor_name.md b/static/include/services/apis/overrides/methods/python.motion.move_on_globe.movement_sensor_name.md new file mode 100644 index 0000000000..affc8eccd0 --- /dev/null +++ b/static/include/services/apis/overrides/methods/python.motion.move_on_globe.movement_sensor_name.md @@ -0,0 +1 @@ +The `ResourceName` of the [movement sensor](/components/movement-sensor/) that you want to use to check the machine's location. diff --git a/static/include/services/apis/overrides/methods/python.motion.move_on_globe.obstacles.md b/static/include/services/apis/overrides/methods/python.motion.move_on_globe.obstacles.md new file mode 100644 index 0000000000..ee2d08a112 --- /dev/null +++ b/static/include/services/apis/overrides/methods/python.motion.move_on_globe.obstacles.md @@ -0,0 +1 @@ +Obstacles to consider when planning the motion of the component, with each represented as a `GeoGeometry`.
  • Default: `None`
diff --git a/static/include/services/apis/overrides/methods/python.motion.move_on_globe.return.md b/static/include/services/apis/overrides/methods/python.motion.move_on_globe.return.md new file mode 100644 index 0000000000..2744731541 --- /dev/null +++ b/static/include/services/apis/overrides/methods/python.motion.move_on_globe.return.md @@ -0,0 +1 @@ +ExecutionID of the `MoveOnGlobe` call. diff --git a/static/include/services/apis/overrides/methods/python.motion.move_on_map.component_name.md b/static/include/services/apis/overrides/methods/python.motion.move_on_map.component_name.md new file mode 100644 index 0000000000..ba562a8cf5 --- /dev/null +++ b/static/include/services/apis/overrides/methods/python.motion.move_on_map.component_name.md @@ -0,0 +1 @@ +The `ResourceName` of the base to move. diff --git a/static/include/services/apis/overrides/methods/python.motion.move_on_map.configuration.md b/static/include/services/apis/overrides/methods/python.motion.move_on_map.configuration.md new file mode 100644 index 0000000000..92f52b9885 --- /dev/null +++ b/static/include/services/apis/overrides/methods/python.motion.move_on_map.configuration.md @@ -0,0 +1,8 @@ + +The configuration you want to set across this machine for this motion service. This parameter and each of its fields are optional. + - `obstacle_detectors` [(Iterable[ObstacleDetector])](https://python.viam.dev/autoapi/viam/proto/service/motion/index.html#viam.proto.service.motion.ObstacleDetector): The names of each [vision service](/services/vision/) and [camera](/components/camera/) resource pair you want to use for transient obstacle avoidance. + - `position_polling_frequency_hz` [(float)](https://docs.python.org/3/library/functions.html#float): The frequency in hz to poll the position of the machine. + - `obstacle_polling_frequency_hz` [(float)](https://docs.python.org/3/library/functions.html#float): The frequency in hz to poll the vision service for new obstacles. + - `plan_deviation_m` [(float)](https://docs.python.org/3/library/functions.html#float): The distance in meters that the machine can deviate from the motion plan. By default this is set to 2.6 m which is an appropriate value for outdoor usage. When you use the `MoveOnMap()` method from the **CONTROL** tab, the default is overwritten to 0.5 m for testing. + - `linear_m_per_sec` [(float)](https://docs.python.org/3/library/functions.html#float): Linear velocity this machine should target when moving. + - `angular_degs_per_sec` [(float)](https://docs.python.org/3/library/functions.html#float): Angular velocity this machine should target when turning. diff --git a/static/include/services/apis/overrides/methods/python.motion.move_on_map.destination.md b/static/include/services/apis/overrides/methods/python.motion.move_on_map.destination.md new file mode 100644 index 0000000000..3769d5bd70 --- /dev/null +++ b/static/include/services/apis/overrides/methods/python.motion.move_on_map.destination.md @@ -0,0 +1 @@ +The destination, which can be any [Pose](https://python.viam.dev/autoapi/viam/proto/common/index.html#viam.proto.common.Pose) with respect to the SLAM map's origin. diff --git a/static/include/services/apis/overrides/methods/python.motion.move_on_map.obstacles.md b/static/include/services/apis/overrides/methods/python.motion.move_on_map.obstacles.md new file mode 100644 index 0000000000..8906886a8c --- /dev/null +++ b/static/include/services/apis/overrides/methods/python.motion.move_on_map.obstacles.md @@ -0,0 +1 @@ +Obstacles, specified in the SLAM frame coordinate system, to be considered when planning the motion of the component. diff --git a/static/include/services/apis/overrides/methods/python.motion.move_on_map.return.md b/static/include/services/apis/overrides/methods/python.motion.move_on_map.return.md new file mode 100644 index 0000000000..dd2b901859 --- /dev/null +++ b/static/include/services/apis/overrides/methods/python.motion.move_on_map.return.md @@ -0,0 +1 @@ +ExecutionID of the `MoveOnMap` call. diff --git a/static/include/services/apis/overrides/methods/python.motion.move_on_map.slam_service_name.md b/static/include/services/apis/overrides/methods/python.motion.move_on_map.slam_service_name.md new file mode 100644 index 0000000000..bb7aa86b01 --- /dev/null +++ b/static/include/services/apis/overrides/methods/python.motion.move_on_map.slam_service_name.md @@ -0,0 +1 @@ +The `ResourceName` of the [SLAM service](/services/slam/) from which the SLAM map is requested. diff --git a/static/include/services/apis/overrides/methods/python.motion.stop_plan.component_name.md b/static/include/services/apis/overrides/methods/python.motion.stop_plan.component_name.md new file mode 100644 index 0000000000..494064755a --- /dev/null +++ b/static/include/services/apis/overrides/methods/python.motion.stop_plan.component_name.md @@ -0,0 +1,3 @@ +The `ResourceName` of the piece of the robot that should arrive at the destination. +Note that `move` moves the distal end of the component to the destination. +For example, when moving a robotic arm, the piece that will arrive at the destination is the end effector attachment point, not the base of the arm. From 14e303aed98fc0e4b74d410f70e1f29d028aca0a Mon Sep 17 00:00:00 2001 From: Sierra Guequierre Date: Mon, 8 Jul 2024 16:30:45 -0400 Subject: [PATCH 10/22] DOCS-2567: Apply feedback on motor quickstart pictures and add more detailed instructions (#3096) --- .../get-started/quickstarts/add-machine.png | Bin 0 -> 37964 bytes .../quickstarts/configure-motor.png | Bin 0 -> 227272 bytes .../get-started/quickstarts/configure-pi.png | Bin 0 -> 129764 bytes docs/get-started/quickstarts/control-motor.md | 35 ++++++++++++------ 4 files changed, 24 insertions(+), 11 deletions(-) create mode 100644 assets/get-started/quickstarts/add-machine.png create mode 100644 assets/get-started/quickstarts/configure-motor.png create mode 100644 assets/get-started/quickstarts/configure-pi.png diff --git a/assets/get-started/quickstarts/add-machine.png b/assets/get-started/quickstarts/add-machine.png new file mode 100644 index 0000000000000000000000000000000000000000..2d5491300b27ff9fa842f7229bf3d4a656a81326 GIT binary patch literal 37964 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%0DEKLCnz0O9!!earo;< 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 literal 0 HcmV?d00001 diff --git a/assets/get-started/quickstarts/configure-motor.png b/assets/get-started/quickstarts/configure-motor.png new file mode 100644 index 0000000000000000000000000000000000000000..52121441122db8d3a0e3488becd5fba02b59e744 GIT binary patch literal 227272 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¥fO7~tJ$xRRT#Cv&9< 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
    n*!y-u}>dJi|CJEIQ34n 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 H(I5W{N)oXR literal 0 HcmV?d00001 diff --git a/assets/get-started/quickstarts/configure-pi.png b/assets/get-started/quickstarts/configure-pi.png new file mode 100644 index 0000000000000000000000000000000000000000..dad62edd8ca4fb87f3f8fa4e146e3e4e971fa9f6 GIT binary patch literal 129764 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 -Go to the Viam app and [add a new machine](/cloud/machines/#add-a-new-machine). +Go to the Viam app. +Select a location and [add a new machine](/cloud/machines/#add-a-new-machine). -![The 'First Location' page on the Viam app with a new machine name in the New machine field and the Add machine button next to the field highlighted.](/fleet/app-usage/create-machine.png) +![The 'My Desk' page on the Viam app with a new machine name in the New machine field and the Add machine button next to the field highlighted.](/get-started/quickstarts/add-machine.png) {{< /expand >}} +{{< expand "Step 2: Install viam-server" >}} -{{< expand "Step 2: Configure a board" >}} +Navigate to the **CONFIGURE** tab of your machine's page in the [Viam app](https://app.viam.com). +Follow the {{< glossary_tooltip term_id="setup" text="setup instructions" >}} that appear on your new machine's **CONFIGURE** page to install `viam-server` on your computer and connect it to the Viam app. -Then, [add a board component](/components/board/), such as a [Raspberry Pi board](/components/board/pi/). +{{< /expand >}} +{{< expand "Step 3: Configure a board" >}} + +Then, [add a board component](/components/board/). + +Look through the [**Supported Models**](/components/motor/#supported-models) to determine the model of component to configure. +For example, configure a [`pi` board](/components/board/pi/) for a Raspberry Pi 4, Raspberry Pi 3 or Raspberry Pi Zero 2 W: -![An example board configuration in the app builder UI. The name (local), type (board) and model (pi) are shown. No other attributes are configured.](/components/board/pi-ui-config.png) +![An example board configuration in the app builder UI. The name (local), type (board) and model (pi) are shown. No other attributes are configured.](/get-started/quickstarts/configure-pi.png) + +Follow the instructions in the board model's documentation to configure any required attributes. {{< /expand >}} +{{< expand "Step 4: Configure a motor" >}} -{{< expand "Step 3: Configure a motor" >}} +[Add a motor component](/components/motor/) that supports the type of motor and motor driver you're using. +Look through the [**Supported Models**](/components/motor/#supported-models) to determine the model of component to configure. +For example, if you are using a standard DC motor (brushed or brushless) wired to a typical GPIO pin-controlled motor driver, configure a [`gpio` motor](/components/motor/gpio/): -[Add a motor component](/components/motor/), such as a [gpio motor](/components/motor/gpio/). -Ensure your motor, motor driver, and board are properly connected. +![The CONFIGURE tab of the Viam app populated with a configured gpio motor.](/get-started/quickstarts/configure-motor.png) -![The CONFIGURE tab of the Viam app populated with a configured gpio motor.](/components/motor/gpio-config-ui.png) +Follow the motor driver manufacturer's data sheet to properly wire your motor driver to your board and to your motor. +Follow the [model's documentation](/components/motor/) to configure the attributes so that the computer can send signals to the motor. {{< /expand >}} - -{{< expand "Step 4: Choose how you will control the motor" >}} +{{< expand "Step 5: Choose how you will control the motor" >}} You can control your motor directly from the Viam app, using the mobile app, or programmatically. From 421ac1d21137338b76389ef330ce416191fcdde7 Mon Sep 17 00:00:00 2001 From: Naomi Pentrel <5212232+npentrel@users.noreply.github.com> Date: Mon, 8 Jul 2024 22:08:09 +0100 Subject: [PATCH 11/22] Update contributing.md --- docs/appendix/contributing.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/appendix/contributing.md b/docs/appendix/contributing.md index b5e5798c7f..569bd44582 100644 --- a/docs/appendix/contributing.md +++ b/docs/appendix/contributing.md @@ -91,7 +91,7 @@ The docs use the [Diátaxis Framework](https://diataxis.fr/) as the basis of t {{< /expand >}} -- **How-to Guide (procedural)**: A task-oriented piece of content that directs a reader to perform actions step by step to complete a task, like a recipe. +- **How-to Guide (procedural)**: A task-oriented piece of content that directs a reader to perform actions step by step to complete a task, like instructions to sauté onions. Generally starts with a description of the task and things to consider, and then provides a set of numbered steps to follow. For example, the [Installation page](/get-started/installation/) or the [Find module page](/registry/configure/). @@ -116,7 +116,7 @@ The docs use the [Diátaxis Framework](https://diataxis.fr/) as the basis of t {{< /expand >}} - **Tutorial**: A learning-oriented piece of content that functions as a lesson for the reader. - A tutorial helps readers to learn and apply skills by doing something meaningful and attainable. + A tutorial helps readers to learn and apply skills by doing something meaningful and attainable, like a recipe to cook a full meal. {{< expand "Click to view template" >}} From f2a5e9410cda7bc8dbe628f30b4bc50bfed12e1c Mon Sep 17 00:00:00 2001 From: Sean Pollock Date: Tue, 9 Jul 2024 02:37:39 -0400 Subject: [PATCH 12/22] RSDK-8119 - delete velodyne builtin docs (#3110) --- .../components/camera/configure-velodyne.png | Bin 129194 -> 0 bytes docs/components/camera/velodyne.md | 64 ------------------ .../rover-resources/rover-tutorial-1.md | 2 +- .../rover-resources/rover-tutorial/_index.md | 2 +- 4 files changed, 2 insertions(+), 66 deletions(-) delete mode 100644 assets/components/camera/configure-velodyne.png delete mode 100644 docs/components/camera/velodyne.md diff --git a/assets/components/camera/configure-velodyne.png b/assets/components/camera/configure-velodyne.png deleted file mode 100644 index 3e7a645dcf6832936d14db51c6558076ee3c63f1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 129194 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 ~?R|wnnkYHtwRic=TsmW`k3X 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 KRKWv7-~R!?x?fEI diff --git a/docs/components/camera/velodyne.md b/docs/components/camera/velodyne.md deleted file mode 100644 index b5ca899ca1..0000000000 --- a/docs/components/camera/velodyne.md +++ /dev/null @@ -1,64 +0,0 @@ ---- -title: "Configure a Velodyne Camera" -linkTitle: "velodyne" -weight: 32 -type: "docs" -description: "Configure a camera that uses velodyne lidar." -images: ["/icons/components/camera.svg"] -tags: ["camera", "components"] -aliases: - - "/components/camera/velodyne/" -component_description: "Uses velodyne lidar." -toc_hide: true -# SMEs: SLAM team ---- - -A `velodyne` camera uses [Velodyne lidar](https://velodynelidar.com/). -The velodyne must be running locally at address `127.0.0.1`. - -{{< tabs name="Configure a Velodyne Camera" >}} -{{% tab name="Config Builder" %}} - -Navigate to the **CONFIGURE** tab of your machine's page in [the Viam app](https://app.viam.com). -Click the **+** icon next to your machine part in the left-hand menu and select **Component**. -Select the `camera` type, then select the `velodyne` model. -Enter a name or use the suggested name for your camera and click **Create**. - -{{< imgproc src="/components/camera/configure-velodyne.png" alt="Configuration of a velodyne camera in the Viam app config builder." resize="1200x" style="width=600x" >}} - -Edit and fill in the attributes as applicable. - -{{% /tab %}} -{{% tab name="JSON Template" %}} - -```json {class="line-numbers linkable-line-numbers"} -{ - "name": "", - "model": "velodyne", - "type": "camera", - "namespace": "rdk", - "attributes": { - "port": , - "ttl_ms": , - } -} -``` - -{{% /tab %}} -{{< /tabs >}} - -The following attributes are available for `velodyne` cameras: - - -| Name | Type | Required? | Description | -| ---- | ---- | --------- | ----------- | -| `port` | int | **Required** | The port the Velodyne camera is running on. Try `2368` if you are unsure. | -| `ttl_ms` | int | **Required** | Frequency in milliseconds to output the [TTL signal](https://en.wikipedia.org/wiki/Transistor%E2%80%93transistor_logic) from the camera. | - -## View the camera stream - -{{< readfile "/static/include/components/camera-view-camera-stream.md" >}} - -## Next steps - -{{< readfile "/static/include/components/camera-model-next-steps.md" >}} diff --git a/docs/get-started/try-viam/rover-resources/rover-tutorial-1.md b/docs/get-started/try-viam/rover-resources/rover-tutorial-1.md index 13c375107f..52d4d2ff2e 100644 --- a/docs/get-started/try-viam/rover-resources/rover-tutorial-1.md +++ b/docs/get-started/try-viam/rover-resources/rover-tutorial-1.md @@ -277,7 +277,7 @@ The following are just a few ideas, but you can expand or modify the rover kit w - For GPS navigation, we support NMEA (using serial and I2C) and RTK. Make and model don't make a difference as long as you use these protocols. See [Movement Sensor Component](/components/movement-sensor/) for more information. -- For [LiDAR laser range scanning](/services/slam/cartographer/), we recommend Velodyne, or RPlidar (including A1, which is a sub-$100 LIDAR). +- For [LiDAR laser range scanning](/services/slam/cartographer/), we recommend RPlidar (including A1, which is a sub-$100 LIDAR). - For robot arms, we tried the [Yahboom DOFBOT robotics arm](https://category.yahboom.net/products/dofbot-jetson_nano) with success. ### Mount an RPlidar to the rover diff --git a/docs/get-started/try-viam/rover-resources/rover-tutorial/_index.md b/docs/get-started/try-viam/rover-resources/rover-tutorial/_index.md index 8d1a6a4548..d384d0988e 100644 --- a/docs/get-started/try-viam/rover-resources/rover-tutorial/_index.md +++ b/docs/get-started/try-viam/rover-resources/rover-tutorial/_index.md @@ -408,7 +408,7 @@ The following are just a few ideas, but you can expand or modify the rover kit w - For GPS navigation, we support NMEA (using serial and I2C) and RTK. Make and model don't make a difference as long as you use these protocols. See [Movement Sensor Component](/components/movement-sensor/) for more information. -- For [LiDAR laser range scanning](/services/slam/cartographer/), we recommend Velodyne, or RPlidar (including A1, which is a sub-$100 LIDAR). +- For [LiDAR laser range scanning](/services/slam/cartographer/), we recommend RPlidar (including A1, which is a sub-$100 LIDAR). - For robot arms, we tried the [Yahboom DOFBOT robotics arm](https://category.yahboom.net/products/dofbot-jetson_nano) with success. ### Mount an RPlidar to the rover From cbc8aa0a1eb69133dd7e6907427ca12f7b7cda5d Mon Sep 17 00:00:00 2001 From: Naomi Pentrel <5212232+npentrel@users.noreply.github.com> Date: Tue, 9 Jul 2024 15:10:02 +0200 Subject: [PATCH 13/22] DOCS-2629: Remove deleted builtin component models (#3113) --- .github/workflows/get_modular_resources.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/get_modular_resources.py b/.github/workflows/get_modular_resources.py index 4378cd00b9..5960d309ea 100644 --- a/.github/workflows/get_modular_resources.py +++ b/.github/workflows/get_modular_resources.py @@ -100,7 +100,7 @@ async def main(): # Deleting documents that didn't get updated (presumably deleted) try: - typesense_client.collections['resources'].documents.delete({'filter_by': 'last_updated: <' + time_now + ',module_id: !builtin' }) + typesense_client.collections['resources'].documents.delete({'filter_by': 'last_updated: <' + time_now}) typesense_client.collections['mlmodels'].documents.delete({'filter_by': 'last_updated: <' + time_now}) except Exception as e: pass From 113b7fd94eb74dca8e21f1e7fc809854433956fa Mon Sep 17 00:00:00 2001 From: Naomi Pentrel <5212232+npentrel@users.noreply.github.com> Date: Tue, 9 Jul 2024 15:43:01 +0200 Subject: [PATCH 14/22] Add logging (#3114) --- .github/workflows/get_modular_resources.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/workflows/get_modular_resources.py b/.github/workflows/get_modular_resources.py index 5960d309ea..eabdd994c8 100644 --- a/.github/workflows/get_modular_resources.py +++ b/.github/workflows/get_modular_resources.py @@ -100,9 +100,14 @@ async def main(): # Deleting documents that didn't get updated (presumably deleted) try: - typesense_client.collections['resources'].documents.delete({'filter_by': 'last_updated: <' + time_now}) - typesense_client.collections['mlmodels'].documents.delete({'filter_by': 'last_updated: <' + time_now}) + res = typesense_client.collections['resources'].documents.delete({'filter_by': 'last_updated: <' + str(time_now)}) + print("Resources deleted") + print(res) + res = typesense_client.collections['mlmodels'].documents.delete({'filter_by': 'last_updated: <' + str(time_now)}) + print("ML models deleted") + print(res) except Exception as e: + print(e) pass From 0ed7f6c465b9b10f728018cc259127b8e0cb2301 Mon Sep 17 00:00:00 2001 From: Naomi Pentrel <5212232+npentrel@users.noreply.github.com> Date: Tue, 9 Jul 2024 16:23:54 +0200 Subject: [PATCH 15/22] DOCS-2626: Fix code snippets (#3115) --- assets/js/prism.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/js/prism.js b/assets/js/prism.js index b97b503a95..85212ed011 100644 --- a/assets/js/prism.js +++ b/assets/js/prism.js @@ -23,7 +23,7 @@ Prism.languages.python={comment:{pattern:/(^|[^\\])#.*/,lookbehind:!0,greedy:!0} !function(){if("undefined"!=typeof Prism&&"undefined"!=typeof document){var e="line-numbers",n=/\n(?!$)/g,t=Prism.plugins.lineNumbers={getLine:function(n,t){if("PRE"===n.tagName&&n.classList.contains(e)){var i=n.querySelector(".line-numbers-rows");if(i){var r=parseInt(n.getAttribute("data-start"),10)||1,s=r+(i.children.length-1);ts&&(t=s);var l=t-r;return i.children[l]}}},resize:function(e){r([e])},assumeViewportIndependence:!0},i=void 0;window.addEventListener("resize",(function(){t.assumeViewportIndependence&&i===window.innerWidth||(i=window.innerWidth,r(Array.prototype.slice.call(document.querySelectorAll("pre.line-numbers"))))})),Prism.hooks.add("complete",(function(t){if(t.code){var i=t.element,s=i.parentNode;if(s&&/pre/i.test(s.nodeName)&&!i.querySelector(".line-numbers-rows")&&Prism.util.isActive(i,e)){i.classList.remove(e),s.classList.add(e);var l,o=t.code.match(n),a=o?o.length+1:1,u=new Array(a+1).join("");(l=document.createElement("span")).setAttribute("aria-hidden","true"),l.className="line-numbers-rows",l.innerHTML=u,s.hasAttribute("data-start")&&(s.style.counterReset="linenumber "+(parseInt(s.getAttribute("data-start"),10)-1)),t.element.appendChild(l),r([s]),Prism.hooks.run("line-numbers",t)}}})),Prism.hooks.add("line-numbers",(function(e){e.plugins=e.plugins||{},e.plugins.lineNumbers=!0}))}function r(e){if(0!=(e=e.filter((function(e){var n,t=(n=e,n?window.getComputedStyle?getComputedStyle(n):n.currentStyle||null:null)["white-space"];return"pre-wrap"===t||"pre-line"===t}))).length){var t=e.map((function(e){var t=e.querySelector("code"),i=e.querySelector(".line-numbers-rows");if(t&&i){var r=e.querySelector(".line-numbers-sizer"),s=t.textContent.split(n);r||((r=document.createElement("span")).className="line-numbers-sizer",t.appendChild(r)),r.innerHTML="0",r.style.display="block";var l=r.getBoundingClientRect().height;return r.innerHTML="",{element:e,lines:s,lineHeights:[],oneLinerHeight:l,sizer:r}}})).filter(Boolean);t.forEach((function(e){var n=e.sizer,t=e.lines,i=e.lineHeights,r=e.oneLinerHeight;i[t.length-1]=void 0,t.forEach((function(e,t){if(e&&e.length>1){var s=n.appendChild(document.createElement("span"));s.style.display="block",s.textContent=e}else i[t]=r}))})),t.forEach((function(e){for(var n=e.sizer,t=e.lineHeights,i=0,r=0;r\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/g,r=/^#?((?:[\da-f]){3,4}|(?:[\da-f]{2}){3,4})$/i,o=[function(n){var o=r.exec(n);if(o){for(var s=(n=o[1]).length>=6?2:1,e=n.length/s,t=1==s?1/15:1/255,i=[],a=0;a=0){for(var s,e=r.content,t=e.split(n).join(""),i=0,a=o.length;i';r.content=c+e}}))}}(); !function(){if("undefined"!=typeof Prism&&"undefined"!=typeof document){var e=/(?:^|\s)command-line(?:\s|$)/,t="command-line-prompt",n="".startsWith?function(e,t){return e.startsWith(t)}:function(e,t){return 0===e.indexOf(t)},a="".endsWith?function(e,t){return e.endsWith(t)}:function(e,t){var n=e.length;return e.substring(n-t.length,n)===t};Prism.hooks.add("before-highlight",(function(i){var o=r(i);if(!o.complete&&i.code){var s=i.element.parentElement;if(s&&/pre/i.test(s.nodeName)&&(e.test(s.className)||e.test(i.element.className))){var l=i.element.querySelector("."+t);l&&l.remove();var m=i.code.split("\n");o.numberOfLines=m.length;var u=o.outputLines=[],c=s.getAttribute("data-output"),d=s.getAttribute("data-filter-output");if(null!==c)c.split(",").forEach((function(e){var t=e.split("-"),n=parseInt(t[0],10),a=2===t.length?parseInt(t[1],10):n;if(!isNaN(n)&&!isNaN(a)){n<1&&(n=1),a>m.length&&(a=m.length),a--;for(var r=--n;r<=a;r++)u[r]=m[r],m[r]=""}}));else if(d)for(var p=0;p0&&v&&n(b,v)&&(m[g]=b.slice(v.length),f.add(g)))}i.code=m.join("\n")}else o.complete=!0}else o.complete=!0})),Prism.hooks.add("before-insert",(function(e){var t=r(e);if(!t.complete){for(var n=e.highlightedCode.split("\n"),a=t.outputLines||[],i=0,o=n.length;i'+Prism.util.encode(a[i])+"":n[i]=''+n[i]+"";e.highlightedCode=n.join("\n")}})),Prism.hooks.add("complete",(function(n){if(function(e){return"command-line"in(e.vars=e.vars||{})}(n)){var a=r(n);if(!a.complete){var i=n.element.parentElement;e.test(n.element.className)&&(n.element.className=n.element.className.replace(e," ")),e.test(i.className)||(i.className+=" command-line");var o,s="",l=a.numberOfLines||0,m=b("data-prompt","");o=""!==m?'':'';for(var u=a.continuationLineIndicies||new Set,c='")+'">',d=0;d { From 4dd5c7bfdf1fe40aed7465a489640dfaec6350ee Mon Sep 17 00:00:00 2001 From: andf-viam <132301587+andf-viam@users.noreply.github.com> Date: Tue, 9 Jul 2024 10:30:54 -0400 Subject: [PATCH 16/22] DOCS-2595: Add more Flutter methods (#3112) --- .github/workflows/sdk_protos_map.csv | 2 +- static/include/app/apis/generated/app.md | 10 +- .../components/apis/generated/arm-table.md | 2 + .../include/components/apis/generated/arm.md | 210 +++++++++++++++- .../components/apis/generated/camera-table.md | 2 + .../components/apis/generated/camera.md | 137 +++++++++- .../components/apis/generated/encoder.md | 10 +- .../components/apis/generated/gantry-table.md | 2 + .../components/apis/generated/gantry.md | 182 ++++++++++++++ .../apis/generated/gripper-table.md | 2 + .../components/apis/generated/gripper.md | 142 +++++++++++ .../apis/generated/input_controller.md | 4 + .../components/apis/generated/motor-table.md | 3 + .../components/apis/generated/motor.md | 210 ++++++++++++++++ .../apis/generated/movement_sensor.md | 237 ++++++++++++++++++ .../apis/generated/power_sensor-table.md | 2 + .../components/apis/generated/power_sensor.md | 146 +++++++++++ .../components/apis/generated/sensor-table.md | 2 + .../components/apis/generated/sensor.md | 85 +++++++ .../components/apis/generated/servo-table.md | 2 + .../components/apis/generated/servo.md | 119 +++++++++ .../methods/python.camera.get_image.after.md | 2 +- static/include/robot/apis/generated/robot.md | 9 +- 23 files changed, 1483 insertions(+), 39 deletions(-) diff --git a/.github/workflows/sdk_protos_map.csv b/.github/workflows/sdk_protos_map.csv index 2d1fa4c22d..32ae1052e4 100644 --- a/.github/workflows/sdk_protos_map.csv +++ b/.github/workflows/sdk_protos_map.csv @@ -444,7 +444,7 @@ robot,StopAll,stop_all,StopAll, robot,StartSession,,, robot,SendSessionHeartbeat,,, robot,Log,log,, -robot,GetCloudMetadata,get_cloud_metadata,GetCloudMetadata,getCloudMetadata +robot,GetCloudMetadata,get_cloud_metadata,CloudMetadata,getCloudMetadata ## HACK: Robot (python) provides additional helper function, adding 4 pseudo-entries: robot,Options.with_api_key,with_api_key,, robot,AtAddress,at_address,,atAddress diff --git a/static/include/app/apis/generated/app.md b/static/include/app/apis/generated/app.md index f1a50a8cd9..3089d11be2 100644 --- a/static/include/app/apis/generated/app.md +++ b/static/include/app/apis/generated/app.md @@ -613,7 +613,7 @@ Get the logs associated with a specific machine {{< glossary_tooltip term_id="pa - `robot_part_id` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): ID of the robot part to get logs from. - `filter` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (optional): Only include logs with messages that contain the string filter. Defaults to empty string “” (that is, no filter). - `dest` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (optional): Optional filepath to write the log entries to. -- `errors_only` ([bool](https://docs.python.org/3/library/stdtypes.html#boolean-type-bool)) (required): Boolean specifying whether or not to only include error logs. Defaults to True. +- `log_levels` (List[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)]) (required): List of log levels for which entries should be returned. Defaults to empty list, which returns all logs. - `num_log_entries` ([int](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (required): Number of log entries to return. Passing 0 returns all logs. Defaults to 100. All logs or the first num_log_entries logs will be returned, whichever comes first. **Returns:** @@ -1212,8 +1212,8 @@ Add a role under the organization you are currently authenticated to. - `org_id` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): The ID of the organization to create the role in. You can obtain your organization ID from the Viam app’s organization settings page. - `identity_id` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): ID of the entity the role belongs to (for example, a user ID). -- `role` (Literal[owner] | Literal[operator]) (required): The role to add (either `"owner"` or `"operator"`). -- `resource_type` (Literal[organization] | Literal[location] | Literal[robot]) (required): The type of the resource to add the role to (either `"organization"`, `"location"`, or `"robot"`). Must match the type of the `resource_id`'s resource. +- `role` (Literal['owner'] | Literal['operator']) (required): The role to add (either `"owner"` or `"operator"`). +- `resource_type` (Literal['organization'] | Literal['location'] | Literal['robot']) (required): The type of the resource to add the role to (either `"organization"`, `"location"`, or `"robot"`). Must match the type of the `resource_id`'s resource. - `resource_id` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): ID of the resource the role applies to (the ID of either an {{< glossary_tooltip term_id="organization" text="organization" >}}, {{< glossary_tooltip term_id="location" text="location" >}}, or {{< glossary_tooltip term_id="machine" text="machine" >}}.). **Returns:** @@ -1251,8 +1251,8 @@ Remove a role under the organization you are currently authenticated to. - `org_id` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): The ID of the organization the role exists in. You can obtain your organization ID from the Viam app’s organization settings page. - `identity_id` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): ID of the entity the role belongs to (for example, a user ID). -- `role` (Literal[owner] | Literal[operator]) (required): The role to remove. -- `resource_type` (Literal[organization] | Literal[location] | Literal[robot]) (required): Type of the resource the role is being removed from. Must match resource_id. +- `role` (Literal['owner'] | Literal['operator']) (required): The role to remove. +- `resource_type` (Literal['organization'] | Literal['location'] | Literal['robot']) (required): Type of the resource the role is being removed from. Must match resource_id. - `resource_id` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): ID of the resource the role applies to (that is, either an organization, location, or robot ID). **Returns:** diff --git a/static/include/components/apis/generated/arm-table.md b/static/include/components/apis/generated/arm-table.md index d395a03a47..6008f71925 100644 --- a/static/include/components/apis/generated/arm-table.md +++ b/static/include/components/apis/generated/arm-table.md @@ -11,4 +11,6 @@ | [`GetGeometries`](/components/arm/#getgeometries) | Get all the geometries associated with the arm in its current configuration, in the frame of the arm. | | [`Reconfigure`](/components/arm/#reconfigure) | Reconfigure this resource. | | [`DoCommand`](/components/arm/#docommand) | Execute model-specific commands that are not otherwise defined by the component API. | +| [`FromRobot`](/components/arm/#fromrobot) | Get the resource from the provided robot with the given name. | +| [`Name`](/components/arm/#name) | Get the `ResourceName` for this arm with the given name. | | [`Close`](/components/arm/#close) | Safely shut down the resource and prevent further use. | diff --git a/static/include/components/apis/generated/arm.md b/static/include/components/apis/generated/arm.md index 24b4cbf43d..a7a409cbfb 100644 --- a/static/include/components/apis/generated/arm.md +++ b/static/include/components/apis/generated/arm.md @@ -12,7 +12,7 @@ Get the current position of the arm as a [pose](/internals/orientation-vector/). **Returns:** -- ([viam.components.arm.Pose](https://python.viam.dev/autoapi/viam/index.html#viam.components.arm.Pose)): A representation of the arm’s current position as a 6 DOF (six degrees of freedom) pose. The Pose is composed of values for location and orientation with respect to the origin. Location is expressed as distance, which is represented by x, y, and z coordinate values. Orientation is expressed as an orientation vector, which is represented by o_x, o_y, o_z, and theta values. +- ([viam.components.arm.Pose](https://python.viam.dev/autoapi/viam/components/arm/index.html#viam.components.arm.Pose)): A representation of the arm’s current position as a 6 DOF (six degrees of freedom) pose. The Pose is composed of values for location and orientation with respect to the origin. Location is expressed as distance, which is represented by x, y, and z coordinate values. Orientation is expressed as an orientation vector, which is represented by o_x, o_y, o_z, and theta values. **Example:** @@ -48,6 +48,26 @@ pos, err := myArm.EndPosition(context.Background(), nil) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/arm#Arm). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[Pose](https://flutter.viam.dev/viam_sdk/Pose-class.html)> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +// Get the pose of an arm named "myArm" +final currentPose = await myArm.endPosition(); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Arm/endPosition.html). + {{% /tab %}} {{< /tabs >}} @@ -60,7 +80,7 @@ Move the end of the arm to the desired [pose](/internals/orientation-vector/), r **Parameters:** -- `pose` ([viam.components.arm.Pose](https://python.viam.dev/autoapi/viam/index.html#viam.components.arm.Pose)) (required): The destination Pose for the arm. The Pose is composed of values for location and orientation with respect to the origin. Location is expressed as distance, which is represented by x, y, and z coordinate values. Orientation is expressed as an orientation vector, which is represented by o_x, o_y, o_z, and theta values. +- `pose` ([viam.components.arm.Pose](https://python.viam.dev/autoapi/viam/components/arm/arm/index.html#viam.components.arm.arm.Pose)) (required): The destination Pose for the arm. The Pose is composed of values for location and orientation with respect to the origin. Location is expressed as distance, which is represented by x, y, and z coordinate values. Orientation is expressed as an orientation vector, which is represented by o_x, o_y, o_z, and theta values. - `extra` (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), Any]) (optional): Extra options to pass to the underlying RPC call. - `timeout` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. @@ -111,6 +131,30 @@ err = myArm.MoveToPosition(context.Background(), examplePose, nil) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/arm#Arm). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `pose` [Pose](https://flutter.viam.dev/viam_sdk/Pose-class.html) (required) +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html) + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +// Create a pose for the arm to move to +final targetPose = Pose.fromBuffer([12, 0, 400, 0, 0, 1, 90]); + +// Move the arm to the pose +await myArm.moveToPosition(targetPose); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Arm/moveToPosition.html). + {{% /tab %}} {{< /tabs >}} @@ -129,7 +173,7 @@ Collision checks are not enabled when doing direct joint control with MoveToJoin **Parameters:** -- `positions` ([viam.proto.component.arm.JointPositions](https://python.viam.dev/autoapi/viam/index.html#viam.components.arm.JointPositions)) (required): The destination JointPositions for the arm. +- `positions` ([viam.proto.component.arm.JointPositions](https://python.viam.dev/autoapi/viam/components/arm/client/index.html#viam.components.arm.client.JointPositions)) (required): The destination JointPositions for the arm. - `extra` (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), Any]) (optional): Extra options to pass to the underlying RPC call. - `timeout` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. @@ -187,6 +231,30 @@ err = myArm.MoveToJointPositions(context.Background(), jointPos, nil) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/arm#Arm). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `positions` [List](https://api.flutter.dev/flutter/dart-core/List-class.html)<[double](https://api.flutter.dev/flutter/dart-core/double-class.html)> (required) +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html) + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +// Create a list of joint angles for each arm joint +List targetPositions = [180, 90, 15.75, 30, 90, 0]; + +// Move the arm joints to those angles +await myArm.moveToJointPositions(targetPositions); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Arm/moveToJointPositions.html). + {{% /tab %}} {{< /tabs >}} @@ -233,20 +301,32 @@ For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/ **Example:** ```go {class="line-numbers linkable-line-numbers"} -// Assumes you have imported "go.viam.com/api/component/arm/v1" as `componentpb` -myArm, err := arm.FromRobot(machine, "my_arm") +myArm , err := arm.FromRobot(machine, "my_arm") -// Declare an array of values with your desired rotational value for each joint on the arm. -degrees := []float64{4.0, 5.0, 6.0} +// Get the current position of each joint on the arm as JointPositions. +pos, err := myArm.JointPositions(context.Background(), nil) +``` -// Declare a new JointPositions with these values. -jointPos := &componentpb.JointPositions{Values: degrees} +For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/arm#Arm). -// Move each joint of the arm to the position these values specify. -err = myArm.MoveToJointPositions(context.Background(), jointPos, nil) +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[List](https://api.flutter.dev/flutter/dart-core/List-class.html)<[double](https://api.flutter.dev/flutter/dart-core/double-class.html)>> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +List currentJointPositions = await myArm.moveToJointPosition(); ``` -For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/arm#Arm). +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Arm/jointPositions.html). {{% /tab %}} {{< /tabs >}} @@ -269,7 +349,7 @@ Get the kinematics information associated with the arm as the format and byte co **Returns:** -- (Tuple[[KinematicsFileFormat.ValueType](https://python.viam.dev/autoapi/viam/components/arm/index.html#viam.components.arm.KinematicsFileFormat), [bytes](https://docs.python.org/3/library/stdtypes.html#bytes-objects)]): A tuple containing two values; the first [0] value represents the format of the file, either in URDF format or Viam’s kinematic parameter format (spatial vector algebra), and the second [1] value represents the byte contents of the file. +- (Tuple[[KinematicsFileFormat.ValueType](https://python.viam.dev/autoapi/viam/components/arm/index.html#viam.components.arm.KinematicsFileFormat), [bytes](https://docs.python.org/3/library/stdtypes.html#bytes-objects)]): A tuple containing two values; the first [0] value represents the format of the file, either in URDF format (KinematicsFileFormat.KINEMATICS_FILE_FORMAT_URDF) or Viam’s kinematic parameter format (spatial vector algebra) (KinematicsFileFormat.KINEMATICS_FILE_FORMAT_SVA), and the second [1] value represents the byte contents of the file. **Example:** @@ -348,6 +428,25 @@ logger.Info(is_moving) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Actuator). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- None. + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[bool](https://api.flutter.dev/flutter/dart-core/bool-class.html)> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +bool isArmMoving = await myArm.isMoving(); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Arm/isMoving.html). + {{% /tab %}} {{< /tabs >}} @@ -402,6 +501,25 @@ err = myArm.Stop(context.Background(), nil) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Actuator). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html) + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +await myArm.stop(); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Arm/stop.html). + {{% /tab %}} {{< /tabs >}} @@ -508,6 +626,10 @@ If you are implementing your own arm and add features that have no built-in API - (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), viam.utils.ValueTypes]): Result of the executed command. +**Raises:** + +- (NotImplementedError): Raised if the Resource does not support arbitrary commands. + **Example:** ```python {class="line-numbers linkable-line-numbers"} @@ -542,6 +664,68 @@ result, err := myArm.DoCommand(context.Background(), command) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Resource). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `command` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic> (required) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +// Example using doCommand with an arm component +const command = {'cmd': 'test', 'data1': 500}; +var result = myArm.doCommand(command); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Resource/doCommand.html). + +{{% /tab %}} +{{< /tabs >}} + +### FromRobot + +Get the resource from the provided robot with the given name. + +{{< tabs >}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `robot` [RobotClient](https://flutter.viam.dev/viam_sdk/RobotClient-class.html) (required) +- `name` [String](https://api.flutter.dev/flutter/dart-core/String-class.html) (required) + +**Returns:** + +- [Arm](https://flutter.viam.dev/viam_sdk/Arm-class.html) + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Arm/fromRobot.html). + +{{% /tab %}} +{{< /tabs >}} + +### Name + +Get the `ResourceName` for this arm with the given name. + +{{< tabs >}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `name` [String](https://api.flutter.dev/flutter/dart-core/String-class.html) (required) + +**Returns:** + +- [ResourceName](https://flutter.viam.dev/viam_sdk/ResourceName-class.html) + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Arm/getResourceName.html). + {{% /tab %}} {{< /tabs >}} diff --git a/static/include/components/apis/generated/camera-table.md b/static/include/components/apis/generated/camera-table.md index 7780cd6e27..ffafd0d3bc 100644 --- a/static/include/components/apis/generated/camera-table.md +++ b/static/include/components/apis/generated/camera-table.md @@ -7,4 +7,6 @@ | [`GetProperties`](/components/camera/#getproperties) | Get the camera intrinsic parameters and camera distortion, as well as whether the camera supports returning point clouds. | | [`DoCommand`](/components/camera/#docommand) | Execute model-specific commands that are not otherwise defined by the component API. | | [`GetGeometries`](/components/camera/#getgeometries) | Get all the geometries associated with the camera in its current configuration, in the frame of the camera. | +| [`FromRobot`](/components/camera/#fromrobot) | Get the resource from the provided robot with the given name. | +| [`Name`](/components/camera/#name) | Get the `ResourceName` for this camera with the given name. | | [`Close`](/components/camera/#close) | Safely shut down the resource and prevent further use. | diff --git a/static/include/components/apis/generated/camera.md b/static/include/components/apis/generated/camera.md index 324c5b9469..0271550f70 100644 --- a/static/include/components/apis/generated/camera.md +++ b/static/include/components/apis/generated/camera.md @@ -32,18 +32,7 @@ standard_frame = frame.bytes_to_depth_array() If the `mime_type` of your image is `image/vnd.viam.dep`, pass the returned image data to the Viam Python SDK's [`ViamImage.bytes_to_depth_array()`](https://python.viam.dev/autoapi/viam/media/video/index.html#viam.media.video.ViamImage.bytes_to_depth_array) method to decode the raw image data to a standard 2D image representation. -For example: - -```python {class="line-numbers linkable-line-numbers"} -# Assume "frame" has a mime_type of "image/vnd.viam.dep" -frame = await my_camera.get_image() - -# Convert "frame" to a standard 2D image representation. -# Remove the 1st 3x8 bytes and reshape the raw bytes to List[List[Int]]. -standard_frame = frame.bytes_to_depth_array() -``` - -The Python SDK provides the helper functions `viam_to_pil_image` and `pil_to_viam_image` to decode the `ViamImage` into a [`PIL Image`](https://omz-software.com/pythonista/docs/ios/Image.html) and vice versa. +In addition, the Python SDK provides the helper functions `viam_to_pil_image` and `pil_to_viam_image` to decode the `ViamImage` into a [`PIL Image`](https://omz-software.com/pythonista/docs/ios/Image.html) and vice versa. For example: @@ -109,6 +98,26 @@ defer release() For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/camera#VideoSource). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `mimeType` [MimeType](https://flutter.viam.dev/viam_sdk/MimeType-class.html)? (optional) +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (required) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[ViamImage](https://flutter.viam.dev/viam_sdk/ViamImage-class.html)> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +var nextImage = await myCamera.image(); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Camera/image.html). + {{% /tab %}} {{< /tabs >}} @@ -231,6 +240,25 @@ properties, err := myCamera.Properties(context.Background()) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/camera#VideoSource). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[ViamImage](https://flutter.viam.dev/viam_sdk/ViamImage-class.html)> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +var nextPointCloud = await myCamera.pointCloud(); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Camera/pointCloud.html). + {{% /tab %}} {{< /tabs >}} @@ -273,6 +301,25 @@ For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/ For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/camera#VideoSource). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- None. + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[CameraProperties](https://flutter.viam.dev/viam_sdk/CameraProperties.html)> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +var cameraProperties = await myCamera.properties(); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Camera/properties.html). + {{% /tab %}} {{< /tabs >}} @@ -294,6 +341,10 @@ If you are implementing your own camera and adding features that have no native - (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), viam.utils.ValueTypes]): Result of the executed command. +**Raises:** + +- (NotImplementedError): Raised if the Resource does not support arbitrary commands. + **Example:** ```python {class="line-numbers linkable-line-numbers"} @@ -303,6 +354,27 @@ result = component.do(command) For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/viam/components/camera/client/index.html#viam.components.camera.client.CameraClient.do_command). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `command` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic> (required) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +// Example using doCommand with an arm component +const command = {'cmd': 'test', 'data1': 500}; +var result = myArm.doCommand(command); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Resource/doCommand.html). + {{% /tab %}} {{< /tabs >}} @@ -338,6 +410,47 @@ For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/ {{% /tab %}} {{< /tabs >}} +### FromRobot + +Get the resource from the provided robot with the given name. + +{{< tabs >}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `robot` [RobotClient](https://flutter.viam.dev/viam_sdk/RobotClient-class.html) (required) +- `name` [String](https://api.flutter.dev/flutter/dart-core/String-class.html) (required) + +**Returns:** + +- [Camera](https://flutter.viam.dev/viam_sdk/Camera-class.html) + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Camera/fromRobot.html). + +{{% /tab %}} +{{< /tabs >}} + +### Name + +Get the `ResourceName` for this camera with the given name. + +{{< tabs >}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `name` [String](https://api.flutter.dev/flutter/dart-core/String-class.html) (required) + +**Returns:** + +- [ResourceName](https://flutter.viam.dev/viam_sdk/ResourceName-class.html) + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Camera/getResourceName.html). + +{{% /tab %}} +{{< /tabs >}} + ### Close Safely shut down the resource and prevent further use. diff --git a/static/include/components/apis/generated/encoder.md b/static/include/components/apis/generated/encoder.md index 1a54ae2208..3dcae4f6aa 100644 --- a/static/include/components/apis/generated/encoder.md +++ b/static/include/components/apis/generated/encoder.md @@ -9,13 +9,13 @@ Absolute encoders return degrees. **Parameters:** -- `position_type` ([PositionType.ValueType](https://python.viam.dev/autoapi/viam/gen/component/encoder/v1/encoder_pb2/index.html#viam.gen.component.encoder.v1.encoder_pb2.PositionType)) (optional): The desired output type of the position. +- `position_type` ([viam.proto.component.encoder.PositionType.ValueType](https://python.viam.dev/autoapi/viam/components/encoder/client/index.html#viam.components.encoder.client.PositionType)) (optional): The desired output type of the position. - `extra` (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), Any]) (optional): Extra options to pass to the underlying RPC call. - `timeout` ([float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex)) (optional): An option to set how long to wait (in seconds) before calling a time-out and closing the underlying RPC call. **Returns:** -- (Tuple[[float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex), [PositionType.ValueType](https://python.viam.dev/autoapi/viam/gen/component/encoder/v1/encoder_pb2/index.html#viam.gen.component.encoder.v1.encoder_pb2.PositionType)]): A tuple containing two values; the first [0] the Position of the encoder which can either be ticks since last zeroing for a relative encoder or degrees for an absolute encoder, and the second [1] the type of position the encoder returns (ticks or degrees). +- (Tuple[[float](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex), [PositionType.ValueType](https://python.viam.dev/autoapi/viam/gen/component/encoder/v1/encoder_pb2/index.html#viam.gen.component.encoder.v1.encoder_pb2.PositionType)]): A tuple containing two values; the first [0] the position of the encoder which can either be ticks since last zeroing for a relative encoder or degrees for an absolute encoder, and the second [1] the type of position the encoder returns (ticks or degrees). **Example:** @@ -23,7 +23,7 @@ Absolute encoders return degrees. my_encoder = Encoder.from_robot(robot=robot, name='my_encoder') # Get the position of the encoder in ticks -position = await my_encoder.get_position(encoder.PositionTypeTicks) +position = await my_encoder.get_position(PositionType.POSITION_TYPE_TICKS_COUNT) print("The encoder position is currently ", position[0], position[1]) ``` @@ -242,6 +242,10 @@ If you are implementing your own encoder as a {{< glossary_tooltip term_id="modu - (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), viam.utils.ValueTypes]): Result of the executed command. +**Raises:** + +- (NotImplementedError): Raised if the Resource does not support arbitrary commands. + **Example:** ```python {class="line-numbers linkable-line-numbers"} diff --git a/static/include/components/apis/generated/gantry-table.md b/static/include/components/apis/generated/gantry-table.md index 7565330572..693ef22662 100644 --- a/static/include/components/apis/generated/gantry-table.md +++ b/static/include/components/apis/generated/gantry-table.md @@ -10,4 +10,6 @@ | [`Stop`](/components/gantry/#stop) | Stop all motion of the gantry. | | [`Reconfigure`](/components/gantry/#reconfigure) | Reconfigure this resource. | | [`DoCommand`](/components/gantry/#docommand) | Execute model-specific commands that are not otherwise defined by the component API. | +| [`FromRobot`](/components/gantry/#fromrobot) | Get the resource from the provided robot with the given name. | +| [`Name`](/components/gantry/#name) | Get the `ResourceName` for this gantry with the given name. | | [`Close`](/components/gantry/#close) | Safely shut down the resource and prevent further use. | diff --git a/static/include/components/apis/generated/gantry.md b/static/include/components/apis/generated/gantry.md index 75a3711b68..266d04437b 100644 --- a/static/include/components/apis/generated/gantry.md +++ b/static/include/components/apis/generated/gantry.md @@ -49,6 +49,25 @@ position, err := myGantry.Position(context.Background(), nil) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/gantry#Gantry). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[List](https://api.flutter.dev/flutter/dart-core/List-class.html)<[double](https://api.flutter.dev/flutter/dart-core/double-class.html)>> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +var position = await myGantry.position(); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Gantry/position.html). + {{% /tab %}} {{< /tabs >}} @@ -119,6 +138,27 @@ myGantry.MoveToPosition(context.Background(), examplePositions, exampleSpeeds, n For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/gantry#Gantry). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `positions` [List](https://api.flutter.dev/flutter/dart-core/List-class.html)<[double](https://api.flutter.dev/flutter/dart-core/double-class.html)> (required) +- `speeds` [List](https://api.flutter.dev/flutter/dart-core/List-class.html)<[double](https://api.flutter.dev/flutter/dart-core/double-class.html)> (required) +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html) + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +await myGantry.moveToPosition([0.0, 20.5], [15, 15]); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Gantry/moveToPosition.html). + {{% /tab %}} {{< /tabs >}} @@ -173,6 +213,25 @@ lengths_mm, err := myGantry.Lengths(context.Background(), nil) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/gantry#Gantry). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[List](https://api.flutter.dev/flutter/dart-core/List-class.html)<[double](https://api.flutter.dev/flutter/dart-core/double-class.html)>> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +var lengths = await myGantry.lengths(); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Gantry/lengths.html). + {{% /tab %}} {{< /tabs >}} @@ -225,6 +284,25 @@ myGantry.Home(context.Background(), nil) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/gantry#Gantry). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[bool](https://api.flutter.dev/flutter/dart-core/bool-class.html)> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +var homed = await myGantry.home(); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Gantry/home.html). + {{% /tab %}} {{< /tabs >}} @@ -318,6 +396,25 @@ logger.Info(is_moving) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Actuator). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- None. + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[bool](https://api.flutter.dev/flutter/dart-core/bool-class.html)> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +var moving = await myGantry.isMoving(); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Gantry/isMoving.html). + {{% /tab %}} {{< /tabs >}} @@ -373,6 +470,25 @@ err = myArm.Stop(context.Background(), nil) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Actuator). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html) + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +await myGantry.stop(); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Gantry/stop.html). + {{% /tab %}} {{< /tabs >}} @@ -417,6 +533,10 @@ If you are implementing your own gantry and add features that have no built-in A - (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), viam.utils.ValueTypes]): Result of the executed command. +**Raises:** + +- (NotImplementedError): Raised if the Resource does not support arbitrary commands. + **Example:** ```python {class="line-numbers linkable-line-numbers"} @@ -451,6 +571,68 @@ result, err := myArm.DoCommand(context.Background(), command) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Resource). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `command` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic> (required) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +// Example using doCommand with an arm component +const command = {'cmd': 'test', 'data1': 500}; +var result = myArm.doCommand(command); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Resource/doCommand.html). + +{{% /tab %}} +{{< /tabs >}} + +### FromRobot + +Get the resource from the provided robot with the given name. + +{{< tabs >}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `robot` [RobotClient](https://flutter.viam.dev/viam_sdk/RobotClient-class.html) (required) +- `name` [String](https://api.flutter.dev/flutter/dart-core/String-class.html) (required) + +**Returns:** + +- [Gantry](https://flutter.viam.dev/viam_sdk/Gantry-class.html) + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Gantry/fromRobot.html). + +{{% /tab %}} +{{< /tabs >}} + +### Name + +Get the `ResourceName` for this gantry with the given name. + +{{< tabs >}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `name` [String](https://api.flutter.dev/flutter/dart-core/String-class.html) (required) + +**Returns:** + +- [ResourceName](https://flutter.viam.dev/viam_sdk/ResourceName-class.html) + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Gantry/getResourceName.html). + {{% /tab %}} {{< /tabs >}} diff --git a/static/include/components/apis/generated/gripper-table.md b/static/include/components/apis/generated/gripper-table.md index 5464fbd95e..4b0ac53bfc 100644 --- a/static/include/components/apis/generated/gripper-table.md +++ b/static/include/components/apis/generated/gripper-table.md @@ -8,4 +8,6 @@ | [`GetGeometries`](/components/gripper/#getgeometries) | Get all the geometries associated with the gripper in its current configuration, in the frame of the gripper. | | [`Reconfigure`](/components/gripper/#reconfigure) | Reconfigure this resource. | | [`DoCommand`](/components/gripper/#docommand) | Execute model-specific commands that are not otherwise defined by the component API. | +| [`FromRobot`](/components/gripper/#fromrobot) | Get the resource from the provided robot with the given name. | +| [`Name`](/components/gripper/#name) | Get the `ResourceName` for this gripper with the given name. | | [`Close`](/components/gripper/#close) | Safely shut down the resource and prevent further use. | diff --git a/static/include/components/apis/generated/gripper.md b/static/include/components/apis/generated/gripper.md index 25036d5ad3..fdb85bd1e5 100644 --- a/static/include/components/apis/generated/gripper.md +++ b/static/include/components/apis/generated/gripper.md @@ -46,6 +46,25 @@ err := myGripper.Open(context.Background(), nil) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/gripper#Gripper). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html) + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +await myGripper.open(); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Gripper/open.html). + {{% /tab %}} {{< /tabs >}} @@ -98,6 +117,25 @@ grabbed, err := myGripper.Grab(context.Background(), nil) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/gripper#Gripper). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html) + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +await myGripper.grab(); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Gripper/grab.html). + {{% /tab %}} {{< /tabs >}} @@ -156,6 +194,25 @@ logger.Info(is_moving) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Actuator). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- None. + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[bool](https://api.flutter.dev/flutter/dart-core/bool-class.html)> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +var isItMoving = await myGripper.isMoving(); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Gripper/isMoving.html). + {{% /tab %}} {{< /tabs >}} @@ -211,6 +268,25 @@ err = myArm.Stop(context.Background(), nil) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Actuator). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html) + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +await myGripper.stop(); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Gripper/stop.html). + {{% /tab %}} {{< /tabs >}} @@ -317,6 +393,10 @@ If you are implementing your own gripper and add features that have no built-in - (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), viam.utils.ValueTypes]): Result of the executed command. +**Raises:** + +- (NotImplementedError): Raised if the Resource does not support arbitrary commands. + **Example:** ```python {class="line-numbers linkable-line-numbers"} @@ -351,6 +431,68 @@ result, err := myArm.DoCommand(context.Background(), command) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Resource). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `command` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic> (required) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +// Example using doCommand with an arm component +const command = {'cmd': 'test', 'data1': 500}; +var result = myArm.doCommand(command); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Resource/doCommand.html). + +{{% /tab %}} +{{< /tabs >}} + +### FromRobot + +Get the resource from the provided robot with the given name. + +{{< tabs >}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `robot` [RobotClient](https://flutter.viam.dev/viam_sdk/RobotClient-class.html) (required) +- `name` [String](https://api.flutter.dev/flutter/dart-core/String-class.html) (required) + +**Returns:** + +- [Gripper](https://flutter.viam.dev/viam_sdk/Gripper-class.html) + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Gripper/fromRobot.html). + +{{% /tab %}} +{{< /tabs >}} + +### Name + +Get the `ResourceName` for this gripper with the given name. + +{{< tabs >}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `name` [String](https://api.flutter.dev/flutter/dart-core/String-class.html) (required) + +**Returns:** + +- [ResourceName](https://flutter.viam.dev/viam_sdk/ResourceName-class.html) + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Gripper/getResourceName.html). + {{% /tab %}} {{< /tabs >}} diff --git a/static/include/components/apis/generated/input_controller.md b/static/include/components/apis/generated/input_controller.md index 2e9b4fca6c..89fe4450ee 100644 --- a/static/include/components/apis/generated/input_controller.md +++ b/static/include/components/apis/generated/input_controller.md @@ -351,6 +351,10 @@ If you are implementing your own input controller and add features that have no - (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), viam.utils.ValueTypes]): Result of the executed command. +**Raises:** + +- (NotImplementedError): Raised if the Resource does not support arbitrary commands. + **Example:** ```python {class="line-numbers linkable-line-numbers"} diff --git a/static/include/components/apis/generated/motor-table.md b/static/include/components/apis/generated/motor-table.md index 47cca488d0..3cc507ca7e 100644 --- a/static/include/components/apis/generated/motor-table.md +++ b/static/include/components/apis/generated/motor-table.md @@ -2,6 +2,7 @@ | Method Name | Description | | ----------- | ----------- | | [`SetPower`](/components/motor/#setpower) | Set the portion of max power to send to the motor (between `-1` and `1`). | +| [`SetRPM`](/components/motor/#setrpm) | Spin the motor indefinitely at the specified speed, in revolutions per minute. If `rpm` is positive, the motor will spin forwards, and if `rpm` is negative, the motor will spin backwards. | | [`GoFor`](/components/motor/#gofor) | Spin the motor the specified number of revolutions at specified revolutions per minute. | | [`GoTo`](/components/motor/#goto) | Turn the motor to a specified position (in terms of revolutions from home/zero) at a specified speed in revolutions per minute (RPM). | | [`ResetZeroPosition`](/components/motor/#resetzeroposition) | Set the current position (modified by `offset`) to be the new zero (home) position. | @@ -13,4 +14,6 @@ | [`Stop`](/components/motor/#stop) | Cut the power to the motor immediately, without any gradual step down. | | [`Reconfigure`](/components/motor/#reconfigure) | Reconfigure this resource. | | [`DoCommand`](/components/motor/#docommand) | Execute model-specific commands that are not otherwise defined by the component API. | +| [`FromRobot`](/components/motor/#fromrobot) | Get the resource from the provided robot with the given name. | +| [`Name`](/components/motor/#name) | Get the `ResourceName` for this motor with the given name. | | [`Close`](/components/motor/#close) | Safely shut down the resource and prevent further use. | diff --git a/static/include/components/apis/generated/motor.md b/static/include/components/apis/generated/motor.md index 3e986b8bca..25d19852c3 100644 --- a/static/include/components/apis/generated/motor.md +++ b/static/include/components/apis/generated/motor.md @@ -52,6 +52,41 @@ myMotorComponent.SetPower(context.Background(), 0.4, nil) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/motor#Motor). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `powerPct` [double](https://api.flutter.dev/flutter/dart-core/double-class.html) (required) +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html) + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Motor/setPower.html). + +{{% /tab %}} +{{< /tabs >}} + +### SetRPM + +Spin the motor indefinitely at the specified speed, in revolutions per minute. If `rpm` is positive, the motor will spin forwards, and if `rpm` is negative, the motor will spin backwards. + +{{< tabs >}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `rpm` [double](https://api.flutter.dev/flutter/dart-core/double-class.html) (required) +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html) + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Motor/setRPM.html). + {{% /tab %}} {{< /tabs >}} @@ -110,6 +145,21 @@ myMotorComponent.GoFor(context.Background(), 60, 7.2, nil) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/motor#Motor). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `rpm` [double](https://api.flutter.dev/flutter/dart-core/double-class.html) (required) +- `revolutions` [double](https://api.flutter.dev/flutter/dart-core/double-class.html) (required) +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html) + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Motor/goFor.html). + {{% /tab %}} {{< /tabs >}} @@ -167,6 +217,21 @@ myMotorComponent.GoTo(context.Background(), 75, 8.3, nil) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/motor#Motor). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `rpm` [double](https://api.flutter.dev/flutter/dart-core/double-class.html) (required) +- `positionRevolutions` [double](https://api.flutter.dev/flutter/dart-core/double-class.html) (required) +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html) + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Motor/goTo.html). + {{% /tab %}} {{< /tabs >}} @@ -220,6 +285,20 @@ myMotorComponent.ResetZeroPosition(context.Background(), 0.0, nil) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/motor#Motor). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `offset` [double](https://api.flutter.dev/flutter/dart-core/double-class.html) (required) +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html) + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Motor/resetZeroPosition.html). + {{% /tab %}} {{< /tabs >}} @@ -278,6 +357,19 @@ logger.Info(position) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/motor#Motor). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[double](https://api.flutter.dev/flutter/dart-core/double-class.html)> + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Motor/position.html). + {{% /tab %}} {{< /tabs >}} @@ -338,6 +430,19 @@ logger.Info(properties) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/motor#Motor). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[MotorProperties](https://flutter.viam.dev/viam_sdk/MotorProperties.html)> + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Motor/properties.html). + {{% /tab %}} {{< /tabs >}} @@ -399,6 +504,19 @@ logger.Info(pct) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/motor#Motor). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[PowerState](https://flutter.viam.dev/viam_sdk/PowerState-class.html)> + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Motor/powerState.html). + {{% /tab %}} {{< /tabs >}} @@ -489,6 +607,19 @@ logger.Info(is_moving) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Actuator). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[bool](https://api.flutter.dev/flutter/dart-core/bool-class.html)> + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Motor/isMoving.html). + {{% /tab %}} {{< /tabs >}} @@ -543,6 +674,19 @@ err = myArm.Stop(context.Background(), nil) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Actuator). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html) + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Motor/stop.html). + {{% /tab %}} {{< /tabs >}} @@ -587,6 +731,10 @@ If you are implementing your own motor and add features that have no built-in AP - (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), viam.utils.ValueTypes]): Result of the executed command. +**Raises:** + +- (NotImplementedError): Raised if the Resource does not support arbitrary commands. + **Example:** ```python {class="line-numbers linkable-line-numbers"} @@ -621,6 +769,68 @@ result, err := myArm.DoCommand(context.Background(), command) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Resource). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `command` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic> (required) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +// Example using doCommand with an arm component +const command = {'cmd': 'test', 'data1': 500}; +var result = myArm.doCommand(command); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Resource/doCommand.html). + +{{% /tab %}} +{{< /tabs >}} + +### FromRobot + +Get the resource from the provided robot with the given name. + +{{< tabs >}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `robot` [RobotClient](https://flutter.viam.dev/viam_sdk/RobotClient-class.html) (required) +- `name` [String](https://api.flutter.dev/flutter/dart-core/String-class.html) (required) + +**Returns:** + +- [Motor](https://flutter.viam.dev/viam_sdk/Motor-class.html) + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Motor/fromRobot.html). + +{{% /tab %}} +{{< /tabs >}} + +### Name + +Get the `ResourceName` for this motor with the given name. + +{{< tabs >}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `name` [String](https://api.flutter.dev/flutter/dart-core/String-class.html) (required) + +**Returns:** + +- [ResourceName](https://flutter.viam.dev/viam_sdk/ResourceName-class.html) + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Motor/getResourceName.html). + {{% /tab %}} {{< /tabs >}} diff --git a/static/include/components/apis/generated/movement_sensor.md b/static/include/components/apis/generated/movement_sensor.md index 66b3df4640..4cc9c0297d 100644 --- a/static/include/components/apis/generated/movement_sensor.md +++ b/static/include/components/apis/generated/movement_sensor.md @@ -50,6 +50,25 @@ linVel, err := myMovementSensor.LinearVelocity(context.Background(), nil) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/movementsensor#MovementSensor). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[Vector3](https://flutter.viam.dev/viam_sdk/Vector3-class.html)> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +var linVel = await myMovementSensor.linearVelocity(); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/MovementSensor/linearVelocity.html). + {{% /tab %}} {{< /tabs >}} @@ -111,6 +130,25 @@ yAngVel := angVel.Y For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/movementsensor#MovementSensor). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[Vector3](https://flutter.viam.dev/viam_sdk/Vector3-class.html)> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +var angVel = await myMovementSensor.angularVelocity(); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/MovementSensor/angularVelocity.html). + {{% /tab %}} {{< /tabs >}} @@ -166,6 +204,25 @@ heading, err := myMovementSensor.CompassHeading(context.Background(), nil) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/movementsensor#MovementSensor). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[double](https://api.flutter.dev/flutter/dart-core/double-class.html)> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +var compassHeading = await myMovementSensor.compassHeading(); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/MovementSensor/compassHeading.html). + {{% /tab %}} {{< /tabs >}} @@ -230,6 +287,25 @@ logger.Info("The number of degrees that the movement sensor is rotated about the For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/movementsensor#MovementSensor). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[Orientation](https://flutter.viam.dev/viam_sdk/Orientation-class.html)> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +var orientation = await myMovementSensor.orientation(); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/MovementSensor/orientation.html). + {{% /tab %}} {{< /tabs >}} @@ -287,6 +363,25 @@ position, altitude, err := myMovementSensor.Position(context.Background(), nil) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/movementsensor#MovementSensor). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[Position](https://flutter.viam.dev/viam_sdk/Position-class.html)> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +var position = await myMovementSensor.position(); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/MovementSensor/position.html). + {{% /tab %}} {{< /tabs >}} @@ -340,6 +435,25 @@ properties, err := myMovementSensor.Properties(context.Background(), nil) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/movementsensor#MovementSensor). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[Properties](https://flutter.viam.dev/viam_sdk/Properties.html)> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +var props = await myMovementSensor.properties(); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/MovementSensor/properties.html). + {{% /tab %}} {{< /tabs >}} @@ -410,6 +524,25 @@ accuracy, err := myMovementSensor.Accuracy(context.Background(), nil) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/movementsensor#MovementSensor). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[Accuracy](https://flutter.viam.dev/viam_sdk/Accuracy.html)> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +var accuracy = await myMovementSensor.accuracy(); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/MovementSensor/accuracy.html). + {{% /tab %}} {{< /tabs >}} @@ -468,6 +601,25 @@ linVel, err := myMovementSensor.LinearVelocity(context.Background(), nil) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/movementsensor#MovementSensor). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[Vector3](https://flutter.viam.dev/viam_sdk/Vector3-class.html)> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +var linAccel = await myMovementSensor.linearAcceleration(); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/MovementSensor/linearAcceleration.html). + {{% /tab %}} {{< /tabs >}} @@ -555,6 +707,25 @@ readings, err := mySensor.Readings(context.Background(), nil) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Sensor). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +var readings = await myMovementSensor.readings(); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/MovementSensor/readings.html). + {{% /tab %}} {{< /tabs >}} @@ -598,6 +769,10 @@ If you are implementing your own movement sensor and add features that have no b - (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), viam.utils.ValueTypes]): Result of the executed command. +**Raises:** + +- (NotImplementedError): Raised if the Resource does not support arbitrary commands. + **Example:** ```python {class="line-numbers linkable-line-numbers"} @@ -632,6 +807,68 @@ result, err := myArm.DoCommand(context.Background(), command) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Resource). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `command` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic> (required) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +// Example using doCommand with an arm component +const command = {'cmd': 'test', 'data1': 500}; +var result = myArm.doCommand(command); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Resource/doCommand.html). + +{{% /tab %}} +{{< /tabs >}} + +### FromRobot + +Get the resource from the provided robot with the given name. + +{{< tabs >}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `robot` [RobotClient](https://flutter.viam.dev/viam_sdk/RobotClient-class.html) (required) +- `name` [String](https://api.flutter.dev/flutter/dart-core/String-class.html) (required) + +**Returns:** + +- [MovementSensor](https://flutter.viam.dev/viam_sdk/MovementSensor-class.html) + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/MovementSensor/fromRobot.html). + +{{% /tab %}} +{{< /tabs >}} + +### Name + +Get the `ResourceName` for this movement sensor with the given name. + +{{< tabs >}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `name` [String](https://api.flutter.dev/flutter/dart-core/String-class.html) (required) + +**Returns:** + +- [ResourceName](https://flutter.viam.dev/viam_sdk/ResourceName-class.html) + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/MovementSensor/getResourceName.html). + {{% /tab %}} {{< /tabs >}} diff --git a/static/include/components/apis/generated/power_sensor-table.md b/static/include/components/apis/generated/power_sensor-table.md index 4b9669ab92..1680520786 100644 --- a/static/include/components/apis/generated/power_sensor-table.md +++ b/static/include/components/apis/generated/power_sensor-table.md @@ -8,4 +8,6 @@ | [`GetGeometries`](/components/power-sensor/#getgeometries) | Get all the geometries associated with the power sensor in its current configuration, in the frame of the power sensor. | | [`Reconfigure`](/components/power-sensor/#reconfigure) | Reconfigure this resource. | | [`DoCommand`](/components/power-sensor/#docommand) | Execute model-specific commands that are not otherwise defined by the component API. | +| [`FromRobot`](/components/power-sensor/#fromrobot) | Get the resource from the provided robot with the given name. | +| [`Name`](/components/power-sensor/#name) | Get the `ResourceName` for this power sensor with the given name. | | [`Close`](/components/power-sensor/#close) | Safely shut down the resource and prevent further use. | diff --git a/static/include/components/apis/generated/power_sensor.md b/static/include/components/apis/generated/power_sensor.md index 86e60c88bb..27ca47d2d8 100644 --- a/static/include/components/apis/generated/power_sensor.md +++ b/static/include/components/apis/generated/power_sensor.md @@ -49,6 +49,27 @@ voltage, isAC, err := myPowerSensor.Voltage(context.Background(), nil) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/powersensor#PowerSensor). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[Voltage](https://flutter.viam.dev/viam_sdk/Voltage.html)> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +var voltageObject = await myPowerSensor.readings(); +double voltageInVolts = voltageObject['volts']; +bool isItAC = voltageObject['isAc']; +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/PowerSensor/voltage.html). + {{% /tab %}} {{< /tabs >}} @@ -103,6 +124,27 @@ current, isAC, err := myPowerSensor.Current(context.Background(), nil) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/powersensor#PowerSensor). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[Current](https://flutter.viam.dev/viam_sdk/Current.html)> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +var currentObject = await myPowerSensor.readings(); +double amps = voltageObject['amperes']; +bool isItAC = voltageObject['isAc']; +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/PowerSensor/current.html). + {{% /tab %}} {{< /tabs >}} @@ -156,6 +198,25 @@ power, err := myPowerSensor.Power(context.Background(), nil) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/powersensor#PowerSensor). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[double](https://api.flutter.dev/flutter/dart-core/double-class.html)> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +var power = await myPowerSensor.power(); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/PowerSensor/power.html). + {{% /tab %}} {{< /tabs >}} @@ -209,6 +270,25 @@ readings, err := mySensor.Readings(context.Background(), nil) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Sensor). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +var readings = await myPowerSensor.readings(); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/PowerSensor/readings.html). + {{% /tab %}} {{< /tabs >}} @@ -284,6 +364,10 @@ If you are implementing your own power sensor and add features that have no buil - (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), viam.utils.ValueTypes]): Result of the executed command. +**Raises:** + +- (NotImplementedError): Raised if the Resource does not support arbitrary commands. + **Example:** ```python {class="line-numbers linkable-line-numbers"} @@ -318,6 +402,68 @@ result, err := myArm.DoCommand(context.Background(), command) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Resource). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `command` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic> (required) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +// Example using doCommand with an arm component +const command = {'cmd': 'test', 'data1': 500}; +var result = myArm.doCommand(command); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Resource/doCommand.html). + +{{% /tab %}} +{{< /tabs >}} + +### FromRobot + +Get the resource from the provided robot with the given name. + +{{< tabs >}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `robot` [RobotClient](https://flutter.viam.dev/viam_sdk/RobotClient-class.html) (required) +- `name` [String](https://api.flutter.dev/flutter/dart-core/String-class.html) (required) + +**Returns:** + +- [PowerSensor](https://flutter.viam.dev/viam_sdk/PowerSensor-class.html) + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/PowerSensor/fromRobot.html). + +{{% /tab %}} +{{< /tabs >}} + +### Name + +Get the `ResourceName` for this power sensor with the given name. + +{{< tabs >}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `name` [String](https://api.flutter.dev/flutter/dart-core/String-class.html) (required) + +**Returns:** + +- [ResourceName](https://flutter.viam.dev/viam_sdk/ResourceName-class.html) + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/PowerSensor/getResourceName.html). + {{% /tab %}} {{< /tabs >}} diff --git a/static/include/components/apis/generated/sensor-table.md b/static/include/components/apis/generated/sensor-table.md index fb5f48f59c..18d05e3dc3 100644 --- a/static/include/components/apis/generated/sensor-table.md +++ b/static/include/components/apis/generated/sensor-table.md @@ -5,4 +5,6 @@ | [`GetGeometries`](/components/sensor/#getgeometries) | Get all the geometries associated with the sensor in its current configuration, in the frame of the sensor. | | [`Reconfigure`](/components/sensor/#reconfigure) | Reconfigure this resource. | | [`DoCommand`](/components/sensor/#docommand) | Execute model-specific commands that are not otherwise defined by the component API. | +| [`FromRobot`](/components/sensor/#fromrobot) | Get the resource from the provided robot with the given name. | +| [`Name`](/components/sensor/#name) | Get the `ResourceName` for this sensor with the given name. | | [`Close`](/components/sensor/#close) | Safely shut down the resource and prevent further use. | diff --git a/static/include/components/apis/generated/sensor.md b/static/include/components/apis/generated/sensor.md index 9c34075c0e..9dda27d4ad 100644 --- a/static/include/components/apis/generated/sensor.md +++ b/static/include/components/apis/generated/sensor.md @@ -47,6 +47,25 @@ readings, err := mySensor.Readings(context.Background(), nil) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Sensor). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +var readings = await mySensor.readings(); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Sensor/readings.html). + {{% /tab %}} {{< /tabs >}} @@ -123,6 +142,10 @@ If you are implementing your own sensor and add features that have no built-in A - (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), viam.utils.ValueTypes]): Result of the executed command. +**Raises:** + +- (NotImplementedError): Raised if the Resource does not support arbitrary commands. + **Example:** ```python {class="line-numbers linkable-line-numbers"} @@ -157,6 +180,68 @@ result, err := myArm.DoCommand(context.Background(), command) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Resource). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `command` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic> (required) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +// Example using doCommand with an arm component +const command = {'cmd': 'test', 'data1': 500}; +var result = myArm.doCommand(command); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Resource/doCommand.html). + +{{% /tab %}} +{{< /tabs >}} + +### FromRobot + +Get the resource from the provided robot with the given name. + +{{< tabs >}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `robot` [RobotClient](https://flutter.viam.dev/viam_sdk/RobotClient-class.html) (required) +- `name` [String](https://api.flutter.dev/flutter/dart-core/String-class.html) (required) + +**Returns:** + +- [Sensor](https://flutter.viam.dev/viam_sdk/Sensor-class.html) + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Sensor/fromRobot.html). + +{{% /tab %}} +{{< /tabs >}} + +### Name + +Get the `ResourceName` for this sensor with the given name. + +{{< tabs >}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `name` [String](https://api.flutter.dev/flutter/dart-core/String-class.html) (required) + +**Returns:** + +- [ResourceName](https://flutter.viam.dev/viam_sdk/ResourceName-class.html) + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Sensor/getResourceName.html). + {{% /tab %}} {{< /tabs >}} diff --git a/static/include/components/apis/generated/servo-table.md b/static/include/components/apis/generated/servo-table.md index 4de771e42a..7daaa9d094 100644 --- a/static/include/components/apis/generated/servo-table.md +++ b/static/include/components/apis/generated/servo-table.md @@ -8,4 +8,6 @@ | [`Stop`](/components/servo/#stop) | Stop the servo from moving. | | [`Reconfigure`](/components/servo/#reconfigure) | Reconfigure this resource. | | [`DoCommand`](/components/servo/#docommand) | Execute model-specific commands that are not otherwise defined by the component API. | +| [`FromRobot`](/components/servo/#fromrobot) | Get the resource from the provided robot with the given name. | +| [`Name`](/components/servo/#name) | Get the `ResourceName` for this servo with the given name. | | [`Close`](/components/servo/#close) | Safely shut down the resource and prevent further use. | diff --git a/static/include/components/apis/generated/servo.md b/static/include/components/apis/generated/servo.md index df17c97f0a..c5868553e2 100644 --- a/static/include/components/apis/generated/servo.md +++ b/static/include/components/apis/generated/servo.md @@ -64,6 +64,20 @@ myServoComponent.Move(context.Background(), 30, nil) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/servo#Servo). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `angle` [int](https://api.flutter.dev/flutter/dart-core/int-class.html) (required) +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html) + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Servo/move.html). + {{% /tab %}} {{< /tabs >}} @@ -134,6 +148,19 @@ logger.Info("Position 2: ", pos2) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/components/servo#Servo). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[int](https://api.flutter.dev/flutter/dart-core/int-class.html)> + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Servo/position.html). + {{% /tab %}} {{< /tabs >}} @@ -222,6 +249,19 @@ logger.Info(is_moving) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Actuator). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- None. + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[bool](https://api.flutter.dev/flutter/dart-core/bool-class.html)> + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Servo/isMoving.html). + {{% /tab %}} {{< /tabs >}} @@ -279,6 +319,19 @@ err = myArm.Stop(context.Background(), nil) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Actuator). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `extra` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>? (optional) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html) + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Servo/stop.html). + {{% /tab %}} {{< /tabs >}} @@ -323,6 +376,10 @@ If you are implementing your own servo and add features that have no built-in AP - (Mapping[[str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str), viam.utils.ValueTypes]): Result of the executed command. +**Raises:** + +- (NotImplementedError): Raised if the Resource does not support arbitrary commands. + **Example:** ```python {class="line-numbers linkable-line-numbers"} @@ -357,6 +414,68 @@ result, err := myArm.DoCommand(context.Background(), command) For more information, see the [Go SDK Docs](https://pkg.go.dev/go.viam.com/rdk/resource#Resource). +{{% /tab %}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `command` [Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic> (required) + +**Returns:** + +- [Future](https://api.flutter.dev/flutter/dart-async/Future-class.html)<[Map](https://api.flutter.dev/flutter/dart-core/Map-class.html)<[String](https://api.flutter.dev/flutter/dart-core/String-class.html), dynamic>> + +**Example:** + +```dart {class="line-numbers linkable-line-numbers"} +// Example using doCommand with an arm component +const command = {'cmd': 'test', 'data1': 500}; +var result = myArm.doCommand(command); +``` + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Resource/doCommand.html). + +{{% /tab %}} +{{< /tabs >}} + +### FromRobot + +Get the resource from the provided robot with the given name. + +{{< tabs >}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `robot` [RobotClient](https://flutter.viam.dev/viam_sdk/RobotClient-class.html) (required) +- `name` [String](https://api.flutter.dev/flutter/dart-core/String-class.html) (required) + +**Returns:** + +- [Servo](https://flutter.viam.dev/viam_sdk/Servo-class.html) + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Servo/fromRobot.html). + +{{% /tab %}} +{{< /tabs >}} + +### Name + +Get the `ResourceName` for this servo with the given name. + +{{< tabs >}} +{{% tab name="Flutter" %}} + +**Parameters:** + +- `name` [String](https://api.flutter.dev/flutter/dart-core/String-class.html) (required) + +**Returns:** + +- [ResourceName](https://flutter.viam.dev/viam_sdk/ResourceName-class.html) + +For more information, see the [Flutter SDK Docs](https://flutter.viam.dev/viam_sdk/Servo/getResourceName.html). + {{% /tab %}} {{< /tabs >}} diff --git a/static/include/components/apis/overrides/methods/python.camera.get_image.after.md b/static/include/components/apis/overrides/methods/python.camera.get_image.after.md index 3df763ac90..75bda83c0c 100644 --- a/static/include/components/apis/overrides/methods/python.camera.get_image.after.md +++ b/static/include/components/apis/overrides/methods/python.camera.get_image.after.md @@ -1,4 +1,4 @@ -As shown above, if the `mime_type` of your image is `image/vnd.viam.dep`, pass the returned image data to the Viam Python SDK's [`ViamImage.bytes_to_depth_array()`](https://python.viam.dev/autoapi/viam/media/video/index.html#viam.media.video.ViamImage.bytes_to_depth_array) method to decode the raw image data to a standard 2D image representation. +If the `mime_type` of your image is `image/vnd.viam.dep`, pass the returned image data to the Viam Python SDK's [`ViamImage.bytes_to_depth_array()`](https://python.viam.dev/autoapi/viam/media/video/index.html#viam.media.video.ViamImage.bytes_to_depth_array) method to decode the raw image data to a standard 2D image representation. In addition, the Python SDK provides the helper functions `viam_to_pil_image` and `pil_to_viam_image` to decode the `ViamImage` into a [`PIL Image`](https://omz-software.com/pythonista/docs/ios/Image.html) and vice versa. diff --git a/static/include/robot/apis/generated/robot.md b/static/include/robot/apis/generated/robot.md index 8ba0076f93..232b2d9953 100644 --- a/static/include/robot/apis/generated/robot.md +++ b/static/include/robot/apis/generated/robot.md @@ -503,7 +503,7 @@ Create a LogEntry object from the log to send to the RDK over gRPC. - `name` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): The logger’s name. - `level` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): The level of the log. - `time` ([datetime.datetime](https://docs.python.org/3/library/datetime.html)) (required): The log creation time. -- `log` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): The log message. +- `message` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): The log message. - `stack` ([str](https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str)) (required): The stack information of the log. **Returns:** @@ -533,7 +533,7 @@ Get app-related information about the robot. **Example:** ```python {class="line-numbers linkable-line-numbers"} -metadata = await machine.get_cloud_metadata() +metadata = machine.get_cloud_metadata() print(metadata.machine_id) print(metadata.machine_part_id) print(metadata.primary_org_id) @@ -557,7 +557,8 @@ For more information, see the [Python SDK Docs](https://python.viam.dev/autoapi/ **Example:** ```go {class="line-numbers linkable-line-numbers"} -metadata, err := machine.CloudMetadata(ctx.Background()) +metadata, err := machine.CloudMetadata() +machine_id = metadata.MachineID machine_part_id = metadata.MachinePartID primary_org_id = metadata.PrimaryOrgID location_id = metadata.LocationID @@ -684,7 +685,7 @@ Any machines created using this method will NOT automatically close the channel **Parameters:** -- `channel` ([grpclib.client.Channel | viam.rpc.dial.ViamChannel](https://python.viam.dev/autoapi/viam/rpc/dial/index.html#viam.rpc.dial.ViamChannel)) (required): The channel that is connected to a robot, obtained by viam.rpc.dial. +- `channel` ([grpclib.client.Channel | viam.rpc.dial.ViamChannel](https://python.viam.dev/autoapi/viam/robot/client/index.html#viam.robot.client.ViamChannel)) (required): The channel that is connected to a robot, obtained by viam.rpc.dial. - `options` ([Options](https://python.viam.dev/autoapi/viam/robot/client/index.html#viam.robot.client.RobotClient.Options)) (required): Options for refreshing. Any connection options will be ignored. **Returns:** From 11a614db6fcf61a699e61f3af45fcf62d43fa2da Mon Sep 17 00:00:00 2001 From: Sierra Guequierre Date: Tue, 9 Jul 2024 11:35:37 -0400 Subject: [PATCH 17/22] DOCS-2559:Add new headers to triggers (#3111) --- docs/build/configure/triggers.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/build/configure/triggers.md b/docs/build/configure/triggers.md index 32947e1a0f..09c7894194 100644 --- a/docs/build/configure/triggers.md +++ b/docs/build/configure/triggers.md @@ -11,7 +11,7 @@ aliases: Triggers allow you to execute actions when certain types of data are sent from your machine to the cloud, or when the internet connectivity of your machine changes. For example, you can configure a trigger to send you a notification when your robot's sensor collects a new reading. -Viam provides two trigger types depending on the event you want to trigger on: +Viam provides three trigger types depending on the event you want to trigger on: - **Data has been synced to the cloud**: trigger when data from the machine is synced - **Part is online**: trigger continuously at a specified interval while the {{< glossary_tooltip term_id="part" text="machine part" >}} is online @@ -192,7 +192,10 @@ If you prefer to configure your trigger with raw JSON instead of the config buil "Org-ID": request.headers['org-id'] if 'org-id' in request.headers else 'no value', "Location-ID": request.headers['location-id'] if 'location-id' in request.headers else 'no value', "Part-ID": request.headers['part-id'] if 'part-id' in request.headers else 'no value', - "Robot-ID": request.headers['robot-id'] if 'robot-id' in request.headers else 'no value' + "Robot-ID": request.headers['robot-id'] if 'robot-id' in request.headers else 'no value', + "Machine-Name": request.headers['machine-name'] if 'machine-name' in request.headers else 'no value', + "Location-Name": request.headers['location-name'] if 'location-name' in request.headers else 'no value', + "Organization-Name": request.headers['organization-name'] if 'organization-name' in request.headers else 'no value' } slack_url = "" From 41b68295f80110b46ffa965976a1ab1541282d8a Mon Sep 17 00:00:00 2001 From: Naomi Pentrel <5212232+npentrel@users.noreply.github.com> Date: Tue, 9 Jul 2024 19:36:33 +0200 Subject: [PATCH 18/22] Pin version and update analytics variable (#3108) --- .github/workflows/docs.yml | 2 +- .github/workflows/pr-deploy-and-comment.yml | 2 +- layouts/docs/baseof.html | 4 +- layouts/docs/changelog.html | 4 +- layouts/docs/content.html | 2 +- layouts/docs/glossary.html | 4 +- layouts/docs/tutorials.html | 4 +- layouts/partials/head.html | 4 +- package-lock.json | 511 ++++++++++++++++++++ package.json | 3 + 10 files changed, 527 insertions(+), 13 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index d2ea3534b5..c318b08b68 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -30,7 +30,7 @@ jobs: - name: Setup Hugo uses: peaceiris/actions-hugo@v2 with: - hugo-version: "latest" + hugo-version: "0.128.2" extended: true - name: Build diff --git a/.github/workflows/pr-deploy-and-comment.yml b/.github/workflows/pr-deploy-and-comment.yml index c7f4c96c57..b7aa96e3d6 100644 --- a/.github/workflows/pr-deploy-and-comment.yml +++ b/.github/workflows/pr-deploy-and-comment.yml @@ -26,7 +26,7 @@ jobs: - name: Setup Hugo uses: peaceiris/actions-hugo@v2 with: - hugo-version: 'latest' + hugo-version: "0.128.2" extended: true - name: Temp config diff --git a/layouts/docs/baseof.html b/layouts/docs/baseof.html index 05016ed7e2..7313107d12 100644 --- a/layouts/docs/baseof.html +++ b/layouts/docs/baseof.html @@ -4,9 +4,9 @@ {{ partial "head.html" . }} - {{- if hugo.IsProduction -}}{{- if .Site.GoogleAnalytics -}} + {{- if hugo.IsProduction -}}{{- if .Site.Config.Services.GoogleAnalytics.ID -}} - {{- end -}}{{- end -}} diff --git a/layouts/docs/changelog.html b/layouts/docs/changelog.html index 176a4ffc28..5004af8469 100644 --- a/layouts/docs/changelog.html +++ b/layouts/docs/changelog.html @@ -4,9 +4,9 @@ {{ partial "head.html" . }} - {{- if hugo.IsProduction -}}{{- if .Site.GoogleAnalytics -}} + {{- if hugo.IsProduction -}}{{- if .Site.Config.Services.GoogleAnalytics.ID -}} - {{- end -}}{{- end -}} diff --git a/layouts/docs/content.html b/layouts/docs/content.html index e36b677e36..7cecb061b4 100644 --- a/layouts/docs/content.html +++ b/layouts/docs/content.html @@ -65,7 +65,7 @@

    DRAFT

    {{ partial "disqus-comment.html" . }} {{ end }} {{ partial "page-meta-lastmod.html" . }} - {{- if hugo.IsProduction -}}{{ if (and (not .Params.hide_feedback) (.Site.Params.ui.feedback.enable) (.Site.GoogleAnalytics)) }} + {{- if hugo.IsProduction -}}{{ if (and (not .Params.hide_feedback) (.Site.Params.ui.feedback.enable) (.Site.Config.Services.GoogleAnalytics.ID)) }} {{ partial "feedback.html" . }}
    {{ end }}{{- end -}} diff --git a/layouts/docs/glossary.html b/layouts/docs/glossary.html index e7212b3ce6..c3e72ea546 100644 --- a/layouts/docs/glossary.html +++ b/layouts/docs/glossary.html @@ -4,9 +4,9 @@ {{ partial "head.html" . }} - {{- if hugo.IsProduction -}}{{- if .Site.GoogleAnalytics -}} + {{- if hugo.IsProduction -}}{{- if .Site.Config.Services.GoogleAnalytics.ID -}} - {{- end -}}{{- end -}} diff --git a/layouts/docs/tutorials.html b/layouts/docs/tutorials.html index 850a889fa6..5fff3139de 100644 --- a/layouts/docs/tutorials.html +++ b/layouts/docs/tutorials.html @@ -4,9 +4,9 @@ {{ partial "head.html" . }} - {{- if hugo.IsProduction -}}{{- if .Site.GoogleAnalytics -}} + {{- if hugo.IsProduction -}}{{- if .Site.Config.Services.GoogleAnalytics.ID -}} - {{- end -}}{{- end -}} diff --git a/layouts/partials/head.html b/layouts/partials/head.html index 328cbaea25..670cc8a302 100644 --- a/layouts/partials/head.html +++ b/layouts/partials/head.html @@ -66,12 +66,12 @@ rel="stylesheet" href="{{ "css/prism.css" | relURL }}"/> {{ end -}} {{ partial "hooks/head-end.html" . -}} {{/* To comply with GDPR, cookie consent scripts places in head-end must execute before Google Analytics is enabled */ -}} {{ if -hugo.IsProduction -}}{{- if .Site.GoogleAnalytics -}} +hugo.IsProduction -}}{{- if .Site.Config.Services.GoogleAnalytics.ID -}} + })(window,document,'script','dataLayer','{{ .Site.Config.Services.GoogleAnalytics.ID }}'); {{- end -}} {{ end -}} diff --git a/package-lock.json b/package-lock.json index 93c8084a2a..4558cbd984 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,9 @@ "name": "tech-doc-hugo", "version": "0.0.1", "license": "ISC", + "dependencies": { + "hugo": "^0.0.3" + }, "devDependencies": { "autoprefixer": "^10.4.13", "postcss": "^8.4.31", @@ -99,6 +102,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/asn1": { + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz", + "integrity": "sha512-Fh9zh3G2mZ8qM/kwsiKwL2U2FmXxVsboP4x1mXjnhKHv3SmzaBZoYvxEQJz/YS2gnCgd8xlAVWcZnQyC9qZBsA==", + "engines": { + "node": ">=0.4.9" + } + }, + "node_modules/assert-plus": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz", + "integrity": "sha512-brU24g7ryhRwGCI2y+1dGQmQXiZF7TtIj583S96y0jjdajIe6wn8BuXyELYhvD22dtIxDQVFk04YTJwwdwOYJw==", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha512-eAkdoKxU6/LkKDBzLpT+t6Ff5EtfSF4wx1WfJiPEEV7WNLnDaRXk0oVysiEPm262roaachGexwUv94WhSgN5TQ==" + }, "node_modules/autoprefixer": { "version": "10.4.13", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz", @@ -132,6 +156,14 @@ "postcss": "^8.1.0" } }, + "node_modules/aws-sign": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/aws-sign/-/aws-sign-0.3.0.tgz", + "integrity": "sha512-pEMJAknifcXqXqYVXzGPIu8mJvxtJxIdpVpAs8HNS+paT+9srRUDMQn+3hULS7WbLmttcmvgMvnDcFujqXJyPw==", + "engines": { + "node": "*" + } + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -141,6 +173,18 @@ "node": ">=8" } }, + "node_modules/boom": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz", + "integrity": "sha512-OvfN8y1oAxxphzkl2SnCS+ztV/uVKTATtgLjWYg/7KwcNyf3rzpHxNQJZCKtsZd4+MteKczhWbSjtEX4bGgU9g==", + "deprecated": "This version has been deprecated in accordance with the hapi support policy (hapi.im/support). Please upgrade to the latest version to get the best features, bug fixes, and security patches. If you are unable to upgrade at this time, paid support is available for older versions (hapi.im/commercial).", + "dependencies": { + "hoek": "0.9.x" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/braces": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", @@ -238,6 +282,15 @@ "node": ">=12" } }, + "node_modules/color": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/color/-/color-0.4.4.tgz", + "integrity": "sha512-Wa2mQ2xBzUsWuaADkn2hW4byONQ+ccYAntGg1OT0ZaULyf8vBgybV49dl60092xYOiFZaPIgZOAowxMqaKesQA==", + "dependencies": { + "color-convert": "0.2.x", + "color-string": "0.1.x" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -256,6 +309,85 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "node_modules/color-string": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-0.1.3.tgz", + "integrity": "sha512-ERkoOp/s/VSrQ5OyH1Gs9LCgFWnTlQXUqAaGNBJzV2gjuunWuxISth8lOaDqfPfDIjiR9MI7WrzH1hDNRVOCfQ==", + "dependencies": { + "color-convert": "0.2.x" + } + }, + "node_modules/color-string/node_modules/color-convert": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.2.1.tgz", + "integrity": "sha512-FWbwpCgyRV41Vml0iKU9UmL0dVTKORnm7ZC8h8cdfvutk2bU7ZcMLtSleggScK/IpUVXILg9Pw86LhPUQyTaVg==", + "engines": { + "node": "*" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.2.1.tgz", + "integrity": "sha512-FWbwpCgyRV41Vml0iKU9UmL0dVTKORnm7ZC8h8cdfvutk2bU7ZcMLtSleggScK/IpUVXILg9Pw86LhPUQyTaVg==", + "engines": { + "node": "*" + } + }, + "node_modules/combined-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", + "integrity": "sha512-qfexlmLp9MyrkajQVyjEDb0Vj+KhRgR/rxLiVhaihlT+ZkX0lReqtH6Ack40CvMDERR4b5eFp3CreskpBs1Pig==", + "dependencies": { + "delayed-stream": "0.0.5" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cookie-jar": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/cookie-jar/-/cookie-jar-0.3.0.tgz", + "integrity": "sha512-dX1400pzPULr+ZovkIsDEqe7XH8xCAYGT5Dege4Eot44Qs2mS2iJmnh45TxTO5MIsCfrV/JGZVloLhm46AHxNw==", + "engines": { + "node": "*" + } + }, + "node_modules/cryptiles": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz", + "integrity": "sha512-gvWSbgqP+569DdslUiCelxIv3IYK5Lgmq1UrRnk+s1WxQOQ16j3GPDcjdtgL5Au65DU/xQi6q3xPtf5Kta+3IQ==", + "deprecated": "This version has been deprecated in accordance with the hapi support policy (hapi.im/support). Please upgrade to the latest version to get the best features, bug fixes, and security patches. If you are unable to upgrade at this time, paid support is available for older versions (hapi.im/commercial).", + "dependencies": { + "boom": "0.4.x" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/ctype": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.3.tgz", + "integrity": "sha512-T6CEkoSV4q50zW3TlTHMbzy1E5+zlnNcY+yb7tWVYlTwPhx9LpnfAkd4wecpWknDyptp4k97LUZeInlf6jdzBg==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/debug": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz", + "integrity": "sha512-EohAb3+DSHSGx8carOSKJe8G0ayV5/i609OD0J2orCkuyae7SyZSz2aoLmQF2s0Pj5gITDebwPH7GFBlqOUQ1Q==", + "engines": { + "node": "*" + } + }, + "node_modules/delayed-stream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz", + "integrity": "sha512-v+7uBd1pqe5YtgPacIIbZ8HuHeLFVNe4mUEyFDXL6KiqzEykjbw+5mXZXpGFgNVasdL4jWKgaKIXrEHiynN1LA==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/dependency-graph": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", @@ -335,6 +467,32 @@ "node": ">=8" } }, + "node_modules/forever-agent": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz", + "integrity": "sha512-PDG5Ef0Dob/JsZUxUltJOhm/Y9mlteAE+46y3M9RBz/Rd3QVENJ75aGRhN56yekTUboaBIkd8KVWX2NjF6+91A==", + "engines": { + "node": "*" + } + }, + "node_modules/form-data": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.4.tgz", + "integrity": "sha512-x8eE+nzFtAMA0YYlSxf/Qhq6vP1f8wSoZ7Aw1GuctBcmudCNuTUmmx45TfEplyb6cjsZO/jvh6+1VpZn24ez+w==", + "dependencies": { + "async": "~0.9.0", + "combined-stream": "~0.0.4", + "mime": "~1.2.11" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/form-data/node_modules/async": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "integrity": "sha512-l6ToIJIotphWahxxHyzK9bnLR6kM4jJIIgLShZeqLY7iboHoGkdgFl7W2/Ivi4SkMJYGKqW8vSuk0uKUj6qsSw==" + }, "node_modules/fraction.js": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", @@ -435,6 +593,55 @@ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, + "node_modules/hawk": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-1.0.0.tgz", + "integrity": "sha512-Sg+VzrI7TjUomO0rjD6UXawsj50ykn5sB/xKNW/IenxzRVyw/wt9A2FLzYpGL/r0QG5hyXY8nLx/2m8UutoDcg==", + "deprecated": "This module moved to @hapi/hawk. Please make sure to switch over as this distribution is no longer supported and may contain bugs and critical security issues.", + "dependencies": { + "boom": "0.4.x", + "cryptiles": "0.2.x", + "hoek": "0.9.x", + "sntp": "0.2.x" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/hoek": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz", + "integrity": "sha512-ZZ6eGyzGjyMTmpSPYVECXy9uNfqBR7x5CavhUaLOeD6W0vWK1mp/b7O3f86XE0Mtfo9rZ6Bh3fnuw9Xr8MF9zA==", + "deprecated": "This version has been deprecated in accordance with the hapi support policy (hapi.im/support). Please upgrade to the latest version to get the best features, bug fixes, and security patches. If you are unable to upgrade at this time, paid support is available for older versions (hapi.im/commercial).", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/http-signature": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.1.tgz", + "integrity": "sha512-coK8uR5rq2IMj+Hen+sKPA5ldgbCc1/spPdKCL1Fw6h+D0s/2LzMcRK0Cqufs1h0ryx/niwBHGFu8HC3hwU+lA==", + "dependencies": { + "asn1": "0.1.11", + "assert-plus": "^0.1.5", + "ctype": "0.5.3" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/hugo": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/hugo/-/hugo-0.0.3.tgz", + "integrity": "sha512-CWJ5p8w0v6M3Z8FUbLOYUtlCZNcC3PYUQuu6rpu1yj61IIIt6qgc4weAMiu0jsoJQAUlFiXgMH4fCo4geEdvbA==", + "dependencies": { + "async": "~0.2.9", + "color": "~0.4.4", + "debug": "~0.7.2", + "lodash": "~2.2.1", + "request": "~2.27.0" + } + }, "node_modules/ignore": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", @@ -495,6 +702,11 @@ "node": ">=0.12.0" } }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" + }, "node_modules/jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", @@ -516,6 +728,15 @@ "node": ">=10" } }, + "node_modules/lodash": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.2.1.tgz", + "integrity": "sha512-rGaKzxe4Biu8YdCPD/tUkBF4/fnAqgj63A6PeAyQnH/NEKNUHgppGZUgYBYOmAZsBKwwAb343Q1Zew0RDB2jIg==", + "engines": [ + "node", + "rhino" + ] + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -538,6 +759,11 @@ "node": ">=8.6" } }, + "node_modules/mime": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz", + "integrity": "sha512-Ysa2F/nqTNGHhhm9MV8ure4+Hc+Y8AWiqUdHxsO7xu8zc92ND9f3kpALHjaP026Ft17UfxrMt95c50PLUeynBw==" + }, "node_modules/nanoid": { "version": "3.3.6", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", @@ -562,6 +788,15 @@ "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", "dev": true }, + "node_modules/node-uuid": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", + "integrity": "sha512-TkCET/3rr9mUuRp+CpO7qfgT++aAxfDRaalQhwPFzI9BY/2rCDn6OfpZOVggi1AXfTPpfkTrg5f5WQx5G1uLxA==", + "deprecated": "Use uuid module instead", + "bin": { + "uuid": "bin/uuid" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -580,6 +815,14 @@ "node": ">=0.10.0" } }, + "node_modules/oauth-sign": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.3.0.tgz", + "integrity": "sha512-Tr31Sh5FnK9YKm7xTUPyDMsNOvMqkVDND0zvK/Wgj7/H9q8mpye0qG2nVzrnsvLhcsX5DtqXD0la0ks6rkPCGQ==", + "engines": { + "node": "*" + } + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -752,6 +995,14 @@ "node": ">= 0.8" } }, + "node_modules/qs": { + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-0.6.6.tgz", + "integrity": "sha512-kN+yNdAf29Jgp+AYHUmC7X4QdJPR8czuMWLNLc0aRxkQ7tB3vJQEONKKT9ou/rW7EbqVec11srC9q9BiVbcnHA==", + "engines": { + "node": "*" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -793,6 +1044,29 @@ "node": ">=8.10.0" } }, + "node_modules/request": { + "version": "2.27.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.27.0.tgz", + "integrity": "sha512-V4AYOEmdUrf0X+CVF2hndyMzIeQ8G7LB45HER/rXZYEwNVI3QFGgLPLafQLHjqtG/ggzHEMb66Ng5IemksixsQ==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "engines": [ + "node >= 0.8.0" + ], + "dependencies": { + "aws-sign": "~0.3.0", + "cookie-jar": "~0.3.0", + "forever-agent": "~0.5.0", + "form-data": "~0.1.0", + "hawk": "~1.0.0", + "http-signature": "~0.10.0", + "json-stringify-safe": "~5.0.0", + "mime": "~1.2.9", + "node-uuid": "~1.4.0", + "oauth-sign": "~0.3.0", + "qs": "~0.6.0", + "tunnel-agent": "~0.3.0" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -847,6 +1121,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/sntp": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-0.2.4.tgz", + "integrity": "sha512-bDLrKa/ywz65gCl+LmOiIhteP1bhEsAAzhfMedPoiHP3dyYnAevlaJshdqb9Yu0sRifyP/fRqSt8t+5qGIWlGQ==", + "deprecated": "This module moved to @hapi/sntp. Please make sure to switch over as this distribution is no longer supported and may contain bugs and critical security issues.", + "dependencies": { + "hoek": "0.9.x" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/source-map-js": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", @@ -900,6 +1186,14 @@ "node": ">=8.0" } }, + "node_modules/tunnel-agent": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.3.0.tgz", + "integrity": "sha512-jlGqHGoKzyyjhwv/c9omAgohntThMcGtw8RV/RDLlkbbc08kni/akVxO62N8HaXMVbVsK1NCnpSK3N2xCt22ww==", + "engines": { + "node": "*" + } + }, "node_modules/universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", @@ -1056,6 +1350,21 @@ "integrity": "sha512-1OvF9IbWwaeiM9VhzYXVQacMibxpXOMYVNIvMtKRyX9SImBXpKcFr8XvFDeEslCyuH/t6KRt7HEO94AlP8Iatw==", "dev": true }, + "asn1": { + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz", + "integrity": "sha512-Fh9zh3G2mZ8qM/kwsiKwL2U2FmXxVsboP4x1mXjnhKHv3SmzaBZoYvxEQJz/YS2gnCgd8xlAVWcZnQyC9qZBsA==" + }, + "assert-plus": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz", + "integrity": "sha512-brU24g7ryhRwGCI2y+1dGQmQXiZF7TtIj583S96y0jjdajIe6wn8BuXyELYhvD22dtIxDQVFk04YTJwwdwOYJw==" + }, + "async": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", + "integrity": "sha512-eAkdoKxU6/LkKDBzLpT+t6Ff5EtfSF4wx1WfJiPEEV7WNLnDaRXk0oVysiEPm262roaachGexwUv94WhSgN5TQ==" + }, "autoprefixer": { "version": "10.4.13", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.13.tgz", @@ -1070,12 +1379,25 @@ "postcss-value-parser": "^4.2.0" } }, + "aws-sign": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/aws-sign/-/aws-sign-0.3.0.tgz", + "integrity": "sha512-pEMJAknifcXqXqYVXzGPIu8mJvxtJxIdpVpAs8HNS+paT+9srRUDMQn+3hULS7WbLmttcmvgMvnDcFujqXJyPw==" + }, "binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", "dev": true }, + "boom": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz", + "integrity": "sha512-OvfN8y1oAxxphzkl2SnCS+ztV/uVKTATtgLjWYg/7KwcNyf3rzpHxNQJZCKtsZd4+MteKczhWbSjtEX4bGgU9g==", + "requires": { + "hoek": "0.9.x" + } + }, "braces": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", @@ -1130,6 +1452,22 @@ "wrap-ansi": "^7.0.0" } }, + "color": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/color/-/color-0.4.4.tgz", + "integrity": "sha512-Wa2mQ2xBzUsWuaADkn2hW4byONQ+ccYAntGg1OT0ZaULyf8vBgybV49dl60092xYOiFZaPIgZOAowxMqaKesQA==", + "requires": { + "color-convert": "0.2.x", + "color-string": "0.1.x" + }, + "dependencies": { + "color-convert": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.2.1.tgz", + "integrity": "sha512-FWbwpCgyRV41Vml0iKU9UmL0dVTKORnm7ZC8h8cdfvutk2bU7ZcMLtSleggScK/IpUVXILg9Pw86LhPUQyTaVg==" + } + } + }, "color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1145,6 +1483,57 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "color-string": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-0.1.3.tgz", + "integrity": "sha512-ERkoOp/s/VSrQ5OyH1Gs9LCgFWnTlQXUqAaGNBJzV2gjuunWuxISth8lOaDqfPfDIjiR9MI7WrzH1hDNRVOCfQ==", + "requires": { + "color-convert": "0.2.x" + }, + "dependencies": { + "color-convert": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.2.1.tgz", + "integrity": "sha512-FWbwpCgyRV41Vml0iKU9UmL0dVTKORnm7ZC8h8cdfvutk2bU7ZcMLtSleggScK/IpUVXILg9Pw86LhPUQyTaVg==" + } + } + }, + "combined-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", + "integrity": "sha512-qfexlmLp9MyrkajQVyjEDb0Vj+KhRgR/rxLiVhaihlT+ZkX0lReqtH6Ack40CvMDERR4b5eFp3CreskpBs1Pig==", + "requires": { + "delayed-stream": "0.0.5" + } + }, + "cookie-jar": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/cookie-jar/-/cookie-jar-0.3.0.tgz", + "integrity": "sha512-dX1400pzPULr+ZovkIsDEqe7XH8xCAYGT5Dege4Eot44Qs2mS2iJmnh45TxTO5MIsCfrV/JGZVloLhm46AHxNw==" + }, + "cryptiles": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz", + "integrity": "sha512-gvWSbgqP+569DdslUiCelxIv3IYK5Lgmq1UrRnk+s1WxQOQ16j3GPDcjdtgL5Au65DU/xQi6q3xPtf5Kta+3IQ==", + "requires": { + "boom": "0.4.x" + } + }, + "ctype": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.3.tgz", + "integrity": "sha512-T6CEkoSV4q50zW3TlTHMbzy1E5+zlnNcY+yb7tWVYlTwPhx9LpnfAkd4wecpWknDyptp4k97LUZeInlf6jdzBg==" + }, + "debug": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz", + "integrity": "sha512-EohAb3+DSHSGx8carOSKJe8G0ayV5/i609OD0J2orCkuyae7SyZSz2aoLmQF2s0Pj5gITDebwPH7GFBlqOUQ1Q==" + }, + "delayed-stream": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz", + "integrity": "sha512-v+7uBd1pqe5YtgPacIIbZ8HuHeLFVNe4mUEyFDXL6KiqzEykjbw+5mXZXpGFgNVasdL4jWKgaKIXrEHiynN1LA==" + }, "dependency-graph": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", @@ -1209,6 +1598,28 @@ "to-regex-range": "^5.0.1" } }, + "forever-agent": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz", + "integrity": "sha512-PDG5Ef0Dob/JsZUxUltJOhm/Y9mlteAE+46y3M9RBz/Rd3QVENJ75aGRhN56yekTUboaBIkd8KVWX2NjF6+91A==" + }, + "form-data": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.4.tgz", + "integrity": "sha512-x8eE+nzFtAMA0YYlSxf/Qhq6vP1f8wSoZ7Aw1GuctBcmudCNuTUmmx45TfEplyb6cjsZO/jvh6+1VpZn24ez+w==", + "requires": { + "async": "~0.9.0", + "combined-stream": "~0.0.4", + "mime": "~1.2.11" + }, + "dependencies": { + "async": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", + "integrity": "sha512-l6ToIJIotphWahxxHyzK9bnLR6kM4jJIIgLShZeqLY7iboHoGkdgFl7W2/Ivi4SkMJYGKqW8vSuk0uKUj6qsSw==" + } + } + }, "fraction.js": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", @@ -1274,6 +1685,44 @@ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, + "hawk": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-1.0.0.tgz", + "integrity": "sha512-Sg+VzrI7TjUomO0rjD6UXawsj50ykn5sB/xKNW/IenxzRVyw/wt9A2FLzYpGL/r0QG5hyXY8nLx/2m8UutoDcg==", + "requires": { + "boom": "0.4.x", + "cryptiles": "0.2.x", + "hoek": "0.9.x", + "sntp": "0.2.x" + } + }, + "hoek": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz", + "integrity": "sha512-ZZ6eGyzGjyMTmpSPYVECXy9uNfqBR7x5CavhUaLOeD6W0vWK1mp/b7O3f86XE0Mtfo9rZ6Bh3fnuw9Xr8MF9zA==" + }, + "http-signature": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.1.tgz", + "integrity": "sha512-coK8uR5rq2IMj+Hen+sKPA5ldgbCc1/spPdKCL1Fw6h+D0s/2LzMcRK0Cqufs1h0ryx/niwBHGFu8HC3hwU+lA==", + "requires": { + "asn1": "0.1.11", + "assert-plus": "^0.1.5", + "ctype": "0.5.3" + } + }, + "hugo": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/hugo/-/hugo-0.0.3.tgz", + "integrity": "sha512-CWJ5p8w0v6M3Z8FUbLOYUtlCZNcC3PYUQuu6rpu1yj61IIIt6qgc4weAMiu0jsoJQAUlFiXgMH4fCo4geEdvbA==", + "requires": { + "async": "~0.2.9", + "color": "~0.4.4", + "debug": "~0.7.2", + "lodash": "~2.2.1", + "request": "~2.27.0" + } + }, "ignore": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", @@ -1316,6 +1765,11 @@ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" + }, "jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", @@ -1332,6 +1786,11 @@ "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==", "dev": true }, + "lodash": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.2.1.tgz", + "integrity": "sha512-rGaKzxe4Biu8YdCPD/tUkBF4/fnAqgj63A6PeAyQnH/NEKNUHgppGZUgYBYOmAZsBKwwAb343Q1Zew0RDB2jIg==" + }, "merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -1348,6 +1807,11 @@ "picomatch": "^2.3.1" } }, + "mime": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz", + "integrity": "sha512-Ysa2F/nqTNGHhhm9MV8ure4+Hc+Y8AWiqUdHxsO7xu8zc92ND9f3kpALHjaP026Ft17UfxrMt95c50PLUeynBw==" + }, "nanoid": { "version": "3.3.6", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", @@ -1360,6 +1824,11 @@ "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==", "dev": true }, + "node-uuid": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", + "integrity": "sha512-TkCET/3rr9mUuRp+CpO7qfgT++aAxfDRaalQhwPFzI9BY/2rCDn6OfpZOVggi1AXfTPpfkTrg5f5WQx5G1uLxA==" + }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -1372,6 +1841,11 @@ "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", "dev": true }, + "oauth-sign": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.3.0.tgz", + "integrity": "sha512-Tr31Sh5FnK9YKm7xTUPyDMsNOvMqkVDND0zvK/Wgj7/H9q8mpye0qG2nVzrnsvLhcsX5DtqXD0la0ks6rkPCGQ==" + }, "path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -1465,6 +1939,11 @@ "integrity": "sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==", "dev": true }, + "qs": { + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-0.6.6.tgz", + "integrity": "sha512-kN+yNdAf29Jgp+AYHUmC7X4QdJPR8czuMWLNLc0aRxkQ7tB3vJQEONKKT9ou/rW7EbqVec11srC9q9BiVbcnHA==" + }, "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -1489,6 +1968,25 @@ "picomatch": "^2.2.1" } }, + "request": { + "version": "2.27.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.27.0.tgz", + "integrity": "sha512-V4AYOEmdUrf0X+CVF2hndyMzIeQ8G7LB45HER/rXZYEwNVI3QFGgLPLafQLHjqtG/ggzHEMb66Ng5IemksixsQ==", + "requires": { + "aws-sign": "~0.3.0", + "cookie-jar": "~0.3.0", + "forever-agent": "~0.5.0", + "form-data": "~0.1.0", + "hawk": "~1.0.0", + "http-signature": "~0.10.0", + "json-stringify-safe": "~5.0.0", + "mime": "~1.2.9", + "node-uuid": "~1.4.0", + "oauth-sign": "~0.3.0", + "qs": "~0.6.0", + "tunnel-agent": "~0.3.0" + } + }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -1516,6 +2014,14 @@ "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", "dev": true }, + "sntp": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-0.2.4.tgz", + "integrity": "sha512-bDLrKa/ywz65gCl+LmOiIhteP1bhEsAAzhfMedPoiHP3dyYnAevlaJshdqb9Yu0sRifyP/fRqSt8t+5qGIWlGQ==", + "requires": { + "hoek": "0.9.x" + } + }, "source-map-js": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", @@ -1557,6 +2063,11 @@ "is-number": "^7.0.0" } }, + "tunnel-agent": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.3.0.tgz", + "integrity": "sha512-jlGqHGoKzyyjhwv/c9omAgohntThMcGtw8RV/RDLlkbbc08kni/akVxO62N8HaXMVbVsK1NCnpSK3N2xCt22ww==" + }, "universalify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", diff --git a/package.json b/package.json index cb4dff3139..32dc865f59 100644 --- a/package.json +++ b/package.json @@ -21,5 +21,8 @@ "postcss": "^8.4.31", "postcss-cli": "^9.1.0", "prettier": "^3.2.5" + }, + "dependencies": { + "hugo": "^0.0.3" } } From e7c8cd17326890035a0cea9f27f9d51f429d3fc9 Mon Sep 17 00:00:00 2001 From: Sierra Guequierre Date: Tue, 9 Jul 2024 14:06:23 -0400 Subject: [PATCH 19/22] Make title caps match (#3109) --- docs/cloud/_index.md | 4 ++-- docs/fleet/_index.md | 4 ++-- docs/use-cases/_index.md | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/cloud/_index.md b/docs/cloud/_index.md index 9659d8bbc3..6f4150809b 100644 --- a/docs/cloud/_index.md +++ b/docs/cloud/_index.md @@ -1,6 +1,6 @@ --- -title: "Cloud organization hierarchy" -linkTitle: "Cloud organization hierarchy" +title: "Cloud Organization Hierarchy" +linkTitle: "Cloud Organization Hierarchy" weight: 430 type: "docs" description: "Configure, control, debug, and manage your machines from the cloud at app.viam.com on your own or with a team." diff --git a/docs/fleet/_index.md b/docs/fleet/_index.md index 292b15c2e0..654496052d 100644 --- a/docs/fleet/_index.md +++ b/docs/fleet/_index.md @@ -1,6 +1,6 @@ --- -title: "Deploy a large fleet" -linkTitle: "Deploy a large fleet" +title: "Deploy a Large Fleet" +linkTitle: "Deploy a Large Fleet" weight: 430 type: "docs" description: "Configure, control, debug, and manage your machines from the cloud at app.viam.com on your own or with a team." diff --git a/docs/use-cases/_index.md b/docs/use-cases/_index.md index 2f8731c1ab..46ac6ee4b6 100644 --- a/docs/use-cases/_index.md +++ b/docs/use-cases/_index.md @@ -1,6 +1,6 @@ --- -title: "How-to guides" -linkTitle: "How-to guides" +title: "How-to Guides" +linkTitle: "How-to Guides" weight: 200 type: "docs" no_list: true From 204728b06ee3f72f778d3ef6c6e67f70644b3b76 Mon Sep 17 00:00:00 2001 From: JessamyT <75634662+JessamyT@users.noreply.github.com> Date: Tue, 9 Jul 2024 13:04:41 -0700 Subject: [PATCH 20/22] Update collect-sensor-data.md (#3099) Co-authored-by: Naomi Pentrel <5212232+npentrel@users.noreply.github.com> --- docs/use-cases/collect-sensor-data.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/use-cases/collect-sensor-data.md b/docs/use-cases/collect-sensor-data.md index e64f96a513..560f282c60 100644 --- a/docs/use-cases/collect-sensor-data.md +++ b/docs/use-cases/collect-sensor-data.md @@ -89,7 +89,7 @@ If you need to sync data conditionally, for example at a certain time, see [Trig To confirm data is being synced, go to the **DATA** tab and select the **Sensors** subtab. Confirm that you are seeing data appear. -{{}} +![Sensor data tab](/get-started/quickstarts/collect-data/data-page.png) {{< /tablestep >}} {{< /table >}} From 0ba5b4308fc6349a1d78f6b503ab6328af4f7d88 Mon Sep 17 00:00:00 2001 From: Sierra Guequierre Date: Tue, 9 Jul 2024 16:12:11 -0400 Subject: [PATCH 21/22] DOCS-2185: Docs for`SubmitCustomTrainingJob` (#2950) --- .github/workflows/sdk_protos_map.csv | 1 + static/include/services/apis/ml-training-client.md | 7 ------- 2 files changed, 1 insertion(+), 7 deletions(-) delete mode 100644 static/include/services/apis/ml-training-client.md diff --git a/.github/workflows/sdk_protos_map.csv b/.github/workflows/sdk_protos_map.csv index 32ae1052e4..f475df2377 100644 --- a/.github/workflows/sdk_protos_map.csv +++ b/.github/workflows/sdk_protos_map.csv @@ -420,6 +420,7 @@ data_sync,StreamingDataCaptureUpload,streaming_data_capture_upload,, ## MLTraining mltraining,SubmitTrainingJob,submit_training_job,, +mltraining,SubmitCustomTrainingJob,submit_custom_training_job,, mltraining,GetTrainingJob,get_training_job,, mltraining,ListTrainingJobs,list_training_jobs,, mltraining,CancelTrainingJob,cancel_training_job,, diff --git a/static/include/services/apis/ml-training-client.md b/static/include/services/apis/ml-training-client.md deleted file mode 100644 index 80ca85a275..0000000000 --- a/static/include/services/apis/ml-training-client.md +++ /dev/null @@ -1,7 +0,0 @@ - -Method Name | Description ------------ | ----------- -[`GetTrainingJob`](/appendix/apis/ml-training-client/#gettrainingjob) | Get training job metadata by ID. -[`ListTrainingJobs`](/appendix/apis/ml-training-client/#listtrainingjobs) | Get training job metadata for all jobs within an organization. -[`SubmitTrainingJob`](/appendix/apis/ml-training-client/#submittrainingjob) | Submit a training job. -[`CancelTrainingJob`](/appendix/apis/ml-training-client/#canceltrainingjob) | Cancel the specified training job. From 3863d3dcd7ad327ad331fbb5a51bf8454d6c1c68 Mon Sep 17 00:00:00 2001 From: andf-viam <132301587+andf-viam@users.noreply.github.com> Date: Tue, 9 Jul 2024 17:41:46 -0400 Subject: [PATCH 22/22] DOCS-2564: Add Grafana to viz howto (#3107) Co-authored-by: Naomi Pentrel <5212232+npentrel@users.noreply.github.com> --- docs/use-cases/collect-sensor-data.md | 16 ++--- docs/use-cases/configure.md | 24 +++---- docs/use-cases/deploy-code.md | 20 +++--- docs/use-cases/deploy-ml.md | 28 ++++---- docs/use-cases/image-data.md | 36 +++++----- docs/use-cases/manage-fleet.md | 20 +++--- docs/use-cases/navigate.md | 16 ++--- docs/use-cases/sensor-data-query-sdk.md | 12 ++-- docs/use-cases/sensor-data-query.md | 20 +++--- docs/use-cases/sensor-data-visualize.md | 93 +++++++++++++++++++++---- layouts/shortcodes/tablestep.html | 2 +- static/include/how-to/query-data.md | 20 +++--- 12 files changed, 188 insertions(+), 119 deletions(-) diff --git a/docs/use-cases/collect-sensor-data.md b/docs/use-cases/collect-sensor-data.md index 560f282c60..6a3c296da4 100644 --- a/docs/use-cases/collect-sensor-data.md +++ b/docs/use-cases/collect-sensor-data.md @@ -45,7 +45,7 @@ If you're not sure which sensor model to choose, start with the [`viam:viam-sens Viam's [data management service](/services/data/) lets you capture data locally from sensors and then sync it to the cloud where you can access all data across different {{< glossary_tooltip term_id="machine" text="machines" >}} or {{< glossary_tooltip term_id="location" text="locations" >}}. {{< table >}} -{{< tablestep link="/services/data/">}} +{{% tablestep link="/services/data/"%}} {{}} **1. Add the data management service** @@ -55,8 +55,8 @@ Then add the **data management** service. Enable **Syncing** to ensure captured data is synced to the cloud and set the sync interval, for example to `0.05` minutes to sync every 3 seconds. -{{< /tablestep >}} -{{< tablestep >}} +{{% /tablestep %}} +{{% tablestep %}} {{}} **2. Capture data from sensor** @@ -64,15 +64,15 @@ On the **CONFIGURE** tab, go to the **sensor**'s card and find the **Data captur Add a new method, `Readings`, to capture data for and set the frequency. For example, setting a frequency of `0.1` will capture data once every ten seconds. -{{< /tablestep >}} -{{< tablestep >}} +{{% /tablestep %}} +{{% tablestep %}} {{}} **3. Save to start capturing** Save the config. With cloud sync enabled, captured data is automatically uploaded to the Viam app after a short delay. -{{< /tablestep >}} +{{% /tablestep %}} {{< /table >}} {{< alert title="Tip" color="tip" >}} @@ -82,7 +82,7 @@ If you need to sync data conditionally, for example at a certain time, see [Trig ## View sensor data {{< table >}} -{{< tablestep link="/services/data/view/">}} +{{% tablestep link="/services/data/view/"%}} {{}} **1. View data in the Viam app** @@ -91,7 +91,7 @@ Confirm that you are seeing data appear. ![Sensor data tab](/get-started/quickstarts/collect-data/data-page.png) -{{< /tablestep >}} +{{% /tablestep %}} {{< /table >}} ## Next steps diff --git a/docs/use-cases/configure.md b/docs/use-cases/configure.md index d08b7c46ea..9fc47bb123 100644 --- a/docs/use-cases/configure.md +++ b/docs/use-cases/configure.md @@ -13,7 +13,7 @@ You can get a smart machine running with Viam in just a few steps. Viam's modular system of {{< glossary_tooltip term_id="component" text="components" >}} and {{< glossary_tooltip term_id="service" text="services" >}} means that you can start doing interesting things with your machine without writing much or any code. {{< table >}} -{{< tablestep >}} +{{% tablestep %}} {{}} **1. Create a machine in the Viam app** @@ -24,15 +24,15 @@ Then create a machine by typing in a name and clicking **Add machine**. {{}} -{{< /tablestep >}} -{{< tablestep >}} +{{% /tablestep %}} +{{% tablestep %}} {{}} **2. Install Viam on your machine** All of the software that runs your smart machine is packaged into a binary called `viam-server`. Install it on the computer controlling your smart machine by following the {{< glossary_tooltip term_id="setup" text="setup instructions" >}} in the [Viam app](https://app.viam.com/). -{{< /tablestep >}} -{{< tablestep >}} +{{% /tablestep %}} +{{% tablestep %}} {{}} **3. Configure your components** @@ -43,8 +43,8 @@ You need to [_configure_](/build/configure/) your machine so that `viam-server` Use the configuration builder tool in the Viam app to create a file that describes what hardware you are using and how it is connected. For example, if you have a DC motor, follow the [corresponding configuration instructions](/components/motor/gpio/) to tell the software which pins it is connected to. -{{< /tablestep >}} -{{< tablestep >}} +{{% /tablestep %}} +{{% tablestep %}} {{}} @@ -54,8 +54,8 @@ For example, if you have a DC motor, follow the [corresponding configuration ins When you configure a component, a remote control panel is generated for it in the **CONTROL** tab of the Viam app. With the panels, you can drive motors at different speeds, view your camera feeds, see sensor readings, and generally test the basic functionality of your machine before you've even written any code. -{{< /tablestep >}} -{{< tablestep >}} +{{% /tablestep %}} +{{% tablestep %}} {{}} **5. Configure services** @@ -64,8 +64,8 @@ Services are built-in Viam software packages that add high-level functionality t If you want to use any services, see their [documentation](/services/) for configuration and usage information. If you are making a simple machine that doesn't use services, you can skip this step! -{{< /tablestep >}} -{{< tablestep >}} +{{% /tablestep %}} +{{% tablestep %}} {{}} **6. Do more with code** @@ -76,7 +76,7 @@ Viam has [SDKs](/sdks/) for Python, Golang, C++, TypeScript and Flutter. The easiest way to get started is to copy the auto-generated boilerplate code from the **Code sample** page of the **CONNECT** tab on your machine's page in the Viam app. You can run this code directly on the machine or from a separate computer; it then connects to the machine using API keys. -{{< /tablestep >}} +{{% /tablestep %}} {{< /table >}} ## Next steps diff --git a/docs/use-cases/deploy-code.md b/docs/use-cases/deploy-code.md index 07c2a7d6ab..86d7887538 100644 --- a/docs/use-cases/deploy-code.md +++ b/docs/use-cases/deploy-code.md @@ -17,22 +17,22 @@ Further, you can determine the accessibility of your module when you upload it - When you deploy a module, whether its one you've written yourself or added from the registry, you maintain complete control over how that module's code is deployed to your machine when new updates to the module are available. {{< table >}} -{{< tablestep >}} +{{% tablestep %}} {{}} **1. Search modules** Once you [have created a machine in the Viam app](/cloud/machines/#add-a-new-machine), [search for modules in the Viam registry](/registry/configure/) that fit your machine's requirements, and then [add a module](/registry/configure/#add-a-modular-resource-from-the-viam-registry) from your machine's configuration page in the Viam app. -{{< /tablestep >}} -{{< tablestep >}} +{{% /tablestep %}} +{{% tablestep %}} {{}} **2. Create your own module** Or, you can [create your own module](/registry/create/) to add support for new hardware, or to extend an existing software service. -{{< /tablestep >}} -{{< tablestep >}} +{{% /tablestep %}} +{{% tablestep %}} {{}} **3. Deploy your module** @@ -44,8 +44,8 @@ Once you have created your new module, you can deploy it to your machine in one If your machine is offline when you deploy a module, it will deploy once your machine comes back online. -{{< /tablestep >}} -{{< tablestep >}} +{{% /tablestep %}} +{{% tablestep %}} {{}} **4. Configure your module** @@ -53,8 +53,8 @@ Once you've deployed a module to your machine or fleet, [configure the module](/ You can also configure [how a deployed module updates itself](/registry/configure/#configure-version-update-management-for-a-registry-module) when new versions of that module become available from the Viam registry. You can choose to always update to the latest version as soon as it becomes available, pin to a specific code revision and never update it, or upgrade only within a specified major or minor version. -{{< /tablestep >}} -{{< tablestep >}} +{{% /tablestep %}} +{{% tablestep %}} {{}} **5. Update your module** @@ -63,5 +63,5 @@ You can also use a [GitHub action to automate module releases](/registry/upload/ These options make it easy to push changes to a fleet of machines. -{{< /tablestep >}} +{{% /tablestep %}} {{< /table >}} diff --git a/docs/use-cases/deploy-ml.md b/docs/use-cases/deploy-ml.md index 2226a2eae0..04b93fa84a 100644 --- a/docs/use-cases/deploy-ml.md +++ b/docs/use-cases/deploy-ml.md @@ -29,15 +29,15 @@ You will not need to write any code. ## Create a dataset and label data {{< table >}} -{{< tablestep >}} +{{% tablestep %}} {{}} **1. Collect images** Start by collecting images from your cameras and syncing it to the [Viam app](https://app.viam.com). See [Collect image data and sync it to the cloud](/use-cases/image-data/#collect-image-data-and-sync-it-to-the-cloud) for instructions. -{{< /tablestep >}} -{{< tablestep link="/services/data/dataset/">}} +{{% /tablestep %}} +{{% tablestep link="/services/data/dataset/" %}} {{}} **2. Label your images** @@ -45,14 +45,14 @@ Once you have enough images of the objects you'd like to classify, use the inter If you want to train an image classifier, use image tags. For an object detector, use bounding boxes. -{{< /tablestep >}} -{{< tablestep link="/services/data/dataset/">}} +{{% /tablestep %}} +{{% tablestep link="/services/data/dataset/"%}} {{}} **2. Create a dataset** Use the interface on the **DATA** tab (or the [`viam data dataset add` command](/cli/#data)) to add all images you want to train the model on to a dataset. -{{< /tablestep >}} +{{% /tablestep %}} {{< /table >}} {{% alert title="Tip" color="tip" %}} @@ -66,15 +66,15 @@ This is not required, since you can use other filters like time or machine ID in ## Train and test a machine learning (ML) model {{< table >}} -{{< tablestep link="/services/ml/train-model/">}} +{{% tablestep link="/services/ml/train-model/"%}} {{}} **1. Train an ML model** In the Viam app, navigate to your list of [**DATASETS**](https://app.viam.com/data/datasets) and select the one you want to train on. Click **Train model** and follow the prompts. -{{< /tablestep >}} -{{< tablestep >}} +{{% /tablestep %}} +{{% tablestep %}} {{}} **2. Deploy your ML model** @@ -82,8 +82,8 @@ On the **Configure** page add the built-in [ML model service](/services/ml/deplo The service will to deploy and run the model. Once you've added the ML model service to your machine, choose your newly-trained model from the dropdown menu in the ML model service's configuration card. -{{< /tablestep >}} -{{< tablestep link="/services/vision/">}} +{{% /tablestep %}} +{{% tablestep link="/services/vision/" %}} {{}} **3. Configure an mlmodel vision service** @@ -92,14 +92,14 @@ The vision service takes the the ML model and applies it to the stream of images Add the `vision / ML model` service to your machine. Then, from the **Select model** dropdown, select the name of the ML model service you configured in the last step (for example, `mlmodel-1`). -{{< /tablestep >}} -{{< tablestep link="/services/vision/mlmodel/#test-your-detector-or-classifier">}} +{{% /tablestep %}} +{{% tablestep link="/services/vision/mlmodel/#test-your-detector-or-classifier" %}} {{}} **4. Test your classifier** Test your ML model classifier with [existing images in the Viam app](/services/vision/mlmodel/#existing-images-in-the-cloud), [live camera footage,](/services/vision/mlmodel/#live-camera-footage) or [existing images on a computer](/services/vision/mlmodel/#existing-images-on-your-machine). -{{< /tablestep >}} +{{% /tablestep %}} {{< /table >}} ## Next steps diff --git a/docs/use-cases/image-data.md b/docs/use-cases/image-data.md index 64ae3c0724..e533917b76 100644 --- a/docs/use-cases/image-data.md +++ b/docs/use-cases/image-data.md @@ -38,14 +38,14 @@ In the next how-to guide you can use the images you collect here to [train compu ## Collect image data and sync it to the cloud {{< table >}} -{{< tablestep link="/components/camera/">}} +{{% tablestep link="/components/camera/"%}} {{}} **1. Configure a camera** Configure a camera component, such as a [webcam](/components/camera/webcam/), on your machine. -{{< /tablestep >}} -{{< tablestep link="/services/data/">}} +{{% /tablestep %}} +{{% tablestep link="/services/data/"%}} {{}} **2. Enable the data management service** @@ -53,16 +53,16 @@ In your camera component configuration panel, find the **Data capture** section. Click **Add method** and follow the prompt to **Create a data management service**. You can leave the default data manager settings. -{{< /tablestep >}} -{{< tablestep >}} +{{% /tablestep %}} +{{% tablestep %}} {{}} **3. Capture data** With the data management service configured on your machine, you can continue configuring how the camera component itself captures data. In the **Data capture** panel of your camera's config, select **Read image** from the method selector, and set your desired capture frequency. -{{< /tablestep >}} -{{< tablestep >}} +{{% /tablestep %}} +{{% tablestep %}} {{}} **4. View data in the Viam app** @@ -70,7 +70,7 @@ Once you have synced images, you can [view those images in the Viam app](/servic You can also [export your data from the Viam app](/services/data/export/) to a deployed machine, or to any computer. -{{< /tablestep >}} +{{% /tablestep %}} {{< /table >}} ## Use filtering to collect and sync only certain images @@ -81,7 +81,7 @@ Contributors have written several filtering {{< glossary_tooltip term_id="module The following steps use the [`filtered_camera`](https://github.com/erh/filtered_camera) module: {{< table >}} -{{< tablestep link="/services/ml/deploy/">}} +{{% tablestep link="/services/ml/deploy/"%}} {{}} **1. Add an ML model to your machine** @@ -89,8 +89,8 @@ Configure an ML model service on your machine that is compatible with the ML mod From the **Model** dropdown, select the preexisting model you want to use, or click **Add new model** to upload your own. -{{< /tablestep >}} -{{< tablestep link="/services/vision/">}} +{{% /tablestep %}} +{{% tablestep link="/services/vision/"%}} {{}} **2. Add a vision service to use with the ML model** @@ -99,8 +99,8 @@ You can think of the vision service as the bridge between the ML model service a Configure the `vision / ML model` service on your machine. From the **Select model** dropdown, select the name of your ML model service (for example, `mlmodel-1`). -{{< /tablestep >}} -{{< tablestep >}} +{{% /tablestep %}} +{{% tablestep %}} {{}} **3. Configure the filtered camera** @@ -108,8 +108,8 @@ The `filtered-camera` {{< glossary_tooltip term_id="modular-resource" text="modu Configure a `filtered-camera` component on your machine, following the [attribute guide in the README](https://github.com/erh/filtered_camera?tab=readme-ov-file#configure-your-filtered-camera) to specify the names of your webcam and vision service, and add classification and object detection filters. -{{< /tablestep >}} -{{< tablestep >}} +{{% /tablestep %}} +{{% tablestep %}} {{}} **4. Configure data capture and sync on the filtered camera** @@ -118,15 +118,15 @@ The filtered camera will only capture image data that passes the filters you con Turn off data capture on your webcam if you haven't already, so that you don't capture duplicate or unfiltered images. -{{< /tablestep >}} -{{< tablestep >}} +{{% /tablestep %}} +{{% tablestep %}} {{}} **5. (Optional) Trigger sync with custom logic** By default, the captured data syncs at the regular interval you specified in the data capture config. If you need to trigger sync in a different way, see [Trigger cloud sync conditionally](/services/data/trigger-sync/) for a documented example of syncing data only at certain times of day. -{{< /tablestep >}} +{{% /tablestep %}} {{< /table >}} ## Next steps diff --git a/docs/use-cases/manage-fleet.md b/docs/use-cases/manage-fleet.md index 3e29deabf0..424a8973c1 100644 --- a/docs/use-cases/manage-fleet.md +++ b/docs/use-cases/manage-fleet.md @@ -18,29 +18,29 @@ You can monitor and teleoperate all of the robots from one online dashboard, and You can grant users different levels of access to individual machines or to groups of machines. {{< table >}} -{{< tablestep >}} +{{% tablestep %}} {{}} **1. Create an account** Go to the [Viam app](https://app.viam.com) and sign up with Google, GitHub, Apple, or an email address. -{{< /tablestep >}} -{{< tablestep >}} +{{% /tablestep %}} +{{% tablestep %}} {{}} **2. Create organizations and locations** Use [organizations](/cloud/organizations/), and [locations](/cloud/locations/) within them, to organize your machines into groups and manage user access. -{{< /tablestep >}} -{{< tablestep >}} +{{% /tablestep %}} +{{% tablestep %}} {{}} **3. Invite other users and assign permissions** Invite other users to an organization or a location to [share access](/fleet/#use-viam-for-collaboration) to the machines within it. Assign each user a role (owner or operator) to manage permissions. -{{< /tablestep >}} -{{< tablestep >}} +{{% /tablestep %}} +{{% tablestep %}} {{}} **4. Connect machines to the cloud** @@ -49,8 +49,8 @@ When you [install `viam-server`](/get-started/installation/) on each machine, un Use the config builder interface to configure {{< glossary_tooltip term_id="component" text="components" >}} and {{< glossary_tooltip term_id="service" text="services" >}} for new or existing smart machines. You can use {{< glossary_tooltip term_id="fragment" text="fragments" >}} to streamline the process of configuring multiple similar machines. -{{< /tablestep >}} -{{< tablestep >}} +{{% /tablestep %}} +{{% tablestep %}} {{}} **5. Monitor your fleet** @@ -62,7 +62,7 @@ Using the [Viam app](https://app.viam.com), you can: Use [modules](/registry/) to deploy code to your fleet and manage versioning. -{{< /tablestep >}} +{{% /tablestep %}} {{< /table >}} ## Next steps diff --git a/docs/use-cases/navigate.md b/docs/use-cases/navigate.md index 2821db38ae..7eed24b0bc 100644 --- a/docs/use-cases/navigate.md +++ b/docs/use-cases/navigate.md @@ -13,7 +13,7 @@ If you have a rover base, you can use Viam to teleoperate it and to navigate aut Once you have configured your machine, you can remotely control your machine on the app's **CONTROL** tab, and set up autonomous navigation with the [navigation service](/services/navigation/). {{< table >}} -{{< tablestep >}} +{{% tablestep %}} {{}} **1. Teleoperate** @@ -22,16 +22,16 @@ Create an account and add a machine, [install `viam-server`](/get-started/instal Then, go to the **CONTROL** tab and access a remote control card for your base, with an interface for controlling speed, direction, and power. You can also view live feeds from any cameras you configure. -{{< /tablestep >}} -{{< tablestep >}} +{{% /tablestep %}} +{{% tablestep %}} {{}} **2. Program to move** Remotely control your rover base programmatically with a [Viam SDK](/sdks/) by making calls to the [base API](/components/base/#api). Or, [configure the base remote control service](/services/base-rc/) to teleoperate your base with an [input controller.](/components/input-controller/) -{{< /tablestep >}} -{{< tablestep >}} +{{% /tablestep %}} +{{% tablestep %}} {{}} **3. Prepare your base to navigate** @@ -39,14 +39,14 @@ Or, [configure the base remote control service](/services/base-rc/) to teleopera Additionally, [configure and calibrate](/services/navigation/#configure-and-calibrate-the-frame-system-service-for-gps-navigation) the frame system for GPS navigation. Then, [configure the navigation service](/services/navigation/) on your machine. -{{< /tablestep >}} -{{< tablestep >}} +{{% /tablestep %}} +{{% tablestep %}} {{}} **4. Navigate autonomously** Define a path for your rover to navigate with waypoints and obstacles. Then, start and stop your machine's motion along the path and view your machine's current location. You can use the map interface on the **CONTROL** tab or the [navigation API](/services/navigation/#api). -{{< /tablestep >}} +{{% /tablestep %}} {{< /table >}} ## Next steps diff --git a/docs/use-cases/sensor-data-query-sdk.md b/docs/use-cases/sensor-data-query-sdk.md index b0a58faa62..97b799a396 100644 --- a/docs/use-cases/sensor-data-query-sdk.md +++ b/docs/use-cases/sensor-data-query-sdk.md @@ -44,7 +44,7 @@ Follow the guide to [capture sensor data](/use-cases/collect-sensor-data/). ## Set up the Python SDK {{< table >}} -{{< tablestep link="/build/program/#requirements">}} +{{% tablestep link="/build/program/#requirements"%}} **1. Install the Python SDK** For macOS (both Intel x86_64 and Apple Silicon) or Linux (x86, aarch64, armv6l), run the following commands: @@ -55,13 +55,13 @@ source .venv/bin/activate pip install viam-sdk ``` -{{< /tablestep >}} +{{% /tablestep %}} {{< /table >}} ## Query data with the Python SDK {{< table >}} -{{< tablestep link="/cli/#organizations">}} +{{% tablestep link="/cli/#organizations"%}} **1. Create an API key** To access your machines using the Python SDK, you must use an API key: @@ -70,8 +70,8 @@ To access your machines using the Python SDK, you must use an API key: viam organizations api-key create --org-id --name my-api-key ``` -{{< /tablestep >}} -{{< tablestep link="/appendix/apis/data-client/">}} +{{% /tablestep %}} +{{% tablestep link="/appendix/apis/data-client/"%}} **2. Use the API key with the `data_client`** Use the API key and [`TabularDataByFilter()`](/appendix/apis/data-client/#tabulardatabyfilter), [`TabularDataBySQL()`](/appendix/apis/data-client/#tabulardatabysql), [`TabularDataByMQL()`](/appendix/apis/data-client/#tabulardatabymql), and[`DeleteTabularData()`](/appendix/apis/data-client/#deletetabulardata) to query data: @@ -134,7 +134,7 @@ if __name__ == '__main__': asyncio.run(main()) ``` -{{< /tablestep >}} +{{% /tablestep %}} {{< /table >}} Adjust the Python script to uery your data further. diff --git a/docs/use-cases/sensor-data-query.md b/docs/use-cases/sensor-data-query.md index 70e75a86bf..bfc4d0313e 100644 --- a/docs/use-cases/sensor-data-query.md +++ b/docs/use-cases/sensor-data-query.md @@ -58,14 +58,14 @@ Once your data has synced, you can query your data from within the Viam app usin You must have the [owner role](/cloud/rbac/) in order to query data in the Viam app. {{< table >}} -{{< tablestep >}} +{{% tablestep %}} **1. Query with SQL or MQL** Navigate to the [**Query** page](https://app.viam.com/data/query). Then, select either **SQL** or **MQL** from the **Query mode** dropdown menu on the right-hand side. -{{< /tablestep >}} -{{< tablestep >}} +{{% /tablestep %}} +{{% tablestep %}} **2. Run your query** This example query returns 5 readings from a component called `my-sensor`: @@ -86,8 +86,8 @@ SELECT * FROM readings WHERE component_name = 'my-sensor' LIMIT 5 {{% /tab %}} {{< /tabs >}} -{{< /tablestep >}} -{{< tablestep >}} +{{% /tablestep %}} +{{% tablestep %}} **3. Review results** Click **Run query** when ready to perform your query and get matching results. @@ -107,7 +107,7 @@ For more information on MQL syntax, see the [MQL (MongoDB Query Language)](https {{% /expand%}} -{{< /tablestep >}} +{{% /tablestep %}} {{< /table >}} ## Configure data query @@ -121,7 +121,7 @@ If you want to query data from third party tools, you have to configure data que You can use third-party tools, such as the [`mongosh` shell](https://www.mongodb.com/docs/mongodb-shell/), [MongoDB Compass](https://www.mongodb.com/docs/compass/current/), to query against captured sensor data. {{< table >}} -{{< tablestep link="/use-cases/sensor-data-query/#configure-data-query">}} +{{% tablestep link="/use-cases/sensor-data-query/#configure-data-query"%}} **1. Connect to your Viam organization's data** Run the following command to connect to your Viam organization's MongoDB Atlas instance from `mongosh` using the connection URI you obtained during query configuration: @@ -130,8 +130,8 @@ Run the following command to connect to your Viam organization's MongoDB Atlas i mongosh "" ``` -{{< /tablestep >}} -{{< tablestep >}} +{{% /tablestep %}} +{{% tablestep %}} **2. Query data from a compatible client** Once connected, you can run SQL or MQL statements to query captured data directly. @@ -178,7 +178,7 @@ db.readings.aggregate( {{% /tab %}} {{< /tabs >}} -{{< /tablestep >}} +{{% /tablestep %}} {{< /table >}} {{< alert title="Tip" color="tip" >}} diff --git a/docs/use-cases/sensor-data-visualize.md b/docs/use-cases/sensor-data-visualize.md index fb5ee0f618..6e9aa20a22 100644 --- a/docs/use-cases/sensor-data-visualize.md +++ b/docs/use-cases/sensor-data-visualize.md @@ -50,14 +50,80 @@ If you want to query data from third party tools, you have to configure data que When you sync captured data to Viam, that data is stored in the Viam organization’s MongoDB Atlas Data Federation instance. Your chosen third-party visualization tool must be able to connect to a [MongoDB Atlas Data Federation](https://www.mongodb.com/docs/atlas/data-federation/query/sql/connect/) instance as its data store. +Select a tab below to learn how to configure your visualization tool for use with Viam: + +{{< tabs >}} +{{< tab name="Grafana" >}} + {{< table >}} -{{< tablestep >}} -**1. Install connector to MongoDB data source** +{{% tablestep %}} +**1. Choose Grafana instance** + +Install or set up Grafana. You can use either a local instance of Grafana or Grafana Cloud, and can use the free trial version of Grafana if desired. + +{{% /tablestep %}} +{{% tablestep %}} +**2. Install connector to MongoDB data source** + +Navigate to your Grafana web UI, and add the [Grafana MongoDB data source](https://grafana.com/grafana/plugins/grafana-mongodb-datasource/) plugin to your Grafana instance. + +{{}} + +{{% /tablestep %}} +{{% tablestep %}} +**3. Configure a data connection** + +Navigate to the Grafana data source management page, and select the Grafana MongoDB data source that you just added. + +Enter the following information in the configuration UI for that plugin: + +- **Connection string**: Enter the following connection string, and replace `` with your database hostname as configured with the `viam data database configure` command, and replace `` with the desired database name to query: + + ```sh + mongodb:///?directConnection=true&authSource=admin&tls=true + ``` + +- **User**: Enter the following username, substituting your organization ID as determined earlier, for ``: + + ```sh + db-user- + ``` + +- **Password**: Enter the password you provided earlier. + + {{}} -For example, if you are using [Grafana](https://grafana.com/), you must install and configure the [Grafana MongoDB data source](https://grafana.com/grafana/plugins/grafana-mongodb-datasource/) plugin. -See the [Visualize Data Using Grafana](/tutorials/services/visualize-data-grafana/) tutorial for a detailed walkthrough for Grafana. {{< /tablestep >}} -{{< tablestep >}} +{{% tablestep %}} +**4. Use Grafana for dashboards** + +With your data connection established, you can then build dashboards that provide insight into your data. + +Grafana additionally supports the ability to directly [query and transform your data](https://grafana.com/docs/grafana/latest/panels-visualizations/query-transform-data/) within a dashboard to generate more granular visualizations of specific data. +You might use this functionality to visualize only a single day's metrics, limit the visualization to a select machine or component, or to isolate an outlier in your reported data, for example. + +You can query your captured data within a Grafana dashboard using either {{< glossary_tooltip term_id="sql" text="SQL" >}} or {{< glossary_tooltip term_id="mql" text="MQL" >}}. +See the [guide on querying sensor data](/use-cases/sensor-data-query/) for more information. + + + +{{% /tablestep %}} +{{< /table >}} + +{{% /tab %}} +{{< tab name="Other visualization tools" >}} + +{{< table >}} +{{% tablestep %}} +**1. Install connector to MongoDB data source** + +Some visualization clients are able to connect to the Viam MongoDB Atlas Data Federation instance natively, while others require that you install and configure an additional plugin or connector. +For example, Tableau requires both the [Atlas SQL JDBC Driver](https://www.mongodb.com/try/download/jdbc-driver) as well as the the [Tableau Connector](https://www.mongodb.com/try/download/tableau-connector) in order to successfully connect and access data. + +Check with the documentation for your third-party visualization tool to be sure you have the required additional software installed to connect to a MongoDB Atlas Data Federation instance. + +{{% /tablestep %}} +{{% tablestep %}} **2. Configure a data connection** Most third-party visualization tools require the _connection URI_ (also called the connection string) to that database server, and the _credentials_ to authenticate to that server in order to visualize your data. @@ -67,7 +133,7 @@ This is what they look like: {{< tabs >}} {{% tab name="Connection URI and credentials" %}} -If your client supports a connection URI, use the following format and repolace `YOUR-PASSWORD-HERE` with your database password as configured with the `viam data database configure` command: +If your client supports a connection URI, use the following format and replace `YOUR-PASSWORD-HERE` with your database password as configured with the `viam data database configure` command: ```sh {class="command-line" data-prompt="$"} mongodb://db-user-abcdef12-abcd-abcd-abcd-abcdef123456:YOUR-PASSWORD-HERE@data-federation-abcdef12-abcd-abcd-abcd-abcdef123456-e4irv.a.query.mongodb.net/?ssl=true&authSource=admin @@ -106,11 +172,11 @@ db-user- Substitute your organization ID for ``. -{{% /tab %}} -{{< /tabs >}} +{{< /tab >}} +{{% /tabs %}} -{{< /tablestep >}} -{{< tablestep >}} +{{% /tablestep %}} +{{% tablestep %}} **3. Use visualization tools for dashboards** Some third-party visualization tools support the ability to directly query your data within their platform to generate more granular visualizations of specific data. @@ -121,12 +187,15 @@ See the [guide on querying sensor data](/use-cases/sensor-data-query/) for more -{{< /tablestep >}} +{{% /tablestep %}} {{< /table >}} +{{< /tab >}} +{{< /tabs >}} + ## Next steps -For a walkthrough of visualizing data with a specific tool, see [visualizing data with Grafana](/tutorials/services/visualize-data-grafana/). +For more detailed instructions on using Grafana, including a full step-by-step configuration walkthrough, see [visualizing data with Grafana](/tutorials/services/visualize-data-grafana/). On top of visualizing sensor data with third-party tools, you can also [query it with the Python SDK](/use-cases/sensor-data-query-sdk/) or [query it with the Viam app](/use-cases/sensor-data-query/). diff --git a/layouts/shortcodes/tablestep.html b/layouts/shortcodes/tablestep.html index dd02d36140..fa85335c4d 100644 --- a/layouts/shortcodes/tablestep.html +++ b/layouts/shortcodes/tablestep.html @@ -5,7 +5,7 @@ {{ end }} {{ with $.Inner }} {{ if eq $.Page.File.Ext "md" }} - {{ $.Inner | markdownify }} + {{ $.Inner }} {{ else }} {{ $.Inner | htmlUnescape | safeHTML }} {{ end }} diff --git a/static/include/how-to/query-data.md b/static/include/how-to/query-data.md index 328b7ddcaf..4e01fe4437 100644 --- a/static/include/how-to/query-data.md +++ b/static/include/how-to/query-data.md @@ -1,5 +1,5 @@ {{< table >}} -{{< tablestep link="/cli/#authenticate">}} +{{% tablestep link="/cli/#authenticate"%}} **1. Authenticate with the CLI** Authenticate using a personal access token: @@ -8,8 +8,8 @@ Authenticate using a personal access token: viam login ``` -{{< /tablestep >}} -{{< tablestep link="/cli/#organizations">}} +{{% /tablestep %}} +{{% tablestep link="/cli/#organizations"%}} **2. Find your organization ID** To create a database user allowing you to access your data, find your organization ID: @@ -18,17 +18,17 @@ To create a database user allowing you to access your data, find your organizati viam organizations list ``` -{{< /tablestep >}} -{{< tablestep >}} +{{% /tablestep %}} +{{% tablestep %}} **3. Configure a new database user** Configure a new database user for the Viam organization's MongoDB [Atlas Data Federation](https://www.mongodb.com/docs/atlas/data-federation/overview/) instance, which is where your machine's synced data is stored. -{{< alert title="Warning" color="warning" >}} +{{% alert title="Warning" color="warning" %}} The command will create a user with your organization ID as the username. If you or someone else in your organization have already created this user, the following steps update the password for that user instead. Dashboards or other integrations relying on this password will then need to be updated. -{{< /alert >}} +{{% /alert %}} Provide your organization's `org-id` from step 2, and a password for your database user. Your password must be at least 8 characters long, and include at least one uppercase, one number, and one special character (such as `$` or `%`): @@ -40,8 +40,8 @@ viam data database configure --org-id= --password=}} -{{< tablestep link="/cli/#data" >}} +{{% /tablestep %}} +{{% tablestep link="/cli/#data" %}} **4. Determine the connection URI** Determine the connection URI (also known as a connection string) for your organization's MongoDB Atlas Data Federation instance by running the following command with the organization's `org-id` from step 2: @@ -59,5 +59,5 @@ This command returns both the _connection URI_ to your organization's MongoDB At You will need this information to query your data in the next section. -{{< /tablestep >}} +{{% /tablestep %}} {{< /table >}}