viam-sdk 0.3.0__py3-none-linux_armv6l.whl → 0.66.0__py3-none-linux_armv6l.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 (452) hide show
  1. viam/__init__.py +29 -2
  2. viam/app/_logs.py +34 -0
  3. viam/app/app_client.py +2696 -0
  4. viam/app/billing_client.py +185 -0
  5. viam/app/data_client.py +2231 -0
  6. viam/app/ml_training_client.py +249 -0
  7. viam/app/provisioning_client.py +93 -0
  8. viam/app/viam_client.py +275 -0
  9. viam/components/arm/__init__.py +3 -26
  10. viam/components/arm/arm.py +123 -8
  11. viam/components/arm/client.py +37 -24
  12. viam/components/arm/service.py +35 -32
  13. viam/components/audio_in/__init__.py +24 -0
  14. viam/components/audio_in/audio_in.py +74 -0
  15. viam/components/audio_in/client.py +76 -0
  16. viam/components/audio_in/service.py +83 -0
  17. viam/components/audio_out/__init__.py +21 -0
  18. viam/components/audio_out/audio_out.py +72 -0
  19. viam/components/audio_out/client.py +67 -0
  20. viam/components/audio_out/service.py +63 -0
  21. viam/components/base/__init__.py +6 -11
  22. viam/components/base/base.py +134 -8
  23. viam/components/base/client.py +51 -23
  24. viam/components/base/service.py +33 -30
  25. viam/components/board/__init__.py +3 -12
  26. viam/components/board/board.py +247 -91
  27. viam/components/board/client.py +149 -83
  28. viam/components/board/service.py +63 -33
  29. viam/components/button/__init__.py +10 -0
  30. viam/components/button/button.py +41 -0
  31. viam/components/button/client.py +52 -0
  32. viam/components/button/service.py +46 -0
  33. viam/components/camera/__init__.py +3 -3
  34. viam/components/camera/camera.py +62 -27
  35. viam/components/camera/client.py +59 -27
  36. viam/components/camera/service.py +42 -65
  37. viam/components/component_base.py +28 -5
  38. viam/components/encoder/__init__.py +1 -1
  39. viam/components/encoder/client.py +25 -14
  40. viam/components/encoder/encoder.py +48 -10
  41. viam/components/encoder/service.py +14 -18
  42. viam/components/gantry/__init__.py +1 -13
  43. viam/components/gantry/client.py +80 -25
  44. viam/components/gantry/gantry.py +123 -9
  45. viam/components/gantry/service.py +51 -29
  46. viam/components/generic/__init__.py +1 -1
  47. viam/components/generic/client.py +21 -8
  48. viam/components/generic/generic.py +10 -2
  49. viam/components/generic/service.py +12 -7
  50. viam/components/gripper/__init__.py +3 -13
  51. viam/components/gripper/client.py +69 -21
  52. viam/components/gripper/gripper.py +123 -3
  53. viam/components/gripper/service.py +44 -22
  54. viam/components/input/__init__.py +1 -14
  55. viam/components/input/client.py +55 -23
  56. viam/components/input/input.py +106 -3
  57. viam/components/input/service.py +16 -21
  58. viam/components/motor/__init__.py +1 -21
  59. viam/components/motor/client.py +56 -33
  60. viam/components/motor/motor.py +127 -4
  61. viam/components/motor/service.py +33 -44
  62. viam/components/movement_sensor/__init__.py +1 -1
  63. viam/components/movement_sensor/client.py +102 -45
  64. viam/components/movement_sensor/movement_sensor.py +130 -61
  65. viam/components/movement_sensor/service.py +38 -41
  66. viam/components/pose_tracker/__init__.py +1 -1
  67. viam/components/pose_tracker/client.py +18 -7
  68. viam/components/pose_tracker/pose_tracker.py +4 -2
  69. viam/components/pose_tracker/service.py +12 -10
  70. viam/components/power_sensor/__init__.py +17 -0
  71. viam/components/power_sensor/client.py +86 -0
  72. viam/components/power_sensor/power_sensor.py +104 -0
  73. viam/components/power_sensor/service.py +72 -0
  74. viam/components/sensor/__init__.py +2 -1
  75. viam/components/sensor/client.py +26 -10
  76. viam/components/sensor/sensor.py +22 -4
  77. viam/components/sensor/service.py +20 -11
  78. viam/components/servo/__init__.py +1 -13
  79. viam/components/servo/client.py +47 -21
  80. viam/components/servo/service.py +15 -22
  81. viam/components/servo/servo.py +61 -2
  82. viam/components/switch/__init__.py +10 -0
  83. viam/components/switch/client.py +83 -0
  84. viam/components/switch/service.py +72 -0
  85. viam/components/switch/switch.py +98 -0
  86. viam/errors.py +10 -0
  87. viam/gen/app/agent/v1/agent_grpc.py +29 -0
  88. viam/gen/app/agent/v1/agent_pb2.py +47 -0
  89. viam/gen/app/agent/v1/agent_pb2.pyi +280 -0
  90. viam/gen/app/cloudslam/v1/__init__.py +0 -0
  91. viam/gen/app/cloudslam/v1/cloud_slam_grpc.py +70 -0
  92. viam/gen/app/cloudslam/v1/cloud_slam_pb2.py +54 -0
  93. viam/gen/app/cloudslam/v1/cloud_slam_pb2.pyi +384 -0
  94. viam/gen/app/data/v1/data_grpc.py +197 -8
  95. viam/gen/app/data/v1/data_pb2.py +238 -99
  96. viam/gen/app/data/v1/data_pb2.pyi +1222 -259
  97. viam/gen/app/datapipelines/__init__.py +0 -0
  98. viam/gen/app/datapipelines/v1/__init__.py +0 -0
  99. viam/gen/app/datapipelines/v1/data_pipelines_grpc.py +84 -0
  100. viam/gen/app/datapipelines/v1/data_pipelines_pb2.py +57 -0
  101. viam/gen/app/datapipelines/v1/data_pipelines_pb2.pyi +387 -0
  102. viam/gen/app/dataset/__init__.py +0 -0
  103. viam/gen/app/dataset/v1/__init__.py +0 -0
  104. viam/gen/app/dataset/v1/dataset_grpc.py +68 -0
  105. viam/gen/app/dataset/v1/dataset_pb2.py +44 -0
  106. viam/gen/app/dataset/v1/dataset_pb2.pyi +214 -0
  107. viam/gen/app/datasync/v1/data_sync_grpc.py +21 -4
  108. viam/gen/app/datasync/v1/data_sync_pb2.py +62 -128
  109. viam/gen/app/datasync/v1/data_sync_pb2.pyi +156 -199
  110. viam/gen/app/mlinference/__init__.py +0 -0
  111. viam/gen/app/mlinference/v1/__init__.py +0 -0
  112. viam/gen/app/mlinference/v1/ml_inference_grpc.py +28 -0
  113. viam/gen/app/mlinference/v1/ml_inference_pb2.py +23 -0
  114. viam/gen/app/mlinference/v1/ml_inference_pb2.pyi +63 -0
  115. viam/gen/app/mltraining/v1/ml_training_grpc.py +51 -3
  116. viam/gen/app/mltraining/v1/ml_training_pb2.py +135 -58
  117. viam/gen/app/mltraining/v1/ml_training_pb2.pyi +328 -39
  118. viam/gen/app/packages/v1/packages_grpc.py +15 -1
  119. viam/gen/app/packages/v1/packages_pb2.py +44 -64
  120. viam/gen/app/packages/v1/packages_pb2.pyi +75 -85
  121. viam/gen/app/v1/app_grpc.py +644 -3
  122. viam/gen/app/v1/app_pb2.py +695 -295
  123. viam/gen/app/v1/app_pb2.pyi +4488 -635
  124. viam/gen/app/v1/billing_grpc.py +53 -11
  125. viam/gen/app/v1/billing_pb2.py +94 -39
  126. viam/gen/app/v1/billing_pb2.pyi +391 -191
  127. viam/gen/app/v1/end_user_grpc.py +59 -0
  128. viam/gen/app/v1/end_user_pb2.py +55 -0
  129. viam/gen/app/v1/end_user_pb2.pyi +181 -0
  130. viam/gen/app/v1/robot_grpc.py +16 -1
  131. viam/gen/app/v1/robot_pb2.py +122 -94
  132. viam/gen/app/v1/robot_pb2.pyi +463 -123
  133. viam/gen/common/v1/common_pb2.py +87 -58
  134. viam/gen/common/v1/common_pb2.pyi +456 -149
  135. viam/gen/component/arm/v1/arm_grpc.py +58 -2
  136. viam/gen/component/arm/v1/arm_pb2.py +68 -51
  137. viam/gen/component/arm/v1/arm_pb2.pyi +108 -42
  138. viam/gen/component/audioin/__init__.py +0 -0
  139. viam/gen/component/audioin/v1/__init__.py +0 -0
  140. viam/gen/component/audioin/v1/audioin_grpc.py +54 -0
  141. viam/gen/component/audioin/v1/audioin_pb2.py +34 -0
  142. viam/gen/component/audioin/v1/audioin_pb2.pyi +94 -0
  143. viam/gen/component/audioinput/v1/audioinput_grpc.py +25 -2
  144. viam/gen/component/audioinput/v1/audioinput_pb2.py +36 -31
  145. viam/gen/component/audioinput/v1/audioinput_pb2.pyi +22 -22
  146. viam/gen/component/audioout/__init__.py +0 -0
  147. viam/gen/component/audioout/v1/__init__.py +0 -0
  148. viam/gen/component/audioout/v1/audioout_grpc.py +54 -0
  149. viam/gen/component/audioout/v1/audioout_pb2.py +32 -0
  150. viam/gen/component/audioout/v1/audioout_pb2.pyi +47 -0
  151. viam/gen/component/base/v1/base_grpc.py +42 -2
  152. viam/gen/component/base/v1/base_pb2.py +58 -47
  153. viam/gen/component/base/v1/base_pb2.pyi +65 -30
  154. viam/gen/component/board/v1/board_grpc.py +59 -7
  155. viam/gen/component/board/v1/board_pb2.py +94 -73
  156. viam/gen/component/board/v1/board_pb2.pyi +165 -68
  157. viam/gen/component/button/__init__.py +0 -0
  158. viam/gen/component/button/v1/__init__.py +0 -0
  159. viam/gen/component/button/v1/button_grpc.py +38 -0
  160. viam/gen/component/button/v1/button_pb2.py +28 -0
  161. viam/gen/component/button/v1/button_pb2.pyi +39 -0
  162. viam/gen/component/camera/v1/camera_grpc.py +38 -2
  163. viam/gen/component/camera/v1/camera_pb2.py +60 -43
  164. viam/gen/component/camera/v1/camera_pb2.pyi +191 -37
  165. viam/gen/component/encoder/v1/encoder_grpc.py +25 -2
  166. viam/gen/component/encoder/v1/encoder_pb2.py +36 -31
  167. viam/gen/component/encoder/v1/encoder_pb2.pyi +15 -15
  168. viam/gen/component/gantry/v1/gantry_grpc.py +47 -2
  169. viam/gen/component/gantry/v1/gantry_pb2.py +56 -43
  170. viam/gen/component/gantry/v1/gantry_pb2.pyi +67 -31
  171. viam/gen/component/generic/v1/generic_grpc.py +16 -2
  172. viam/gen/component/generic/v1/generic_pb2.py +16 -11
  173. viam/gen/component/gripper/v1/gripper_grpc.py +44 -2
  174. viam/gen/component/gripper/v1/gripper_pb2.py +48 -35
  175. viam/gen/component/gripper/v1/gripper_pb2.pyi +62 -24
  176. viam/gen/component/inputcontroller/v1/input_controller_grpc.py +28 -2
  177. viam/gen/component/inputcontroller/v1/input_controller_pb2.py +46 -41
  178. viam/gen/component/inputcontroller/v1/input_controller_pb2.pyi +32 -36
  179. viam/gen/component/motor/v1/motor_grpc.py +51 -2
  180. viam/gen/component/motor/v1/motor_pb2.py +78 -67
  181. viam/gen/component/motor/v1/motor_pb2.pyi +75 -46
  182. viam/gen/component/movementsensor/v1/movementsensor_grpc.py +48 -2
  183. viam/gen/component/movementsensor/v1/movementsensor_pb2.py +70 -63
  184. viam/gen/component/movementsensor/v1/movementsensor_pb2.pyi +84 -57
  185. viam/gen/component/posetracker/v1/pose_tracker_grpc.py +19 -2
  186. viam/gen/component/posetracker/v1/pose_tracker_pb2.py +26 -21
  187. viam/gen/component/posetracker/v1/pose_tracker_pb2.pyi +9 -13
  188. viam/gen/component/powersensor/__init__.py +0 -0
  189. viam/gen/component/powersensor/v1/__init__.py +0 -0
  190. viam/gen/component/powersensor/v1/powersensor_grpc.py +62 -0
  191. viam/gen/component/powersensor/v1/powersensor_pb2.py +42 -0
  192. viam/gen/component/powersensor/v1/powersensor_pb2.pyi +124 -0
  193. viam/gen/component/sensor/v1/sensor_grpc.py +21 -5
  194. viam/gen/component/sensor/v1/sensor_pb2.py +18 -22
  195. viam/gen/component/sensor/v1/sensor_pb2.pyi +1 -69
  196. viam/gen/component/servo/v1/servo_grpc.py +28 -2
  197. viam/gen/component/servo/v1/servo_pb2.py +42 -37
  198. viam/gen/component/servo/v1/servo_pb2.pyi +22 -26
  199. viam/gen/component/switch/__init__.py +0 -0
  200. viam/gen/component/switch/v1/__init__.py +0 -0
  201. viam/gen/component/switch/v1/switch_grpc.py +54 -0
  202. viam/gen/component/switch/v1/switch_pb2.py +40 -0
  203. viam/gen/component/switch/v1/switch_pb2.pyi +116 -0
  204. viam/gen/component/testecho/v1/testecho_grpc.py +15 -0
  205. viam/gen/component/testecho/v1/testecho_pb2.py +29 -26
  206. viam/gen/component/testecho/v1/testecho_pb2.pyi +16 -20
  207. viam/gen/module/v1/module_grpc.py +18 -0
  208. viam/gen/module/v1/module_pb2.py +36 -33
  209. viam/gen/module/v1/module_pb2.pyi +39 -34
  210. viam/gen/opentelemetry/__init__.py +0 -0
  211. viam/gen/opentelemetry/proto/__init__.py +0 -0
  212. viam/gen/opentelemetry/proto/common/__init__.py +0 -0
  213. viam/gen/opentelemetry/proto/common/v1/__init__.py +0 -0
  214. viam/gen/opentelemetry/proto/common/v1/common_grpc.py +0 -0
  215. viam/gen/opentelemetry/proto/common/v1/common_pb2.py +27 -0
  216. viam/gen/opentelemetry/proto/common/v1/common_pb2.pyi +208 -0
  217. viam/gen/opentelemetry/proto/resource/__init__.py +0 -0
  218. viam/gen/opentelemetry/proto/resource/v1/__init__.py +0 -0
  219. viam/gen/opentelemetry/proto/resource/v1/resource_grpc.py +0 -0
  220. viam/gen/opentelemetry/proto/resource/v1/resource_pb2.py +18 -0
  221. viam/gen/opentelemetry/proto/resource/v1/resource_pb2.pyi +59 -0
  222. viam/gen/opentelemetry/proto/trace/__init__.py +0 -0
  223. viam/gen/opentelemetry/proto/trace/v1/__init__.py +0 -0
  224. viam/gen/opentelemetry/proto/trace/v1/trace_grpc.py +0 -0
  225. viam/gen/opentelemetry/proto/trace/v1/trace_pb2.py +37 -0
  226. viam/gen/opentelemetry/proto/trace/v1/trace_pb2.pyi +402 -0
  227. viam/gen/proto/rpc/examples/echo/v1/echo_grpc.py +12 -0
  228. viam/gen/proto/rpc/examples/echo/v1/echo_pb2.py +25 -22
  229. viam/gen/proto/rpc/examples/echo/v1/echo_pb2.pyi +13 -17
  230. viam/gen/proto/rpc/examples/echoresource/v1/echoresource_grpc.py +12 -0
  231. viam/gen/proto/rpc/examples/echoresource/v1/echoresource_pb2.py +23 -20
  232. viam/gen/proto/rpc/examples/echoresource/v1/echoresource_pb2.pyi +13 -17
  233. viam/gen/proto/rpc/v1/auth_grpc.py +11 -0
  234. viam/gen/proto/rpc/v1/auth_pb2.py +27 -24
  235. viam/gen/proto/rpc/v1/auth_pb2.pyi +12 -16
  236. viam/gen/proto/rpc/webrtc/v1/grpc_pb2.py +35 -32
  237. viam/gen/proto/rpc/webrtc/v1/grpc_pb2.pyi +37 -41
  238. viam/gen/proto/rpc/webrtc/v1/signaling_grpc.py +15 -0
  239. viam/gen/proto/rpc/webrtc/v1/signaling_pb2.py +62 -57
  240. viam/gen/proto/rpc/webrtc/v1/signaling_pb2.pyi +78 -69
  241. viam/gen/provisioning/__init__.py +0 -0
  242. viam/gen/provisioning/v1/__init__.py +0 -0
  243. viam/gen/provisioning/v1/provisioning_grpc.py +59 -0
  244. viam/gen/provisioning/v1/provisioning_pb2.py +45 -0
  245. viam/gen/provisioning/v1/provisioning_pb2.pyi +229 -0
  246. viam/gen/robot/v1/robot_grpc.py +144 -15
  247. viam/gen/robot/v1/robot_pb2.py +193 -119
  248. viam/gen/robot/v1/robot_pb2.pyi +565 -137
  249. viam/gen/service/datamanager/v1/data_manager_grpc.py +20 -2
  250. viam/gen/service/datamanager/v1/data_manager_pb2.py +27 -17
  251. viam/gen/service/datamanager/v1/data_manager_pb2.pyi +52 -10
  252. viam/gen/service/discovery/__init__.py +0 -0
  253. viam/gen/service/discovery/v1/__init__.py +0 -0
  254. viam/gen/service/discovery/v1/discovery_grpc.py +39 -0
  255. viam/gen/service/discovery/v1/discovery_pb2.py +29 -0
  256. viam/gen/service/discovery/v1/discovery_pb2.pyi +51 -0
  257. viam/gen/service/generic/__init__.py +0 -0
  258. viam/gen/service/generic/v1/__init__.py +0 -0
  259. viam/gen/service/generic/v1/generic_grpc.py +29 -0
  260. viam/gen/service/generic/v1/generic_pb2.py +21 -0
  261. viam/gen/service/generic/v1/generic_pb2.pyi +6 -0
  262. viam/gen/service/mlmodel/v1/mlmodel_grpc.py +9 -0
  263. viam/gen/service/mlmodel/v1/mlmodel_pb2.py +76 -29
  264. viam/gen/service/mlmodel/v1/mlmodel_pb2.pyi +307 -28
  265. viam/gen/service/motion/v1/motion_grpc.py +42 -4
  266. viam/gen/service/motion/v1/motion_pb2.py +119 -51
  267. viam/gen/service/motion/v1/motion_pb2.pyi +595 -120
  268. viam/gen/service/navigation/v1/navigation_grpc.py +49 -1
  269. viam/gen/service/navigation/v1/navigation_pb2.py +76 -51
  270. viam/gen/service/navigation/v1/navigation_pb2.pyi +188 -33
  271. viam/gen/service/sensors/v1/sensors_grpc.py +12 -0
  272. viam/gen/service/sensors/v1/sensors_pb2.py +60 -29
  273. viam/gen/service/sensors/v1/sensors_pb2.pyi +18 -21
  274. viam/gen/service/shell/v1/shell_grpc.py +27 -1
  275. viam/gen/service/shell/v1/shell_pb2.py +37 -15
  276. viam/gen/service/shell/v1/shell_pb2.pyi +260 -7
  277. viam/gen/service/slam/v1/slam_grpc.py +24 -2
  278. viam/gen/service/slam/v1/slam_pb2.py +44 -30
  279. viam/gen/service/slam/v1/slam_pb2.pyi +128 -27
  280. viam/gen/service/video/__init__.py +0 -0
  281. viam/gen/service/video/v1/__init__.py +0 -0
  282. viam/gen/service/video/v1/video_grpc.py +39 -0
  283. viam/gen/service/video/v1/video_pb2.py +29 -0
  284. viam/gen/service/video/v1/video_pb2.pyi +72 -0
  285. viam/gen/service/vision/v1/vision_grpc.py +39 -1
  286. viam/gen/service/vision/v1/vision_pb2.py +61 -45
  287. viam/gen/service/vision/v1/vision_pb2.pyi +180 -41
  288. viam/gen/service/worldstatestore/__init__.py +0 -0
  289. viam/gen/service/worldstatestore/v1/__init__.py +0 -0
  290. viam/gen/service/worldstatestore/v1/world_state_store_grpc.py +55 -0
  291. viam/gen/service/worldstatestore/v1/world_state_store_pb2.py +39 -0
  292. viam/gen/service/worldstatestore/v1/world_state_store_pb2.pyi +171 -0
  293. viam/gen/stream/__init__.py +0 -0
  294. viam/gen/stream/v1/__init__.py +0 -0
  295. viam/gen/stream/v1/stream_grpc.py +59 -0
  296. viam/gen/stream/v1/stream_pb2.py +39 -0
  297. viam/gen/stream/v1/stream_pb2.pyi +161 -0
  298. viam/gen/tagger/v1/tagger_pb2.py +9 -8
  299. viam/logging.py +160 -17
  300. viam/media/__init__.py +0 -9
  301. viam/media/audio.py +22 -10
  302. viam/media/utils/__init__.py +0 -0
  303. viam/media/utils/pil/__init__.py +55 -0
  304. viam/media/{viam_rgba_plugin.py → utils/pil/viam_rgba_plugin.py} +10 -16
  305. viam/media/viam_rgba.py +10 -0
  306. viam/media/video.py +197 -73
  307. viam/module/module.py +191 -44
  308. viam/module/resource_data_consumer.py +41 -0
  309. viam/module/service.py +9 -1
  310. viam/module/types.py +4 -5
  311. viam/operations.py +4 -3
  312. viam/proto/app/__init__.py +361 -5
  313. viam/proto/app/agent/__init__.py +28 -0
  314. viam/proto/app/billing.py +51 -27
  315. viam/proto/app/cloudslam/__init__.py +48 -0
  316. viam/proto/app/data/__init__.py +103 -17
  317. viam/proto/app/datapipelines/__init__.py +56 -0
  318. viam/proto/app/dataset/__init__.py +40 -0
  319. viam/proto/app/datasync/__init__.py +11 -5
  320. viam/proto/app/end_user.py +34 -0
  321. viam/proto/app/mlinference/__init__.py +15 -0
  322. viam/proto/app/mltraining/__init__.py +25 -1
  323. viam/proto/app/packages/__init__.py +3 -3
  324. viam/proto/app/robot.py +19 -1
  325. viam/proto/common/__init__.py +35 -8
  326. viam/proto/component/arm/__init__.py +9 -1
  327. viam/proto/component/audioin/__init__.py +16 -0
  328. viam/proto/component/audioinput/__init__.py +3 -1
  329. viam/proto/component/audioout/__init__.py +15 -0
  330. viam/proto/component/base/__init__.py +7 -1
  331. viam/proto/component/board/__init__.py +13 -5
  332. viam/proto/component/button/__init__.py +15 -0
  333. viam/proto/component/camera/__init__.py +9 -1
  334. viam/proto/component/encoder/__init__.py +3 -1
  335. viam/proto/component/gantry/__init__.py +7 -1
  336. viam/proto/component/generic/__init__.py +3 -1
  337. viam/proto/component/gripper/__init__.py +7 -1
  338. viam/proto/component/inputcontroller/__init__.py +7 -1
  339. viam/proto/component/motor/__init__.py +7 -1
  340. viam/proto/component/movementsensor/__init__.py +7 -1
  341. viam/proto/component/posetracker/__init__.py +7 -1
  342. viam/proto/component/powersensor/__init__.py +30 -0
  343. viam/proto/component/sensor/__init__.py +3 -4
  344. viam/proto/component/servo/__init__.py +3 -1
  345. viam/proto/component/switch/__init__.py +26 -0
  346. viam/proto/component/testecho/__init__.py +3 -1
  347. viam/proto/module/__init__.py +3 -1
  348. viam/proto/opentelemetry/__init__.py +0 -0
  349. viam/proto/opentelemetry/proto/__init__.py +0 -0
  350. viam/proto/opentelemetry/proto/common/__init__.py +15 -0
  351. viam/proto/opentelemetry/proto/resource/__init__.py +10 -0
  352. viam/proto/opentelemetry/proto/trace/__init__.py +15 -0
  353. viam/proto/provisioning/__init__.py +42 -0
  354. viam/proto/robot/__init__.py +57 -9
  355. viam/proto/rpc/auth.py +11 -1
  356. viam/proto/rpc/examples/echo/__init__.py +3 -1
  357. viam/proto/rpc/examples/echoresource/__init__.py +7 -1
  358. viam/proto/rpc/webrtc/grpc.py +3 -1
  359. viam/proto/rpc/webrtc/signaling.py +5 -1
  360. viam/proto/service/datamanager/__init__.py +15 -2
  361. viam/proto/service/discovery/__init__.py +15 -0
  362. viam/proto/service/generic/__init__.py +12 -0
  363. viam/proto/service/mlmodel/__init__.py +27 -1
  364. viam/proto/service/motion/__init__.py +35 -5
  365. viam/proto/service/navigation/__init__.py +19 -1
  366. viam/proto/service/sensors/__init__.py +3 -1
  367. viam/proto/service/shell/__init__.py +25 -2
  368. viam/proto/service/slam/__init__.py +13 -1
  369. viam/proto/service/video/__init__.py +15 -0
  370. viam/proto/service/vision/__init__.py +11 -1
  371. viam/proto/service/worldstatestore/__init__.py +32 -0
  372. viam/proto/stream/__init__.py +36 -0
  373. viam/py.typed +0 -0
  374. viam/resource/base.py +45 -8
  375. viam/resource/easy_resource.py +149 -0
  376. viam/resource/manager.py +35 -14
  377. viam/resource/registry.py +40 -52
  378. viam/resource/rpc_client_base.py +33 -1
  379. viam/resource/rpc_service_base.py +15 -8
  380. viam/resource/types.py +39 -26
  381. viam/robot/client.py +458 -91
  382. viam/robot/service.py +13 -107
  383. viam/rpc/dial.py +133 -15
  384. viam/rpc/libviam_rust_utils.so +0 -0
  385. viam/rpc/server.py +59 -15
  386. viam/rpc/types.py +2 -4
  387. viam/services/discovery/__init__.py +12 -0
  388. viam/services/discovery/client.py +55 -0
  389. viam/services/discovery/discovery.py +52 -0
  390. viam/services/discovery/service.py +43 -0
  391. viam/services/generic/__init__.py +18 -0
  392. viam/services/generic/client.py +58 -0
  393. viam/services/generic/generic.py +58 -0
  394. viam/services/generic/service.py +29 -0
  395. viam/services/mlmodel/__init__.py +15 -1
  396. viam/services/mlmodel/client.py +20 -15
  397. viam/services/mlmodel/mlmodel.py +44 -7
  398. viam/services/mlmodel/service.py +9 -13
  399. viam/services/mlmodel/utils.py +101 -0
  400. viam/services/motion/__init__.py +15 -3
  401. viam/services/motion/client.py +109 -150
  402. viam/services/motion/motion.py +380 -0
  403. viam/services/motion/service.py +132 -0
  404. viam/services/navigation/__init__.py +11 -0
  405. viam/services/navigation/client.py +99 -0
  406. viam/services/navigation/navigation.py +250 -0
  407. viam/services/navigation/service.py +137 -0
  408. viam/services/service_base.py +43 -4
  409. viam/services/service_client_base.py +4 -4
  410. viam/services/slam/__init__.py +4 -1
  411. viam/services/slam/client.py +21 -11
  412. viam/services/slam/service.py +16 -19
  413. viam/services/slam/slam.py +66 -5
  414. viam/services/vision/__init__.py +8 -0
  415. viam/services/vision/client.py +115 -111
  416. viam/services/vision/service.py +143 -0
  417. viam/services/vision/vision.py +317 -0
  418. viam/services/worldstatestore/__init__.py +18 -0
  419. viam/services/worldstatestore/client.py +94 -0
  420. viam/services/worldstatestore/service.py +55 -0
  421. viam/services/worldstatestore/worldstatestore.py +90 -0
  422. viam/sessions_client.py +254 -0
  423. viam/streams.py +44 -0
  424. viam/utils.py +143 -15
  425. viam/version_metadata.py +4 -0
  426. viam_sdk-0.66.0.dist-info/METADATA +157 -0
  427. viam_sdk-0.66.0.dist-info/RECORD +531 -0
  428. {viam_sdk-0.3.0.dist-info → viam_sdk-0.66.0.dist-info}/WHEEL +1 -1
  429. viam/components/audio_input/__init__.py +0 -18
  430. viam/components/audio_input/audio_input.py +0 -79
  431. viam/components/audio_input/client.py +0 -60
  432. viam/components/audio_input/service.py +0 -118
  433. viam/components/types.py +0 -5
  434. viam/gen/app/model/v1/model_grpc.py +0 -39
  435. viam/gen/app/model/v1/model_pb2.py +0 -71
  436. viam/gen/app/model/v1/model_pb2.pyi +0 -285
  437. viam/gen/proto/rpc/examples/fileupload/v1/fileupload_grpc.py +0 -21
  438. viam/gen/proto/rpc/examples/fileupload/v1/fileupload_pb2.py +0 -18
  439. viam/gen/proto/rpc/examples/fileupload/v1/fileupload_pb2.pyi +0 -49
  440. viam/media/media.py +0 -53
  441. viam/proto/app/model/__init__.py +0 -40
  442. viam/proto/rpc/examples/fileupload/__init__.py +0 -13
  443. viam/services/sensors/__init__.py +0 -5
  444. viam/services/sensors/client.py +0 -63
  445. viam_sdk-0.3.0.dist-info/LICENSE +0 -202
  446. viam_sdk-0.3.0.dist-info/METADATA +0 -122
  447. viam_sdk-0.3.0.dist-info/RECORD +0 -372
  448. /viam/{gen/app/model → app}/__init__.py +0 -0
  449. /viam/gen/app/{model/v1 → agent}/__init__.py +0 -0
  450. /viam/gen/{proto/rpc/examples/fileupload → app/agent/v1}/__init__.py +0 -0
  451. /viam/gen/{proto/rpc/examples/fileupload/v1 → app/cloudslam}/__init__.py +0 -0
  452. /LICENSE → /viam_sdk-0.66.0.dist-info/licenses/LICENSE +0 -0
