robocad-py 1.0.2.7__tar.gz → 1.1.1.0__tar.gz

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.
Files changed (43) hide show
  1. {robocad-py-1.0.2.7 → robocad_py-1.1.1.0}/CHANGELOG.md +4 -1
  2. robocad_py-1.1.1.0/MANIFEST.in +2 -0
  3. {robocad-py-1.0.2.7/robocad_py.egg-info → robocad_py-1.1.1.0}/PKG-INFO +18 -3
  4. robocad_py-1.1.1.0/robocad/algaritm.py +188 -0
  5. robocad_py-1.1.1.0/robocad/internal/algaritm_internal.py +385 -0
  6. {robocad-py-1.0.2.7/robocad/internal/studica → robocad_py-1.1.1.0/robocad/internal/common}/connection.py +37 -29
  7. {robocad-py-1.0.2.7/robocad/internal/studica → robocad_py-1.1.1.0/robocad/internal/common}/connection_base.py +3 -5
  8. robocad_py-1.1.1.0/robocad/internal/common/connection_real.py +84 -0
  9. robocad_py-1.1.1.0/robocad/internal/common/connection_sim.py +49 -0
  10. robocad_py-1.1.1.0/robocad/internal/common/lidar.py +342 -0
  11. robocad_py-1.1.1.0/robocad/internal/common/robot.py +47 -0
  12. robocad_py-1.1.1.0/robocad/internal/common/robot_configuration.py +34 -0
  13. robocad_py-1.1.1.0/robocad/internal/common/shared.py +42 -0
  14. robocad_py-1.1.1.0/robocad/internal/common/updaters.py +43 -0
  15. robocad_py-1.1.1.0/robocad/internal/studica_internal.py +446 -0
  16. robocad_py-1.1.1.0/robocad/shufflecad.py +449 -0
  17. robocad_py-1.1.1.0/robocad/studica.py +132 -0
  18. {robocad-py-1.0.2.7 → robocad_py-1.1.1.0/robocad_py.egg-info}/PKG-INFO +18 -3
  19. robocad_py-1.1.1.0/robocad_py.egg-info/SOURCES.txt +25 -0
  20. {robocad-py-1.0.2.7 → robocad_py-1.1.1.0}/setup.py +2 -2
  21. robocad-py-1.0.2.7/MANIFEST.in +0 -3
  22. robocad-py-1.0.2.7/robocad/common.py +0 -20
  23. robocad-py-1.0.2.7/robocad/internal/logger.py +0 -18
  24. robocad-py-1.0.2.7/robocad/internal/studica/COM.py +0 -133
  25. robocad-py-1.0.2.7/robocad/internal/studica/SPI.py +0 -111
  26. robocad-py-1.0.2.7/robocad/internal/studica/__init__.py +0 -0
  27. robocad-py-1.0.2.7/robocad/internal/studica/connection_real.py +0 -55
  28. robocad-py-1.0.2.7/robocad/internal/studica/connection_sim.py +0 -106
  29. robocad-py-1.0.2.7/robocad/internal/studica/shared.py +0 -137
  30. robocad-py-1.0.2.7/robocad/shufflecad/__init__.py +0 -0
  31. robocad-py-1.0.2.7/robocad/shufflecad/connections.py +0 -357
  32. robocad-py-1.0.2.7/robocad/shufflecad/shufflecad.py +0 -40
  33. robocad-py-1.0.2.7/robocad/shufflecad/shufflecad_holder.py +0 -80
  34. robocad-py-1.0.2.7/robocad/studica.py +0 -158
  35. robocad-py-1.0.2.7/robocad/vex.py +0 -76
  36. robocad-py-1.0.2.7/robocad_py.egg-info/SOURCES.txt +0 -27
  37. {robocad-py-1.0.2.7 → robocad_py-1.1.1.0}/LICENSE +0 -0
  38. {robocad-py-1.0.2.7 → robocad_py-1.1.1.0}/robocad/__init__.py +0 -0
  39. {robocad-py-1.0.2.7 → robocad_py-1.1.1.0}/robocad/internal/__init__.py +0 -0
  40. {robocad-py-1.0.2.7 → robocad_py-1.1.1.0}/robocad_py.egg-info/dependency_links.txt +0 -0
  41. {robocad-py-1.0.2.7 → robocad_py-1.1.1.0}/robocad_py.egg-info/requires.txt +0 -0
  42. {robocad-py-1.0.2.7 → robocad_py-1.1.1.0}/robocad_py.egg-info/top_level.txt +0 -0
  43. {robocad-py-1.0.2.7 → robocad_py-1.1.1.0}/setup.cfg +0 -0
