hackerbot 0.2.0__py3-none-any.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.
@@ -0,0 +1,156 @@
1
+ ################################################################################
2
+ # Copyright (c) 2025 Hackerbot Industries LLC
3
+ #
4
+ # This source code is licensed under the MIT license found in the
5
+ # LICENSE file in the root directory of this source tree.
6
+ #
7
+ # Created By: Allen Chien
8
+ # Created: April 2025
9
+ # Updated: 2025.04.07
10
+ #
11
+ # This module contains the SerialHelper class, which is a base class does the
12
+ # serial handling. Including sending serial commands, finding serial ports
13
+ # reading serial outputs.
14
+ #
15
+ # Special thanks to the following for their code contributions to this codebase:
16
+ # Allen Chien - https://github.com/AllenChienXXX
17
+ ################################################################################
18
+
19
+
20
+ import serial
21
+ import serial.tools.list_ports
22
+ import threading
23
+ import os
24
+ import json
25
+ from collections import deque
26
+
27
+ class SerialHelper:
28
+ HOME_DIR = os.environ['HOME']
29
+
30
+ LOG_FILE_PATH = os.path.join(HOME_DIR, "hackerbot/logs/serial_log.txt")
31
+ MAP_DATA_PATH = os.path.join(HOME_DIR, "hackerbot/logs/map_{map_id}.txt")
32
+
33
+ # port = '/dev/ttyACM1'
34
+ def __init__(self, port=None, board="adafruit:samd:adafruit_qt_py_m0", baudrate=230400):
35
+ self.port = port
36
+ self.board = board
37
+ self.baudrate = baudrate
38
+ self.ser = None
39
+ self.state = None
40
+ self.ser_error = None
41
+
42
+ self.json_entries = deque(maxlen=10) # Store up to 10 most recent JSON entries
43
+
44
+ try:
45
+ if self.port is None:
46
+ self.port = self.find_port()
47
+ self.ser = serial.Serial(port=self.port, baudrate=baudrate, timeout=1)
48
+ except ConnectionError as e:
49
+ raise ConnectionError(f"Error initializing main controller: {e}")
50
+ except serial.SerialException as e:
51
+ raise ConnectionError(f"Serial connection error: {port}. {e}")
52
+ except Exception as e:
53
+ raise RuntimeError(f"Error initializing main controller: {e}")
54
+
55
+ self.read_thread_stop_event = threading.Event()
56
+ self.read_thread = threading.Thread(target=self.read_serial)
57
+ self.read_thread.daemon = False
58
+ self.read_thread.start()
59
+
60
+ def find_port(self):
61
+ ports = list(serial.tools.list_ports.comports())
62
+ for port in ports:
63
+ if "QT Py" in port.description:
64
+ return port.device
65
+
66
+ raise ConnectionError(f"No Port found for {self.board}, are you using a different board?")
67
+
68
+ def get_board_and_port(self):
69
+ return self.board, self.port
70
+
71
+ def send_raw_command(self, command):
72
+ if self.ser and self.ser.is_open:
73
+ try:
74
+ self.ser.write(command.encode('utf-8') + b'\r\n')
75
+ self.state = command
76
+ except serial.SerialException as e:
77
+ raise IOError(f"Error writing to serial port: {e}")
78
+ else:
79
+ raise ConnectionError("Serial port is closed or unavailable!")
80
+
81
+ def get_state(self):
82
+ return self.state
83
+
84
+ def get_ser_error(self):
85
+ return self.ser_error
86
+
87
+ def read_serial(self):
88
+ if not self.ser:
89
+ self.ser_error = "Serial connection not initialized."
90
+ # raise ConnectionError("Serial connection not initialized.")
91
+
92
+ try:
93
+ while not self.read_thread_stop_event.is_set(): # Check the stop event to exit the loop
94
+ try:
95
+ if not self.ser.is_open:
96
+ self.ser_error = "Serial port is closed or unavailable!"
97
+ # raise ConnectionError("Serial port is closed or unavailable!")
98
+
99
+ if self.ser.in_waiting > 0:
100
+ response = self.ser.readline().decode('utf-8').strip()
101
+ if response:
102
+ # Try to parse the response as JSON
103
+ try:
104
+ json_entry = json.loads(response)
105
+ if json_entry.get("command"): # Only store JSON entries with a "command" key
106
+ self.json_entries.append(json_entry) # Store the latest JSON entry
107
+ except json.JSONDecodeError:
108
+ # If it's not a valid JSON entry, just continue
109
+ continue
110
+ except serial.SerialException as e:
111
+ self.ser_error = f"Serial read error: {e}"
112
+ # raise IOError(f"Serial read error: {e}")
113
+ except Exception as e:
114
+ self.ser_error = f"Unexpected read error: {e}"
115
+ # raise RuntimeError(f"Unexpected read error: {e}")
116
+ except PermissionError as e:
117
+ self.ser_error = f"Permission error: {e}"
118
+ # raise IOError(f"File write error: {e}")
119
+ except Exception as e:
120
+ self.ser_error = f"Unexpected error: {e}"
121
+ # raise IOError(f"File write error: {e}")
122
+
123
+ def get_json_from_command(self, command_filter=None):
124
+ if command_filter is None:
125
+ raise ValueError("command_filter cannot be None")
126
+ if self.json_entries is None or len(self.json_entries) == 0:
127
+ raise ValueError("No JSON entries found")
128
+
129
+ for entry in reversed(self.json_entries):
130
+ if entry.get("command") == command_filter:
131
+ if entry.get("success") == "true":
132
+ return entry
133
+ raise Exception("Fail to fetch...")
134
+ raise Exception(f"Command {command_filter} not found in JSON entries")
135
+
136
+ def stop_read_thread(self):
137
+ """Call this method to stop the serial reading thread."""
138
+ self.read_thread_stop_event.set()
139
+ self.read_thread.join() # Wait for the thread to fully terminate
140
+
141
+ def disconnect_serial(self):
142
+ """Disconnect the serial port and stop the read thread cleanly."""
143
+ # Stop the reading thread first
144
+ self.stop_read_thread()
145
+
146
+ # Close the serial connection safely
147
+ if self.ser:
148
+ try:
149
+ if self.ser.is_open:
150
+ self.ser.close()
151
+ except serial.SerialException as e:
152
+ raise ConnectionError(f"Error closing serial connection: {e}")
153
+ except Exception as e:
154
+ raise RuntimeError(f"Unexpected error while disconnecting serial: {e}")
155
+ finally:
156
+ self.ser = None
@@ -0,0 +1,67 @@
1
+ Metadata-Version: 2.4
2
+ Name: hackerbot
3
+ Version: 0.2.0
4
+ Summary: This module contains the setup for the hackerbot python package.
5
+ Author-email: Allen Chien <allen71090@gmail.com>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/hackerbotindustries/hackerbot-python-package
8
+ Requires-Python: >=3.11
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ Requires-Dist: pyserial
12
+ Requires-Dist: pytest
13
+ Dynamic: license-file
14
+
15
+
16
+ # Hackerbot Python Package
17
+
18
+ Hackerbot python package (`hackerbot-python-package`) is a project that includes modules for controlling and managing the Hackerbot system.
19
+
20
+ ## Installation
21
+
22
+ Follow these steps to clone the repository and set up the required dependencies.
23
+
24
+ ### 1. Clone the Repository
25
+ Use SSH to clone the repository:
26
+ ```bash
27
+ https://github.com/hackerbotindustries/hackerbot-python-package.git
28
+ ```
29
+ This will create a directory named `hackerbot-python-package` and download all necessary files.
30
+
31
+ ### 2. Navigate to the Modules Directory
32
+ Move into the `hackerbot_modules` directory:
33
+ ```bash
34
+ cd hackerbot-python-package/
35
+ ```
36
+
37
+ ### 3. Install Dependencies
38
+ Install the `hackerbot` package using `pip`:
39
+ ```bash
40
+ pip install .
41
+ ```
42
+ This will install the package locally for your Python environment.
43
+
44
+ ## Usage
45
+ Once installed, you can import `hackerbot` in your Python scripts:
46
+ ```python
47
+ import hackerbot
48
+ ```
49
+
50
+ ### 4. Testing
51
+ To run the unit tests run:
52
+ ```bash
53
+ cd tests/unit_tests
54
+ pytest
55
+ ```
56
+
57
+ ## Troubleshooting
58
+ If you run into issues with the installation, try the following:
59
+ - Ensure you're using a virtual environment:
60
+ ```bash
61
+ python3 -m venv venv
62
+ source venv/bin/activate
63
+ ```
64
+ - Upgrade `pip` before installation:
65
+ ```bash
66
+ pip install --upgrade pip
67
+ ```
@@ -0,0 +1,20 @@
1
+ hackerbot/__init__.py,sha256=fjzBB_VsqCuKJ3yeLdqLSvAQIz9bAMI-KXrUC6ZrscY,1248
2
+ hackerbot/core.py,sha256=oZKQEUaMn19MnaWNTDFqTZ7lziBEZeSk5CdLXLSLvr0,5521
3
+ hackerbot/arm/__init__.py,sha256=JdCmTDQ2qk3W8-Alc4mjaQYxHnmH26r0nSy0JFg6J4A,3400
4
+ hackerbot/arm/gripper.py,sha256=xZux0PZL4vvrJTTKHL7dDsRBfJr4zbmgMaA0uBR1u4s,1986
5
+ hackerbot/base/__init__.py,sha256=PG2Ykn2MLfobAWClGFnp-AZfh1xVsnbc1nQ8E6Yy6eA,8698
6
+ hackerbot/base/maps.py,sha256=I_4NdvOMnkHX6-mW43QpDPJ-eVc6npgcBXq99tU7D1g,5916
7
+ hackerbot/examples/keyboard_teleop_examples/AI_ELITE_teleop.py,sha256=cy9Cp4QSYafvm-5xBoq-ks8tdLJHzMvO_6pRweWLVaU,7832
8
+ hackerbot/examples/keyboard_teleop_examples/AI_PRO_teleop.py,sha256=Q99v488MncM8uZ0okPzrWihQmLKCufEgbLIIT3Od0ig,5959
9
+ hackerbot/examples/keyboard_teleop_examples/arm_teleop.py,sha256=GoyhRuqqpqwMdBKMq_pgTsbcuVNfO8sGcJKGNiD6P8k,7730
10
+ hackerbot/examples/keyboard_teleop_examples/base_teleop.py,sha256=3XpXUWtPeCcsYhffcUMWMI_upkA9VFXB0qoinWpaqvA,6545
11
+ hackerbot/examples/keyboard_teleop_examples/head_teleop.py,sha256=PfE-VzuaF4-bF0wtziyaTX2SnyKqZmDaNDbM8b-W9gQ,5328
12
+ hackerbot/head/__init__.py,sha256=fNW1u1Kb5jE7q-UGQgZ511gnHPOjeR_monSm8p9CfNI,2466
13
+ hackerbot/head/eyes.py,sha256=xqeKMxL12iaa8KQzDlbgbNy3LzcmWm8aXkebztYJ4P8,1370
14
+ hackerbot/utils/hackerbot_helper.py,sha256=egQPVBBUo52ywsO6jGAGVqhyGLpMPz-b2fgLBb39WSM,4998
15
+ hackerbot/utils/serial_helper.py,sha256=l7pj32mnoVZN-7foU_FPUxdmZaE43jcMmZRBAGEX8Zc,6457
16
+ hackerbot-0.2.0.dist-info/licenses/LICENSE,sha256=SCcXH0bf35ISRu_Ks8xEKySHXiqclANLWhBUbVU8VvA,1081
17
+ hackerbot-0.2.0.dist-info/METADATA,sha256=PvGs4X-ax2hoGDWe12HjmBS55bXcK3MMAgb6mgt8Irg,1714
18
+ hackerbot-0.2.0.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
19
+ hackerbot-0.2.0.dist-info/top_level.txt,sha256=2n_FStAr1SiI3BV67x7KJHYIOmEwxjUD59zedw2hLkU,10
20
+ hackerbot-0.2.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (78.1.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Hackerbot Industries LLC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ hackerbot