viam/media/video.py CHANGED
@@ -1,107 +1,231 @@
1
1
  from array import array
2
- from enum import Enum
3
- from io import BytesIO
4
- from typing import List, NamedTuple, Tuple, Union
2
+ from typing import Any, List, Optional, Tuple
5
3
 
6
- from PIL.Image import Image
7
- from typing_extensions import Self
4
+ from typing_extensions import ClassVar, Self
8
5
 
9
6
  from viam.errors import NotSupportedError
10
7
 
11
- from .viam_rgba_plugin import RGBA_FORMAT_LABEL
8
+ from .viam_rgba import RGBA_HEADER_LENGTH, RGBA_MAGIC_NUMBER
12
9
 
13
- LAZY_SUFFIX = "+lazy"
14
10
 
15
- # Formats that are supported by PIL
16
- LIBRARY_SUPPORTED_FORMATS = ["JPEG", "PNG", RGBA_FORMAT_LABEL]
11
+ class _FrozenClassAttributesMeta(type):
12
+ """
13
+ A metaclass that prevents the reassignment of existing class attributes.
14
+ """
17
15
 
16
+ def __setattr__(cls, name: str, value: Any):
17
+ # Check if the attribute `name` already exists on the class
18
+ if name in cls.__dict__:
19
+ # If it exists, raise an error to prevent overwriting
20
+ raise AttributeError(f"Cannot reassign constant '{name}'")
21
+ # If it's a new attribute, allow it to be set
22
+ super().__setattr__(name, value)
18
23
 