@@ -22,4 +22,7 @@
22
22
  - Thread joins fixed 2 :)
23
23
 
24
24
  ## 0.0.1.1
25
- - Library fully changed according to the new requirements
25
+ - Library fully changed according to the new requirements
26
+
27
+ ## 1.0.3.0
28
+ - Fully rewritten
@@ -0,0 +1,2 @@
1
+ recursive-include robocad *.py
2
+ include CHANGELOG.md LICENSE
@@ -1,18 +1,30 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: robocad-py
3
- Version: 1.0.2.7
3
+ Version: 1.1.1.0
4
4
  Summary: python lib for real and virtual robots
5
5
  Home-page: https://github.com/Soft-V/robocad-py
6
6
  Author: Airat Abdrakov
7
7
  Author-email: softvery@yandex.ru
8
8
  License: MIT
9
- Keywords: simulator,robotics,robot,3d,raspberry,control
9
+ Keywords: simulator,robotics,robot,3d,raspberry,control,robocad
10
10
  Classifier: Development Status :: 5 - Production/Stable
11
11
  Classifier: Intended Audience :: Education
12
12
  Classifier: Operating System :: Microsoft :: Windows :: Windows 10
13
13
  Classifier: License :: OSI Approved :: MIT License
14
14
  Classifier: Programming Language :: Python :: 3
15
15
  License-File: LICENSE
16
+ Requires-Dist: numpy
17
+ Requires-Dist: funcad
18
+ Dynamic: author
19
+ Dynamic: author-email
20
+ Dynamic: classifier
21
+ Dynamic: description
22
+ Dynamic: home-page
23
+ Dynamic: keywords
24
+ Dynamic: license
25
+ Dynamic: license-file
26
+ Dynamic: requires-dist
27
+ Dynamic: summary
16
28
 
17
29
  Python library for real and virtual robots
18
30
 
@@ -41,3 +53,6 @@ Python library for real and virtual robots
41
53
 
42
54
  ## 0.0.1.1
43
55
  - Library fully changed according to the new requirements
