viam-sdk 0.45.2__py3-none-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of viam-sdk might be problematic. Click here for more details.
- viam/__init__.py +71 -0
- viam/app/__init__.py +0 -0
- viam/app/_logs.py +34 -0
- viam/app/app_client.py +2525 -0
- viam/app/billing_client.py +143 -0
- viam/app/data_client.py +1715 -0
- viam/app/ml_training_client.py +251 -0
- viam/app/provisioning_client.py +95 -0
- viam/app/viam_client.py +260 -0
- viam/components/__init__.py +0 -0
- viam/components/arm/__init__.py +16 -0
- viam/components/arm/arm.py +223 -0
- viam/components/arm/client.py +124 -0
- viam/components/arm/service.py +123 -0
- viam/components/audio_input/__init__.py +18 -0
- viam/components/audio_input/audio_input.py +81 -0
- viam/components/audio_input/client.py +70 -0
- viam/components/audio_input/service.py +114 -0
- viam/components/base/__init__.py +13 -0
- viam/components/base/base.py +260 -0
- viam/components/base/client.py +153 -0
- viam/components/base/service.py +138 -0
- viam/components/board/__init__.py +9 -0
- viam/components/board/board.py +414 -0
- viam/components/board/client.py +241 -0
- viam/components/board/service.py +223 -0
- viam/components/button/__init__.py +10 -0
- viam/components/button/button.py +41 -0
- viam/components/button/client.py +52 -0
- viam/components/button/service.py +46 -0
- viam/components/camera/__init__.py +22 -0
- viam/components/camera/camera.py +138 -0
- viam/components/camera/client.py +98 -0
- viam/components/camera/service.py +105 -0
- viam/components/component_base.py +65 -0
- viam/components/encoder/__init__.py +18 -0
- viam/components/encoder/client.py +83 -0
- viam/components/encoder/encoder.py +118 -0
- viam/components/encoder/service.py +72 -0
- viam/components/gantry/__init__.py +11 -0
- viam/components/gantry/client.py +115 -0
- viam/components/gantry/gantry.py +156 -0
- viam/components/gantry/service.py +113 -0
- viam/components/generic/__init__.py +18 -0
- viam/components/generic/client.py +62 -0
- viam/components/generic/generic.py +76 -0
- viam/components/generic/service.py +40 -0
- viam/components/gripper/__init__.py +11 -0
- viam/components/gripper/client.py +85 -0
- viam/components/gripper/gripper.py +114 -0
- viam/components/gripper/service.py +81 -0
- viam/components/input/__init__.py +15 -0
- viam/components/input/client.py +194 -0
- viam/components/input/input.py +297 -0
- viam/components/input/service.py +175 -0
- viam/components/motor/__init__.py +11 -0
- viam/components/motor/client.py +168 -0
- viam/components/motor/motor.py +301 -0
- viam/components/motor/service.py +150 -0
- viam/components/movement_sensor/__init__.py +21 -0
- viam/components/movement_sensor/client.py +161 -0
- viam/components/movement_sensor/movement_sensor.py +253 -0
- viam/components/movement_sensor/service.py +146 -0
- viam/components/pose_tracker/__init__.py +17 -0
- viam/components/pose_tracker/client.py +50 -0
- viam/components/pose_tracker/pose_tracker.py +40 -0
- viam/components/pose_tracker/service.py +45 -0
- viam/components/power_sensor/__init__.py +17 -0
- viam/components/power_sensor/client.py +86 -0
- viam/components/power_sensor/power_sensor.py +104 -0
- viam/components/power_sensor/service.py +72 -0
- viam/components/sensor/__init__.py +18 -0
- viam/components/sensor/client.py +49 -0
- viam/components/sensor/sensor.py +48 -0
- viam/components/sensor/service.py +51 -0
- viam/components/servo/__init__.py +11 -0
- viam/components/servo/client.py +86 -0
- viam/components/servo/service.py +80 -0
- viam/components/servo/servo.py +114 -0
- viam/components/switch/__init__.py +10 -0
- viam/components/switch/client.py +83 -0
- viam/components/switch/service.py +72 -0
- viam/components/switch/switch.py +95 -0
- viam/errors.py +105 -0
- viam/gen/__init__.py +0 -0
- viam/gen/app/__init__.py +0 -0
- viam/gen/app/agent/__init__.py +0 -0
- viam/gen/app/agent/v1/__init__.py +0 -0
- viam/gen/app/agent/v1/agent_grpc.py +29 -0
- viam/gen/app/agent/v1/agent_pb2.py +47 -0
- viam/gen/app/agent/v1/agent_pb2.pyi +280 -0
- viam/gen/app/cloudslam/__init__.py +0 -0
- viam/gen/app/cloudslam/v1/__init__.py +0 -0
- viam/gen/app/cloudslam/v1/cloud_slam_grpc.py +70 -0
- viam/gen/app/cloudslam/v1/cloud_slam_pb2.py +54 -0
- viam/gen/app/cloudslam/v1/cloud_slam_pb2.pyi +384 -0
- viam/gen/app/data/__init__.py +0 -0
- viam/gen/app/data/v1/__init__.py +0 -0
- viam/gen/app/data/v1/data_grpc.py +206 -0
- viam/gen/app/data/v1/data_pb2.py +178 -0
- viam/gen/app/data/v1/data_pb2.pyi +1485 -0
- viam/gen/app/datapipelines/__init__.py +0 -0
- viam/gen/app/datapipelines/v1/__init__.py +0 -0
- viam/gen/app/datapipelines/v1/data_pipelines_grpc.py +84 -0
- viam/gen/app/datapipelines/v1/data_pipelines_pb2.py +56 -0
- viam/gen/app/datapipelines/v1/data_pipelines_pb2.pyi +370 -0
- viam/gen/app/dataset/__init__.py +0 -0
- viam/gen/app/dataset/v1/__init__.py +0 -0
- viam/gen/app/dataset/v1/dataset_grpc.py +60 -0
- viam/gen/app/dataset/v1/dataset_pb2.py +40 -0
- viam/gen/app/dataset/v1/dataset_pb2.pyi +179 -0
- viam/gen/app/datasync/__init__.py +0 -0
- viam/gen/app/datasync/v1/__init__.py +0 -0
- viam/gen/app/datasync/v1/data_sync_grpc.py +47 -0
- viam/gen/app/datasync/v1/data_sync_pb2.py +70 -0
- viam/gen/app/datasync/v1/data_sync_pb2.pyi +425 -0
- viam/gen/app/mlinference/__init__.py +0 -0
- viam/gen/app/mlinference/v1/__init__.py +0 -0
- viam/gen/app/mlinference/v1/ml_inference_grpc.py +28 -0
- viam/gen/app/mlinference/v1/ml_inference_pb2.py +23 -0
- viam/gen/app/mlinference/v1/ml_inference_pb2.pyi +63 -0
- viam/gen/app/mltraining/__init__.py +0 -0
- viam/gen/app/mltraining/v1/__init__.py +0 -0
- viam/gen/app/mltraining/v1/ml_training_grpc.py +78 -0
- viam/gen/app/mltraining/v1/ml_training_pb2.py +124 -0
- viam/gen/app/mltraining/v1/ml_training_pb2.pyi +415 -0
- viam/gen/app/packages/__init__.py +0 -0
- viam/gen/app/packages/v1/__init__.py +0 -0
- viam/gen/app/packages/v1/packages_grpc.py +54 -0
- viam/gen/app/packages/v1/packages_pb2.py +52 -0
- viam/gen/app/packages/v1/packages_pb2.pyi +311 -0
- viam/gen/app/v1/__init__.py +0 -0
- viam/gen/app/v1/app_grpc.py +863 -0
- viam/gen/app/v1/app_pb2.py +649 -0
- viam/gen/app/v1/app_pb2.pyi +5279 -0
- viam/gen/app/v1/billing_grpc.py +76 -0
- viam/gen/app/v1/billing_pb2.py +92 -0
- viam/gen/app/v1/billing_pb2.pyi +463 -0
- viam/gen/app/v1/end_user_grpc.py +59 -0
- viam/gen/app/v1/end_user_pb2.py +55 -0
- viam/gen/app/v1/end_user_pb2.pyi +181 -0
- viam/gen/app/v1/robot_grpc.py +55 -0
- viam/gen/app/v1/robot_pb2.py +127 -0
- viam/gen/app/v1/robot_pb2.pyi +1202 -0
- viam/gen/common/__init__.py +0 -0
- viam/gen/common/v1/__init__.py +0 -0
- viam/gen/common/v1/common_grpc.py +0 -0
- viam/gen/common/v1/common_pb2.py +78 -0
- viam/gen/common/v1/common_pb2.pyi +687 -0
- viam/gen/component/__init__.py +0 -0
- viam/gen/component/arm/__init__.py +0 -0
- viam/gen/component/arm/v1/__init__.py +0 -0
- viam/gen/component/arm/v1/arm_grpc.py +102 -0
- viam/gen/component/arm/v1/arm_pb2.py +74 -0
- viam/gen/component/arm/v1/arm_pb2.pyi +344 -0
- viam/gen/component/audioinput/__init__.py +0 -0
- viam/gen/component/audioinput/v1/__init__.py +0 -0
- viam/gen/component/audioinput/v1/audioinput_grpc.py +63 -0
- viam/gen/component/audioinput/v1/audioinput_pb2.py +45 -0
- viam/gen/component/audioinput/v1/audioinput_pb2.pyi +179 -0
- viam/gen/component/base/__init__.py +0 -0
- viam/gen/component/base/v1/__init__.py +0 -0
- viam/gen/component/base/v1/base_grpc.py +94 -0
- viam/gen/component/base/v1/base_pb2.py +66 -0
- viam/gen/component/base/v1/base_pb2.pyi +258 -0
- viam/gen/component/board/__init__.py +0 -0
- viam/gen/component/board/v1/__init__.py +0 -0
- viam/gen/component/board/v1/board_grpc.py +127 -0
- viam/gen/component/board/v1/board_pb2.py +103 -0
- viam/gen/component/board/v1/board_pb2.pyi +496 -0
- viam/gen/component/button/__init__.py +0 -0
- viam/gen/component/button/v1/__init__.py +0 -0
- viam/gen/component/button/v1/button_grpc.py +38 -0
- viam/gen/component/button/v1/button_pb2.py +28 -0
- viam/gen/component/button/v1/button_pb2.pyi +39 -0
- viam/gen/component/camera/__init__.py +0 -0
- viam/gen/component/camera/v1/__init__.py +0 -0
- viam/gen/component/camera/v1/camera_grpc.py +79 -0
- viam/gen/component/camera/v1/camera_pb2.py +67 -0
- viam/gen/component/camera/v1/camera_pb2.pyi +373 -0
- viam/gen/component/encoder/__init__.py +0 -0
- viam/gen/component/encoder/v1/__init__.py +0 -0
- viam/gen/component/encoder/v1/encoder_grpc.py +62 -0
- viam/gen/component/encoder/v1/encoder_pb2.py +44 -0
- viam/gen/component/encoder/v1/encoder_pb2.pyi +147 -0
- viam/gen/component/gantry/__init__.py +0 -0
- viam/gen/component/gantry/v1/__init__.py +0 -0
- viam/gen/component/gantry/v1/gantry_grpc.py +86 -0
- viam/gen/component/gantry/v1/gantry_pb2.py +62 -0
- viam/gen/component/gantry/v1/gantry_pb2.pyi +239 -0
- viam/gen/component/generic/__init__.py +0 -0
- viam/gen/component/generic/v1/__init__.py +0 -0
- viam/gen/component/generic/v1/generic_grpc.py +37 -0
- viam/gen/component/generic/v1/generic_pb2.py +23 -0
- viam/gen/component/generic/v1/generic_pb2.pyi +6 -0
- viam/gen/component/gripper/__init__.py +0 -0
- viam/gen/component/gripper/v1/__init__.py +0 -0
- viam/gen/component/gripper/v1/gripper_grpc.py +70 -0
- viam/gen/component/gripper/v1/gripper_pb2.py +48 -0
- viam/gen/component/gripper/v1/gripper_pb2.pyi +137 -0
- viam/gen/component/inputcontroller/__init__.py +0 -0
- viam/gen/component/inputcontroller/v1/__init__.py +0 -0
- viam/gen/component/inputcontroller/v1/input_controller_grpc.py +71 -0
- viam/gen/component/inputcontroller/v1/input_controller_pb2.py +55 -0
- viam/gen/component/inputcontroller/v1/input_controller_pb2.pyi +243 -0
- viam/gen/component/motor/__init__.py +0 -0
- viam/gen/component/motor/v1/__init__.py +0 -0
- viam/gen/component/motor/v1/motor_grpc.py +118 -0
- viam/gen/component/motor/v1/motor_pb2.py +86 -0
- viam/gen/component/motor/v1/motor_pb2.pyi +368 -0
- viam/gen/component/movementsensor/__init__.py +0 -0
- viam/gen/component/movementsensor/v1/__init__.py +0 -0
- viam/gen/component/movementsensor/v1/movementsensor_grpc.py +110 -0
- viam/gen/component/movementsensor/v1/movementsensor_pb2.py +78 -0
- viam/gen/component/movementsensor/v1/movementsensor_pb2.pyi +384 -0
- viam/gen/component/posetracker/__init__.py +0 -0
- viam/gen/component/posetracker/v1/__init__.py +0 -0
- viam/gen/component/posetracker/v1/pose_tracker_grpc.py +46 -0
- viam/gen/component/posetracker/v1/pose_tracker_pb2.py +34 -0
- viam/gen/component/posetracker/v1/pose_tracker_pb2.pyi +79 -0
- viam/gen/component/powersensor/__init__.py +0 -0
- viam/gen/component/powersensor/v1/__init__.py +0 -0
- viam/gen/component/powersensor/v1/powersensor_grpc.py +62 -0
- viam/gen/component/powersensor/v1/powersensor_pb2.py +42 -0
- viam/gen/component/powersensor/v1/powersensor_pb2.pyi +124 -0
- viam/gen/component/sensor/__init__.py +0 -0
- viam/gen/component/sensor/v1/__init__.py +0 -0
- viam/gen/component/sensor/v1/sensor_grpc.py +45 -0
- viam/gen/component/sensor/v1/sensor_pb2.py +25 -0
- viam/gen/component/sensor/v1/sensor_pb2.pyi +6 -0
- viam/gen/component/servo/__init__.py +0 -0
- viam/gen/component/servo/v1/__init__.py +0 -0
- viam/gen/component/servo/v1/servo_grpc.py +70 -0
- viam/gen/component/servo/v1/servo_pb2.py +50 -0
- viam/gen/component/servo/v1/servo_pb2.pyi +150 -0
- viam/gen/component/switch/__init__.py +0 -0
- viam/gen/component/switch/v1/__init__.py +0 -0
- viam/gen/component/switch/v1/switch_grpc.py +54 -0
- viam/gen/component/switch/v1/switch_pb2.py +40 -0
- viam/gen/component/switch/v1/switch_pb2.pyi +109 -0
- viam/gen/component/testecho/__init__.py +0 -0
- viam/gen/component/testecho/v1/__init__.py +0 -0
- viam/gen/component/testecho/v1/testecho_grpc.py +52 -0
- viam/gen/component/testecho/v1/testecho_pb2.py +36 -0
- viam/gen/component/testecho/v1/testecho_pb2.pyi +114 -0
- viam/gen/module/__init__.py +0 -0
- viam/gen/module/v1/__init__.py +0 -0
- viam/gen/module/v1/module_grpc.py +61 -0
- viam/gen/module/v1/module_pb2.py +43 -0
- viam/gen/module/v1/module_pb2.pyi +211 -0
- viam/gen/proto/__init__.py +0 -0
- viam/gen/proto/rpc/__init__.py +0 -0
- viam/gen/proto/rpc/examples/__init__.py +0 -0
- viam/gen/proto/rpc/examples/echo/__init__.py +0 -0
- viam/gen/proto/rpc/examples/echo/v1/__init__.py +0 -0
- viam/gen/proto/rpc/examples/echo/v1/echo_grpc.py +44 -0
- viam/gen/proto/rpc/examples/echo/v1/echo_pb2.py +32 -0
- viam/gen/proto/rpc/examples/echo/v1/echo_pb2.pyi +87 -0
- viam/gen/proto/rpc/examples/echoresource/__init__.py +0 -0
- viam/gen/proto/rpc/examples/echoresource/v1/__init__.py +0 -0
- viam/gen/proto/rpc/examples/echoresource/v1/echoresource_grpc.py +43 -0
- viam/gen/proto/rpc/examples/echoresource/v1/echoresource_pb2.py +29 -0
- viam/gen/proto/rpc/examples/echoresource/v1/echoresource_pb2.pyi +93 -0
- viam/gen/proto/rpc/v1/__init__.py +0 -0
- viam/gen/proto/rpc/v1/auth_grpc.py +47 -0
- viam/gen/proto/rpc/v1/auth_pb2.py +34 -0
- viam/gen/proto/rpc/v1/auth_pb2.pyi +92 -0
- viam/gen/proto/rpc/webrtc/__init__.py +0 -0
- viam/gen/proto/rpc/webrtc/v1/__init__.py +0 -0
- viam/gen/proto/rpc/webrtc/v1/grpc_grpc.py +0 -0
- viam/gen/proto/rpc/webrtc/v1/grpc_pb2.py +43 -0
- viam/gen/proto/rpc/webrtc/v1/grpc_pb2.pyi +304 -0
- viam/gen/proto/rpc/webrtc/v1/signaling_grpc.py +54 -0
- viam/gen/proto/rpc/webrtc/v1/signaling_pb2.py +70 -0
- viam/gen/proto/rpc/webrtc/v1/signaling_pb2.pyi +496 -0
- viam/gen/provisioning/__init__.py +0 -0
- viam/gen/provisioning/v1/__init__.py +0 -0
- viam/gen/provisioning/v1/provisioning_grpc.py +51 -0
- viam/gen/provisioning/v1/provisioning_pb2.py +39 -0
- viam/gen/provisioning/v1/provisioning_pb2.pyi +188 -0
- viam/gen/robot/__init__.py +0 -0
- viam/gen/robot/v1/__init__.py +0 -0
- viam/gen/robot/v1/robot_grpc.py +208 -0
- viam/gen/robot/v1/robot_pb2.py +188 -0
- viam/gen/robot/v1/robot_pb2.pyi +1020 -0
- viam/gen/service/__init__.py +0 -0
- viam/gen/service/datamanager/__init__.py +0 -0
- viam/gen/service/datamanager/v1/__init__.py +0 -0
- viam/gen/service/datamanager/v1/data_manager_grpc.py +38 -0
- viam/gen/service/datamanager/v1/data_manager_pb2.py +28 -0
- viam/gen/service/datamanager/v1/data_manager_pb2.pyi +39 -0
- viam/gen/service/discovery/__init__.py +0 -0
- viam/gen/service/discovery/v1/__init__.py +0 -0
- viam/gen/service/discovery/v1/discovery_grpc.py +39 -0
- viam/gen/service/discovery/v1/discovery_pb2.py +29 -0
- viam/gen/service/discovery/v1/discovery_pb2.pyi +51 -0
- viam/gen/service/generic/__init__.py +0 -0
- viam/gen/service/generic/v1/__init__.py +0 -0
- viam/gen/service/generic/v1/generic_grpc.py +29 -0
- viam/gen/service/generic/v1/generic_pb2.py +21 -0
- viam/gen/service/generic/v1/generic_pb2.pyi +6 -0
- viam/gen/service/mlmodel/__init__.py +0 -0
- viam/gen/service/mlmodel/v1/__init__.py +0 -0
- viam/gen/service/mlmodel/v1/mlmodel_grpc.py +37 -0
- viam/gen/service/mlmodel/v1/mlmodel_pb2.py +83 -0
- viam/gen/service/mlmodel/v1/mlmodel_pb2.pyi +480 -0
- viam/gen/service/motion/__init__.py +0 -0
- viam/gen/service/motion/v1/__init__.py +0 -0
- viam/gen/service/motion/v1/motion_grpc.py +87 -0
- viam/gen/service/motion/v1/motion_pb2.py +97 -0
- viam/gen/service/motion/v1/motion_pb2.pyi +838 -0
- viam/gen/service/navigation/__init__.py +0 -0
- viam/gen/service/navigation/v1/__init__.py +0 -0
- viam/gen/service/navigation/v1/navigation_grpc.py +102 -0
- viam/gen/service/navigation/v1/navigation_pb2.py +84 -0
- viam/gen/service/navigation/v1/navigation_pb2.pyi +419 -0
- viam/gen/service/sensors/__init__.py +0 -0
- viam/gen/service/sensors/v1/__init__.py +0 -0
- viam/gen/service/sensors/v1/sensors_grpc.py +46 -0
- viam/gen/service/sensors/v1/sensors_pb2.py +68 -0
- viam/gen/service/sensors/v1/sensors_pb2.pyi +137 -0
- viam/gen/service/shell/__init__.py +0 -0
- viam/gen/service/shell/v1/__init__.py +0 -0
- viam/gen/service/shell/v1/shell_grpc.py +55 -0
- viam/gen/service/shell/v1/shell_pb2.py +45 -0
- viam/gen/service/shell/v1/shell_pb2.pyi +307 -0
- viam/gen/service/slam/__init__.py +0 -0
- viam/gen/service/slam/v1/__init__.py +0 -0
- viam/gen/service/slam/v1/slam_grpc.py +61 -0
- viam/gen/service/slam/v1/slam_pb2.py +51 -0
- viam/gen/service/slam/v1/slam_pb2.pyi +213 -0
- viam/gen/service/vision/__init__.py +0 -0
- viam/gen/service/vision/v1/__init__.py +0 -0
- viam/gen/service/vision/v1/vision_grpc.py +87 -0
- viam/gen/service/vision/v1/vision_pb2.py +69 -0
- viam/gen/service/vision/v1/vision_pb2.pyi +454 -0
- viam/gen/stream/__init__.py +0 -0
- viam/gen/stream/v1/__init__.py +0 -0
- viam/gen/stream/v1/stream_grpc.py +59 -0
- viam/gen/stream/v1/stream_pb2.py +39 -0
- viam/gen/stream/v1/stream_pb2.pyi +161 -0
- viam/gen/tagger/__init__.py +0 -0
- viam/gen/tagger/v1/__init__.py +0 -0
- viam/gen/tagger/v1/tagger_grpc.py +0 -0
- viam/gen/tagger/v1/tagger_pb2.py +16 -0
- viam/gen/tagger/v1/tagger_pb2.pyi +15 -0
- viam/logging.py +216 -0
- viam/media/__init__.py +0 -0
- viam/media/audio.py +16 -0
- viam/media/utils/__init__.py +0 -0
- viam/media/utils/pil/__init__.py +51 -0
- viam/media/utils/pil/viam_rgba_plugin.py +73 -0
- viam/media/viam_rgba.py +10 -0
- viam/media/video.py +217 -0
- viam/module/__init__.py +5 -0
- viam/module/module.py +281 -0
- viam/module/service.py +66 -0
- viam/module/types.py +23 -0
- viam/operations.py +124 -0
- viam/proto/__init__.py +0 -0
- viam/proto/app/__init__.py +554 -0
- viam/proto/app/agent/__init__.py +28 -0
- viam/proto/app/billing.py +58 -0
- viam/proto/app/cloudslam/__init__.py +48 -0
- viam/proto/app/data/__init__.py +138 -0
- viam/proto/app/datapipelines/__init__.py +56 -0
- viam/proto/app/dataset/__init__.py +36 -0
- viam/proto/app/datasync/__init__.py +44 -0
- viam/proto/app/end_user.py +34 -0
- viam/proto/app/mlinference/__init__.py +15 -0
- viam/proto/app/mltraining/__init__.py +52 -0
- viam/proto/app/packages/__init__.py +38 -0
- viam/proto/app/robot.py +84 -0
- viam/proto/common/__init__.py +66 -0
- viam/proto/component/__init__.py +0 -0
- viam/proto/component/arm/__init__.py +48 -0
- viam/proto/component/audioinput/__init__.py +30 -0
- viam/proto/component/base/__init__.py +42 -0
- viam/proto/component/board/__init__.py +62 -0
- viam/proto/component/button/__init__.py +15 -0
- viam/proto/component/camera/__init__.py +46 -0
- viam/proto/component/encoder/__init__.py +28 -0
- viam/proto/component/gantry/__init__.py +40 -0
- viam/proto/component/generic/__init__.py +12 -0
- viam/proto/component/gripper/__init__.py +30 -0
- viam/proto/component/inputcontroller/__init__.py +38 -0
- viam/proto/component/motor/__init__.py +56 -0
- viam/proto/component/movementsensor/__init__.py +50 -0
- viam/proto/component/posetracker/__init__.py +19 -0
- viam/proto/component/powersensor/__init__.py +30 -0
- viam/proto/component/sensor/__init__.py +12 -0
- viam/proto/component/servo/__init__.py +32 -0
- viam/proto/component/switch/__init__.py +26 -0
- viam/proto/component/testecho/__init__.py +30 -0
- viam/proto/module/__init__.py +38 -0
- viam/proto/provisioning/__init__.py +36 -0
- viam/proto/robot/__init__.py +130 -0
- viam/proto/rpc/__init__.py +0 -0
- viam/proto/rpc/auth.py +34 -0
- viam/proto/rpc/examples/__init__.py +0 -0
- viam/proto/rpc/examples/echo/__init__.py +26 -0
- viam/proto/rpc/examples/echoresource/__init__.py +30 -0
- viam/proto/rpc/webrtc/__init__.py +0 -0
- viam/proto/rpc/webrtc/grpc.py +36 -0
- viam/proto/rpc/webrtc/signaling.py +58 -0
- viam/proto/service/__init__.py +0 -0
- viam/proto/service/datamanager/__init__.py +19 -0
- viam/proto/service/discovery/__init__.py +15 -0
- viam/proto/service/generic/__init__.py +12 -0
- viam/proto/service/mlmodel/__init__.py +54 -0
- viam/proto/service/motion/__init__.py +68 -0
- viam/proto/service/navigation/__init__.py +58 -0
- viam/proto/service/sensors/__init__.py +18 -0
- viam/proto/service/shell/__init__.py +36 -0
- viam/proto/service/slam/__init__.py +36 -0
- viam/proto/service/vision/__init__.py +46 -0
- viam/proto/stream/__init__.py +36 -0
- viam/proto/tagger/__init__.py +6 -0
- viam/py.typed +0 -0
- viam/resource/__init__.py +0 -0
- viam/resource/base.py +123 -0
- viam/resource/easy_resource.py +153 -0
- viam/resource/manager.py +126 -0
- viam/resource/registry.py +199 -0
- viam/resource/rpc_client_base.py +65 -0
- viam/resource/rpc_service_base.py +48 -0
- viam/resource/types.py +213 -0
- viam/robot/__init__.py +0 -0
- viam/robot/client.py +909 -0
- viam/robot/service.py +69 -0
- viam/rpc/__init__.py +0 -0
- viam/rpc/dial.py +420 -0
- viam/rpc/libviam_rust_utils.dll +0 -0
- viam/rpc/server.py +201 -0
- viam/rpc/signaling.py +29 -0
- viam/rpc/types.py +22 -0
- viam/services/__init__.py +0 -0
- viam/services/discovery/__init__.py +12 -0
- viam/services/discovery/client.py +55 -0
- viam/services/discovery/discovery.py +52 -0
- viam/services/discovery/service.py +43 -0
- viam/services/generic/__init__.py +18 -0
- viam/services/generic/client.py +58 -0
- viam/services/generic/generic.py +58 -0
- viam/services/generic/service.py +29 -0
- viam/services/mlmodel/__init__.py +24 -0
- viam/services/mlmodel/client.py +37 -0
- viam/services/mlmodel/mlmodel.py +78 -0
- viam/services/mlmodel/service.py +38 -0
- viam/services/mlmodel/utils.py +101 -0
- viam/services/motion/__init__.py +17 -0
- viam/services/motion/client.py +215 -0
- viam/services/motion/motion.py +378 -0
- viam/services/motion/service.py +132 -0
- viam/services/navigation/__init__.py +11 -0
- viam/services/navigation/client.py +99 -0
- viam/services/navigation/navigation.py +250 -0
- viam/services/navigation/service.py +137 -0
- viam/services/service_base.py +78 -0
- viam/services/service_client_base.py +46 -0
- viam/services/slam/__init__.py +17 -0
- viam/services/slam/client.py +62 -0
- viam/services/slam/service.py +75 -0
- viam/services/slam/slam.py +111 -0
- viam/services/vision/__init__.py +15 -0
- viam/services/vision/client.py +206 -0
- viam/services/vision/service.py +146 -0
- viam/services/vision/vision.py +315 -0
- viam/sessions_client.py +245 -0
- viam/streams.py +44 -0
- viam/utils.py +365 -0
- viam/version_metadata.py +4 -0
- viam_sdk-0.45.2.dist-info/METADATA +157 -0
- viam_sdk-0.45.2.dist-info/RECORD +476 -0
- viam_sdk-0.45.2.dist-info/WHEEL +4 -0
- viam_sdk-0.45.2.dist-info/licenses/LICENSE +202 -0
viam/robot/service.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
from typing import Any, Dict, List, Set
|
|
2
|
+
|
|
3
|
+
from grpclib.server import Stream
|
|
4
|
+
|
|
5
|
+
from viam import logging
|
|
6
|
+
from viam.components.movement_sensor import MovementSensor
|
|
7
|
+
from viam.components.sensor import Sensor
|
|
8
|
+
from viam.errors import ViamGRPCError
|
|
9
|
+
from viam.proto.common import ResourceName
|
|
10
|
+
from viam.proto.robot import (
|
|
11
|
+
ResourceNamesRequest,
|
|
12
|
+
ResourceNamesResponse,
|
|
13
|
+
StopAllRequest,
|
|
14
|
+
StopAllResponse,
|
|
15
|
+
UnimplementedRobotServiceBase,
|
|
16
|
+
)
|
|
17
|
+
from viam.resource.rpc_service_base import ResourceRPCServiceBase
|
|
18
|
+
from viam.utils import resource_names_for_resource, struct_to_dict
|
|
19
|
+
|
|
20
|
+
LOGGER = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class RobotService(UnimplementedRobotServiceBase, ResourceRPCServiceBase):
|
|
24
|
+
def _generate_metadata(self) -> List[ResourceName]:
|
|
25
|
+
md: Set[ResourceName] = set()
|
|
26
|
+
|
|
27
|
+
for resource in self.manager.resources.values():
|
|
28
|
+
# If the resource is a MovementSensor, DO NOT include Sensor as well (it will get added via MovementSensor)
|
|
29
|
+
if resource.API == Sensor.API and MovementSensor.get_resource_name(resource.name) in self.manager.resources:
|
|
30
|
+
continue
|
|
31
|
+
|
|
32
|
+
md.update(resource_names_for_resource(resource))
|
|
33
|
+
|
|
34
|
+
return list(md)
|
|
35
|
+
|
|
36
|
+
async def ResourceNames(self, stream: Stream[ResourceNamesRequest, ResourceNamesResponse]) -> None:
|
|
37
|
+
request = await stream.recv_message()
|
|
38
|
+
assert request is not None
|
|
39
|
+
metadata = self._generate_metadata()
|
|
40
|
+
response = ResourceNamesResponse(resources=metadata)
|
|
41
|
+
await stream.send_message(response)
|
|
42
|
+
|
|
43
|
+
async def StopAll(self, stream: Stream[StopAllRequest, StopAllResponse]) -> None:
|
|
44
|
+
request = await stream.recv_message()
|
|
45
|
+
assert request is not None
|
|
46
|
+
|
|
47
|
+
extra: Dict[ResourceName, Dict[str, Any]] = {}
|
|
48
|
+
for ex in request.extra:
|
|
49
|
+
extra[ex.name] = struct_to_dict(ex.params)
|
|
50
|
+
|
|
51
|
+
errors: List[str] = []
|
|
52
|
+
for component in self.manager.resources.values():
|
|
53
|
+
if callable(getattr(component, "stop", None)):
|
|
54
|
+
try:
|
|
55
|
+
rn = component.get_resource_name(component.name)
|
|
56
|
+
if rn in extra:
|
|
57
|
+
try:
|
|
58
|
+
await component.stop(extra=extra[rn]) # type: ignore
|
|
59
|
+
except TypeError:
|
|
60
|
+
await component.stop() # type: ignore
|
|
61
|
+
else:
|
|
62
|
+
await component.stop() # type: ignore
|
|
63
|
+
except Exception:
|
|
64
|
+
LOGGER.exception(f"Failed to stop component named {component.name}")
|
|
65
|
+
errors.append(component.name)
|
|
66
|
+
|
|
67
|
+
if errors:
|
|
68
|
+
raise ViamGRPCError(f'Failed to stop components named {", ".join(errors)}')
|
|
69
|
+
await stream.send_message(StopAllResponse())
|
viam/rpc/__init__.py
ADDED
|
File without changes
|
viam/rpc/dial.py
ADDED
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
import ctypes
|
|
2
|
+
import pathlib
|
|
3
|
+
import re
|
|
4
|
+
import socket
|
|
5
|
+
import ssl
|
|
6
|
+
import sys
|
|
7
|
+
import uuid
|
|
8
|
+
import warnings
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from typing import Callable, Literal, Optional, Tuple, Type, Union
|
|
11
|
+
|
|
12
|
+
from grpclib.client import Channel, Stream
|
|
13
|
+
from grpclib.const import Cardinality
|
|
14
|
+
from grpclib.events import SendRequest, listen
|
|
15
|
+
from grpclib.metadata import Deadline, _MetadataLike
|
|
16
|
+
from grpclib.protocol import H2Protocol
|
|
17
|
+
from grpclib.stream import _RecvType, _SendType
|
|
18
|
+
from typing_extensions import Self
|
|
19
|
+
|
|
20
|
+
from viam import logging
|
|
21
|
+
from viam.errors import InsecureConnectionError, ViamError
|
|
22
|
+
from viam.proto.rpc.auth import AuthenticateRequest, AuthServiceStub
|
|
23
|
+
from viam.proto.rpc.auth import Credentials as PBCredentials
|
|
24
|
+
from viam.utils import to_thread
|
|
25
|
+
from viam.version_metadata import API_VERSION, SDK_VERSION
|
|
26
|
+
|
|
27
|
+
LOGGER = logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class Credentials:
|
|
32
|
+
"""Credentials to connect to the robot and the Viam app."""
|
|
33
|
+
|
|
34
|
+
type: Union[Literal["robot-location-secret"], Literal["robot-secret"], Literal["api-key"]]
|
|
35
|
+
"""The type of credential
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
payload: str
|
|
39
|
+
"""The credential
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class DialOptions:
|
|
44
|
+
disable_webrtc: bool
|
|
45
|
+
"""Bypass Web RTC and connect directly to the robot.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
auth_entity: Optional[str]
|
|
49
|
+
"""The URL to authenticate against. Should be used if the address passed in and FQDN of the server do not match.
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
credentials: Optional[Credentials]
|
|
53
|
+
"""Credentials for connecting to the robot
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
insecure: bool = False
|
|
57
|
+
"""Determine if the RPC connection is TLS based. Must be provided to
|
|
58
|
+
establish an insecure connection. Otherwise, a TLS based connection
|
|
59
|
+
will be assumed."""
|
|
60
|
+
|
|
61
|
+
allow_insecure_downgrade: bool = False
|
|
62
|
+
"""Allow the RPC connection to be downgraded to an insecure connection
|
|
63
|
+
if detected. This is only used when credentials are not present."""
|
|
64
|
+
|
|
65
|
+
allow_insecure_with_creds_downgrade: bool = False
|
|
66
|
+
"""Allow the RPC connection to be downgraded to an insecure connection
|
|
67
|
+
if detected, even with credentials present. This is generally
|
|
68
|
+
unsafe to use, but can be requested."""
|
|
69
|
+
|
|
70
|
+
max_reconnect_attempts: int = 3
|
|
71
|
+
"""Max number of times the client attempts to reconnect when connection is lost"""
|
|
72
|
+
|
|
73
|
+
initial_connection_attempts: int = 3
|
|
74
|
+
"""Max number of times the client will attempt to establish an initial connection
|
|
75
|
+
If set to a non-positive integer, then there will be no limit to initial connection attempts"""
|
|
76
|
+
|
|
77
|
+
initial_connection_attempt_timeout: float
|
|
78
|
+
"""Number of seconds before dial connection times out on initial connection attempts
|
|
79
|
+
Defaults to whatever value is set in the `timeout` field"""
|
|
80
|
+
|
|
81
|
+
timeout: float = 20
|
|
82
|
+
"""Number of seconds before the dial connection times out
|
|
83
|
+
Set to 20sec to match _defaultOfferDeadline in goutils/rpc/wrtc_call_queue.go"""
|
|
84
|
+
|
|
85
|
+
def __init__(
|
|
86
|
+
self,
|
|
87
|
+
*,
|
|
88
|
+
disable_webrtc: bool = False,
|
|
89
|
+
auth_entity: Optional[str] = None,
|
|
90
|
+
credentials: Optional[Credentials] = None,
|
|
91
|
+
insecure: bool = False,
|
|
92
|
+
allow_insecure_downgrade: bool = False,
|
|
93
|
+
allow_insecure_with_creds_downgrade: bool = False,
|
|
94
|
+
max_reconnect_attempts: int = 3,
|
|
95
|
+
timeout: float = 20,
|
|
96
|
+
initial_connection_attempts: int = 3,
|
|
97
|
+
initial_connection_attempt_timeout: Optional[float] = None,
|
|
98
|
+
) -> None:
|
|
99
|
+
self.disable_webrtc = disable_webrtc
|
|
100
|
+
self.auth_entity = auth_entity
|
|
101
|
+
self.credentials = credentials
|
|
102
|
+
self.insecure = insecure
|
|
103
|
+
self.allow_insecure_downgrade = allow_insecure_downgrade
|
|
104
|
+
self.allow_insecure_with_creds_downgrade = allow_insecure_with_creds_downgrade
|
|
105
|
+
self.max_reconnect_attempts = max_reconnect_attempts
|
|
106
|
+
self.timeout = timeout
|
|
107
|
+
self.initial_connection_attempts = initial_connection_attempts
|
|
108
|
+
self.initial_connection_attempt_timeout = initial_connection_attempt_timeout if initial_connection_attempt_timeout else timeout
|
|
109
|
+
|
|
110
|
+
@classmethod
|
|
111
|
+
def with_api_key(cls, api_key: str, api_key_id: str) -> Self:
|
|
112
|
+
"""Create DialOptions with an API key for credentials and default values for other arguments.
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
api_key (str): your API key
|
|
116
|
+
api_key_id (str): your API key ID. Must be a valid UUID
|
|
117
|
+
|
|
118
|
+
Raises:
|
|
119
|
+
ValueError: Raised if the api_key_id is not a valid UUID
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
Self: the DialOptions
|
|
123
|
+
"""
|
|
124
|
+
try:
|
|
125
|
+
uuid.UUID(api_key_id)
|
|
126
|
+
except ValueError:
|
|
127
|
+
raise ValueError(f"{api_key_id} is not a valid UUID")
|
|
128
|
+
|
|
129
|
+
credentials = Credentials(type="api-key", payload=api_key)
|
|
130
|
+
return cls(credentials=credentials, auth_entity=api_key_id)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _host_port_from_url(url) -> Tuple[Optional[str], Optional[int]]:
|
|
134
|
+
query = "(?:.*://)?(?P<host>[^:/ ]+).?(?P<port>[0-9]*).*"
|
|
135
|
+
match = re.search(query, url)
|
|
136
|
+
|
|
137
|
+
if not match:
|
|
138
|
+
return (None, None)
|
|
139
|
+
|
|
140
|
+
host = match.group("host")
|
|
141
|
+
try:
|
|
142
|
+
port = int(match.group("port"))
|
|
143
|
+
except ValueError:
|
|
144
|
+
port = None
|
|
145
|
+
return (host, port)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
async def _get_access_token(channel: Channel, address: str, opts: DialOptions) -> str:
|
|
149
|
+
entity = opts.auth_entity if opts.auth_entity else re.sub(r"^(.*:\/\/)/", "", address)
|
|
150
|
+
creds = PBCredentials(
|
|
151
|
+
type=opts.credentials.type if opts.credentials else "", payload=opts.credentials.payload if opts.credentials else ""
|
|
152
|
+
)
|
|
153
|
+
request = AuthenticateRequest(entity=entity, credentials=creds)
|
|
154
|
+
|
|
155
|
+
auth_service = AuthServiceStub(channel=channel)
|
|
156
|
+
response = await auth_service.Authenticate(request)
|
|
157
|
+
return response.access_token
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class AuthenticatedChannel(Channel):
|
|
161
|
+
_metadata: _MetadataLike
|
|
162
|
+
|
|
163
|
+
def __init__(
|
|
164
|
+
self,
|
|
165
|
+
host: Optional[str] = None,
|
|
166
|
+
port: Optional[int] = None,
|
|
167
|
+
*,
|
|
168
|
+
ssl: Union[None, bool, ssl.SSLContext] = None,
|
|
169
|
+
server_hostname: Optional[str] = None,
|
|
170
|
+
):
|
|
171
|
+
super().__init__(host, port, ssl=ssl)
|
|
172
|
+
self._server_hostname = server_hostname
|
|
173
|
+
|
|
174
|
+
async def _create_connection(self) -> H2Protocol:
|
|
175
|
+
_, protocol = await self._loop.create_connection(
|
|
176
|
+
self._protocol_factory,
|
|
177
|
+
self._host,
|
|
178
|
+
self._port,
|
|
179
|
+
ssl=self._ssl,
|
|
180
|
+
server_hostname=self._server_hostname,
|
|
181
|
+
)
|
|
182
|
+
return protocol
|
|
183
|
+
|
|
184
|
+
def request(
|
|
185
|
+
self,
|
|
186
|
+
name: str,
|
|
187
|
+
cardinality: Cardinality,
|
|
188
|
+
request_type: Type[_SendType],
|
|
189
|
+
reply_type: Type[_RecvType],
|
|
190
|
+
*,
|
|
191
|
+
timeout: Optional[float] = None,
|
|
192
|
+
deadline: Optional[Deadline] = None,
|
|
193
|
+
metadata: Optional[_MetadataLike] = None,
|
|
194
|
+
) -> Stream[_SendType, _RecvType]:
|
|
195
|
+
if not metadata and hasattr(self, "_metadata"):
|
|
196
|
+
metadata = self._metadata
|
|
197
|
+
return super().request(name, cardinality, request_type, reply_type, timeout=timeout, deadline=deadline, metadata=metadata)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
@dataclass
|
|
201
|
+
class ViamChannel:
|
|
202
|
+
channel: Channel
|
|
203
|
+
release: Callable[[], None]
|
|
204
|
+
_closed: bool = False
|
|
205
|
+
|
|
206
|
+
def close(self):
|
|
207
|
+
if not self._closed:
|
|
208
|
+
try:
|
|
209
|
+
self.channel.close()
|
|
210
|
+
except RuntimeError as e:
|
|
211
|
+
# ignore event loop is closed errors - robot is getting shutdown
|
|
212
|
+
if len(e.args) > 0 and e.args[0] == "Event loop is closed":
|
|
213
|
+
LOGGER.debug("ViamChannel might not have shut down cleanly - Event loop was closed")
|
|
214
|
+
return
|
|
215
|
+
raise
|
|
216
|
+
finally:
|
|
217
|
+
self.release()
|
|
218
|
+
self._closed = True
|
|
219
|
+
|
|
220
|
+
def __del__(self):
|
|
221
|
+
self.close()
|
|
222
|
+
|
|
223
|
+
async def __aenter__(self):
|
|
224
|
+
return self
|
|
225
|
+
|
|
226
|
+
async def __aexit__(self, exc_type, exc_value, traceback):
|
|
227
|
+
self.close()
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
class _Runtime:
|
|
231
|
+
_lib: ctypes.CDLL
|
|
232
|
+
_ptr: ctypes.c_void_p
|
|
233
|
+
|
|
234
|
+
def __init__(self) -> None:
|
|
235
|
+
suffix = "dylib" if sys.platform == "darwin" else "so" if "linux" in sys.platform else "dll"
|
|
236
|
+
LOGGER.debug("Creating new viam-rust-utils runtime")
|
|
237
|
+
libname = pathlib.Path(__file__).parent.absolute() / f"libviam_rust_utils.{suffix}"
|
|
238
|
+
self._lib = ctypes.CDLL(libname.__str__())
|
|
239
|
+
self._lib.init_rust_runtime.argtypes = ()
|
|
240
|
+
self._lib.init_rust_runtime.restype = ctypes.c_void_p
|
|
241
|
+
|
|
242
|
+
self._lib.dial.argtypes = (
|
|
243
|
+
ctypes.c_char_p,
|
|
244
|
+
ctypes.c_char_p,
|
|
245
|
+
ctypes.c_char_p,
|
|
246
|
+
ctypes.c_char_p,
|
|
247
|
+
ctypes.c_bool,
|
|
248
|
+
ctypes.c_float,
|
|
249
|
+
ctypes.c_void_p,
|
|
250
|
+
)
|
|
251
|
+
self._lib.dial.restype = ctypes.c_void_p
|
|
252
|
+
|
|
253
|
+
self._lib.free_rust_runtime.argtypes = (ctypes.c_void_p,)
|
|
254
|
+
self._lib.free_rust_runtime.restype = None
|
|
255
|
+
|
|
256
|
+
self._lib.free_string.argtypes = (ctypes.c_void_p,)
|
|
257
|
+
self._lib.free_string.restype = None
|
|
258
|
+
|
|
259
|
+
self._ptr = self._lib.init_rust_runtime()
|
|
260
|
+
|
|
261
|
+
async def dial(self, address: str, options: DialOptions) -> Tuple[Optional[str], ctypes.c_void_p]:
|
|
262
|
+
type = options.credentials.type if options.credentials else ""
|
|
263
|
+
payload = options.credentials.payload if options.credentials else ""
|
|
264
|
+
insecure = (
|
|
265
|
+
options.insecure
|
|
266
|
+
or options.allow_insecure_with_creds_downgrade
|
|
267
|
+
or (not type and not payload and options.allow_insecure_downgrade)
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
LOGGER.debug(f"Dialing {address} using viam-rust-utils library")
|
|
271
|
+
path_ptr = await to_thread(
|
|
272
|
+
self._lib.dial,
|
|
273
|
+
address.encode("utf-8"),
|
|
274
|
+
options.auth_entity.encode("utf-8") if options.auth_entity else None,
|
|
275
|
+
type.encode("utf-8") if type else None,
|
|
276
|
+
payload.encode("utf-8") if payload else None,
|
|
277
|
+
insecure,
|
|
278
|
+
ctypes.c_float(options.timeout),
|
|
279
|
+
self._ptr,
|
|
280
|
+
)
|
|
281
|
+
path = ctypes.cast(path_ptr, ctypes.c_char_p).value
|
|
282
|
+
path = path.decode("utf-8") if path else ""
|
|
283
|
+
return (path, path_ptr)
|
|
284
|
+
|
|
285
|
+
def release(self):
|
|
286
|
+
LOGGER.debug("Freeing viam-rust-utils runtime")
|
|
287
|
+
self._lib.free_rust_runtime(self._ptr)
|
|
288
|
+
|
|
289
|
+
def free_str(self, ptr: ctypes.c_void_p):
|
|
290
|
+
LOGGER.debug("Freeing socket string")
|
|
291
|
+
self._lib.free_string(ptr)
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
async def dial(address: str, options: Optional[DialOptions] = None) -> ViamChannel:
|
|
295
|
+
options = options if options else DialOptions()
|
|
296
|
+
timeout = options.timeout
|
|
297
|
+
options.timeout = options.initial_connection_attempt_timeout
|
|
298
|
+
if options.initial_connection_attempts == 0:
|
|
299
|
+
options.initial_connection_attempts = -1
|
|
300
|
+
attempt_countdown = options.initial_connection_attempts
|
|
301
|
+
exception: Exception
|
|
302
|
+
while attempt_countdown != 0:
|
|
303
|
+
try:
|
|
304
|
+
chan = await _dial_inner(address, options)
|
|
305
|
+
options.timeout = timeout
|
|
306
|
+
return chan
|
|
307
|
+
except Exception as e:
|
|
308
|
+
exception = e
|
|
309
|
+
attempt_countdown -= 1
|
|
310
|
+
# the only way we could get here is if we failed at least once which means we've set the
|
|
311
|
+
# exception, so typechecker concerns about a possibly unbounded variable are unfounded
|
|
312
|
+
raise exception # type: ignore
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def _create_chan(path: str) -> Channel:
|
|
316
|
+
if sys.platform == "win32" or sys.platform == "cygwin":
|
|
317
|
+
# we have to use a TCP connection, so we want a host and port for our channel.
|
|
318
|
+
host, port = _host_port_from_url(path)
|
|
319
|
+
return Channel(host=host, port=port, ssl=None)
|
|
320
|
+
# we're not on windows and so can use a UDS
|
|
321
|
+
return Channel(path=path, ssl=None)
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
async def _dial_inner(address: str, options: Optional[DialOptions] = None) -> ViamChannel:
|
|
325
|
+
async def send_request(event: SendRequest):
|
|
326
|
+
event.metadata["viam-client"] = f"python;v{SDK_VERSION};v{API_VERSION}"
|
|
327
|
+
|
|
328
|
+
opts = options if options else DialOptions()
|
|
329
|
+
if opts.disable_webrtc:
|
|
330
|
+
channel = await _dial_direct(address, options)
|
|
331
|
+
listen(channel, SendRequest, send_request)
|
|
332
|
+
return ViamChannel(channel, lambda: None)
|
|
333
|
+
runtime = _Runtime()
|
|
334
|
+
path, path_ptr = await runtime.dial(address, opts)
|
|
335
|
+
if path:
|
|
336
|
+
LOGGER.info(f"Connecting to socket: {path}")
|
|
337
|
+
chan = _create_chan(path)
|
|
338
|
+
listen(chan, SendRequest, send_request)
|
|
339
|
+
|
|
340
|
+
def release():
|
|
341
|
+
runtime.free_str(path_ptr)
|
|
342
|
+
runtime.release()
|
|
343
|
+
|
|
344
|
+
channel = ViamChannel(chan, release)
|
|
345
|
+
return channel
|
|
346
|
+
|
|
347
|
+
runtime.release()
|
|
348
|
+
raise ViamError(f"Unable to establish a connection to {address}")
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
async def _dial_direct(address: str, options: Optional[DialOptions] = None) -> Channel:
|
|
352
|
+
opts = options if options else DialOptions()
|
|
353
|
+
insecure = opts.insecure
|
|
354
|
+
|
|
355
|
+
if pathlib.Path(address).is_socket():
|
|
356
|
+
return Channel(path=address)
|
|
357
|
+
|
|
358
|
+
host, port = _host_port_from_url(address)
|
|
359
|
+
if not port:
|
|
360
|
+
port = 80 if insecure else 443
|
|
361
|
+
server_hostname = host
|
|
362
|
+
|
|
363
|
+
if insecure:
|
|
364
|
+
ctx = None
|
|
365
|
+
else:
|
|
366
|
+
is_local_host = host is not None and (host.startswith("localhost") or host.startswith("0.0.0.0") or host.startswith("127."))
|
|
367
|
+
if is_local_host:
|
|
368
|
+
ctx = ssl._create_unverified_context(purpose=ssl.Purpose.SERVER_AUTH)
|
|
369
|
+
else:
|
|
370
|
+
ctx = ssl.create_default_context(purpose=ssl.Purpose.SERVER_AUTH)
|
|
371
|
+
ctx.minimum_version = ssl.TLSVersion.TLSv1_2
|
|
372
|
+
ctx.set_ciphers("ECDHE+AESGCM:ECDHE+CHACHA20:DHE+AESGCM:DHE+CHACHA20")
|
|
373
|
+
ctx.set_alpn_protocols(["h2"])
|
|
374
|
+
|
|
375
|
+
if (
|
|
376
|
+
options is not None
|
|
377
|
+
and options.auth_entity
|
|
378
|
+
and host != options.auth_entity
|
|
379
|
+
and options.credentials is not None
|
|
380
|
+
and options.credentials.type != "api-key"
|
|
381
|
+
):
|
|
382
|
+
server_hostname = options.auth_entity
|
|
383
|
+
|
|
384
|
+
# Test if downgrade is required.
|
|
385
|
+
downgrade = False
|
|
386
|
+
with socket.create_connection((host, port), timeout=opts.timeout) as sock:
|
|
387
|
+
try:
|
|
388
|
+
with ctx.wrap_socket(sock, server_hostname=server_hostname) as ssock:
|
|
389
|
+
_ = ssock.version()
|
|
390
|
+
except ssl.SSLError as e:
|
|
391
|
+
if e.reason != "WRONG_VERSION_NUMBER":
|
|
392
|
+
raise e
|
|
393
|
+
downgrade = True
|
|
394
|
+
|
|
395
|
+
if downgrade:
|
|
396
|
+
if opts.credentials:
|
|
397
|
+
if not opts.allow_insecure_with_creds_downgrade:
|
|
398
|
+
raise InsecureConnectionError(address, authenticated=True)
|
|
399
|
+
elif not opts.allow_insecure_downgrade:
|
|
400
|
+
raise InsecureConnectionError(address)
|
|
401
|
+
ctx = None
|
|
402
|
+
|
|
403
|
+
if opts.credentials:
|
|
404
|
+
channel = AuthenticatedChannel(host, port, ssl=ctx, server_hostname=server_hostname)
|
|
405
|
+
access_token = await _get_access_token(channel, address, opts)
|
|
406
|
+
metadata = {"authorization": f"Bearer {access_token}"}
|
|
407
|
+
channel._metadata = metadata
|
|
408
|
+
else:
|
|
409
|
+
channel = Channel(host, port, ssl=ctx)
|
|
410
|
+
|
|
411
|
+
return channel
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
async def dial_direct(address: str, options: Optional[DialOptions] = None) -> Channel:
|
|
415
|
+
warnings.warn("dial_direct is deprecated. Use rpc.dial.dial instead.", DeprecationWarning, stacklevel=2)
|
|
416
|
+
return await _dial_direct(address, options)
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
async def _dial_app(app_url: str) -> Channel:
|
|
420
|
+
return await _dial_direct(app_url)
|
|
Binary file
|
viam/rpc/server.py
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, Callable, List, Optional
|
|
2
|
+
|
|
3
|
+
from grpclib import GRPCError, Status
|
|
4
|
+
from grpclib._typing import IServable
|
|
5
|
+
from grpclib.const import Handler
|
|
6
|
+
from grpclib.events import RecvRequest, listen
|
|
7
|
+
from grpclib.reflection.service import ServerReflection
|
|
8
|
+
from grpclib.server import Server as GRPCServer
|
|
9
|
+
from grpclib.server import Stream
|
|
10
|
+
from grpclib.utils import graceful_exit
|
|
11
|
+
|
|
12
|
+
from viam import logging
|
|
13
|
+
from viam.errors import ViamGRPCError
|
|
14
|
+
from viam.resource.base import ResourceBase
|
|
15
|
+
from viam.resource.manager import ResourceManager
|
|
16
|
+
from viam.resource.registry import Registry
|
|
17
|
+
from viam.resource.rpc_service_base import ResourceRPCServiceBase
|
|
18
|
+
from viam.robot.service import RobotService
|
|
19
|
+
|
|
20
|
+
from .signaling import SignalingService
|
|
21
|
+
|
|
22
|
+
if TYPE_CHECKING:
|
|
23
|
+
from viam.module.service import ModuleRPCService
|
|
24
|
+
|
|
25
|
+
LOGGER = logging.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class Server(ResourceManager):
|
|
29
|
+
"""
|
|
30
|
+
gRPC Server
|
|
31
|
+
"""
|
|
32
|
+
|
|
33
|
+
def __init__(self, resources: List[ResourceBase], *, module_service: Optional["ModuleRPCService"] = None):
|
|
34
|
+
"""
|
|
35
|
+
Initialize the Server with a list of resources
|
|
36
|
+
to be managed.
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
resources (List[ComponentBase]): List of resources to be managed
|
|
40
|
+
"""
|
|
41
|
+
super().__init__(resources)
|
|
42
|
+
|
|
43
|
+
services = [SignalingService(), RobotService(manager=self)]
|
|
44
|
+
for registration in Registry.REGISTERED_APIS().values():
|
|
45
|
+
if issubclass(registration.rpc_service, ResourceRPCServiceBase):
|
|
46
|
+
services.append(registration.rpc_service(manager=self))
|
|
47
|
+
else:
|
|
48
|
+
services.append(registration.rpc_service())
|
|
49
|
+
|
|
50
|
+
if module_service is not None:
|
|
51
|
+
services.append(module_service)
|
|
52
|
+
services = ServerReflection.extend(services)
|
|
53
|
+
services = _patch_mappings(services)
|
|
54
|
+
|
|
55
|
+
self._server = GRPCServer(services)
|
|
56
|
+
|
|
57
|
+
async def _grpc_recvrequest_handler(self, event: RecvRequest):
|
|
58
|
+
host = None
|
|
59
|
+
port = None
|
|
60
|
+
address = event.peer.addr()
|
|
61
|
+
if address:
|
|
62
|
+
host = address[0]
|
|
63
|
+
port = address[1]
|
|
64
|
+
method_func = event.method_func
|
|
65
|
+
|
|
66
|
+
async def log_resource_name(stream: Stream):
|
|
67
|
+
recv_msg = stream.recv_message
|
|
68
|
+
|
|
69
|
+
async def rcv_and_log_msg():
|
|
70
|
+
msg = await recv_msg()
|
|
71
|
+
log_msg = f"[gRPC] Received message from {host or 'xxxx'}:{port or 'xxxx'} - {event.method_name}"
|
|
72
|
+
if msg and hasattr(msg, "name"):
|
|
73
|
+
log_msg += f" for resource named: {msg.name}"
|
|
74
|
+
LOGGER.debug(log_msg)
|
|
75
|
+
return msg
|
|
76
|
+
|
|
77
|
+
stream.recv_message = rcv_and_log_msg
|
|
78
|
+
try:
|
|
79
|
+
return await method_func(stream)
|
|
80
|
+
finally:
|
|
81
|
+
LOGGER.debug(f"[gRPC] Finished call from {host or 'xxxx'}:{port or 'xxxx'} - {event.method_name}")
|
|
82
|
+
|
|
83
|
+
event.method_func = log_resource_name
|
|
84
|
+
|
|
85
|
+
async def serve(
|
|
86
|
+
self,
|
|
87
|
+
host: Optional[str] = "localhost",
|
|
88
|
+
port: Optional[int] = 9090,
|
|
89
|
+
log_level: Optional[int] = logging.INFO,
|
|
90
|
+
*,
|
|
91
|
+
path: Optional[str] = None,
|
|
92
|
+
):
|
|
93
|
+
"""
|
|
94
|
+
Server the gRPC server on the provided host and port
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
host (Optional[str], optional): Desired hostname of the server. Defaults to "localhost".
|
|
98
|
+
port (Optional[int], optional): Desired port of the server. Defaults to 9090.
|
|
99
|
+
log_level (Optional[int], optional): The minimum log level. To not receive any logs, set to None. Defaults to logging.INFO.
|
|
100
|
+
path (Optional[str], optional): UNIX socket path. Takes precedence over `host` and `port` if set. Defaults to None.
|
|
101
|
+
"""
|
|
102
|
+
if log_level is None:
|
|
103
|
+
logging.silence()
|
|
104
|
+
else:
|
|
105
|
+
logging.setLevel(log_level)
|
|
106
|
+
listen(self._server, RecvRequest, self._grpc_recvrequest_handler)
|
|
107
|
+
|
|
108
|
+
with graceful_exit([self._server]):
|
|
109
|
+
if path:
|
|
110
|
+
await self._server.start(path=path)
|
|
111
|
+
LOGGER.info(f"Serving on {path}")
|
|
112
|
+
else:
|
|
113
|
+
await self._server.start(host, port)
|
|
114
|
+
LOGGER.info(f"Serving on {host}:{port}")
|
|
115
|
+
await self._server.wait_closed()
|
|
116
|
+
await self.close()
|
|
117
|
+
LOGGER.debug("gRPC server closed")
|
|
118
|
+
|
|
119
|
+
@classmethod
|
|
120
|
+
async def create_and_serve(
|
|
121
|
+
cls,
|
|
122
|
+
components: List[ResourceBase],
|
|
123
|
+
host: Optional[str] = "localhost",
|
|
124
|
+
port: Optional[int] = 9090,
|
|
125
|
+
log_level: int = logging.INFO,
|
|
126
|
+
*,
|
|
127
|
+
path: Optional[str] = None,
|
|
128
|
+
):
|
|
129
|
+
"""
|
|
130
|
+
Convenience method to create and start the server.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
components (List[ComponentBase]): List of components to manage
|
|
134
|
+
host (str, optional): Desired hostname. Defaults to "localhost".
|
|
135
|
+
port (int, optional): Desired port. Defaults to 9090.
|
|
136
|
+
log_level(int, optional): The minimum log level.
|
|
137
|
+
To not receive any logs, set to None.
|
|
138
|
+
Defaults to logging.INFO
|
|
139
|
+
path (Optional[str], optional): UNIX socket path. Takes precedence over `host` and `port` if set. Defaults to None.
|
|
140
|
+
"""
|
|
141
|
+
server = cls(components)
|
|
142
|
+
await server.serve(host, port, log_level, path=path)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _grpc_error_wrapper(func: Callable):
|
|
146
|
+
"""
|
|
147
|
+
Wrap a function so that any exceptions get raised as GRPCErrors to the client.
|
|
148
|
+
|
|
149
|
+
Args:
|
|
150
|
+
func (Callable): The function that should be wrapped
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
The method that is now wrapped to raise GRPCErrors
|
|
154
|
+
"""
|
|
155
|
+
|
|
156
|
+
async def interceptor(*args, **kwargs):
|
|
157
|
+
try:
|
|
158
|
+
new_func = await func(*args, **kwargs)
|
|
159
|
+
return new_func
|
|
160
|
+
except GRPCError:
|
|
161
|
+
raise
|
|
162
|
+
except ViamGRPCError as e:
|
|
163
|
+
raise e.grpc_error
|
|
164
|
+
except Exception as e:
|
|
165
|
+
tb = e.__traceback__
|
|
166
|
+
file_name = None
|
|
167
|
+
func_name = None
|
|
168
|
+
line_num = None
|
|
169
|
+
# only print the last entry in the stacktrace - not perfect but gives users a starting point
|
|
170
|
+
while tb is not None:
|
|
171
|
+
file_name = tb.tb_frame.f_code.co_filename
|
|
172
|
+
func_name = tb.tb_frame.f_code.co_name
|
|
173
|
+
line_num = tb.tb_lineno
|
|
174
|
+
tb = tb.tb_next
|
|
175
|
+
raise GRPCError(Status.UNKNOWN, f"{e.__class__.__name__} - {e} - {file_name=} {func_name=} {line_num=}")
|
|
176
|
+
|
|
177
|
+
return interceptor
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def _patch_mappings(services: List[IServable]) -> List[IServable]:
|
|
181
|
+
"""Replace the methods of all given services with a wrapped method that has error handling
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
services (List[IServable]): The services that should be patched
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
services (List[IServable]): The patched services with new mapping functions
|
|
188
|
+
"""
|
|
189
|
+
for service in services:
|
|
190
|
+
|
|
191
|
+
def patch_mapping():
|
|
192
|
+
mapping = service.__mapping__()
|
|
193
|
+
new_mapping = {}
|
|
194
|
+
for method, handler in mapping.items():
|
|
195
|
+
new_method = _grpc_error_wrapper(handler[0])
|
|
196
|
+
new_mapping[method] = Handler(new_method, *handler[1:])
|
|
197
|
+
|
|
198
|
+
return lambda: new_mapping
|
|
199
|
+
|
|
200
|
+
service.__mapping__ = patch_mapping()
|
|
201
|
+
return services
|