19
- class RawImage(NamedTuple):
20
- """A raw bytes representation of an image.
21
24
 
22
- A RawImage should be returned instead of a PIL Image instance under one of
23
- the following conditions
25
+ class CameraMimeType(str, metaclass=_FrozenClassAttributesMeta):
26
+ """
27
+ The compatible mime-types for cameras and vision services.
24
28
 
25
- 1) The requested mime type has the LAZY_SUFFIX string appended to it
26
- 2) The requested mime type is not supported for decoding/encoding by Viam's
27
- Python SDK
29
+ You can use the `CameraMimeType.CUSTOM(...)` method to use an unlisted mime-type.
28
30
  """
29
31
 
30
- data: bytes
31
- """The raw data of the image"""
32
+ VIAM_RGBA: ClassVar[Self]
33
+ VIAM_RAW_DEPTH: ClassVar[Self]
34
+ JPEG: ClassVar[Self]
35
+ PNG: ClassVar[Self]
36
+ PCD: ClassVar[Self]
32
37
 
33
- mime_type: str
34
- """The mimetype of the image"""
38
+ @property
39
+ def name(self) -> str:
40
+ for key, value in self.__class__.__dict__.items():
41
+ if value == self:
42
+ return key
43
+ return "CUSTOM"
35
44
 
36
- def close(self):
37
- """Close the image and release resources. For RawImage, this is a noop."""
38
- return
45
+ @property
46
+ def value(self) -> str:
47
+ return self
39
48
 
40
- def bytes_to_depth_array(self) -> List[List[int]]:
41
- """Decode the data of an image that has the custom depth MIME type ``image/vnd.viam.dep`` into
42
- a standard representation.
49
+ @classmethod
50
+ def CUSTOM(cls, mime_type: str) -> Self:
51
+ """
52
+ Create a custom mime type.
43
53
 
44
- Raises:
45
- NotSupportedError: Raised if given an image that is not of MIME type `image/vnd.viam.dep`.
54
+ Args:
55
+ mime_type (str): The mimetype as a string
56
+ """
57
+ return cls.from_string(mime_type)
58
+
59
+ @classmethod
60
+ def from_string(cls, value: str) -> Self:
61
+ """Return the mimetype from a string.
62
+
63
+ Args:
64
+ value (str): The mimetype as a string
46
65
 
47
66
  Returns:
48
- List[List[int]]: The standard representation of the image.
67
+ Self: The mimetype
49
68
  """
