diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index c463acd..32e32a7 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -3973,7 +3973,7 @@ "os:linux,arch:amd64": { "bzlTransitiveDigest": "O6takntg2fy8btRIFrv9hsGqQu89+vUPw5N/OGj+EuI=", "recordedFileInputs": { - "@@//requirements_lock.txt": "7f619226d8d5919afa9c944dbbecb48c08251f8ce7838b5c0d2a87db13e7d81f" + "@@//requirements_lock.txt": "e10b4e4ce0b128f529873655b3287e356ba1a314901835ffd2e0cffcd28d38b3" }, "recordedDirentsInputs": {}, "envVariables": {}, @@ -4024,6 +4024,29 @@ "group_deps": [] } }, + "pypi_311_pyserial": { + "bzlFile": "@@rules_python~//python/pip_install:pip_repository.bzl", + "ruleClassName": "whl_library", + "attributes": { + "requirement": "pyserial==3.5 --hash=sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb --hash=sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0", + "repo": "pypi_311", + "repo_prefix": "pypi_311_", + "whl_patches": {}, + "experimental_target_platforms": [], + "python_interpreter": "", + "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "quiet": true, + "timeout": 600, + "isolated": true, + "extra_pip_args": [], + "download_only": false, + "pip_data_exclude": [], + "enable_implicit_namespace_pkgs": false, + "environment": {}, + "group_name": "", + "group_deps": [] + } + }, "pypi_311_numpy": { "bzlFile": "@@rules_python~//python/pip_install:pip_repository.bzl", "ruleClassName": "whl_library", @@ -4277,6 +4300,29 @@ "group_deps": [] } }, + "pypi_311_pygame": { + "bzlFile": "@@rules_python~//python/pip_install:pip_repository.bzl", + "ruleClassName": "whl_library", + "attributes": { + "requirement": "pygame==2.5.2 --hash=sha256:03879ec299c9f4ba23901b2649a96b2143f0a5d787f0b6c39469989e2320caf1 --hash=sha256:074aa6c6e110c925f7f27f00c7733c6303407edc61d738882985091d1eb2ef17 --hash=sha256:0e24d05184e4195fe5ebcdce8b18ecb086f00182b9ae460a86682d312ce8d31f --hash=sha256:1822d534bb7fe756804647b6da2c9ea5d7a62d8796b2e15d172d3be085de28c6 --hash=sha256:1a2a43802bb5e89ce2b3b775744e78db4f9a201bf8d059b946c61722840ceea8 --hash=sha256:1c289f2613c44fe70a1e40769de4a49c5ab5a29b9376f1692bb1a15c9c1c9bfa --hash=sha256:1f3849f97372a3381c66955f99a0d58485ccd513c3d00c030b869094ce6997a6 --hash=sha256:224c308856334bc792f696e9278e50d099a87c116f7fc314cd6aa3ff99d21592 --hash=sha256:263b4a7cbfc9fe2055abc21b0251cc17dea6dff750f0e1c598919ff350cdbffe --hash=sha256:2b34c73cb328024f8db3cb6487a37e54000148988275d8d6e5adf99d9323c937 --hash=sha256:30a8d7cf12363b4140bf2f93b5eec4028376ca1d0fe4b550588f836279485308 --hash=sha256:31648d38ecdc2335ffc0e38fb18a84b3339730521505dac68514f83a1092e3f4 --hash=sha256:34646ca20e163dc6f6cf8170f1e12a2e41726780112594ac061fa448cf7ccd75 --hash=sha256:35632035fd81261f2d797fa810ea8c46111bd78ceb6089d52b61ed7dc3c5d05f --hash=sha256:35cf093a51cb294ede56c29d4acf41538c00f297fcf78a9b186fb7d23c0577b6 --hash=sha256:39690e9be9baf58b7359d1f3b2336e1fd6f92fedbbce42987be5df27f8d30718 --hash=sha256:3b3e619e33d11c297d7a57a82db40681f9c2c3ae1d5bf06003520b4fe30c435d --hash=sha256:3b8a6e351665ed26ea791f0e1fd649d3f483e8681892caef9d471f488f9ea5ee --hash=sha256:41f8779f52e0f6e6e6ccb8f0b5536e432bf386ee29c721a1c22cada7767b0cef --hash=sha256:47a8415d2bd60e6909823b5643a1d4ef5cc29417d817f2a214b255f6fa3a1e4c --hash=sha256:485239c7d32265fd35b76ae8f64f34b0637ae11e69d76de15710c4b9edcc7c8d --hash=sha256:4f1559e7efe4efb9dc19d2d811d702f325d9605f9f6f9ececa39ee6890c798f5 --hash=sha256:4ff21201df6278b8ca2e948fb148ffe88f5481fd03760f381dd61e45954c7dff --hash=sha256:5697528266b4716d9cdd44a5a1d210f4d86ef801d0f64ca5da5d0816704009d9 --hash=sha256:677e37bc0ea7afd89dde5a88ced4458aa8656159c70a576eea68b5622ee1997b --hash=sha256:68c4e8e60b725ffc7a6c6ecd9bb5fcc5ed2d6e0e2a2c4a29a8454856ef16ad63 --hash=sha256:6cf2257447ce7f2d6de37e5fb019d2bbe32ed05a5721ace8bc78c2d9beaf3aee --hash=sha256:6d58c8cf937815d3b7cdc0fa9590c5129cb2c9658b72d00e8a4568dea2ff1d42 --hash=sha256:6fe323acbf53a0195c8c98b1b941eba7ac24e3e2b28ae48e8cda566f15fc4945 --hash=sha256:74e1d6284100e294f445832e6f6343be4fe4748decc4f8a51131ae197dae8584 --hash=sha256:78fcd7643358b886a44127ff7dec9041c056c212b3a98977674f83f99e9b12d3 --hash=sha256:7d0a2794649defa57ef50b096a99f7113d3d0c2e32d1426cafa7d618eadce4c7 --hash=sha256:88d1cdacc2d3471eceab98bf0c93c14d3a8461f93e58e3d926f20d4de3a75554 --hash=sha256:9b30bc1220c457169571aac998e54b013aaeb732d2fd8744966cb1cfab1f61d1 --hash=sha256:9bd738fd4ecc224769d0b4a719f96900a86578e26e0105193658a32966df2aae --hash=sha256:9dcff6cbba1584cf7732ce1dbdd044406cd4f6e296d13bcb7fba963fb4aeefc9 --hash=sha256:a0769eb628c818761755eb0a0ca8216b95270ea8cbcbc82227e39ac9644643da --hash=sha256:a0bd67426c02ffe6c9827fc4bcbda9442fbc451d29b17c83a3c088c56fef2c90 --hash=sha256:bc12e4dea3e88ea8a553de6d56a37b704dbe2aed95105889f6afeb4b96e62097 --hash=sha256:c13edebc43c240fb0532969e914f0ccefff5ae7e50b0b788d08ad2c15ef793e4 --hash=sha256:c1b89eb5d539e7ac5cf75513125fb5f2f0a2d918b1fd6e981f23bf0ac1b1c24a --hash=sha256:ce4b6c0bfe44d00bb0998a6517bd0cf9455f642f30f91bc671ad41c05bf6f6ae --hash=sha256:cf2191b756ceb0e8458a761d0c665b0c70b538570449e0d39b75a5ba94ac5cf0 --hash=sha256:d29a84b2e02814b9ba925357fd2e1df78efe5e1aa64dc3051eaed95d2b96eafd --hash=sha256:d75cbbfaba2b81434d62631d0b08b85fab16cf4a36e40b80298d3868927e1299 --hash=sha256:d78485c4d21133d6b2fbb504cd544ca655e50b6eb551d2995b3aa6035928adda --hash=sha256:d851247239548aa357c4a6840fb67adc2d570ce7cb56988d036a723d26b48bff --hash=sha256:daca456d5b9f52e088e06a127dec182b3638a775684fb2260f25d664351cf1ae --hash=sha256:dc346965847aef00013fa2364f41a64f068cd096dcc7778fc306ca3735f0eedf --hash=sha256:dd2d2650faf54f9a0f5bd0db8409f79609319725f8f08af6507a0609deadcad4 --hash=sha256:e58e2b0c791041e4bccafa5bd7650623ba1592b8fe62ae0a276b7d0ecb314b6c --hash=sha256:e708fc8f709a0fe1d1876489345f2e443d47f3976d33455e2e1e937f972f8677 --hash=sha256:ed9a3d98adafa0805ccbaaff5d2996a2b5795381285d8437a4a5d248dbd12b4a --hash=sha256:edda1f7cff4806a4fa39e0e8ccd75f38d1d340fa5fc52d8582ade87aca247d92 --hash=sha256:f02c1c7505af18d426d355ac9872bd5c916b27f7b0fe224749930662bea47a50 --hash=sha256:f30d1618672a55e8c6669281ba264464b3ab563158e40d89e8c8b3faa0febebd --hash=sha256:fe0228501ec616779a0b9c4299e837877783e18df294dd690b9ab0eed3d8aaab", + "repo": "pypi_311", + "repo_prefix": "pypi_311_", + "whl_patches": {}, + "experimental_target_platforms": [], + "python_interpreter": "", + "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "quiet": true, + "timeout": 600, + "isolated": true, + "extra_pip_args": [], + "download_only": false, + "pip_data_exclude": [], + "enable_implicit_namespace_pkgs": false, + "environment": {}, + "group_name": "", + "group_deps": [] + } + }, "pypi_311_python_dotenv": { "bzlFile": "@@rules_python~//python/pip_install:pip_repository.bzl", "ruleClassName": "whl_library", @@ -4300,6 +4346,29 @@ "group_deps": [] } }, + "pypi_311_dynamixel_sdk": { + "bzlFile": "@@rules_python~//python/pip_install:pip_repository.bzl", + "ruleClassName": "whl_library", + "attributes": { + "requirement": "dynamixel-sdk==3.7.31 --hash=sha256:74e8c112ca6b0b869b196dd8c6a44ffd5dd5c1a3cb9fe2030e9933922406b466", + "repo": "pypi_311", + "repo_prefix": "pypi_311_", + "whl_patches": {}, + "experimental_target_platforms": [], + "python_interpreter": "", + "python_interpreter_target": "@@rules_python~~python~python_3_11_host//:python", + "quiet": true, + "timeout": 600, + "isolated": true, + "extra_pip_args": [], + "download_only": false, + "pip_data_exclude": [], + "enable_implicit_namespace_pkgs": false, + "environment": {}, + "group_name": "", + "group_deps": [] + } + }, "pypi_311_pyyaml": { "bzlFile": "@@rules_python~//python/pip_install:pip_repository.bzl", "ruleClassName": "whl_library", @@ -4510,6 +4579,9 @@ "click": [ "3.11" ], + "dynamixel_sdk": [ + "3.11" + ], "fastapi": [ "3.11" ], @@ -4540,6 +4612,12 @@ "pydantic_core": [ "3.11" ], + "pygame": [ + "3.11" + ], + "pyserial": [ + "3.11" + ], "python_dotenv": [ "3.11" ], diff --git a/requirements.in b/requirements.in index 7eec5ea..19d7ba7 100644 --- a/requirements.in +++ b/requirements.in @@ -1,7 +1,9 @@ asyncio +dynamixel_sdk fastapi jinja2 numpy opencv-python +pygame uvicorn[standard] -websockets \ No newline at end of file +websockets diff --git a/requirements_lock.txt b/requirements_lock.txt index 2f1ffd1..6353f98 100644 --- a/requirements_lock.txt +++ b/requirements_lock.txt @@ -24,6 +24,9 @@ click==8.1.7 \ --hash=sha256:ae74fb96c20a0277a1d615f1e4d73c8414f5a98db8b799a7931d1582f3390c28 \ --hash=sha256:ca9853ad459e787e2192211578cc907e7594e294c7ccc834310722b41b9ca6de # via uvicorn +dynamixel-sdk==3.7.31 \ + --hash=sha256:74e8c112ca6b0b869b196dd8c6a44ffd5dd5c1a3cb9fe2030e9933922406b466 + # via -r requirements.in fastapi==0.110.0 \ --hash=sha256:266775f0dcc95af9d3ef39bad55cff525329a931d5fd51930aadd4f428bf7ff3 \ --hash=sha256:87a1f6fb632a218222c5984be540055346a8f5d8a68e8f6fb647b1dc9934de4b @@ -274,6 +277,69 @@ pydantic-core==2.16.3 \ --hash=sha256:fc4ad7f7ee1a13d9cb49d8198cd7d7e3aa93e425f371a68235f784e99741561f \ --hash=sha256:fee427241c2d9fb7192b658190f9f5fd6dfe41e02f3c1489d2ec1e6a5ab1e04a # via pydantic +pygame==2.5.2 \ + --hash=sha256:03879ec299c9f4ba23901b2649a96b2143f0a5d787f0b6c39469989e2320caf1 \ + --hash=sha256:074aa6c6e110c925f7f27f00c7733c6303407edc61d738882985091d1eb2ef17 \ + --hash=sha256:0e24d05184e4195fe5ebcdce8b18ecb086f00182b9ae460a86682d312ce8d31f \ + --hash=sha256:1822d534bb7fe756804647b6da2c9ea5d7a62d8796b2e15d172d3be085de28c6 \ + --hash=sha256:1a2a43802bb5e89ce2b3b775744e78db4f9a201bf8d059b946c61722840ceea8 \ + --hash=sha256:1c289f2613c44fe70a1e40769de4a49c5ab5a29b9376f1692bb1a15c9c1c9bfa \ + --hash=sha256:1f3849f97372a3381c66955f99a0d58485ccd513c3d00c030b869094ce6997a6 \ + --hash=sha256:224c308856334bc792f696e9278e50d099a87c116f7fc314cd6aa3ff99d21592 \ + --hash=sha256:263b4a7cbfc9fe2055abc21b0251cc17dea6dff750f0e1c598919ff350cdbffe \ + --hash=sha256:2b34c73cb328024f8db3cb6487a37e54000148988275d8d6e5adf99d9323c937 \ + --hash=sha256:30a8d7cf12363b4140bf2f93b5eec4028376ca1d0fe4b550588f836279485308 \ + --hash=sha256:31648d38ecdc2335ffc0e38fb18a84b3339730521505dac68514f83a1092e3f4 \ + --hash=sha256:34646ca20e163dc6f6cf8170f1e12a2e41726780112594ac061fa448cf7ccd75 \ + --hash=sha256:35632035fd81261f2d797fa810ea8c46111bd78ceb6089d52b61ed7dc3c5d05f \ + --hash=sha256:35cf093a51cb294ede56c29d4acf41538c00f297fcf78a9b186fb7d23c0577b6 \ + --hash=sha256:39690e9be9baf58b7359d1f3b2336e1fd6f92fedbbce42987be5df27f8d30718 \ + --hash=sha256:3b3e619e33d11c297d7a57a82db40681f9c2c3ae1d5bf06003520b4fe30c435d \ + --hash=sha256:3b8a6e351665ed26ea791f0e1fd649d3f483e8681892caef9d471f488f9ea5ee \ + --hash=sha256:41f8779f52e0f6e6e6ccb8f0b5536e432bf386ee29c721a1c22cada7767b0cef \ + --hash=sha256:47a8415d2bd60e6909823b5643a1d4ef5cc29417d817f2a214b255f6fa3a1e4c \ + --hash=sha256:485239c7d32265fd35b76ae8f64f34b0637ae11e69d76de15710c4b9edcc7c8d \ + --hash=sha256:4f1559e7efe4efb9dc19d2d811d702f325d9605f9f6f9ececa39ee6890c798f5 \ + --hash=sha256:4ff21201df6278b8ca2e948fb148ffe88f5481fd03760f381dd61e45954c7dff \ + --hash=sha256:5697528266b4716d9cdd44a5a1d210f4d86ef801d0f64ca5da5d0816704009d9 \ + --hash=sha256:677e37bc0ea7afd89dde5a88ced4458aa8656159c70a576eea68b5622ee1997b \ + --hash=sha256:68c4e8e60b725ffc7a6c6ecd9bb5fcc5ed2d6e0e2a2c4a29a8454856ef16ad63 \ + --hash=sha256:6cf2257447ce7f2d6de37e5fb019d2bbe32ed05a5721ace8bc78c2d9beaf3aee \ + --hash=sha256:6d58c8cf937815d3b7cdc0fa9590c5129cb2c9658b72d00e8a4568dea2ff1d42 \ + --hash=sha256:6fe323acbf53a0195c8c98b1b941eba7ac24e3e2b28ae48e8cda566f15fc4945 \ + --hash=sha256:74e1d6284100e294f445832e6f6343be4fe4748decc4f8a51131ae197dae8584 \ + --hash=sha256:78fcd7643358b886a44127ff7dec9041c056c212b3a98977674f83f99e9b12d3 \ + --hash=sha256:7d0a2794649defa57ef50b096a99f7113d3d0c2e32d1426cafa7d618eadce4c7 \ + --hash=sha256:88d1cdacc2d3471eceab98bf0c93c14d3a8461f93e58e3d926f20d4de3a75554 \ + --hash=sha256:9b30bc1220c457169571aac998e54b013aaeb732d2fd8744966cb1cfab1f61d1 \ + --hash=sha256:9bd738fd4ecc224769d0b4a719f96900a86578e26e0105193658a32966df2aae \ + --hash=sha256:9dcff6cbba1584cf7732ce1dbdd044406cd4f6e296d13bcb7fba963fb4aeefc9 \ + --hash=sha256:a0769eb628c818761755eb0a0ca8216b95270ea8cbcbc82227e39ac9644643da \ + --hash=sha256:a0bd67426c02ffe6c9827fc4bcbda9442fbc451d29b17c83a3c088c56fef2c90 \ + --hash=sha256:bc12e4dea3e88ea8a553de6d56a37b704dbe2aed95105889f6afeb4b96e62097 \ + --hash=sha256:c13edebc43c240fb0532969e914f0ccefff5ae7e50b0b788d08ad2c15ef793e4 \ + --hash=sha256:c1b89eb5d539e7ac5cf75513125fb5f2f0a2d918b1fd6e981f23bf0ac1b1c24a \ + --hash=sha256:ce4b6c0bfe44d00bb0998a6517bd0cf9455f642f30f91bc671ad41c05bf6f6ae \ + --hash=sha256:cf2191b756ceb0e8458a761d0c665b0c70b538570449e0d39b75a5ba94ac5cf0 \ + --hash=sha256:d29a84b2e02814b9ba925357fd2e1df78efe5e1aa64dc3051eaed95d2b96eafd \ + --hash=sha256:d75cbbfaba2b81434d62631d0b08b85fab16cf4a36e40b80298d3868927e1299 \ + --hash=sha256:d78485c4d21133d6b2fbb504cd544ca655e50b6eb551d2995b3aa6035928adda \ + --hash=sha256:d851247239548aa357c4a6840fb67adc2d570ce7cb56988d036a723d26b48bff \ + --hash=sha256:daca456d5b9f52e088e06a127dec182b3638a775684fb2260f25d664351cf1ae \ + --hash=sha256:dc346965847aef00013fa2364f41a64f068cd096dcc7778fc306ca3735f0eedf \ + --hash=sha256:dd2d2650faf54f9a0f5bd0db8409f79609319725f8f08af6507a0609deadcad4 \ + --hash=sha256:e58e2b0c791041e4bccafa5bd7650623ba1592b8fe62ae0a276b7d0ecb314b6c \ + --hash=sha256:e708fc8f709a0fe1d1876489345f2e443d47f3976d33455e2e1e937f972f8677 \ + --hash=sha256:ed9a3d98adafa0805ccbaaff5d2996a2b5795381285d8437a4a5d248dbd12b4a \ + --hash=sha256:edda1f7cff4806a4fa39e0e8ccd75f38d1d340fa5fc52d8582ade87aca247d92 \ + --hash=sha256:f02c1c7505af18d426d355ac9872bd5c916b27f7b0fe224749930662bea47a50 \ + --hash=sha256:f30d1618672a55e8c6669281ba264464b3ab563158e40d89e8c8b3faa0febebd \ + --hash=sha256:fe0228501ec616779a0b9c4299e837877783e18df294dd690b9ab0eed3d8aaab + # via -r requirements.in +pyserial==3.5 \ + --hash=sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb \ + --hash=sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0 + # via dynamixel-sdk python-dotenv==1.0.1 \ --hash=sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca \ --hash=sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a diff --git a/src/api/v2/BUILD.bazel b/src/api/BUILD.bazel similarity index 81% rename from src/api/v2/BUILD.bazel rename to src/api/BUILD.bazel index 3f685eb..5469438 100644 --- a/src/api/v2/BUILD.bazel +++ b/src/api/BUILD.bazel @@ -7,20 +7,30 @@ py_library( name = "api_src", srcs = ["api.py"], deps = [ + ":robot_src", "@pypi//fastapi:pkg", "@pypi//jinja2:pkg", "@pypi//opencv_python:pkg", + "@pypi//pygame:pkg", "@pypi//websockets:pkg", ], ) +py_library( + name = "robot_src", + srcs = ["robot.py"], + deps = [ + "@pypi//dynamixel_sdk:pkg", + ], +) + # This is how we execute the API. This forces the API # to run under uvicorn, which serves the app for the user. py_binary( name = "main", srcs = ["main.py"], args = [ - "src.api.v2.api:app", + "src.api.api:app", "--host", "0.0.0.0", ], @@ -39,11 +49,11 @@ py_oci_image( base = "@py3.11", binary = ":main", cmd = [ - "src.api.v2.api:app", + "src.api.api:app", "--host", "0.0.0.0", ], - entrypoint = ["src/api/v2/main"], + entrypoint = ["src/api/main"], exposed_ports = ["8000"], ) @@ -53,7 +63,7 @@ py_oci_image( oci_tarball( name = "tarball", image = ":image", - repo_tags = ["trentonbot_api_v2:latest"], + repo_tags = ["trentonbot_api:latest"], visibility = ["//visibility:public"], ) @@ -61,5 +71,5 @@ oci_push( name = "push", image = ":image", remote_tags = ["v0.0.1"], - repository = "trentonbot_api_v2", + repository = "trentonbot_api", ) diff --git a/src/api/v2/README.md b/src/api/README.md similarity index 54% rename from src/api/v2/README.md rename to src/api/README.md index aac7e93..3721020 100644 --- a/src/api/v2/README.md +++ b/src/api/README.md @@ -1,4 +1,46 @@ -# TrentonBot API +# API v1 + +This version of the API uses a local venv to install packages, and only runs locally. + +## Creating a venv + +Use the following command to create your venv: + +``` +python3.11 -m venv venv +``` + +From here, you can source your venv with the following commands. + +Windows: + +``` +.\venv\Scripts\Activate.ps1 +``` + +Linux: + +``` +. venv/bin/activate +``` + +## Installing needed packages + +You can install packages required for the application using the command: + +``` +pip install -r requirements.txt +``` + +## Saving pip requirements + +Run the following command to update the `requirements.txt` file, which is needed whenever you install a new package: + +``` +pip freeze > requirements.txt +``` + +# API v2 This API allows for interfacing between the webapp and the vehicle code over ROS 2. @@ -9,7 +51,7 @@ This API allows for interfacing between the webapp and the vehicle code over ROS You can test this code locally by running the command: ``` -bazel run //src/api/v2:main +bazel run //src/api:main ``` This will start up the API using uvicorn, running on port 8000 unless the port is in use. @@ -17,7 +59,7 @@ This will start up the API using uvicorn, running on port 8000 unless the port i If you want to use args with the app, you can run the following: ``` -bazel run //src/api/v2:main -- +bazel run //src/api:main -- ``` The extra `--` allows us to skip over the _bazel_ arguments, and start passing arguments directly to our process. @@ -29,22 +71,22 @@ The extra `--` allows us to skip over the _bazel_ arguments, and start passing a The docker image can be built and published locally using the command: ``` -bazel run //src/api/v2:tarball +bazel run //src/api:tarball ``` This will publish the image to your local docker images based on the provided tag: ``` -INFO: Running command line: bazel-bin/src/api/v2/tarball.sh +INFO: Running command line: bazel-bin/src/api/tarball.sh f4ba9898d2a2: Loading layer 17.56kB/17.56kB -The image trentonbot_api_v2:latest already exists, renaming the old one with ID sha256:ccb7026cdc6bcb8f6a46a86b8e363581f33e5168f5cab5cde88f77cd8e0539b5 to empty string -Loaded image: trentonbot_api_v2:latest +The image trentonbot_api:latest already exists, renaming the old one with ID sha256:ccb7026cdc6bcb8f6a46a86b8e363581f33e5168f5cab5cde88f77cd8e0539b5 to empty string +Loaded image: trentonbot_api:latest ``` In this case, if you want to run the image locally, you can used the loaded image above to run: ``` -docker run -p 8000:8000 trentonbot_api_v2:latest +docker run -p 8000:8000 trentonbot_api:latest ``` This will spin up the docker container locally, and allow you to test from it. diff --git a/src/api/v2/api.py b/src/api/api.py similarity index 84% rename from src/api/v2/api.py rename to src/api/api.py index 8c10512..100157c 100644 --- a/src/api/v2/api.py +++ b/src/api/api.py @@ -2,7 +2,7 @@ from websockets.exceptions import ConnectionClosed from fastapi.templating import Jinja2Templates from fastapi.middleware.cors import CORSMiddleware -from robot import ( +from src.api.robot import ( Position2D, RobotControl, DynamixelMotor, @@ -11,6 +11,9 @@ from pygame import mixer import asyncio import cv2 +import logging + +logger = logging.getLogger(__file__) app = FastAPI() camera = cv2.VideoCapture(0) @@ -32,7 +35,18 @@ DynamixelMotor(13, DYNAMIXEL_MX_12_ADDR_CONFIG, Position2D(0, 0, 0), True), ] ctrl = RobotControl("/dev/ttyUSB0", 1, motors) -ctrl.init(1000000) + +try: + ctrl.init(1000000) +except: + logger.error( + "Unable to connect to Dynamixel, continuing without motor connection..." + ) + + +@app.get("/status") +def status(): + return {"status": "online"} # https://stackoverflow.com/a/70626324 @@ -71,9 +85,9 @@ async def command(request: Request): data = await request.json() if data.get("sfx") == "bloop": - mixer.music.load("./bloop.mp3") + mixer.music.load("./assets/bloop.mp3") elif data.get("sfx") == "bong": - mixer.music.load("./bong.mp3") + mixer.music.load("./assets/bong.mp3") mixer.music.play() return {"status": "success"} diff --git a/src/api/v1/main.py b/src/api/main.py similarity index 100% rename from src/api/v1/main.py rename to src/api/main.py diff --git a/src/api/requirements.txt b/src/api/requirements.txt new file mode 100644 index 0000000..db899ac --- /dev/null +++ b/src/api/requirements.txt @@ -0,0 +1,163 @@ +action-msgs==1.2.1 +action-tutorials-interfaces==0.20.3 +action-tutorials-py==0.20.3 +actionlib-msgs==4.2.3 +ament-cmake-test==1.3.8 +ament-copyright==0.12.10 +ament-cppcheck==0.12.10 +ament-cpplint==0.12.10 +ament-flake8==0.12.10 +ament-index-python==1.4.0 +ament-lint==0.12.10 +ament-lint-cmake==0.12.10 +ament-package==0.14.0 +ament-pep257==0.12.10 +ament-uncrustify==0.12.10 +ament-xmllint==0.12.10 +angles==1.15.0 +annotated-types==0.6.0 +anyio==4.3.0 +builtin-interfaces==1.2.1 +click==8.1.7 +composition-interfaces==1.2.1 +control-msgs==4.4.0 +controller-manager-msgs==2.40.0 +cv-bridge==3.2.1 +demo-nodes-py==0.20.3 +diagnostic-msgs==4.2.3 +domain-coordinator==0.10.0 +dynamixel-sdk==3.7.31 +example-interfaces==0.9.3 +examples-rclpy-executors==0.15.1 +examples-rclpy-minimal-action-client==0.15.1 +examples-rclpy-minimal-action-server==0.15.1 +examples-rclpy-minimal-client==0.15.1 +examples-rclpy-minimal-publisher==0.15.1 +examples-rclpy-minimal-service==0.15.1 +examples-rclpy-minimal-subscriber==0.15.1 +fastapi==0.110.2 +geometry-msgs==4.2.3 +h11==0.14.0 +idna==3.7 +image-geometry==3.2.1 +interactive-markers==2.3.2 +Jinja2==3.1.3 +laser-geometry==2.4.0 +launch==1.0.5 +launch-ros==0.19.7 +launch-testing==1.0.5 +launch-testing-ros==0.19.7 +launch-xml==1.0.5 +launch-yaml==1.0.5 +lifecycle-msgs==1.2.1 +logging-demo==0.20.3 +map-msgs==2.1.0 +MarkupSafe==2.1.5 +message-filters==4.3.3 +mocap4r2-control-msgs==0.0.7 +nav-msgs==4.2.3 +numpy==1.26.4 +opencv-python==4.9.0.80 +osrf-pycommon==2.0.2 +pcl-msgs==1.0.0 +pendulum-msgs==0.20.3 +pydantic==2.7.0 +pydantic_core==2.18.1 +pygame==2.5.2 +pyserial==3.5 +python-qt-binding==1.1.2 +qt-dotgraph==2.2.3 +qt-gui==2.2.3 +qt-gui-cpp==2.2.3 +qt-gui-py-common==2.2.3 +quality-of-service-demo-py==0.20.3 +rcl-interfaces==1.2.1 +rclpy==3.3.12 +rcutils==5.1.5 +resource-retriever==3.1.1 +rmw-dds-common==1.6.0 +ros2action==0.18.9 +ros2bag==0.15.9 +ros2cli==0.18.9 +ros2component==0.18.9 +ros2doctor==0.18.9 +ros2interface==0.18.9 +ros2launch==0.19.7 +ros2lifecycle==0.18.9 +ros2multicast==0.18.9 +ros2node==0.18.9 +ros2param==0.18.9 +ros2pkg==0.18.9 +ros2run==0.18.9 +ros2service==0.18.9 +ros2topic==0.18.9 +rosbag2-interfaces==0.15.9 +rosbag2-py==0.15.9 +rosgraph-msgs==1.2.1 +rosidl-adapter==3.1.5 +rosidl-cli==3.1.5 +rosidl-cmake==3.1.5 +rosidl-generator-c==3.1.5 +rosidl-generator-cpp==3.1.5 +rosidl-generator-py==0.14.4 +rosidl-parser==3.1.5 +rosidl-runtime-py==0.9.3 +rosidl-typesupport-c==2.0.1 +rosidl-typesupport-cpp==2.0.1 +rosidl-typesupport-fastrtps-c==2.2.2 +rosidl-typesupport-fastrtps-cpp==2.2.2 +rosidl-typesupport-introspection-c==3.1.5 +rosidl-typesupport-introspection-cpp==3.1.5 +rpyutils==0.2.1 +rqt==1.1.7 +rqt-action==2.0.1 +rqt-bag==1.1.4 +rqt-bag-plugins==1.1.4 +rqt-console==2.0.3 +rqt-controller-manager==2.40.0 +rqt-gauges==0.0.3 +rqt-graph==1.3.0 +rqt-gui==1.1.7 +rqt-gui-py==1.1.7 +rqt-joint-trajectory-controller==2.34.0 +rqt-moveit==1.0.1 +rqt-msg==1.2.0 +rqt-plot==1.1.2 +rqt-publisher==1.5.0 +rqt-py-common==1.1.7 +rqt-py-console==1.0.2 +rqt-reconfigure==1.1.2 +rqt-robot-dashboard==0.5.8 +rqt-robot-monitor==1.0.5 +rqt-robot-steering==1.0.0 +rqt-runtime-monitor==1.0.0 +rqt-service-caller==1.0.5 +rqt-shell==1.0.2 +rqt-srv==1.0.3 +rqt-tf-tree==1.0.4 +rqt-topic==1.5.0 +sensor-msgs==4.2.3 +sensor-msgs-py==4.2.3 +shape-msgs==4.2.3 +sniffio==1.3.1 +sros2==0.10.4 +starlette==0.37.2 +statistics-msgs==1.2.1 +std-msgs==4.2.3 +std-srvs==4.2.3 +stereo-msgs==4.2.3 +teleop-twist-keyboard==2.4.0 +tf2-geometry-msgs==0.25.6 +tf2-kdl==0.25.6 +tf2-msgs==0.25.6 +tf2-py==0.25.6 +tf2-ros-py==0.25.6 +tf2-tools==0.25.6 +topic-monitor==0.20.3 +trajectory-msgs==4.2.3 +turtlesim==1.4.2 +typing_extensions==4.11.0 +unique-identifier-msgs==2.2.1 +uvicorn==0.29.0 +visualization-msgs==4.2.3 +websockets==12.0 diff --git a/src/api/v1/robot.py b/src/api/robot.py similarity index 100% rename from src/api/v1/robot.py rename to src/api/robot.py diff --git a/src/api/v1/README.md b/src/api/v1/README.md deleted file mode 100644 index 2d48274..0000000 --- a/src/api/v1/README.md +++ /dev/null @@ -1,41 +0,0 @@ -# API v1 - -This version of the API uses a local venv to install packages, and only runs locally. - -## Creating a venv - -Use the following command to create your venv: - -``` -python3.11 -m venv venv -``` - -From here, you can source your venv with the following commands. - -Windows: - -``` -.\venv\Scripts\Activate.ps1 -``` - -Linux: - -``` -. venv/bin/activate -``` - -## Installing needed packages - -You can install packages required for the application using the command: - -``` -pip install -r requirements.txt -``` - -## Saving pip requirements - -Run the following command to update the `requirements.txt` file, which is needed whenever you install a new package: - -``` -pip freeze > requirements.txt -``` diff --git a/src/api/v1/api.py b/src/api/v1/api.py deleted file mode 100644 index 8c10512..0000000 --- a/src/api/v1/api.py +++ /dev/null @@ -1,79 +0,0 @@ -from fastapi import FastAPI, Request, WebSocket, WebSocketDisconnect -from websockets.exceptions import ConnectionClosed -from fastapi.templating import Jinja2Templates -from fastapi.middleware.cors import CORSMiddleware -from robot import ( - Position2D, - RobotControl, - DynamixelMotor, - DYNAMIXEL_MX_12_ADDR_CONFIG, -) -from pygame import mixer -import asyncio -import cv2 - -app = FastAPI() -camera = cv2.VideoCapture(0) -templates = Jinja2Templates(directory="templates") -mixer.init() - -app.add_middleware( - CORSMiddleware, - allow_origins=["*"], - allow_credentials=True, - allow_methods=["*"], # Allow all HTTP methods - allow_headers=["*"], # Allow all headers -) - -motors = [ - DynamixelMotor(10, DYNAMIXEL_MX_12_ADDR_CONFIG, Position2D(0, 0, 0)), - DynamixelMotor(11, DYNAMIXEL_MX_12_ADDR_CONFIG, Position2D(0, 0, 0)), - DynamixelMotor(12, DYNAMIXEL_MX_12_ADDR_CONFIG, Position2D(0, 0, 0), True), - DynamixelMotor(13, DYNAMIXEL_MX_12_ADDR_CONFIG, Position2D(0, 0, 0), True), -] -ctrl = RobotControl("/dev/ttyUSB0", 1, motors) -ctrl.init(1000000) - - -# https://stackoverflow.com/a/70626324 -@app.websocket("/ws") -async def get_stream(websocket: WebSocket): - await websocket.accept() - try: - while True: - success, frame = camera.read() - if not success: - break - else: - ret, buffer = cv2.imencode(".jpg", frame) - await websocket.send_bytes(buffer.tobytes()) - await asyncio.sleep(0.03) - except (WebSocketDisconnect, ConnectionClosed): - print("Client disconnected") - - -@app.post("/command") -async def command(request: Request): - data = await request.json() - - x_val = -data.get("left", 0) + data.get("right", 0) - y_val = -data.get("down", 0) + data.get("up", 0) - rl = data.get("rotateleft", 0) - rr = data.get("rotateright", 0) - - ctrl.set_velocity(Position2D(x_val, y_val, rr - rl)) - - return {"status": "success"} - - -@app.post("/sfx") -async def command(request: Request): - data = await request.json() - - if data.get("sfx") == "bloop": - mixer.music.load("./bloop.mp3") - elif data.get("sfx") == "bong": - mixer.music.load("./bong.mp3") - mixer.music.play() - - return {"status": "success"} diff --git a/src/api/v1/requirements.txt b/src/api/v1/requirements.txt deleted file mode 100644 index e426d82..0000000 --- a/src/api/v1/requirements.txt +++ /dev/null @@ -1,22 +0,0 @@ -annotated-types==0.6.0 -anyio==4.3.0 -click==8.1.7 -fastapi==0.110.0 -h11==0.14.0 -httptools==0.6.1 -idna==3.6 -Jinja2==3.1.3 -MarkupSafe==2.1.5 -numpy==1.26.4 -opencv-python==4.9.0.80 -pydantic==2.6.4 -pydantic_core==2.16.3 -python-dotenv==1.0.1 -PyYAML==6.0.1 -sniffio==1.3.1 -starlette==0.36.3 -typing_extensions==4.10.0 -uvicorn==0.29.0 -# uvloop==0.19.0 -watchfiles==0.21.0 -websockets==12.0 diff --git a/src/api/v2/main.py b/src/api/v2/main.py deleted file mode 100644 index a35513b..0000000 --- a/src/api/v2/main.py +++ /dev/null @@ -1,5 +0,0 @@ -import uvicorn -import sys - -if __name__ == "__main__": - sys.exit(uvicorn.main()) diff --git a/src/robot/v1/BUILD.bazel b/src/robot/BUILD.bazel similarity index 100% rename from src/robot/v1/BUILD.bazel rename to src/robot/BUILD.bazel diff --git a/src/robot/README.md b/src/robot/README.md new file mode 100644 index 0000000..a6d16e1 --- /dev/null +++ b/src/robot/README.md @@ -0,0 +1,3 @@ +# Robot Code + +ROS_DOMAIN_ID=0 diff --git a/src/robot/v1/config.py b/src/robot/config.py similarity index 100% rename from src/robot/v1/config.py rename to src/robot/config.py diff --git a/src/robot/v2/publisher.py b/src/robot/publisher.py similarity index 100% rename from src/robot/v2/publisher.py rename to src/robot/publisher.py diff --git a/src/robot/v1/robot.py b/src/robot/robot.py similarity index 99% rename from src/robot/v1/robot.py rename to src/robot/robot.py index 8337e93..a7cc7e6 100644 --- a/src/robot/v1/robot.py +++ b/src/robot/robot.py @@ -1,4 +1,3 @@ -# from dynamixel_sdk import * import math from typing import Optional import dynamixel_sdk as dynamixel diff --git a/src/robot/v1/structs.py b/src/robot/structs.py similarity index 100% rename from src/robot/v1/structs.py rename to src/robot/structs.py diff --git a/src/robot/v2/subscriber.py b/src/robot/subscriber.py similarity index 100% rename from src/robot/v2/subscriber.py rename to src/robot/subscriber.py diff --git a/src/robot/v2/README.md b/src/robot/v2/README.md deleted file mode 100644 index 791fcc3..0000000 --- a/src/robot/v2/README.md +++ /dev/null @@ -1,5 +0,0 @@ -# Robot v1 - -This is our ROS workspace for the robot facing code. This will allow us to send commands to the robot. - -ROS_DOMAIN_ID=0 diff --git a/src/robot/v2/__init__.py b/src/robot/v2/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/robot/v2/robot_control.py b/src/robot/v2/robot_control.py deleted file mode 100644 index a08ee47..0000000 --- a/src/robot/v2/robot_control.py +++ /dev/null @@ -1,141 +0,0 @@ -# import os -# import rclpy -# from rclpy.node import Node -# from dynamixel_sdk import * -# from std_msgs.msg import String - - -# # Pre-init configuration when running -# if os.name == 'nt': -# import msvcrt -# def getch(): -# return msvcrt.getch().decode() -# else: -# import sys, tty, termios -# fd = sys.stdin.fileno() -# old_settings = termios.tcgetattr(fd) -# def getch(): -# try: -# tty.setraw(sys.stdin.fileno()) -# ch = sys.stdin.read(1) -# finally: -# termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) -# return ch - - -# class RobotSubscriber(Node): - -# def __init__(self): -# super().__init__('robot_scriber') -# self.subscription = self.create_subscription( -# String, -# 'topic', -# self.listener_callback, -# 10) -# self.subscription # prevent unused variable warning - -# def listener_callback(self, msg): -# self.get_logger().info('I heard: "%s"' % msg.data) - - -# def main(args=None): -# rclpy.init(args=args) - -# minimal_subscriber = MinimalSubscriber() - -# rclpy.spin(minimal_subscriber) - -# # Destroy the node explicitly -# # (optional - otherwise it will be done automatically -# # when the garbage collector destroys the node object) -# minimal_subscriber.destroy_node() -# rclpy.shutdown() - - -# if __name__ == '__main__': -# main() - -# def init() - - -# # Control table address -# ADDR_TORQUE_ENABLE = 64 # Control table address is different in Dynamixel model -# ADDR_GOAL_POSITION = 116 -# ADDR_PRESENT_POSITION = 132 - -# # Protocol version -# PROTOCOL_VERSION = 2.0 # See which protocol version is used in the Dynamixel - -# # Default setting -# DXL_ID = 1 # Dynamixel ID : 1 -# BAUDRATE = 57600 # Dynamixel default baudrate : 57600 -# DEVICENAME = '/dev/ttyUSB0' # Check which port is being used on your controller -# # ex) Windows: "COM1" Linux: "/dev/ttyUSB0" Mac: "/dev/tty.usbserial-*" - -# TORQUE_ENABLE = 1 # Value for enabling the torque -# TORQUE_DISABLE = 0 # Value for disabling the torque -# DXL_MINIMUM_POSITION_VALUE = 0 # Dynamixel will rotate between this value -# DXL_MAXIMUM_POSITION_VALUE = 1000 # and this value (note that the Dynamixel would not move when the position value is out of movable range. Check e-manual about the range of the Dynamixel you use.) -# DXL_MOVING_STATUS_THRESHOLD = 20 # Dynamixel moving status threshold - -# portHandler = PortHandler(DEVICENAME) -# packetHandler = PacketHandler(PROTOCOL_VERSION) - -# def set_goal_pos_callback(data): -# print("Set Goal Position of ID %s = %s" % (data.id, data.position)) -# dxl_comm_result, dxl_error = packetHandler.write4ByteTxRx(portHandler, data.id, ADDR_GOAL_POSITION, data.position) - -# def get_present_pos(req): -# dxl_present_position, dxl_comm_result, dxl_error = packetHandler.read4ByteTxRx(portHandler, req.id, ADDR_PRESENT_POSITION) -# print("Present Position of ID %s = %s" % (req.id, dxl_present_position)) -# return dxl_present_position - -# def read_write_py_node(): -# rospy.init_node('read_write_py_node') -# rospy.Subscriber('set_position', SetPosition, set_goal_pos_callback) -# rospy.Service('get_position', GetPosition, get_present_pos) -# rospy.spin() - -# def main(): -# # Open port -# try: -# portHandler.openPort() -# print("Succeeded to open the port") -# except: -# print("Failed to open the port") -# print("Press any key to terminate...") -# getch() -# quit() - -# # Set port baudrate -# try: -# portHandler.setBaudRate(BAUDRATE) -# print("Succeeded to change the baudrate") -# except: -# print("Failed to change the baudrate") -# print("Press any key to terminate...") -# getch() -# quit() - -# # Enable Dynamixel Torque -# dxl_comm_result, dxl_error = packetHandler.write1ByteTxRx(portHandler, DXL_ID, ADDR_TORQUE_ENABLE, TORQUE_ENABLE) -# if dxl_comm_result != COMM_SUCCESS: -# print("%s" % packetHandler.getTxRxResult(dxl_comm_result)) -# print("Press any key to terminate...") -# getch() -# quit() -# elif dxl_error != 0: -# print("%s" % packetHandler.getRxPacketError(dxl_error)) -# print("Press any key to terminate...") -# getch() -# quit() -# else: -# print("DYNAMIXEL has been successfully connected") - -# print("Ready to get & set Position.") - -# read_write_py_node() - - -# if __name__ == '__main__': -# main() diff --git a/src/ui/v1/.eslintrc.json b/src/ui/.eslintrc.json similarity index 100% rename from src/ui/v1/.eslintrc.json rename to src/ui/.eslintrc.json diff --git a/src/ui/v1/.gitignore b/src/ui/.gitignore similarity index 100% rename from src/ui/v1/.gitignore rename to src/ui/.gitignore diff --git a/src/ui/v1/README.md b/src/ui/README.md similarity index 100% rename from src/ui/v1/README.md rename to src/ui/README.md diff --git a/src/ui/v1/next.config.mjs b/src/ui/next.config.mjs similarity index 100% rename from src/ui/v1/next.config.mjs rename to src/ui/next.config.mjs diff --git a/src/ui/v1/nodelete b/src/ui/nodelete similarity index 100% rename from src/ui/v1/nodelete rename to src/ui/nodelete diff --git a/src/ui/v1/package-lock.json b/src/ui/package-lock.json similarity index 100% rename from src/ui/v1/package-lock.json rename to src/ui/package-lock.json diff --git a/src/ui/v1/package.json b/src/ui/package.json similarity index 100% rename from src/ui/v1/package.json rename to src/ui/package.json diff --git a/src/ui/v1/postcss.config.js b/src/ui/postcss.config.js similarity index 100% rename from src/ui/v1/postcss.config.js rename to src/ui/postcss.config.js diff --git a/src/api/v1/bloop.mp3 b/src/ui/public/bloop.mp3 similarity index 100% rename from src/api/v1/bloop.mp3 rename to src/ui/public/bloop.mp3 diff --git a/src/api/v1/bong.mp3 b/src/ui/public/bong.mp3 similarity index 100% rename from src/api/v1/bong.mp3 rename to src/ui/public/bong.mp3 diff --git a/src/ui/v1/src/app/favicon.ico b/src/ui/src/app/favicon.ico similarity index 100% rename from src/ui/v1/src/app/favicon.ico rename to src/ui/src/app/favicon.ico diff --git a/src/ui/v1/src/app/globals.css b/src/ui/src/app/globals.css similarity index 100% rename from src/ui/v1/src/app/globals.css rename to src/ui/src/app/globals.css diff --git a/src/ui/v1/src/app/layout.tsx b/src/ui/src/app/layout.tsx similarity index 100% rename from src/ui/v1/src/app/layout.tsx rename to src/ui/src/app/layout.tsx diff --git a/src/ui/v1/src/app/page.tsx b/src/ui/src/app/page.tsx similarity index 100% rename from src/ui/v1/src/app/page.tsx rename to src/ui/src/app/page.tsx diff --git a/src/ui/v1/tailwind.config.ts b/src/ui/tailwind.config.ts similarity index 100% rename from src/ui/v1/tailwind.config.ts rename to src/ui/tailwind.config.ts diff --git a/src/ui/v1/tsconfig.json b/src/ui/tsconfig.json similarity index 100% rename from src/ui/v1/tsconfig.json rename to src/ui/tsconfig.json diff --git a/src/ui/v1/public/bloop.mp3 b/src/ui/v1/public/bloop.mp3 deleted file mode 100644 index 4e8f14a..0000000 Binary files a/src/ui/v1/public/bloop.mp3 and /dev/null differ diff --git a/src/ui/v1/public/bong.mp3 b/src/ui/v1/public/bong.mp3 deleted file mode 100644 index 83e2faf..0000000 Binary files a/src/ui/v1/public/bong.mp3 and /dev/null differ