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.

Files changed (476) hide show
  1. viam/__init__.py +71 -0
  2. viam/app/__init__.py +0 -0
  3. viam/app/_logs.py +34 -0
  4. viam/app/app_client.py +2525 -0
  5. viam/app/billing_client.py +143 -0
  6. viam/app/data_client.py +1715 -0
  7. viam/app/ml_training_client.py +251 -0
  8. viam/app/provisioning_client.py +95 -0
  9. viam/app/viam_client.py +260 -0
  10. viam/components/__init__.py +0 -0
  11. viam/components/arm/__init__.py +16 -0
  12. viam/components/arm/arm.py +223 -0
  13. viam/components/arm/client.py +124 -0
  14. viam/components/arm/service.py +123 -0
  15. viam/components/audio_input/__init__.py +18 -0
  16. viam/components/audio_input/audio_input.py +81 -0
  17. viam/components/audio_input/client.py +70 -0
  18. viam/components/audio_input/service.py +114 -0
  19. viam/components/base/__init__.py +13 -0
  20. viam/components/base/base.py +260 -0
  21. viam/components/base/client.py +153 -0
  22. viam/components/base/service.py +138 -0
  23. viam/components/board/__init__.py +9 -0
  24. viam/components/board/board.py +414 -0
  25. viam/components/board/client.py +241 -0
  26. viam/components/board/service.py +223 -0
  27. viam/components/button/__init__.py +10 -0
  28. viam/components/button/button.py +41 -0
  29. viam/components/button/client.py +52 -0
  30. viam/components/button/service.py +46 -0
  31. viam/components/camera/__init__.py +22 -0
  32. viam/components/camera/camera.py +138 -0
  33. viam/components/camera/client.py +98 -0
  34. viam/components/camera/service.py +105 -0
  35. viam/components/component_base.py +65 -0
  36. viam/components/encoder/__init__.py +18 -0
  37. viam/components/encoder/client.py +83 -0
  38. viam/components/encoder/encoder.py +118 -0
  39. viam/components/encoder/service.py +72 -0
  40. viam/components/gantry/__init__.py +11 -0
  41. viam/components/gantry/client.py +115 -0
  42. viam/components/gantry/gantry.py +156 -0
  43. viam/components/gantry/service.py +113 -0
  44. viam/components/generic/__init__.py +18 -0
  45. viam/components/generic/client.py +62 -0
  46. viam/components/generic/generic.py +76 -0
  47. viam/components/generic/service.py +40 -0
  48. viam/components/gripper/__init__.py +11 -0
  49. viam/components/gripper/client.py +85 -0
  50. viam/components/gripper/gripper.py +114 -0
  51. viam/components/gripper/service.py +81 -0
  52. viam/components/input/__init__.py +15 -0
  53. viam/components/input/client.py +194 -0
  54. viam/components/input/input.py +297 -0
  55. viam/components/input/service.py +175 -0
  56. viam/components/motor/__init__.py +11 -0
  57. viam/components/motor/client.py +168 -0
  58. viam/components/motor/motor.py +301 -0
  59. viam/components/motor/service.py +150 -0
  60. viam/components/movement_sensor/__init__.py +21 -0
  61. viam/components/movement_sensor/client.py +161 -0
  62. viam/components/movement_sensor/movement_sensor.py +253 -0
  63. viam/components/movement_sensor/service.py +146 -0
  64. viam/components/pose_tracker/__init__.py +17 -0
  65. viam/components/pose_tracker/client.py +50 -0
  66. viam/components/pose_tracker/pose_tracker.py +40 -0
  67. viam/components/pose_tracker/service.py +45 -0
  68. viam/components/power_sensor/__init__.py +17 -0
  69. viam/components/power_sensor/client.py +86 -0
  70. viam/components/power_sensor/power_sensor.py +104 -0
  71. viam/components/power_sensor/service.py +72 -0
  72. viam/components/sensor/__init__.py +18 -0
  73. viam/components/sensor/client.py +49 -0
  74. viam/components/sensor/sensor.py +48 -0
  75. viam/components/sensor/service.py +51 -0
  76. viam/components/servo/__init__.py +11 -0
  77. viam/components/servo/client.py +86 -0
  78. viam/components/servo/service.py +80 -0
  79. viam/components/servo/servo.py +114 -0
  80. viam/components/switch/__init__.py +10 -0
  81. viam/components/switch/client.py +83 -0
  82. viam/components/switch/service.py +72 -0
  83. viam/components/switch/switch.py +95 -0
  84. viam/errors.py +105 -0
  85. viam/gen/__init__.py +0 -0
  86. viam/gen/app/__init__.py +0 -0
  87. viam/gen/app/agent/__init__.py +0 -0
  88. viam/gen/app/agent/v1/__init__.py +0 -0
  89. viam/gen/app/agent/v1/agent_grpc.py +29 -0
  90. viam/gen/app/agent/v1/agent_pb2.py +47 -0
  91. viam/gen/app/agent/v1/agent_pb2.pyi +280 -0
  92. viam/gen/app/cloudslam/__init__.py +0 -0
  93. viam/gen/app/cloudslam/v1/__init__.py +0 -0
  94. viam/gen/app/cloudslam/v1/cloud_slam_grpc.py +70 -0
  95. viam/gen/app/cloudslam/v1/cloud_slam_pb2.py +54 -0
  96. viam/gen/app/cloudslam/v1/cloud_slam_pb2.pyi +384 -0
  97. viam/gen/app/data/__init__.py +0 -0
  98. viam/gen/app/data/v1/__init__.py +0 -0
  99. viam/gen/app/data/v1/data_grpc.py +206 -0
  100. viam/gen/app/data/v1/data_pb2.py +178 -0
  101. viam/gen/app/data/v1/data_pb2.pyi +1485 -0
  102. viam/gen/app/datapipelines/__init__.py +0 -0
  103. viam/gen/app/datapipelines/v1/__init__.py +0 -0
  104. viam/gen/app/datapipelines/v1/data_pipelines_grpc.py +84 -0
  105. viam/gen/app/datapipelines/v1/data_pipelines_pb2.py +56 -0
  106. viam/gen/app/datapipelines/v1/data_pipelines_pb2.pyi +370 -0
  107. viam/gen/app/dataset/__init__.py +0 -0
  108. viam/gen/app/dataset/v1/__init__.py +0 -0
  109. viam/gen/app/dataset/v1/dataset_grpc.py +60 -0
  110. viam/gen/app/dataset/v1/dataset_pb2.py +40 -0
  111. viam/gen/app/dataset/v1/dataset_pb2.pyi +179 -0
  112. viam/gen/app/datasync/__init__.py +0 -0
  113. viam/gen/app/datasync/v1/__init__.py +0 -0
  114. viam/gen/app/datasync/v1/data_sync_grpc.py +47 -0
  115. viam/gen/app/datasync/v1/data_sync_pb2.py +70 -0
  116. viam/gen/app/datasync/v1/data_sync_pb2.pyi +425 -0
  117. viam/gen/app/mlinference/__init__.py +0 -0
  118. viam/gen/app/mlinference/v1/__init__.py +0 -0
  119. viam/gen/app/mlinference/v1/ml_inference_grpc.py +28 -0
  120. viam/gen/app/mlinference/v1/ml_inference_pb2.py +23 -0
  121. viam/gen/app/mlinference/v1/ml_inference_pb2.pyi +63 -0
  122. viam/gen/app/mltraining/__init__.py +0 -0
  123. viam/gen/app/mltraining/v1/__init__.py +0 -0
  124. viam/gen/app/mltraining/v1/ml_training_grpc.py +78 -0
  125. viam/gen/app/mltraining/v1/ml_training_pb2.py +124 -0
  126. viam/gen/app/mltraining/v1/ml_training_pb2.pyi +415 -0
  127. viam/gen/app/packages/__init__.py +0 -0
  128. viam/gen/app/packages/v1/__init__.py +0 -0
  129. viam/gen/app/packages/v1/packages_grpc.py +54 -0
  130. viam/gen/app/packages/v1/packages_pb2.py +52 -0
  131. viam/gen/app/packages/v1/packages_pb2.pyi +311 -0
  132. viam/gen/app/v1/__init__.py +0 -0
  133. viam/gen/app/v1/app_grpc.py +863 -0
  134. viam/gen/app/v1/app_pb2.py +649 -0
  135. viam/gen/app/v1/app_pb2.pyi +5279 -0
  136. viam/gen/app/v1/billing_grpc.py +76 -0
  137. viam/gen/app/v1/billing_pb2.py +92 -0
  138. viam/gen/app/v1/billing_pb2.pyi +463 -0
  139. viam/gen/app/v1/end_user_grpc.py +59 -0
  140. viam/gen/app/v1/end_user_pb2.py +55 -0
  141. viam/gen/app/v1/end_user_pb2.pyi +181 -0
  142. viam/gen/app/v1/robot_grpc.py +55 -0
  143. viam/gen/app/v1/robot_pb2.py +127 -0
  144. viam/gen/app/v1/robot_pb2.pyi +1202 -0
  145. viam/gen/common/__init__.py +0 -0
  146. viam/gen/common/v1/__init__.py +0 -0
  147. viam/gen/common/v1/common_grpc.py +0 -0
  148. viam/gen/common/v1/common_pb2.py +78 -0
  149. viam/gen/common/v1/common_pb2.pyi +687 -0
  150. viam/gen/component/__init__.py +0 -0
  151. viam/gen/component/arm/__init__.py +0 -0
  152. viam/gen/component/arm/v1/__init__.py +0 -0
  153. viam/gen/component/arm/v1/arm_grpc.py +102 -0
  154. viam/gen/component/arm/v1/arm_pb2.py +74 -0
  155. viam/gen/component/arm/v1/arm_pb2.pyi +344 -0
  156. viam/gen/component/audioinput/__init__.py +0 -0
  157. viam/gen/component/audioinput/v1/__init__.py +0 -0
  158. viam/gen/component/audioinput/v1/audioinput_grpc.py +63 -0
  159. viam/gen/component/audioinput/v1/audioinput_pb2.py +45 -0
  160. viam/gen/component/audioinput/v1/audioinput_pb2.pyi +179 -0
  161. viam/gen/component/base/__init__.py +0 -0
  162. viam/gen/component/base/v1/__init__.py +0 -0
  163. viam/gen/component/base/v1/base_grpc.py +94 -0
  164. viam/gen/component/base/v1/base_pb2.py +66 -0
  165. viam/gen/component/base/v1/base_pb2.pyi +258 -0
  166. viam/gen/component/board/__init__.py +0 -0
  167. viam/gen/component/board/v1/__init__.py +0 -0
  168. viam/gen/component/board/v1/board_grpc.py +127 -0
  169. viam/gen/component/board/v1/board_pb2.py +103 -0
  170. viam/gen/component/board/v1/board_pb2.pyi +496 -0
  171. viam/gen/component/button/__init__.py +0 -0
  172. viam/gen/component/button/v1/__init__.py +0 -0
  173. viam/gen/component/button/v1/button_grpc.py +38 -0
  174. viam/gen/component/button/v1/button_pb2.py +28 -0
  175. viam/gen/component/button/v1/button_pb2.pyi +39 -0
  176. viam/gen/component/camera/__init__.py +0 -0
  177. viam/gen/component/camera/v1/__init__.py +0 -0
  178. viam/gen/component/camera/v1/camera_grpc.py +79 -0
  179. viam/gen/component/camera/v1/camera_pb2.py +67 -0
  180. viam/gen/component/camera/v1/camera_pb2.pyi +373 -0
  181. viam/gen/component/encoder/__init__.py +0 -0
  182. viam/gen/component/encoder/v1/__init__.py +0 -0
  183. viam/gen/component/encoder/v1/encoder_grpc.py +62 -0
  184. viam/gen/component/encoder/v1/encoder_pb2.py +44 -0
  185. viam/gen/component/encoder/v1/encoder_pb2.pyi +147 -0
  186. viam/gen/component/gantry/__init__.py +0 -0
  187. viam/gen/component/gantry/v1/__init__.py +0 -0
  188. viam/gen/component/gantry/v1/gantry_grpc.py +86 -0
  189. viam/gen/component/gantry/v1/gantry_pb2.py +62 -0
  190. viam/gen/component/gantry/v1/gantry_pb2.pyi +239 -0
  191. viam/gen/component/generic/__init__.py +0 -0
  192. viam/gen/component/generic/v1/__init__.py +0 -0
  193. viam/gen/component/generic/v1/generic_grpc.py +37 -0
  194. viam/gen/component/generic/v1/generic_pb2.py +23 -0
  195. viam/gen/component/generic/v1/generic_pb2.pyi +6 -0
  196. viam/gen/component/gripper/__init__.py +0 -0
  197. viam/gen/component/gripper/v1/__init__.py +0 -0
  198. viam/gen/component/gripper/v1/gripper_grpc.py +70 -0
  199. viam/gen/component/gripper/v1/gripper_pb2.py +48 -0
  200. viam/gen/component/gripper/v1/gripper_pb2.pyi +137 -0
  201. viam/gen/component/inputcontroller/__init__.py +0 -0
  202. viam/gen/component/inputcontroller/v1/__init__.py +0 -0
  203. viam/gen/component/inputcontroller/v1/input_controller_grpc.py +71 -0
  204. viam/gen/component/inputcontroller/v1/input_controller_pb2.py +55 -0
  205. viam/gen/component/inputcontroller/v1/input_controller_pb2.pyi +243 -0
  206. viam/gen/component/motor/__init__.py +0 -0
  207. viam/gen/component/motor/v1/__init__.py +0 -0
  208. viam/gen/component/motor/v1/motor_grpc.py +118 -0
  209. viam/gen/component/motor/v1/motor_pb2.py +86 -0
  210. viam/gen/component/motor/v1/motor_pb2.pyi +368 -0
  211. viam/gen/component/movementsensor/__init__.py +0 -0
  212. viam/gen/component/movementsensor/v1/__init__.py +0 -0
  213. viam/gen/component/movementsensor/v1/movementsensor_grpc.py +110 -0
  214. viam/gen/component/movementsensor/v1/movementsensor_pb2.py +78 -0
  215. viam/gen/component/movementsensor/v1/movementsensor_pb2.pyi +384 -0
  216. viam/gen/component/posetracker/__init__.py +0 -0
  217. viam/gen/component/posetracker/v1/__init__.py +0 -0
  218. viam/gen/component/posetracker/v1/pose_tracker_grpc.py +46 -0
  219. viam/gen/component/posetracker/v1/pose_tracker_pb2.py +34 -0
  220. viam/gen/component/posetracker/v1/pose_tracker_pb2.pyi +79 -0
  221. viam/gen/component/powersensor/__init__.py +0 -0
  222. viam/gen/component/powersensor/v1/__init__.py +0 -0
  223. viam/gen/component/powersensor/v1/powersensor_grpc.py +62 -0
  224. viam/gen/component/powersensor/v1/powersensor_pb2.py +42 -0
  225. viam/gen/component/powersensor/v1/powersensor_pb2.pyi +124 -0
  226. viam/gen/component/sensor/__init__.py +0 -0
  227. viam/gen/component/sensor/v1/__init__.py +0 -0
  228. viam/gen/component/sensor/v1/sensor_grpc.py +45 -0
  229. viam/gen/component/sensor/v1/sensor_pb2.py +25 -0
  230. viam/gen/component/sensor/v1/sensor_pb2.pyi +6 -0
  231. viam/gen/component/servo/__init__.py +0 -0
  232. viam/gen/component/servo/v1/__init__.py +0 -0
  233. viam/gen/component/servo/v1/servo_grpc.py +70 -0
  234. viam/gen/component/servo/v1/servo_pb2.py +50 -0
  235. viam/gen/component/servo/v1/servo_pb2.pyi +150 -0
  236. viam/gen/component/switch/__init__.py +0 -0
  237. viam/gen/component/switch/v1/__init__.py +0 -0
  238. viam/gen/component/switch/v1/switch_grpc.py +54 -0
  239. viam/gen/component/switch/v1/switch_pb2.py +40 -0
  240. viam/gen/component/switch/v1/switch_pb2.pyi +109 -0
  241. viam/gen/component/testecho/__init__.py +0 -0
  242. viam/gen/component/testecho/v1/__init__.py +0 -0
  243. viam/gen/component/testecho/v1/testecho_grpc.py +52 -0
  244. viam/gen/component/testecho/v1/testecho_pb2.py +36 -0
  245. viam/gen/component/testecho/v1/testecho_pb2.pyi +114 -0
  246. viam/gen/module/__init__.py +0 -0
  247. viam/gen/module/v1/__init__.py +0 -0
  248. viam/gen/module/v1/module_grpc.py +61 -0
  249. viam/gen/module/v1/module_pb2.py +43 -0
  250. viam/gen/module/v1/module_pb2.pyi +211 -0
  251. viam/gen/proto/__init__.py +0 -0
  252. viam/gen/proto/rpc/__init__.py +0 -0
  253. viam/gen/proto/rpc/examples/__init__.py +0 -0
  254. viam/gen/proto/rpc/examples/echo/__init__.py +0 -0
  255. viam/gen/proto/rpc/examples/echo/v1/__init__.py +0 -0
  256. viam/gen/proto/rpc/examples/echo/v1/echo_grpc.py +44 -0
  257. viam/gen/proto/rpc/examples/echo/v1/echo_pb2.py +32 -0
  258. viam/gen/proto/rpc/examples/echo/v1/echo_pb2.pyi +87 -0
  259. viam/gen/proto/rpc/examples/echoresource/__init__.py +0 -0
  260. viam/gen/proto/rpc/examples/echoresource/v1/__init__.py +0 -0
  261. viam/gen/proto/rpc/examples/echoresource/v1/echoresource_grpc.py +43 -0
  262. viam/gen/proto/rpc/examples/echoresource/v1/echoresource_pb2.py +29 -0
  263. viam/gen/proto/rpc/examples/echoresource/v1/echoresource_pb2.pyi +93 -0
  264. viam/gen/proto/rpc/v1/__init__.py +0 -0
  265. viam/gen/proto/rpc/v1/auth_grpc.py +47 -0
  266. viam/gen/proto/rpc/v1/auth_pb2.py +34 -0
  267. viam/gen/proto/rpc/v1/auth_pb2.pyi +92 -0
  268. viam/gen/proto/rpc/webrtc/__init__.py +0 -0
  269. viam/gen/proto/rpc/webrtc/v1/__init__.py +0 -0
  270. viam/gen/proto/rpc/webrtc/v1/grpc_grpc.py +0 -0
  271. viam/gen/proto/rpc/webrtc/v1/grpc_pb2.py +43 -0
  272. viam/gen/proto/rpc/webrtc/v1/grpc_pb2.pyi +304 -0
  273. viam/gen/proto/rpc/webrtc/v1/signaling_grpc.py +54 -0
  274. viam/gen/proto/rpc/webrtc/v1/signaling_pb2.py +70 -0
  275. viam/gen/proto/rpc/webrtc/v1/signaling_pb2.pyi +496 -0
  276. viam/gen/provisioning/__init__.py +0 -0
  277. viam/gen/provisioning/v1/__init__.py +0 -0
  278. viam/gen/provisioning/v1/provisioning_grpc.py +51 -0
  279. viam/gen/provisioning/v1/provisioning_pb2.py +39 -0
  280. viam/gen/provisioning/v1/provisioning_pb2.pyi +188 -0
  281. viam/gen/robot/__init__.py +0 -0
  282. viam/gen/robot/v1/__init__.py +0 -0
  283. viam/gen/robot/v1/robot_grpc.py +208 -0
  284. viam/gen/robot/v1/robot_pb2.py +188 -0
  285. viam/gen/robot/v1/robot_pb2.pyi +1020 -0
  286. viam/gen/service/__init__.py +0 -0
  287. viam/gen/service/datamanager/__init__.py +0 -0
  288. viam/gen/service/datamanager/v1/__init__.py +0 -0
  289. viam/gen/service/datamanager/v1/data_manager_grpc.py +38 -0
  290. viam/gen/service/datamanager/v1/data_manager_pb2.py +28 -0
  291. viam/gen/service/datamanager/v1/data_manager_pb2.pyi +39 -0
  292. viam/gen/service/discovery/__init__.py +0 -0
  293. viam/gen/service/discovery/v1/__init__.py +0 -0
  294. viam/gen/service/discovery/v1/discovery_grpc.py +39 -0
  295. viam/gen/service/discovery/v1/discovery_pb2.py +29 -0
  296. viam/gen/service/discovery/v1/discovery_pb2.pyi +51 -0
  297. viam/gen/service/generic/__init__.py +0 -0
  298. viam/gen/service/generic/v1/__init__.py +0 -0
  299. viam/gen/service/generic/v1/generic_grpc.py +29 -0
  300. viam/gen/service/generic/v1/generic_pb2.py +21 -0
  301. viam/gen/service/generic/v1/generic_pb2.pyi +6 -0
  302. viam/gen/service/mlmodel/__init__.py +0 -0
  303. viam/gen/service/mlmodel/v1/__init__.py +0 -0
  304. viam/gen/service/mlmodel/v1/mlmodel_grpc.py +37 -0
  305. viam/gen/service/mlmodel/v1/mlmodel_pb2.py +83 -0
  306. viam/gen/service/mlmodel/v1/mlmodel_pb2.pyi +480 -0
  307. viam/gen/service/motion/__init__.py +0 -0
  308. viam/gen/service/motion/v1/__init__.py +0 -0
  309. viam/gen/service/motion/v1/motion_grpc.py +87 -0
  310. viam/gen/service/motion/v1/motion_pb2.py +97 -0
  311. viam/gen/service/motion/v1/motion_pb2.pyi +838 -0
  312. viam/gen/service/navigation/__init__.py +0 -0
  313. viam/gen/service/navigation/v1/__init__.py +0 -0
  314. viam/gen/service/navigation/v1/navigation_grpc.py +102 -0
  315. viam/gen/service/navigation/v1/navigation_pb2.py +84 -0
  316. viam/gen/service/navigation/v1/navigation_pb2.pyi +419 -0
  317. viam/gen/service/sensors/__init__.py +0 -0
  318. viam/gen/service/sensors/v1/__init__.py +0 -0
  319. viam/gen/service/sensors/v1/sensors_grpc.py +46 -0
  320. viam/gen/service/sensors/v1/sensors_pb2.py +68 -0
  321. viam/gen/service/sensors/v1/sensors_pb2.pyi +137 -0
  322. viam/gen/service/shell/__init__.py +0 -0
  323. viam/gen/service/shell/v1/__init__.py +0 -0
  324. viam/gen/service/shell/v1/shell_grpc.py +55 -0
  325. viam/gen/service/shell/v1/shell_pb2.py +45 -0
  326. viam/gen/service/shell/v1/shell_pb2.pyi +307 -0
  327. viam/gen/service/slam/__init__.py +0 -0
  328. viam/gen/service/slam/v1/__init__.py +0 -0
  329. viam/gen/service/slam/v1/slam_grpc.py +61 -0
  330. viam/gen/service/slam/v1/slam_pb2.py +51 -0
  331. viam/gen/service/slam/v1/slam_pb2.pyi +213 -0
  332. viam/gen/service/vision/__init__.py +0 -0
  333. viam/gen/service/vision/v1/__init__.py +0 -0
  334. viam/gen/service/vision/v1/vision_grpc.py +87 -0
  335. viam/gen/service/vision/v1/vision_pb2.py +69 -0
  336. viam/gen/service/vision/v1/vision_pb2.pyi +454 -0
  337. viam/gen/stream/__init__.py +0 -0
  338. viam/gen/stream/v1/__init__.py +0 -0
  339. viam/gen/stream/v1/stream_grpc.py +59 -0
  340. viam/gen/stream/v1/stream_pb2.py +39 -0
  341. viam/gen/stream/v1/stream_pb2.pyi +161 -0
  342. viam/gen/tagger/__init__.py +0 -0
  343. viam/gen/tagger/v1/__init__.py +0 -0
  344. viam/gen/tagger/v1/tagger_grpc.py +0 -0
  345. viam/gen/tagger/v1/tagger_pb2.py +16 -0
  346. viam/gen/tagger/v1/tagger_pb2.pyi +15 -0
  347. viam/logging.py +216 -0
  348. viam/media/__init__.py +0 -0
  349. viam/media/audio.py +16 -0
  350. viam/media/utils/__init__.py +0 -0
  351. viam/media/utils/pil/__init__.py +51 -0
  352. viam/media/utils/pil/viam_rgba_plugin.py +73 -0
  353. viam/media/viam_rgba.py +10 -0
  354. viam/media/video.py +217 -0
  355. viam/module/__init__.py +5 -0
  356. viam/module/module.py +281 -0
  357. viam/module/service.py +66 -0
  358. viam/module/types.py +23 -0
  359. viam/operations.py +124 -0
  360. viam/proto/__init__.py +0 -0
  361. viam/proto/app/__init__.py +554 -0
  362. viam/proto/app/agent/__init__.py +28 -0
  363. viam/proto/app/billing.py +58 -0
  364. viam/proto/app/cloudslam/__init__.py +48 -0
  365. viam/proto/app/data/__init__.py +138 -0
  366. viam/proto/app/datapipelines/__init__.py +56 -0
  367. viam/proto/app/dataset/__init__.py +36 -0
  368. viam/proto/app/datasync/__init__.py +44 -0
  369. viam/proto/app/end_user.py +34 -0
  370. viam/proto/app/mlinference/__init__.py +15 -0
  371. viam/proto/app/mltraining/__init__.py +52 -0
  372. viam/proto/app/packages/__init__.py +38 -0
  373. viam/proto/app/robot.py +84 -0
  374. viam/proto/common/__init__.py +66 -0
  375. viam/proto/component/__init__.py +0 -0
  376. viam/proto/component/arm/__init__.py +48 -0
  377. viam/proto/component/audioinput/__init__.py +30 -0
  378. viam/proto/component/base/__init__.py +42 -0
  379. viam/proto/component/board/__init__.py +62 -0
  380. viam/proto/component/button/__init__.py +15 -0
  381. viam/proto/component/camera/__init__.py +46 -0
  382. viam/proto/component/encoder/__init__.py +28 -0
  383. viam/proto/component/gantry/__init__.py +40 -0
  384. viam/proto/component/generic/__init__.py +12 -0
  385. viam/proto/component/gripper/__init__.py +30 -0
  386. viam/proto/component/inputcontroller/__init__.py +38 -0
  387. viam/proto/component/motor/__init__.py +56 -0
  388. viam/proto/component/movementsensor/__init__.py +50 -0
  389. viam/proto/component/posetracker/__init__.py +19 -0
  390. viam/proto/component/powersensor/__init__.py +30 -0
  391. viam/proto/component/sensor/__init__.py +12 -0
  392. viam/proto/component/servo/__init__.py +32 -0
  393. viam/proto/component/switch/__init__.py +26 -0
  394. viam/proto/component/testecho/__init__.py +30 -0
  395. viam/proto/module/__init__.py +38 -0
  396. viam/proto/provisioning/__init__.py +36 -0
  397. viam/proto/robot/__init__.py +130 -0
  398. viam/proto/rpc/__init__.py +0 -0
  399. viam/proto/rpc/auth.py +34 -0
  400. viam/proto/rpc/examples/__init__.py +0 -0
  401. viam/proto/rpc/examples/echo/__init__.py +26 -0
  402. viam/proto/rpc/examples/echoresource/__init__.py +30 -0
  403. viam/proto/rpc/webrtc/__init__.py +0 -0
  404. viam/proto/rpc/webrtc/grpc.py +36 -0
  405. viam/proto/rpc/webrtc/signaling.py +58 -0
  406. viam/proto/service/__init__.py +0 -0
  407. viam/proto/service/datamanager/__init__.py +19 -0
  408. viam/proto/service/discovery/__init__.py +15 -0
  409. viam/proto/service/generic/__init__.py +12 -0
  410. viam/proto/service/mlmodel/__init__.py +54 -0
  411. viam/proto/service/motion/__init__.py +68 -0
  412. viam/proto/service/navigation/__init__.py +58 -0
  413. viam/proto/service/sensors/__init__.py +18 -0
  414. viam/proto/service/shell/__init__.py +36 -0
  415. viam/proto/service/slam/__init__.py +36 -0
  416. viam/proto/service/vision/__init__.py +46 -0
  417. viam/proto/stream/__init__.py +36 -0
  418. viam/proto/tagger/__init__.py +6 -0
  419. viam/py.typed +0 -0
  420. viam/resource/__init__.py +0 -0
  421. viam/resource/base.py +123 -0
  422. viam/resource/easy_resource.py +153 -0
  423. viam/resource/manager.py +126 -0
  424. viam/resource/registry.py +199 -0
  425. viam/resource/rpc_client_base.py +65 -0
  426. viam/resource/rpc_service_base.py +48 -0
  427. viam/resource/types.py +213 -0
  428. viam/robot/__init__.py +0 -0
  429. viam/robot/client.py +909 -0
  430. viam/robot/service.py +69 -0
  431. viam/rpc/__init__.py +0 -0
  432. viam/rpc/dial.py +420 -0
  433. viam/rpc/libviam_rust_utils.dll +0 -0
  434. viam/rpc/server.py +201 -0
  435. viam/rpc/signaling.py +29 -0
  436. viam/rpc/types.py +22 -0
  437. viam/services/__init__.py +0 -0
  438. viam/services/discovery/__init__.py +12 -0
  439. viam/services/discovery/client.py +55 -0
  440. viam/services/discovery/discovery.py +52 -0
  441. viam/services/discovery/service.py +43 -0
  442. viam/services/generic/__init__.py +18 -0
  443. viam/services/generic/client.py +58 -0
  444. viam/services/generic/generic.py +58 -0
  445. viam/services/generic/service.py +29 -0
  446. viam/services/mlmodel/__init__.py +24 -0
  447. viam/services/mlmodel/client.py +37 -0
  448. viam/services/mlmodel/mlmodel.py +78 -0
  449. viam/services/mlmodel/service.py +38 -0
  450. viam/services/mlmodel/utils.py +101 -0
  451. viam/services/motion/__init__.py +17 -0
  452. viam/services/motion/client.py +215 -0
  453. viam/services/motion/motion.py +378 -0
  454. viam/services/motion/service.py +132 -0
  455. viam/services/navigation/__init__.py +11 -0
  456. viam/services/navigation/client.py +99 -0
  457. viam/services/navigation/navigation.py +250 -0
  458. viam/services/navigation/service.py +137 -0
  459. viam/services/service_base.py +78 -0
  460. viam/services/service_client_base.py +46 -0
  461. viam/services/slam/__init__.py +17 -0
  462. viam/services/slam/client.py +62 -0
  463. viam/services/slam/service.py +75 -0
  464. viam/services/slam/slam.py +111 -0
  465. viam/services/vision/__init__.py +15 -0
  466. viam/services/vision/client.py +206 -0
  467. viam/services/vision/service.py +146 -0
  468. viam/services/vision/vision.py +315 -0
  469. viam/sessions_client.py +245 -0
  470. viam/streams.py +44 -0
  471. viam/utils.py +365 -0
  472. viam/version_metadata.py +4 -0
  473. viam_sdk-0.45.2.dist-info/METADATA +157 -0
  474. viam_sdk-0.45.2.dist-info/RECORD +476 -0
  475. viam_sdk-0.45.2.dist-info/WHEEL +4 -0
  476. 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