50
- if self.mime_type != "image/vnd.viam.dep":
51
- raise NotSupportedError("Type must be `image/vnd.viam.dep` to use bytes_to_depth_array()")
69
+ value_mime = value[:-5] if value.endswith("+lazy") else value # ViamImage lazy encodes by default
70
+ return cls(value_mime)
52
71
 
53
- width = int.from_bytes(self.data[8:16], "big")
54
- height = int.from_bytes(self.data[16:24], "big")
55
- depth_arr = array("H", self.data[24:])
56
- depth_arr.byteswap()
57
72
 
58
- depth_arr_2d = [[depth_arr[row * width + col] for col in range(width)] for row in range(height)]
59
- return depth_arr_2d
73
+ CameraMimeType.VIAM_RGBA = CameraMimeType.from_string("image/vnd.viam.rgba")
74
+ CameraMimeType.VIAM_RAW_DEPTH = CameraMimeType.from_string("image/vnd.viam.dep")
75
+ CameraMimeType.JPEG = CameraMimeType.from_string("image/jpeg")
76
+ CameraMimeType.PNG = CameraMimeType.from_string("image/png")
77
+ CameraMimeType.PCD = CameraMimeType.from_string("pointcloud/pcd")
60
78
 
61
79
 
62
- class CameraMimeType(str, Enum):
63
- VIAM_RGBA = "image/vnd.viam.rgba"
64
- JPEG = "image/jpeg"
65
- PNG = "image/png"
66
- PCD = "pointcloud/pcd"
67
- UNSUPPORTED = "unsupported"
80
+ class ViamImage:
81
+ """A native implementation of an image.
68
82
 
69
- @classmethod
70
- def from_lazy(cls, value: str) -> Tuple[Self, bool]:
71
- is_lazy = False
72
- mime_type = value
73
- if value.endswith(LAZY_SUFFIX):
74
- mime_type = value[: (len(value) - len(LAZY_SUFFIX))]
75
- is_lazy = True
76
- if not cls.is_supported(value) and not is_lazy:
77
- mime_type = CameraMimeType.UNSUPPORTED
78
- return (cls(mime_type), is_lazy)
83
+ Provides the raw data and the mime type.
84
+ """
85
+
86
+ _data: bytes
87
+ _mime_type: CameraMimeType
88
+ _height: Optional[int] = None
89
+ _width: Optional[int] = None
90
+
91
+ def __init__(self, data: bytes, mime_type: CameraMimeType) -> None:
92
+ self._data = data
93
+ self._mime_type = mime_type
94
+ self._width, self._height = _getDimensions(data, mime_type)
79
95
 