56
+
57
+ ## 1.0.3.0
58
+ - Fully rewritten
@@ -0,0 +1,188 @@
1
+ import signal
2
+
3
+ from .internal.common.robot import Robot
4
+ from .internal.common.robot_configuration import DefaultAlgaritmConfiguration
5
+ from .internal.algaritm_internal import AlgaritmInternal
6
+
7
+
8
+ class RobotAlgaritm(Robot):
9
+ def __init__(self, is_real_robot: bool = True, conf: DefaultAlgaritmConfiguration = None):
10
+ if conf is None: conf = DefaultAlgaritmConfiguration()
11
+
12
+ super().__init__(is_real_robot, conf)
13
+ self.__algaritm_internal = AlgaritmInternal(self, conf)
14
+ self.__reseted_yaw_val = 0.0
15
+
16
+ signal.signal(signal.SIGTERM, self.handler)
17
+ signal.signal(signal.SIGINT, self.handler)
18
+
19
+ def stop(self):
20
+ self.__algaritm_internal.stop()
21
+ self.write_log("Program stopped")
22
+
23
+ def handler(self, signum, _):
24
+ self.write_log("Program stopped from handler")
25
+ self.write_log('Signal handler called with signal' + str(signum))
26
+ self.stop()
27
+ raise SystemExit("Exited")
28
+
29
+ @property
30
+ def motor_speed_0(self):
31
+ return self.__algaritm_internal.speed_motor_0
32
+
33
+ @motor_speed_0.setter
34
+ def motor_speed_0(self, value):
35
+ self.__algaritm_internal.speed_motor_0 = value
36
+
37
+ @property
38
+ def motor_speed_1(self):
39
+ return self.__algaritm_internal.speed_motor_1
40
+
41
+ @motor_speed_1.setter
42
+ def motor_speed_1(self, value):
43
+ self.__algaritm_internal.speed_motor_1 = value
44
+
45
+ @property
46
+ def motor_speed_2(self):
47
+ return self.__algaritm_internal.speed_motor_2
48
+
49
+ @motor_speed_2.setter
50
+ def motor_speed_2(self, value):
51
+ self.__algaritm_internal.speed_motor_2 = value
52
+
53
+ @property
54
+ def motor_speed_3(self):
55
+ return self.__algaritm_internal.speed_motor_3
56
+
57
+ @motor_speed_3.setter
58
+ def motor_speed_3(self, value):
59
+ self.__algaritm_internal.speed_motor_3 = value
60
+
61
+ @property
62
+ def motor_enc_0(self):
63
+ return self.__algaritm_internal.enc_motor_0
64
+
65
+ @property
66
+ def motor_enc_1(self):
67
+ return self.__algaritm_internal.enc_motor_1
68
+
69
+ @property
70
+ def motor_enc_2(self):
71
+ return self.__algaritm_internal.enc_motor_2
72
+
73
+ @property
74
+ def motor_enc_3(self):
75
+ return self.__algaritm_internal.enc_motor_3
76
+
77
+ @property
78
+ def yaw(self):
79
+ return self.__algaritm_internal.yaw
80
+
81
+ @property
82
+ def pitch(self):
83
+ return self.__algaritm_internal.pitch
84
+
85
+ @property
86
+ def roll(self):
87
+ return self.__algaritm_internal.roll
88
+
89
+ @property
90
+ def us_1(self):
91
+ return self.__algaritm_internal.ultrasound_1
92
+
93
+ @property
94
+ def us_2(self):
95
+ return self.__algaritm_internal.ultrasound_2
96
+
97
+ @property
98
+ def us_3(self):
99
+ return self.__algaritm_internal.ultrasound_3
100
+
101
+ @property
102
+ def us_4(self):
103
+ return self.__algaritm_internal.ultrasound_4
104
+
105
+ @property
106
+ def analog_1(self):
107
+ return self.__algaritm_internal.analog_1
108
+
109
+ @property
110
+ def analog_2(self):
111
+ return self.__algaritm_internal.analog_2
112
+
113
+ @property
114
+ def analog_3(self):
115
+ return self.__algaritm_internal.analog_3
116
+
117
+ @property
118
+ def analog_4(self):
119
+ return self.__algaritm_internal.analog_4
120
+
121
+ @property
122
+ def analog_5(self):
123
+ return self.__algaritm_internal.analog_5
124
+
125
+ @property
126
+ def analog_6(self):
127
+ return self.__algaritm_internal.analog_6
128
+
129
+ @property
130
+ def analog_7(self):
131
+ return self.__algaritm_internal.analog_7
132
+
133
+ @property
134
+ def analog_8(self):
135
+ return self.__algaritm_internal.analog_8
136
+
137
+ @property
138
+ def additional_servo_1(self):
139
+ return self.__algaritm_internal.additional_servo_1
140
+
141
+ @additional_servo_1.setter
142
+ def additional_servo_1(self, value):
143
+ self.__algaritm_internal.additional_servo_1 = value
144
+
145
+ @property
146
+ def additional_servo_2(self):
147
+ return self.__algaritm_internal.additional_servo_2
148
+
149
+ @additional_servo_2.setter
150
+ def additional_servo_2(self, value):
151
+ self.__algaritm_internal.additional_servo_2 = value
152
+
153
+ def set_pid_settings(self, use_pid, p, i, d):
154
+ self.__algaritm_internal.use_pid = use_pid
155
+ self.__algaritm_internal.p_pid = p
156
+ self.__algaritm_internal.i_pid = i
157
+ self.__algaritm_internal.d_pid = d
158
+
159
+ # num 1 or 2
160
+ def step_motor_move(self, num, steps: int, steps_per_second: int, direction: bool):
161
+ self.__algaritm_internal.step_motor_move(num, steps, steps_per_second, direction)
162
+
163
+ @property
164
+ def is_step_1_busy(self):
165
+ return self.__algaritm_internal.is_step_1_busy
166
+
167
+ @property
168
+ def is_step_2_busy(self):
169
+ return self.__algaritm_internal.is_step_2_busy
170
+
171
+ @property
172
+ def titan_limits(self) -> list:
173
+ return [self.__algaritm_internal.limit_h_0, self.__algaritm_internal.limit_l_0,
174
+ self.__algaritm_internal.limit_h_1, self.__algaritm_internal.limit_l_1,
175
+ self.__algaritm_internal.limit_h_2, self.__algaritm_internal.limit_l_2,
176
+ self.__algaritm_internal.limit_h_3, self.__algaritm_internal.limit_l_3]
177
+
178
+ @property
179
+ def camera_image(self):
180
+ return self.__algaritm_internal.get_camera()
181
+
182
+ @property
183
+ def lidar_data(self):
184
+ return self.__algaritm_internal.get_lidar()
185
+
186
+ # port is from 1 to 8 included
187
+ def set_angle_servo(self, value: float, port: int):
188
+ self.__algaritm_internal.set_servo_angle(value, port - 1)
@@ -0,0 +1,385 @@
1
+ import struct
2
+ import sys
3
+ import os
4
+ import time
5
+ import numpy
6
+ from threading import Thread
7
+
8
+ from funcad.funcad import Funcad
9
+
10
+ from .common.shared import LibHolder
11
+ from .common.robot import Robot
12
+ from .common.connection_base import ConnectionBase
13
+ from .common.connection_sim import ConnectionSim
14
+ from .common.connection_real import ConnectionReal
15
+ from .common.updaters import RepkaUpdater
16
+ from .common.robot_configuration import DefaultAlgaritmConfiguration
17
+
18
+
19
+ class AlgaritmInternal:
20
+ def __init__(self, robot: Robot, conf: DefaultAlgaritmConfiguration):
21
+ self.__robot = robot
22
+
23
+ # from Titan
24
+ self.speed_motor_0: float = 0.0
25
+ self.speed_motor_1: float = 0.0
26
+ self.speed_motor_2: float = 0.0
27
+ self.speed_motor_3: float = 0.0
28
+ self.enc_motor_0: int = 0
29
+ self.enc_motor_1: int = 0
30
+ self.enc_motor_2: int = 0
31
+ self.enc_motor_3: int = 0
32
+ self.limit_l_0: bool = False
33
+ self.limit_h_0: bool = False
34
+ self.limit_l_1: bool = False
35
+ self.limit_h_1: bool = False
36
+ self.limit_l_2: bool = False
37
+ self.limit_h_2: bool = False
38
+ self.limit_l_3: bool = False
39
+ self.limit_h_3: bool = False
40
+ self.additional_servo_1: float = 0.0
41
+ self.additional_servo_2: float = 0.0
42
+ self.is_step_1_busy: bool = False
43
+ self.is_step_2_busy: bool = False
44
+ self.step_motor_1_steps: int = 0
45
+ self.step_motor_2_steps: int = 0
46
+ self.step_motor_1_steps_per_s: int = 0
47
+ self.step_motor_2_steps_per_s: int = 0
48
+ self.step_motor_1_direction: bool = False
49
+ self.step_motor_2_direction: bool = False
50
+ self.use_pid: bool = False
51
+ self.p_pid: float = 0.14
52
+ self.i_pid: float = 0.1
53
+ self.d_pid: float = 0.0
54
+
55
+ # from vmx
56
+ self.yaw: float = 0
57
+ self.yaw_unlim: float = 0
58
+ self.pitch: float = 0
59
+ self.pitch_unlim: float = 0
60
+ self.roll: float = 0
61
+ self.roll_unlim: float = 0
62
+ self.ultrasound_1: float = 0
63
+ self.ultrasound_2: float = 0
64
+ self.ultrasound_3: float = 0
65
+ self.ultrasound_4: float = 0
66
+ self.analog_1: int = 0
67
+ self.analog_2: int = 0
68
+ self.analog_3: int = 0
69
+ self.analog_4: int = 0
70
+ self.analog_5: int = 0
71
+ self.analog_6: int = 0
72
+ self.analog_7: int = 0
73
+ self.analog_8: int = 0
74
+
75
+ self.servo_angles: list = [0.0] * 8
76
+
77
+ self.__connection: ConnectionBase = None
78
+ if not self.__robot.on_real_robot:
79
+ pass
80
+ # self.__connection = ConnectionSim(self.__robot)
81
+ # self.__robocad_conn = RobocadConnection()
82
+ # self.__robocad_conn.start(self.__connection, self.__robot, self)
83
+ else:
84
+ updater = RepkaUpdater(self.__robot)
85
+ self.__connection = ConnectionReal(self.__robot, updater, conf)
86
+ self.__titan = TitanCOM()
87
+ self.__titan.start_com(self.__connection, self.__robot, self, conf)
88
+ self.__vmx = VMXSPI()
89
+ self.__vmx.start_spi(self.__connection, self.__robot, self, conf)
90
+
91
+ def stop(self):
92
+ self.__connection.stop()
93
+ if not self.__robot.on_real_robot:
94
+ pass
95
+ # if self.__robocad_conn is not None:
96
+ # self.__robocad_conn.stop()
97
+ else:
98
+ if self.__titan is not None:
99
+ self.__titan.stop()
100
+ if self.__vmx is not None:
101
+ self.__vmx.stop()
102
+
103
+ def get_camera(self):
104
+ return self.__connection.get_camera()
105
+
106
+ def get_lidar(self):
107
+ return self.__connection.get_lidar()
108
+
109
+ def set_servo_angle(self, angle: float, pin: int):
110
+ self.servo_angles[pin] = angle
111
+
112
+ def step_motor_move(self, num, steps: int, steps_per_second: int, direction: bool):
113
+ if num == 1:
114
+ self.step_motor_1_steps = steps
115
+ self.step_motor_1_steps_per_s = steps_per_second
116
+ self.step_motor_1_direction = direction
117
+ elif num == 2:
118
+ self.step_motor_2_steps = steps
119
+ self.step_motor_2_steps_per_s = steps_per_second
120
+ self.step_motor_2_direction = direction
121
+
122
+
123
+ class TitanCOM:
124
+ def __init__(self):
125
+ self.__th: Thread = None
126
+ self.__stop_th: bool = False
127
+
128
+ def start_com(self, connection: ConnectionReal, robot: Robot, robot_internal: AlgaritmInternal, conf: DefaultAlgaritmConfiguration) -> None:
129
+ self.__connection: ConnectionReal = connection
130
+ self.__robot: Robot = robot
131
+ self.__robot_internal: AlgaritmInternal = robot_internal
132
+ self.__conf: DefaultAlgaritmConfiguration = conf
133
+
134
+ self.__stop_th: bool = False
135
+ self.__th: Thread = Thread(target=self.com_loop)
136
+ self.__th.daemon = True
137
+ self.__th.start()
138
+
139
+ def stop(self):
140
+ self.__stop_th = True
141
+ if self.__th is not None:
142
+ self.__th.join()
143
+
144
+ def com_loop(self) -> None:
145
+ try:
146
+ com_result = self.__connection.com_ini(self.__conf.titan_port, self.__conf.titan_baud)
147
+ if com_result != 0:
148
+ self.__robot.write_log("Failed to open COM")
149
+ return
150
+
151
+ start_time: int = round(time.time() * 10000)
152
+ send_count_time: float = time.time()
153
+ comm_counter = 0
154
+ while not self.__stop_th:
155
+ tx_time: float = round(time.time() * 10000)
156
+ tx_data = self.set_up_tx_data()
157
+ self.__robot.robot_info.tx_com_time_dev = round(time.time() * 10000) - tx_time
158
+
159
+ rx_data: bytearray = self.__connection.com_rw(tx_data)
160
+
161
+ rx_time: float = round(time.time() * 10000)
162
+ self.set_up_rx_data(rx_data)
163
+ self.__robot.robot_info.rx_com_time_dev = round(time.time() * 10000) - rx_time
164
+
165
+ comm_counter += 1
166
+ if time.time() - send_count_time > 1:
167
+ send_count_time = time.time()
168
+ self.__robot.robot_info.com_count_dev = comm_counter
169
+ comm_counter = 0
170
+
171
+ time.sleep(0.001)
172
+ self.__robot.robot_info.com_time_dev = round(time.time() * 10000) - start_time
173
+ start_time = round(time.time() * 10000)
174
+ except Exception as e:
175
+ self.__connection.com_stop()
176
+ exc_type, exc_obj, exc_tb = sys.exc_info()
177
+ file_name = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
178
+ self.__robot.write_log(" ".join(map(str, [exc_type, file_name, exc_tb.tb_lineno])))
179
+ self.__robot.write_log(str(e))
180
+
181
+ def set_up_rx_data(self, data: bytearray) -> None:
182
+ if data[0] == 1:
183
+ if data[40] == 222:
184
+ self.__robot_internal.enc_motor_0 = ((data[4] & 0xff) << 24) | ((data[3] & 0xff) << 16) | ((data[2] & 0xff) << 8) | (data[1] & 0xff)
185
+ self.__robot_internal.enc_motor_1 = ((data[8] & 0xff) << 24) | ((data[7] & 0xff) << 16) | ((data[6] & 0xff) << 8) | (data[5] & 0xff)
186
+ self.__robot_internal.enc_motor_2 = ((data[12] & 0xff) << 24) | ((data[11] & 0xff) << 16) | ((data[10] & 0xff) << 8) | (data[9] & 0xff)
187
+ self.__robot_internal.enc_motor_3 = ((data[16] & 0xff) << 24) | ((data[15] & 0xff) << 16) | ((data[14] & 0xff) << 8) | (data[13] & 0xff)
188
+
189
+ self.__robot_internal.limit_l_0 = Funcad.access_bit(data[17], 0)
190
+ self.__robot_internal.limit_h_0 = Funcad.access_bit(data[17], 1)
191
+ self.__robot_internal.limit_l_1 = Funcad.access_bit(data[17], 2)
192
+ self.__robot_internal.limit_h_1 = Funcad.access_bit(data[17], 3)
193
+ self.__robot_internal.limit_l_2 = Funcad.access_bit(data[17], 4)
194
+ self.__robot_internal.limit_h_2 = Funcad.access_bit(data[17], 5)
195
+ self.__robot_internal.limit_l_3 = Funcad.access_bit(data[17], 6)
196
+ self.__robot_internal.limit_h_3 = Funcad.access_bit(data[17], 7)
197
+
198
+ self.__robot_internal.is_step_1_busy = (data[18] != 0)
199
+ self.__robot_internal.is_step_2_busy = (data[19] != 0)
200
+ else:
201
+ self.__robot.write_log("received wrong data " + " ".join(map(str, data)))
202
+
203
+ def set_up_tx_data(self) -> bytearray:
204
+ tx_data: bytearray = bytearray([0] * 48)
205
+ tx_data[0] = 1
206
+
207
+ tx_data[1] = int(numpy.clip(self.__robot_internal.speed_motor_0, -100, 100)).to_bytes(1, 'big', signed = True)[0]
208
+ tx_data[2] = int(numpy.clip(self.__robot_internal.speed_motor_1, -100, 100)).to_bytes(1, 'big', signed = True)[0]
209
+ tx_data[3] = int(numpy.clip(self.__robot_internal.speed_motor_2, -100, 100)).to_bytes(1, 'big', signed = True)[0]
210
+ tx_data[4] = int(numpy.clip(self.__robot_internal.speed_motor_3, -100, 100)).to_bytes(1, 'big', signed = True)[0]
211
+
212
+ # for ProgramIsRunning and directions
213
+ tx_data[5] = int('11' +
214
+ ("1" if self.__robot_internal.step_motor_1_direction else "0") +
215
+ ("1" if self.__robot_internal.step_motor_2_direction else "0") +
216
+ ("1" if self.__robot_internal.use_pid else "0") + '001', 2)
217
+
218
+ tx_data[6] = int(self.__robot_internal.additional_servo_1)
219
+ tx_data[7] = int(self.__robot_internal.additional_servo_2)
220
+
221
+ step1_steps: bytearray = Funcad.int_to_4_bytes(abs(self.__robot_internal.step_motor_1_steps))
222
+ tx_data[8] = step1_steps[0]
223
+ tx_data[9] = step1_steps[1]
224
+ tx_data[10] = step1_steps[2]
225
+ tx_data[11] = step1_steps[3]
226
+ step2_steps: bytearray = Funcad.int_to_4_bytes(abs(self.__robot_internal.step_motor_2_steps))
227
+ tx_data[12] = step2_steps[0]
228
+ tx_data[13] = step2_steps[1]
229
+ tx_data[14] = step2_steps[2]
230
+ tx_data[15] = step2_steps[3]
231
+
232
+ step1_steps_ps: bytearray = Funcad.int_to_4_bytes(abs(self.__robot_internal.step_motor_1_steps_per_s))
233
+ tx_data[16] = step1_steps_ps[0]
234
+ tx_data[17] = step1_steps_ps[1]
235
+ tx_data[18] = step1_steps_ps[2]
236
+ tx_data[19] = step1_steps_ps[3]
237
+ step2_steps_ps: bytearray = Funcad.int_to_4_bytes(abs(self.__robot_internal.step_motor_2_steps_per_s))
238
+ tx_data[20] = step2_steps_ps[0]
239
+ tx_data[21] = step2_steps_ps[1]
240
+ tx_data[22] = step2_steps_ps[2]
241
+ tx_data[23] = step2_steps_ps[3]
242
+
243
+ packed_p = struct.pack('<f', self.__robot_internal.p_pid)
244
+ tx_data[24] = packed_p[0]
245
+ tx_data[25] = packed_p[1]
246
+ tx_data[26] = packed_p[2]
247
+ tx_data[27] = packed_p[3]
248
+ packed_i = struct.pack('<f', self.__robot_internal.i_pid)
249
+ tx_data[28] = packed_i[0]
250
+ tx_data[29] = packed_i[1]
251
+ tx_data[30] = packed_i[2]
252
+ tx_data[31] = packed_i[3]
253
+ packed_d = struct.pack('<f', self.__robot_internal.d_pid)
254
+ tx_data[32] = packed_d[0]
255
+ tx_data[33] = packed_d[1]
256
+ tx_data[34] = packed_d[2]
257
+ tx_data[35] = packed_d[3]
258
+
259
+ tx_data[40] = 222
260
+
261
+ return tx_data
262
+
263
+ class VMXSPI:
264
+ def __init__(self):
265
+ self.__th: Thread = None
266
+ self.__stop_th: bool = False
267
+
268
+ def start_spi(self, connection: ConnectionReal, robot: Robot, robot_internal: AlgaritmInternal, conf: DefaultAlgaritmConfiguration) -> None:
269
+ self.__connection: ConnectionReal = connection
270
+ self.__robot: Robot = robot
271
+ self.__robot_internal: AlgaritmInternal = robot_internal
272
+ self.__conf: DefaultAlgaritmConfiguration = conf
273
+
274
+ self.__toggler: int = 0
275
+ self.__stop_th: bool = False
276
+ self.__th: Thread = Thread(target=self.spi_loop)
277
+ self.__th.daemon = True
278
+ self.__th.start()
279
+
280
+ def stop(self):
281
+ self.__stop_th = True
282
+ if self.__th is not None:
283
+ self.__th.join()
284
+
285
+ def spi_loop(self) -> None:
286
+ try:
287
+ spi_result = self.__connection.spi_ini(self.__conf.vmx_port, self.__conf.vmx_ch, self.__conf.vmx_speed, self.__conf.vmx_mode)
288
+ if spi_result != 0:
289
+ self.__robot.write_log("Failed to open SPI")
290
+ return
291
+
292
+ start_time: float = round(time.time() * 10000)
293
+ send_count_time: float = time.time()
294
+ comm_counter = 0
295
+ while not self.__stop_th:
296
+ tx_time: float = round(time.time() * 10000)
297
+ tx_list = self.set_up_tx_data()
298
+ self.__robot.robot_info.tx_spi_time_dev = round(time.time() * 10000) - tx_time
299
+
300
+ rx_list: bytearray = self.__connection.spi_rw(tx_list)
301
+
302
+ rx_time: float = round(time.time() * 10000)
303
+ self.set_up_rx_data(rx_list)
304
+ self.__robot.robot_info.rx_spi_time_dev = round(time.time() * 10000) - rx_time
305
+
306
+ comm_counter += 1
307
+ if time.time() - send_count_time > 1:
308
+ send_count_time = time.time()
309
+ self.__robot.robot_info.spi_count_dev = comm_counter
310
+ comm_counter = 0
311
+
312
+ time.sleep(0.002)
313
+ self.__robot.robot_info.spi_time_dev = round(time.time() * 10000) - start_time
314
+ start_time = round(time.time() * 10000)
315
+ except (Exception, EOFError) as e:
316
+ self.__connection.spi_stop()
317
+ exc_type, exc_obj, exc_tb = sys.exc_info()
318
+ file_name = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
319
+ self.__robot.write_log(" ".join(map(str, [exc_type, file_name, exc_tb.tb_lineno])))
320
+ self.__robot.write_log(str(e))
321
+
322
+ def set_up_rx_data(self, data: bytearray) -> None:
323
+ if data[0] == 1:
324
+ self.__robot_internal.analog_1 = (data[2] & 0xff) << 8 | (data[1] & 0xff)
325
+ self.__robot_internal.analog_2 = (data[4] & 0xff) << 8 | (data[3] & 0xff)
326
+ self.__robot_internal.analog_3 = (data[6] & 0xff) << 8 | (data[5] & 0xff)
327
+ self.__robot_internal.analog_4 = (data[8] & 0xff) << 8 | (data[7] & 0xff)
328
+ self.__robot_internal.analog_5 = (data[10] & 0xff) << 8 | (data[9] & 0xff)
329
+ self.__robot_internal.analog_6 = (data[12] & 0xff) << 8 | (data[11] & 0xff)
330
+ self.__robot_internal.analog_7 = (data[14] & 0xff) << 8 | (data[13] & 0xff)
331
+ elif data[0] == 2:
332
+ self.__robot_internal.analog_8 = (data[2] & 0xff) << 8 | (data[1] & 0xff)
333
+
334
+ us1_ui: int = (data[4] & 0xff) << 8 | (data[3] & 0xff)
335
+ self.__robot_internal.ultrasound_1 = us1_ui / 100
336
+ us2_ui: int = (data[6] & 0xff) << 8 | (data[5] & 0xff)
337
+ self.__robot_internal.ultrasound_2 = us2_ui / 100
338
+ us3_ui: int = (data[8] & 0xff) << 8 | (data[7] & 0xff)
339
+ self.__robot_internal.ultrasound_3 = us3_ui / 100
340
+ us4_ui: int = (data[10] & 0xff) << 8 | (data[9] & 0xff)
341
+ self.__robot_internal.ultrasound_4 = us4_ui / 100
342
+ elif data[0] == 3:
343
+ yaw_ui: int = (data[2] & 0xff) << 8 | (data[1] & 0xff)
344
+ new_yaw = (yaw_ui / 100) * (1 if Funcad.access_bit(data[7], 1) else -1)
345
+ self.__robot_internal.yaw_unlim += self.calc_angle_unlim(new_yaw, self.__robot_internal.yaw)
346
+ self.__robot_internal.yaw = new_yaw
347
+
348
+ pitch_ui: int = (data[4] & 0xff) << 8 | (data[3] & 0xff)
349
+ new_pitch = (pitch_ui / 100) * (1 if Funcad.access_bit(data[7], 2) else -1)
350
+ self.__robot_internal.pitch_unlim += self.calc_angle_unlim(new_pitch, self.__robot_internal.pitch)
351
+ self.__robot_internal.pitch = new_pitch
352
+
353
+ roll_ui: int = (data[6] & 0xff) << 8 | (data[5] & 0xff)
354
+ new_roll = (roll_ui / 100) * (1 if Funcad.access_bit(data[7], 3) else -1)
355
+ self.__robot_internal.roll_unlim += self.calc_angle_unlim(new_roll, self.__robot_internal.roll)
356
+ self.__robot_internal.roll = new_roll
357
+
358
+ power: float = ((data[8] & 0xff) << 8 | (data[7] & 0xff)) / 100
359
+ self.__robot.power = power
360
+
361
+ def set_up_tx_data(self) -> bytearray:
362
+ tx_list: bytearray = bytearray([0x00] * 16)
363
+
364
+ if self.__toggler == 0:
365
+ tx_list[0] = 1
366
+
367
+ tx_list[1] = int(self.__robot_internal.servo_angles[0])
368
+ tx_list[2] = int(self.__robot_internal.servo_angles[1])
369
+ tx_list[3] = int(self.__robot_internal.servo_angles[2])
370
+ tx_list[4] = int(self.__robot_internal.servo_angles[3])
371
+ tx_list[5] = int(self.__robot_internal.servo_angles[4])
372
+ tx_list[6] = int(self.__robot_internal.servo_angles[5])
373
+ tx_list[7] = int(self.__robot_internal.servo_angles[6])
374
+ tx_list[8] = int(self.__robot_internal.servo_angles[7])
375
+ return tx_list
376
+
377
+ def calc_angle_unlim(self, new_angle: float, old_angle: float) -> float:
378
+ delta_angle = new_angle - old_angle
379
+ if delta_angle < -180:
380
+ delta_angle = 180 - old_angle
381
+ delta_angle += 180 + new_angle
382
+ elif delta_angle > 180:
383
+ delta_angle = (180 + old_angle) * -1
384
+ delta_angle += (180 - new_angle) * -1
385
+ return delta_angle