80
96
  @property
81
- def with_lazy_suffix(self) -> str:
82
- return f"{self.value}{LAZY_SUFFIX}"
97
+ def data(self) -> bytes:
98
+ """The raw bytes of the image"""
99
+ return self._data
83
100
 
84
- def encode_image(self, image: Union[Image, RawImage]) -> bytes:
85
- if isinstance(image, RawImage):
86
- return image.data
101
+ @property
102
+ def mime_type(self) -> CameraMimeType:
103
+ """The mime type of the image"""
104
+ return self._mime_type
87
105
 
88
- if self.name in LIBRARY_SUPPORTED_FORMATS:
89
- buf = BytesIO()
90
- image.save(buf, format=self.name)
91
- return buf.getvalue()
92
- else:
93
- raise ValueError(f"Cannot encode image to {self}")
106
+ @property
107
+ def width(self) -> Optional[int]:
108
+ """The width of the image"""
109
+ return self._width
94
110
 
95
- @classmethod
96
- def is_supported(cls, mime_type: str) -> bool:
97
- """Check if the provided mime_type is supported
111
+ @property
112
+ def height(self) -> Optional[int]:
113
+ """The height of the image"""
114
+ return self._height
98
115
 
99
- Args:
100
- mime_type (str): The mime_type to check
116
+ def bytes_to_depth_array(self) -> List[List[int]]:
117
+ """
118
+ Decode the data of an image that has the custom depth MIME type ``image/vnd.viam.dep`` into a standard representation.
119
+
120
+ Raises:
121
+ NotSupportedError: Raised if the image is not of MIME type `image/vnd.viam.dep`.
101
122
 
102
123
  Returns:
103
- bool: Whether the mime_type is supported
124
+ List[List[int]]: The standard representation of the image.
104
125
  """
105
- if mime_type == cls.UNSUPPORTED:
106
- return False
107
- return mime_type in set(item.value for item in cls)
126
+ if self.mime_type != CameraMimeType.VIAM_RAW_DEPTH:
127
+ raise NotSupportedError("Type must be `image/vnd.viam.dep` to use bytes_to_depth_array()")
128
+
129
+ self._width = int.from_bytes(self.data[8:16], "big")
130
+ self._height = int.from_bytes(self.data[16:24], "big")
131
+ depth_arr = array("H", self.data[24:])
132
+ depth_arr.byteswap()
133
+
134
+ depth_arr_2d = [[depth_arr[row * self._width + col] for col in range(self._width)] for row in range(self._height)]
135
+ return depth_arr_2d
136
+
137
+
138
+ class NamedImage(ViamImage):
139
+ """An implementation of ViamImage that contains a name attribute."""
140
+
141
+ name: str
142
+ """The name of the image
143
+ """
144
+
145
+ def __init__(self, name: str, data: bytes, mime_type: CameraMimeType) -> None:
146
+ self.name = name
147
+ super().__init__(data, mime_type)
148
+
149
+
150
+ def _getDimensions(image: bytes, mime_type: CameraMimeType) -> Tuple[Optional[int], Optional[int]]:
151
+ try:
152
+ if mime_type == CameraMimeType.JPEG:
153
+ return _getDimensionsFromJPEG(image)
154
+ if mime_type == CameraMimeType.PNG:
155
+ return _getDimensionsFromPNG(image)
156
+ if mime_type == CameraMimeType.VIAM_RGBA:
157
+ return _getDimensionsFromRGBA(image)
158
+ except ValueError:
159
+ return (None, None)
160
+ return (None, None)
161
+
162
+
163
+ def _getDimensionsFromJPEG(image: bytes) -> Tuple[int, int]:
164
+ # JPEG Specification: https://www.w3.org/Graphics/JPEG/itu-t81.pdf
165
+ # Specification for markers: Table B.1
166
+
167
+ offset = 0
168
+ while offset < len(image):
169
+ while image[offset] == 0xFF:
170
+ # Skip all 0xFF bytes
171
+ offset += 1
172
+
173
+ marker = image[offset]
174
+ offset += 1
175
+ if marker == 0x01:
176
+ # Temporary/private use marker
177
+ offset += 1
178
+ continue
179
+ if marker in range(0xD0, 0xD7):
180
+ # Restart (RST) marker
181
+ offset += 1
182
+ continue
183
+ if marker == 0xD8:
184
+ # Start of image (SOI) marker
185
+ offset += 1
186
+ continue
187
+ if marker == 0xD9:
188
+ # End of image (EOI) marker
189
+ break
190
+
191
+ length = int.from_bytes(image[offset : offset + 1], byteorder="big") # length of section
192
+ if marker == 0xC0 or marker == 0xC2:
193
+ height = int.from_bytes(image[offset + 3 : offset + 5], byteorder="big")
194
+ width = int.from_bytes(image[offset + 5 : offset + 7], byteorder="big")
195
+ return (width, height)
196
+
197
+ offset += length
198
+
199
+ raise ValueError("Invalid JPEG: Could not extract dimensions")
200
+
201
+
202
+ def _getDimensionsFromPNG(image: bytes) -> Tuple[int, int]:
203
+ # PNG Specification: https://www.w3.org/TR/png/
204
+
205
+ # PNG will always start with this signature
206
+ signature = image[:8]
207
+ if signature != [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]:
208
+ ValueError("Invalid PNG: Invalid signature")
209
+
210
+ header = image[12:24]
211
+ chunk_type = header[:4].decode()
212
+ if chunk_type != "IHDR":
213
+ ValueError("Invalid PNG: Invalid headers")
214
+
215
+ width = int.from_bytes(header[4:8], byteorder="big")
216
+ height = int.from_bytes(header[8:], byteorder="big")
217
+ return (width, height)
218
+
219
+
220
+ def _getDimensionsFromRGBA(image: bytes) -> Tuple[int, int]:
221
+ # Viam RGBA header comes in 3 4-byte chunks:
222
+ # * Magic Number/Signature
223
+ # * Width
224
+ # * Height
225
+ header = image[:RGBA_HEADER_LENGTH]
226
+ if header[:4] != RGBA_MAGIC_NUMBER:
227
+ raise ValueError("Invalid Viam RGBA: Invalid headers")
228
+
229
+ width = int.from_bytes(header[4:8], byteorder="big")
230
+ height = int.from_bytes(header[8:], byteorder="big")
231
+ return (width, height)
viam/module/module.py CHANGED
@@ -1,12 +1,20 @@
1
+ import argparse
2
+ import io
3
+ import logging as pylogging
4
+ import os
5
+ import sys
6
+ from collections.abc import Iterable
1
7
  from inspect import iscoroutinefunction
2
8
  from threading import Lock
3
9
  from typing import List, Mapping, Optional, Sequence, Tuple
4
10
 
11
+ from grpclib.metadata import Deadline
5
12
  from grpclib.utils import _service_name
13
+ from typing_extensions import Self
6
14
 
7
15
  from viam import logging
8
- from viam.components.component_base import ComponentBase
9
16
  from viam.errors import ResourceNotFoundError, ValidationError
17
+ from viam.logging import update_log_level
10
18
  from viam.proto.app.robot import ComponentConfig
11
19
  from viam.proto.module import (
12
20
  AddResourceRequest,
@@ -22,15 +30,50 @@ from viam.proto.module import (
22
30
  from viam.proto.robot import ResourceRPCSubtype
23
31
  from viam.resource.base import ResourceBase
24
32
  from viam.resource.registry import Registry
25
- from viam.resource.types import Model, ResourceName, Subtype, resource_name_from_string
33
+ from viam.resource.types import API, RESOURCE_TYPE_COMPONENT, RESOURCE_TYPE_SERVICE, Model, ResourceName, resource_name_from_string
26
34
  from viam.robot.client import RobotClient
27
- from viam.rpc.dial import DialOptions
35
+ from viam.rpc.dial import DialOptions, _host_port_from_url
28
36
  from viam.rpc.server import Server
29
37
 
38
+ # These imports are required to register built-in resources with the registry
39
+ from ..components.arm import Arm # noqa: F401
40
+ from ..components.base import Base # noqa: F401
41
+ from ..components.board import Board # noqa: F401
42
+ from ..components.button import Button # noqa: F401
43
+ from ..components.camera import Camera # noqa: F401
44
+ from ..components.encoder import Encoder # noqa: F401
45
+ from ..components.gantry import Gantry # noqa: F401
46
+ from ..components.generic import Generic as GenericComponent # noqa: F401
47
+ from ..components.gripper import Gripper # noqa: F401
48
+ from ..components.input import Controller # noqa: F401
49
+ from ..components.motor import Motor # noqa: F401
50
+ from ..components.movement_sensor import MovementSensor # noqa: F401
51
+ from ..components.pose_tracker import PoseTracker # noqa: F401
52
+ from ..components.power_sensor import PowerSensor # noqa: F401
53
+ from ..components.sensor import Sensor # noqa: F401
54
+ from ..components.servo import Servo # noqa: F401
55
+ from ..components.switch import Switch # noqa: F401
56
+ from ..services.discovery import Discovery # noqa: F401
57
+ from ..services.generic import Generic as GenericService # noqa: F401
58
+ from ..services.motion import Motion # noqa: F401
59
+ from ..services.navigation import Navigation # noqa: F401
60
+ from ..services.slam import SLAM # noqa: F401
61
+ from ..services.vision import Vision # noqa: F401
30
62
  from .service import ModuleRPCService
31
63
  from .types import Reconfigurable, Stoppable
32
64
 
33
- LOGGER = logging.getLogger(__name__)
65
+ NO_MODULE_PARENT = os.environ.get("VIAM_NO_MODULE_PARENT", "").lower() == "true"
66
+
67
+
68
+ def _parse_module_args() -> argparse.Namespace:
69
+ """
70
+ Parse command-line args. Used by the various `Module` entrypoints.
71
+ """
72
+ p = argparse.ArgumentParser(description="Start this viam python module")
73
+ p.add_argument("socket_path", help="path where this module will serve a unix socket")
74
+ p.add_argument("--log-level", type=lambda name: pylogging._nameToLevel[name.upper()], default=logging.INFO)
75
+ p.add_argument("--tcp-mode", action="store_true")
76
+ return p.parse_known_args()[0]
34
77
 
35
78
 
36
79
  class Module:
@@ -39,17 +82,83 @@ class Module:
39
82
  _ready: bool
40
83
  _log_level: int
41
84
  _lock: Lock
85
+ _tcp_mode: bool
42
86
  parent: Optional[RobotClient] = None
43
87
  server: Server
88
+ logger: pylogging.Logger
89
+
90
+ @classmethod
91
+ def from_args(cls) -> Self:
92
+ """Create a new Module with the args provided in the command line. The first argument after the command must be
93
+ the socket path. If the second argument after the command is "--log-level=debug", the Module's logger will be
94
+ DEBUG level. Otherwise, it will be INFO level. See LogLevel documentation in the RDK for more information on how
95
+ to start modules with a "log-level" commandline argument.
96
+
97
+ Raises:
98
+ Exception: If there is no socket path provided in the command line argument
99
+
100
+ Returns:
101
+ Module: a new Module instance
102
+ """
103
+ args = _parse_module_args()
104
+ return cls(args.socket_path, log_level=args.log_level, tcp_mode=args.tcp_mode)
105
+
106
+ @classmethod
107
+ async def run_with_models(cls, *models: ResourceBase):
108
+ """
109
+ Module entrypoint that takes a list of ResourceBase implementations.
110
+ In most cases you'll want to use run_from_registry instead (see below).
111
+ """
112
+ module = cls.from_args()
113
+ for model in models:
114
+ if not hasattr(model, "MODEL"):
115
+ raise TypeError(f"missing MODEL field on {model}. Resource implementations must define MODEL")
116
+ module.add_model_from_registry(model.API, model.MODEL) # pyright: ignore [reportAttributeAccessIssue]
117
+ await module.start()
118
+
119
+ @classmethod
120
+ async def run_from_registry(cls):
121
+ """
122
+ Module entrypoint that automatically includes all the resources you've created in your program.
123
+
124
+ Example:
125
+
126
+ if __name__ == '__main__':
127
+ asyncio.run(Module.run_from_registry())
44
128
 
45
- def __init__(self, address: str, *, log_level: int = logging.DEBUG) -> None:
129
+ Full example at examples/easy_resource/main.py.
130
+ """
131
+ module = cls.from_args()
132
+ for key in Registry.REGISTERED_RESOURCE_CREATORS().keys():
133
+ module.add_model_from_registry(*key.split("/")) # pyright: ignore [reportArgumentType]
134
+ await module.start()
135
+
136
+ def __init__(self, address: str, *, log_level: int = logging.INFO, tcp_mode: bool = False) -> None:
137
+ # When a module is launched by viam-server, its stdout is not connected to a tty. In
138
+ # response, python disables line buffering, which prevents `print` statements from being
139
+ # immediately flushed to viam-server. This behavior can be confusing, interfere with
140
+ # debugging, and is non-standard when compared to other languages. Here, stdout and stderr
141
+ # are reconfigured to immediately flush.
142
+ if isinstance(sys.stdout, io.TextIOWrapper):
143
+ sys.stdout.reconfigure(line_buffering=True)
144
+ if isinstance(sys.stderr, io.TextIOWrapper):
145
+ sys.stderr.reconfigure(line_buffering=True)
46
146
  self._address = address
147
+ self._tcp_mode = tcp_mode
47
148
  self.server = Server(resources=[], module_service=ModuleRPCService(self))
48
149
  self._log_level = log_level
150
+
151
+ module_name = os.environ.get("VIAM_MODULE_NAME")
152
+ # this can happen if the user is running an old version of viam-server that doesn't set `VIAM_MODULE_NAME`
153
+ if module_name is None:
154
+ module_name = __name__
155
+ self.logger = logging.getLogger(module_name)
49
156
  self._ready = True
50
157
  self._lock = Lock()
51
158
 
52
159
  async def _connect_to_parent(self):
160
+ if NO_MODULE_PARENT:
161
+ return
53
162
  if self.parent is None:
54
163
  if self._parent_address is None:
55
164
  raise ValueError("Parent address not found")
@@ -60,40 +169,50 @@ class Module:
60
169
  log_level=self._log_level,
61
170
  ),
62
171
  )
172
+ self.logger.debug("Starting module logging")
173
+ logging.setParent(self.parent)
63
174
 
64
- async def _get_component(self, name: ResourceName) -> ComponentBase:
175
+ async def _get_resource(self, name: ResourceName) -> ResourceBase:
65
176
  await self._connect_to_parent()
66
177
  assert self.parent is not None
67
178
  await self.parent.refresh()
68
- return self.parent.get_component(name)
179
+ if name.type == RESOURCE_TYPE_COMPONENT:
180
+ return self.parent.get_component(name)
181
+ elif name.type == RESOURCE_TYPE_SERVICE:
182
+ return self.parent.get_service(name)
183
+ raise ValueError("Dependency does not describe a component nor a service")
69
184
 
70
- async def _get_dependencies(self, dependencies: Sequence[str]) -> Mapping[ResourceName, ComponentBase]:
71
- deps: Mapping[ResourceName, ComponentBase] = {}
185
+ async def _get_dependencies(self, dependencies: Sequence[str]) -> Mapping[ResourceName, ResourceBase]:
186
+ deps: Mapping[ResourceName, ResourceBase] = {}
72
187
  for dep in dependencies:
73
188
  rn = resource_name_from_string(dep)
74
- component = await self._get_component(rn)
75
- deps[rn] = component
189
+ deps[rn] = await self._get_resource(rn)
76
190
  return deps
77
191
 
78
192
  async def start(self):
79
193
  """Start the module service and gRPC server"""
80
194
  try:
81
- await self.server.serve(log_level=self._log_level, path=self._address)
195
+ if self._tcp_mode:
196
+ host, port = _host_port_from_url(self._address)
197
+ await self.server.serve(log_level=self._log_level, host=host, port=port)
198
+ else:
199
+ await self.server.serve(log_level=self._log_level, path=self._address)
82
200
  finally:
83
201
  await self.stop()
84
202
 
85
203
  async def stop(self):
86
204
  """Stop the module service and gRPC server"""
87
- LOGGER.debug("Shutting down module")
205
+ self.logger.debug("Shutting down module")
88
206
  try:
207
+ logging.shutdown()
89
208
  if self.parent is not None:
90
209
  await self.parent.close()
91
210
  except Exception as e:
92
- LOGGER.error("Encountered error while shutting down module", exc_info=e)
211
+ self.logger.error("Encountered error while shutting down module", exc_info=e)
93
212
 
94
213
  def set_ready(self, ready: bool):
95
214
  """Set the module's ready state. The module automatically sets to READY on load. Setting to False can be useful
96
- in instances where the module is not instantly ready (e.g. waiting on hardware)
215
+ in instances where the module is not instantly ready (for example waiting on hardware)
97
216
 
98
217
  Args:
99
218
  ready (bool): Whether the module is ready
@@ -101,21 +220,24 @@ class Module:
101
220
  with self._lock:
102
221
  self._ready = ready
103
222
 
104
- async def add_resource(self, request: AddResourceRequest):
223
+ async def add_resource(self, request: AddResourceRequest, *, deadline: Optional[Deadline] = None):
105
224
  dependencies = await self._get_dependencies(request.dependencies)
106
225
  config: ComponentConfig = request.config
107
- subtype = Subtype.from_string(config.api)
226
+ api = API.from_string(config.api)
108
227
  model = Model.from_string(config.model, ignore_errors=True)
109
- creator = Registry.lookup_resource_creator(subtype, model)
228
+ creator = Registry.lookup_resource_creator(api, model)
110
229
  resource = creator(config, dependencies)
230
+ if deadline is not None and deadline.time_remaining() <= 0:
231
+ raise TimeoutError("Deadline expired")
232
+ update_log_level(resource.logger, config.log_configuration.level.upper())
111
233
  self.server.register(resource)
112
234
 
113
235
  async def reconfigure_resource(self, request: ReconfigureResourceRequest):
114
236
  dependencies = await self._get_dependencies(request.dependencies)
115
237
  config: ComponentConfig = request.config
116
- subtype = Subtype.from_string(config.api)
238
+ api = API.from_string(config.api)
117
239
  name = config.name
118
- rn = ResourceName(namespace=subtype.namespace, type=subtype.resource_type, subtype=subtype.resource_subtype, name=name)
240
+ rn = ResourceName(namespace=api.namespace, type=api.resource_type, subtype=api.resource_subtype, name=name)
119
241
  resource = self.server.get_resource(ResourceBase, rn)
120
242
  if isinstance(resource, Reconfigurable):
121
243
  resource.reconfigure(config, dependencies)
@@ -126,44 +248,48 @@ class Module:
126
248
  else:
127
249
  resource.stop()
128
250
  add_request = AddResourceRequest(config=request.config, dependencies=request.dependencies)
129
- self.server.remove_resource(rn)
251
+ await self.server.remove_resource(rn)
130
252
  await self.add_resource(add_request)
131
253
 
132
254
  async def remove_resource(self, request: RemoveResourceRequest):
133
255
  rn = resource_name_from_string(request.name)
134
256
  resource = self.server.get_resource(ResourceBase, rn)
135
257
  if isinstance(resource, Stoppable):
136
- if iscoroutinefunction(resource.stop):
137
- await resource.stop()
138
- else:
139
- resource.stop()
140
- self.server.remove_resource(rn)
258
+ try:
259
+ if iscoroutinefunction(resource.stop):
260
+ await resource.stop()
261
+ else:
262
+ resource.stop()
263
+ except Exception as e:
264
+ self.logger.warning(f"Could not remove resource named {resource.name}", exc_info=e)
265
+ await self.server.remove_resource(rn)
141
266
 
142
267
  async def ready(self, request: ReadyRequest) -> ReadyResponse:
143
268
  self._parent_address = request.parent_address
269
+ await self._connect_to_parent()
144
270
 
145
- svcname_to_models: Mapping[Tuple[str, Subtype], List[Model]] = {}
146
- for subtype_model_str in Registry.REGISTERED_RESOURCE_CREATORS().keys():
147
- subtype_str, model_str = subtype_model_str.split("/")
148
- subtype = Subtype.from_string(subtype_str)
271
+ svcname_to_models: Mapping[Tuple[str, API], List[Model]] = {}
272
+ for api_model_str in Registry.REGISTERED_RESOURCE_CREATORS().keys():
273
+ api_str, model_str = api_model_str.split("/")
274
+ api = API.from_string(api_str)
149
275
  model = Model.from_string(model_str)
150
276
 
151
- registration = Registry.lookup_subtype(subtype)
277
+ registration = Registry.lookup_api(api)
152
278
  service = registration.rpc_service(self.server)
153
279
  service_name = _service_name(service)
154
280
 
155
- models = svcname_to_models.get((service_name, subtype), [])
281
+ models = svcname_to_models.get((service_name, api), [])
156
282
  models.append(model)
157
- svcname_to_models[(service_name, subtype)] = models
283
+ svcname_to_models[(service_name, api)] = models
158
284
 
159
285
  handlers: List[HandlerDefinition] = []
160
286
  for key, value in svcname_to_models.items():
161
- svc_name, subtype = key
287
+ svc_name, api = key
162
288
  rpc_subtype = ResourceRPCSubtype(
163
289
  subtype=ResourceName(
164
- namespace=subtype.namespace,
165
- type=subtype.resource_type,
166
- subtype=subtype.resource_subtype,
290
+ namespace=api.namespace,
291
+ type=api.resource_type,
292
+ subtype=api.resource_subtype,
167
293
  name="",
168
294
  ),
169
295
  proto_service=svc_name,
@@ -173,22 +299,43 @@ class Module:
173
299
 
174
300
  return ReadyResponse(ready=self._ready, handlermap=HandlerMap(handlers=handlers))
175
301
 
176
- def add_model_from_registry(self, subtype: Subtype, model: Model):
302
+ def add_model_from_registry(self, api: API, model: Model):
177
303
  """Add a pre-registered model to this Module"""
178
304
 
179
305
  # All we need to do is double check that the model has already been registered
180
306
  try:
181
- Registry.lookup_resource_creator(subtype, model)
307
+ Registry.lookup_resource_creator(api, model)
182
308
  except ResourceNotFoundError:
183
- raise ValueError(f"Cannot add model because it has not been registered. Subtype: {subtype}. Model: {model}")
309
+ raise ValueError(f"Cannot add model because it has not been registered. API: {api}. Model: {model}")
184
310
 
185
311
  async def validate_config(self, request: ValidateConfigRequest) -> ValidateConfigResponse:
186
312
  config: ComponentConfig = request.config
187
- subtype = Subtype.from_string(config.api)
313
+ api = API.from_string(config.api)
188
314
  model = Model.from_string(config.model)
189
- validator = Registry.lookup_validator(subtype, model)
315
+ validator = Registry.lookup_validator(api, model)
190
316
  try:
191
- dependencies = validator(config)
192
- return ValidateConfigResponse(dependencies=dependencies)
317
+ # backwards compatibility. Support both ([], []) or [] with deprecation warning.
318
+ # If user's validate returns [str], it will be treated as required dependencies only.
319
+ # Incorect formats, e.g. int, will raise ValidationError.
320
+ _validator_return_test = validator(config)
321
+ if not (isinstance(_validator_return_test, tuple) and len(_validator_return_test) == 2):
322
+ msg = f"Your validate function {validator.__name__} did not return \
323
+ type tuple[Sequence[str], Sequence[str]]. Got {_validator_return_test}."
324
+ self.logger.warning(msg)
325
+ if (isinstance(_validator_return_test, Iterable) and not isinstance(_validator_return_test, str)) and all(
326
+ isinstance(e, str)
327
+ for e in _validator_return_test # type: ignore
328
+ ):
329
+ self.logger.warning(
330
+ f"Detected deprecated validate function signature. \
331
+ Treating all dependencies {_validator_return_test} as required dependencies. \
332
+ Please update to new signature Tuple[Sequence[str], Sequence[str]] soon."
333
+ )
334
+ return ValidateConfigResponse(dependencies=_validator_return_test)
335
+ else:
336
+ raise ValidationError(msg)
337
+
338
+ dependencies, optional_dependencies = _validator_return_test
339
+ return ValidateConfigResponse(dependencies=dependencies, optional_dependencies=optional_dependencies)
193
340
  except Exception as e:
194
341
  raise ValidationError(f"{type(Exception)}: {e}").grpc_error