isar 1.20.2__py3-none-any.whl → 1.34.9__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.
- isar/apis/api.py +148 -76
- isar/apis/models/__init__.py +0 -1
- isar/apis/models/models.py +21 -11
- isar/apis/models/start_mission_definition.py +110 -168
- isar/apis/robot_control/robot_controller.py +41 -0
- isar/apis/schedule/scheduling_controller.py +124 -162
- isar/apis/security/authentication.py +5 -5
- isar/config/certs/ca-cert.pem +33 -31
- isar/config/keyvault/keyvault_service.py +1 -1
- isar/config/log.py +45 -40
- isar/config/logging.conf +16 -31
- isar/config/open_telemetry.py +102 -0
- isar/config/predefined_mission_definition/default_exr.json +0 -2
- isar/config/predefined_mission_definition/default_mission.json +1 -5
- isar/config/predefined_mission_definition/default_turtlebot.json +4 -11
- isar/config/predefined_missions/default.json +67 -87
- isar/config/predefined_missions/default_extra_capabilities.json +107 -0
- isar/config/settings.py +76 -111
- isar/eventhandlers/eventhandler.py +123 -0
- isar/mission_planner/local_planner.py +6 -20
- isar/mission_planner/mission_planner_interface.py +1 -1
- isar/models/events.py +184 -0
- isar/models/status.py +18 -0
- isar/modules.py +118 -199
- isar/robot/robot.py +377 -0
- isar/robot/robot_battery.py +60 -0
- isar/robot/robot_monitor_mission.py +357 -0
- isar/robot/robot_pause_mission.py +74 -0
- isar/robot/robot_resume_mission.py +67 -0
- isar/robot/robot_start_mission.py +66 -0
- isar/robot/robot_status.py +61 -0
- isar/robot/robot_stop_mission.py +68 -0
- isar/robot/robot_upload_inspection.py +75 -0
- isar/script.py +57 -40
- isar/services/service_connections/mqtt/mqtt_client.py +47 -11
- isar/services/service_connections/mqtt/robot_heartbeat_publisher.py +5 -2
- isar/services/service_connections/mqtt/robot_info_publisher.py +3 -3
- isar/services/service_connections/persistent_memory.py +69 -0
- isar/services/utilities/mqtt_utilities.py +93 -0
- isar/services/utilities/robot_utilities.py +20 -0
- isar/services/utilities/scheduling_utilities.py +393 -65
- isar/state_machine/state_machine.py +219 -538
- isar/state_machine/states/__init__.py +0 -8
- isar/state_machine/states/await_next_mission.py +114 -0
- isar/state_machine/states/blocked_protective_stop.py +60 -0
- isar/state_machine/states/going_to_lockdown.py +95 -0
- isar/state_machine/states/going_to_recharging.py +92 -0
- isar/state_machine/states/home.py +115 -0
- isar/state_machine/states/intervention_needed.py +77 -0
- isar/state_machine/states/lockdown.py +38 -0
- isar/state_machine/states/maintenance.py +36 -0
- isar/state_machine/states/monitor.py +137 -247
- isar/state_machine/states/offline.py +51 -53
- isar/state_machine/states/paused.py +92 -23
- isar/state_machine/states/pausing.py +48 -0
- isar/state_machine/states/pausing_return_home.py +48 -0
- isar/state_machine/states/recharging.py +80 -0
- isar/state_machine/states/resuming.py +57 -0
- isar/state_machine/states/resuming_return_home.py +64 -0
- isar/state_machine/states/return_home_paused.py +109 -0
- isar/state_machine/states/returning_home.py +217 -0
- isar/state_machine/states/stopping.py +61 -0
- isar/state_machine/states/stopping_due_to_maintenance.py +61 -0
- isar/state_machine/states/stopping_go_to_lockdown.py +60 -0
- isar/state_machine/states/stopping_go_to_recharge.py +51 -0
- isar/state_machine/states/stopping_return_home.py +77 -0
- isar/state_machine/states/unknown_status.py +72 -0
- isar/state_machine/states_enum.py +21 -5
- isar/state_machine/transitions/mission.py +192 -0
- isar/state_machine/transitions/return_home.py +106 -0
- isar/state_machine/transitions/robot_status.py +80 -0
- isar/state_machine/utils/common_event_handlers.py +73 -0
- isar/storage/blob_storage.py +70 -52
- isar/storage/local_storage.py +25 -12
- isar/storage/storage_interface.py +28 -7
- isar/storage/uploader.py +174 -55
- isar/storage/utilities.py +32 -29
- {isar-1.20.2.dist-info → isar-1.34.9.dist-info}/METADATA +73 -110
- isar-1.34.9.dist-info/RECORD +135 -0
- {isar-1.20.2.dist-info → isar-1.34.9.dist-info}/WHEEL +1 -1
- {isar-1.20.2.dist-info → isar-1.34.9.dist-info}/entry_points.txt +1 -0
- robot_interface/models/exceptions/robot_exceptions.py +91 -41
- robot_interface/models/initialize/__init__.py +0 -1
- robot_interface/models/inspection/__init__.py +0 -13
- robot_interface/models/inspection/inspection.py +42 -33
- robot_interface/models/mission/mission.py +14 -15
- robot_interface/models/mission/status.py +20 -26
- robot_interface/models/mission/task.py +154 -121
- robot_interface/models/robots/battery_state.py +6 -0
- robot_interface/models/robots/media.py +13 -0
- robot_interface/models/robots/robot_model.py +7 -7
- robot_interface/robot_interface.py +119 -84
- robot_interface/telemetry/mqtt_client.py +74 -12
- robot_interface/telemetry/payloads.py +91 -13
- robot_interface/utilities/json_service.py +7 -1
- isar/config/predefined_missions/default_turtlebot.json +0 -110
- isar/config/predefined_poses/__init__.py +0 -0
- isar/config/predefined_poses/predefined_poses.py +0 -616
- isar/config/settings.env +0 -25
- isar/mission_planner/sequential_task_selector.py +0 -23
- isar/mission_planner/task_selector_interface.py +0 -31
- isar/models/communication/__init__.py +0 -0
- isar/models/communication/message.py +0 -12
- isar/models/communication/queues/__init__.py +0 -4
- isar/models/communication/queues/queue_io.py +0 -12
- isar/models/communication/queues/queue_timeout_error.py +0 -2
- isar/models/communication/queues/queues.py +0 -19
- isar/models/communication/queues/status_queue.py +0 -20
- isar/models/mission_metadata/__init__.py +0 -0
- isar/services/readers/__init__.py +0 -0
- isar/services/readers/base_reader.py +0 -37
- isar/services/service_connections/stid/__init__.py +0 -0
- isar/services/utilities/queue_utilities.py +0 -39
- isar/state_machine/states/idle.py +0 -85
- isar/state_machine/states/initialize.py +0 -71
- isar/state_machine/states/initiate.py +0 -142
- isar/state_machine/states/off.py +0 -18
- isar/state_machine/states/stop.py +0 -95
- isar/storage/slimm_storage.py +0 -191
- isar-1.20.2.dist-info/RECORD +0 -116
- robot_interface/models/initialize/initialize_params.py +0 -9
- robot_interface/models/mission/step.py +0 -234
- {isar-1.20.2.dist-info → isar-1.34.9.dist-info/licenses}/LICENSE +0 -0
- {isar-1.20.2.dist-info → isar-1.34.9.dist-info}/top_level.txt +0 -0
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
from abc import ABCMeta, abstractmethod
|
|
2
2
|
from queue import Queue
|
|
3
3
|
from threading import Thread
|
|
4
|
-
from typing import List,
|
|
4
|
+
from typing import Callable, List, Optional
|
|
5
5
|
|
|
6
|
-
from robot_interface.models.initialize import InitializeParams
|
|
7
6
|
from robot_interface.models.inspection.inspection import Inspection
|
|
8
7
|
from robot_interface.models.mission.mission import Mission
|
|
9
|
-
from robot_interface.models.mission.status import MissionStatus, RobotStatus,
|
|
10
|
-
from robot_interface.models.mission.
|
|
8
|
+
from robot_interface.models.mission.status import MissionStatus, RobotStatus, TaskStatus
|
|
9
|
+
from robot_interface.models.mission.task import InspectionTask
|
|
10
|
+
from robot_interface.models.robots.media import MediaConfig
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class RobotInterface(metaclass=ABCMeta):
|
|
@@ -17,10 +17,6 @@ class RobotInterface(metaclass=ABCMeta):
|
|
|
17
17
|
def initiate_mission(self, mission: Mission) -> None:
|
|
18
18
|
"""Send a mission to the robot and initiate execution of the mission
|
|
19
19
|
|
|
20
|
-
This function should be used in combination with the mission_status function
|
|
21
|
-
if the robot is designed to run full missions and not in a stepwise
|
|
22
|
-
configuration.
|
|
23
|
-
|
|
24
20
|
Parameters
|
|
25
21
|
----------
|
|
26
22
|
mission: Mission
|
|
@@ -31,55 +27,66 @@ class RobotInterface(metaclass=ABCMeta):
|
|
|
31
27
|
|
|
32
28
|
Raises
|
|
33
29
|
------
|
|
30
|
+
RobotAlreadyHomeException
|
|
31
|
+
If the mission is a return home mission and the robot wish to disregard the
|
|
32
|
+
mission as it is already at home
|
|
34
33
|
RobotInfeasibleMissionException
|
|
35
34
|
If the mission input is infeasible and the mission fails to be scheduled in
|
|
36
35
|
a way that means attempting to schedule again is not necessary
|
|
37
36
|
RobotException
|
|
38
37
|
Will catch all RobotExceptions not previously listed and retry scheduling of
|
|
39
38
|
the mission until the number of allowed retries is exceeded
|
|
40
|
-
NotImplementedError
|
|
41
|
-
If the robot is designed for stepwise mission execution
|
|
42
39
|
|
|
43
40
|
"""
|
|
44
41
|
raise NotImplementedError
|
|
45
42
|
|
|
46
|
-
|
|
47
|
-
|
|
43
|
+
@abstractmethod
|
|
44
|
+
def task_status(self, task_id: str) -> TaskStatus:
|
|
45
|
+
"""Gets the status of the currently active task on robot.
|
|
46
|
+
|
|
47
|
+
Returns
|
|
48
|
+
-------
|
|
49
|
+
TaskStatus
|
|
50
|
+
Status of the execution of current task.
|
|
51
|
+
|
|
52
|
+
Raises
|
|
53
|
+
------
|
|
54
|
+
RobotCommunicationTimeoutException or RobotCommunicationException
|
|
55
|
+
If the robot package is unable to communicate with the robot API the fetching
|
|
56
|
+
of task status will be attempted again until a certain number of retries
|
|
57
|
+
RobotTaskStatusException
|
|
58
|
+
If there was an error when retrieving the task status
|
|
59
|
+
RobotException
|
|
60
|
+
If the task status could not be retrieved.
|
|
61
|
+
|
|
62
|
+
"""
|
|
63
|
+
raise NotImplementedError
|
|
48
64
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
65
|
+
@abstractmethod
|
|
66
|
+
def mission_status(self, mission_id: str) -> MissionStatus:
|
|
67
|
+
"""Gets the status of the mission with ID mission_id on robot.
|
|
52
68
|
|
|
53
69
|
Returns
|
|
54
70
|
-------
|
|
55
71
|
MissionStatus
|
|
56
|
-
Status of the
|
|
72
|
+
Status of the execution of mission.
|
|
57
73
|
|
|
58
74
|
Raises
|
|
59
75
|
------
|
|
76
|
+
RobotCommunicationTimeoutException or RobotCommunicationException
|
|
77
|
+
If the robot package is unable to communicate with the robot API the fetching
|
|
78
|
+
of mission status will be attempted again until a certain number of retries
|
|
60
79
|
RobotMissionStatusException
|
|
61
|
-
If
|
|
62
|
-
being marked as failed
|
|
80
|
+
If there was an error when retrieving the mission status
|
|
63
81
|
RobotException
|
|
64
|
-
|
|
65
|
-
will cause the mission to be marked as failed
|
|
66
|
-
NotImplementedError
|
|
67
|
-
If the robot is designed for stepwise mission execution
|
|
82
|
+
If the mission status could not be retrieved.
|
|
68
83
|
|
|
69
84
|
"""
|
|
85
|
+
raise NotImplementedError
|
|
70
86
|
|
|
71
87
|
@abstractmethod
|
|
72
|
-
def
|
|
73
|
-
"""
|
|
74
|
-
|
|
75
|
-
This function should be used in combination with the step_status function
|
|
76
|
-
if the robot is designed to run stepwise missions and not in a full mission
|
|
77
|
-
configuration.
|
|
78
|
-
|
|
79
|
-
Parameters
|
|
80
|
-
----------
|
|
81
|
-
step : Step
|
|
82
|
-
The step that should be initiated on the robot.
|
|
88
|
+
def stop(self) -> None:
|
|
89
|
+
"""Stops the execution of the current task and corresponding mission.
|
|
83
90
|
|
|
84
91
|
Returns
|
|
85
92
|
-------
|
|
@@ -87,45 +94,37 @@ class RobotInterface(metaclass=ABCMeta):
|
|
|
87
94
|
|
|
88
95
|
Raises
|
|
89
96
|
------
|
|
90
|
-
|
|
91
|
-
If the
|
|
92
|
-
|
|
97
|
+
RobotActionException
|
|
98
|
+
If the robot fails to perform the requested action to stop mission execution
|
|
99
|
+
the action to stop will be attempted again until a certain number of retries
|
|
93
100
|
RobotException
|
|
94
|
-
Will catch
|
|
95
|
-
of the step until the number of allowed retries is exceeded before the step
|
|
96
|
-
will be marked as failed and the mission cancelled
|
|
97
|
-
NotImplementedError
|
|
98
|
-
If the robot is designed for full mission execution.
|
|
101
|
+
Will catch other RobotExceptions and retry to stop the mission
|
|
99
102
|
|
|
100
103
|
"""
|
|
101
104
|
raise NotImplementedError
|
|
102
105
|
|
|
103
106
|
@abstractmethod
|
|
104
|
-
def
|
|
105
|
-
"""
|
|
106
|
-
|
|
107
|
-
This function should be used in combination with the initiate_step function
|
|
108
|
-
if the robot is designed to run stepwise missions and not in a full mission
|
|
109
|
-
configuration.
|
|
107
|
+
def pause(self) -> None:
|
|
108
|
+
"""Pauses the execution of the current task and stops the movement of the robot.
|
|
110
109
|
|
|
111
110
|
Returns
|
|
112
111
|
-------
|
|
113
|
-
|
|
114
|
-
Status of the execution of current step.
|
|
112
|
+
None
|
|
115
113
|
|
|
116
114
|
Raises
|
|
117
115
|
------
|
|
116
|
+
RobotActionException
|
|
117
|
+
If the robot fails to perform the requested action to pause mission execution
|
|
118
|
+
the action to pause will be attempted again until a certain number of retries
|
|
118
119
|
RobotException
|
|
119
|
-
|
|
120
|
-
NotImplementedError
|
|
121
|
-
If the robot is designed for full mission execution.
|
|
120
|
+
Will catch other RobotExceptions and retry to pause the mission
|
|
122
121
|
|
|
123
122
|
"""
|
|
124
123
|
raise NotImplementedError
|
|
125
124
|
|
|
126
125
|
@abstractmethod
|
|
127
|
-
def
|
|
128
|
-
"""
|
|
126
|
+
def resume(self) -> None:
|
|
127
|
+
"""Resumes the execution of the current task and continues the rest of the mission.
|
|
129
128
|
|
|
130
129
|
Returns
|
|
131
130
|
-------
|
|
@@ -134,32 +133,34 @@ class RobotInterface(metaclass=ABCMeta):
|
|
|
134
133
|
Raises
|
|
135
134
|
------
|
|
136
135
|
RobotActionException
|
|
137
|
-
If the robot fails to perform the requested action to
|
|
138
|
-
the action to
|
|
136
|
+
If the robot fails to perform the requested action to resume mission execution
|
|
137
|
+
the action to resume will be attempted again until a certain number of retries
|
|
139
138
|
RobotException
|
|
140
|
-
Will catch other RobotExceptions and retry to
|
|
139
|
+
Will catch other RobotExceptions and retry to resume the mission
|
|
141
140
|
|
|
142
141
|
"""
|
|
143
142
|
raise NotImplementedError
|
|
144
143
|
|
|
145
144
|
@abstractmethod
|
|
146
|
-
def
|
|
147
|
-
"""Return the
|
|
145
|
+
def get_inspection(self, task: InspectionTask) -> Inspection:
|
|
146
|
+
"""Return the inspection connected to the given task.
|
|
148
147
|
|
|
149
148
|
Parameters
|
|
150
149
|
----------
|
|
151
|
-
|
|
150
|
+
task : InspectionTask
|
|
152
151
|
|
|
153
152
|
Returns
|
|
154
153
|
-------
|
|
155
|
-
|
|
156
|
-
|
|
154
|
+
Inspection
|
|
155
|
+
The inspection connected to the given task.
|
|
156
|
+
get_inspection has responsibility to assign the inspection_id of the task
|
|
157
|
+
to the inspection that it returns.
|
|
157
158
|
|
|
158
159
|
Raises
|
|
159
160
|
------
|
|
160
161
|
RobotRetrieveInspectionException
|
|
161
162
|
If the robot package is unable to retrieve the inspections for the relevant
|
|
162
|
-
mission or
|
|
163
|
+
mission or task an error message is logged and the state machine continues
|
|
163
164
|
RobotException
|
|
164
165
|
Catches other RobotExceptions that lead to the same result as a
|
|
165
166
|
RobotRetrieveInspectionException
|
|
@@ -168,27 +169,33 @@ class RobotInterface(metaclass=ABCMeta):
|
|
|
168
169
|
raise NotImplementedError
|
|
169
170
|
|
|
170
171
|
@abstractmethod
|
|
171
|
-
def
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
172
|
+
def register_inspection_callback(
|
|
173
|
+
self, callback_function: Callable[[Inspection, Mission], None]
|
|
174
|
+
) -> None:
|
|
175
|
+
"""Register a function which should be run when inspection data is received
|
|
176
|
+
asynchronously. This function should expect to receive an Inspection from.
|
|
175
177
|
|
|
176
178
|
Parameters
|
|
177
179
|
----------
|
|
178
|
-
|
|
180
|
+
callback_function : Callable[[Inspection, Mission], None]
|
|
179
181
|
|
|
180
182
|
Returns
|
|
181
183
|
-------
|
|
182
184
|
None
|
|
183
185
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
186
|
+
"""
|
|
187
|
+
raise NotImplementedError
|
|
188
|
+
|
|
189
|
+
@abstractmethod
|
|
190
|
+
def generate_media_config(self) -> Optional[MediaConfig]:
|
|
191
|
+
"""
|
|
192
|
+
Generate a JSON containing the url and token needed to establish a media stream
|
|
193
|
+
connection to a robot.
|
|
194
|
+
|
|
195
|
+
Returns
|
|
196
|
+
-------
|
|
197
|
+
MediaConfig
|
|
198
|
+
An object containing the connection information for a media stream connection
|
|
192
199
|
|
|
193
200
|
"""
|
|
194
201
|
raise NotImplementedError
|
|
@@ -212,16 +219,14 @@ class RobotInterface(metaclass=ABCMeta):
|
|
|
212
219
|
-------
|
|
213
220
|
List[Thread]
|
|
214
221
|
List containing all threads that will be started to publish telemetry.
|
|
222
|
+
|
|
215
223
|
"""
|
|
216
224
|
raise NotImplementedError
|
|
217
225
|
|
|
218
226
|
@abstractmethod
|
|
219
227
|
def robot_status(self) -> RobotStatus:
|
|
220
228
|
"""
|
|
221
|
-
Method which returns an enum indicating
|
|
222
|
-
the interface which is used to communicate with the robot. This is further used
|
|
223
|
-
by ISAR to indicate whether the ISAR instance is fully functional and may be
|
|
224
|
-
used by other systems.
|
|
229
|
+
Method which returns an enum indicating the status of the robot.
|
|
225
230
|
|
|
226
231
|
Returns
|
|
227
232
|
-------
|
|
@@ -231,13 +236,43 @@ class RobotInterface(metaclass=ABCMeta):
|
|
|
231
236
|
Raises
|
|
232
237
|
-------
|
|
233
238
|
RobotCommunicationException
|
|
234
|
-
Raised if the robot package is unable to communicate with the robot API
|
|
239
|
+
Raised if the robot package is unable to communicate with the robot API.
|
|
240
|
+
The fetching of robot status will be attempted again until success
|
|
241
|
+
RobotAPIException
|
|
242
|
+
Raised if the robot package is able to communicate with the API but an error
|
|
243
|
+
occurred while interpreting the response. The fetching of robot status will
|
|
244
|
+
be attempted again until success
|
|
245
|
+
RobotException
|
|
246
|
+
Catches other RobotExceptions that may have occurred while retrieving the
|
|
247
|
+
robot status. The fetching of robot status will be attempted again until
|
|
248
|
+
success
|
|
249
|
+
|
|
250
|
+
"""
|
|
251
|
+
raise NotImplementedError
|
|
252
|
+
|
|
253
|
+
@abstractmethod
|
|
254
|
+
def get_battery_level(self) -> float:
|
|
255
|
+
"""
|
|
256
|
+
Method which returns the percent charge remaining in the robot battery.
|
|
257
|
+
|
|
258
|
+
Returns
|
|
259
|
+
-------
|
|
260
|
+
float
|
|
261
|
+
The battery percentage on the robot
|
|
262
|
+
|
|
263
|
+
Raises
|
|
264
|
+
-------
|
|
265
|
+
RobotCommunicationException
|
|
266
|
+
Raised if the robot package is unable to communicate with the robot API.
|
|
267
|
+
The fetching of robot battery level will be attempted again until success
|
|
235
268
|
RobotAPIException
|
|
236
269
|
Raised if the robot package is able to communicate with the API but an error
|
|
237
|
-
occurred while interpreting the response
|
|
270
|
+
occurred while interpreting the response. The fetching of robot battery level
|
|
271
|
+
will be attempted again until success
|
|
238
272
|
RobotException
|
|
239
273
|
Catches other RobotExceptions that may have occurred while retrieving the
|
|
240
|
-
robot
|
|
241
|
-
|
|
274
|
+
robot battery level. The fetching of robot battery level will
|
|
275
|
+
be attempted again until success
|
|
276
|
+
|
|
242
277
|
"""
|
|
243
278
|
raise NotImplementedError
|
|
@@ -1,19 +1,38 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import time
|
|
3
3
|
from abc import ABCMeta, abstractmethod
|
|
4
|
-
from datetime import
|
|
4
|
+
from datetime import datetime, timezone
|
|
5
5
|
from queue import Queue
|
|
6
6
|
from typing import Callable, Tuple
|
|
7
7
|
|
|
8
|
-
from
|
|
8
|
+
from paho.mqtt.packettypes import PacketTypes
|
|
9
|
+
from paho.mqtt.properties import Properties
|
|
10
|
+
|
|
11
|
+
from isar.config.settings import settings
|
|
12
|
+
from robot_interface.models.exceptions.robot_exceptions import (
|
|
13
|
+
RobotTelemetryException,
|
|
14
|
+
RobotTelemetryNoUpdateException,
|
|
15
|
+
RobotTelemetryPoseException,
|
|
16
|
+
)
|
|
9
17
|
from robot_interface.telemetry.payloads import CloudHealthPayload
|
|
10
18
|
from robot_interface.utilities.json_service import EnhancedJSONEncoder
|
|
11
19
|
|
|
12
20
|
|
|
21
|
+
def props_expiry(seconds: int) -> Properties:
|
|
22
|
+
p = Properties(PacketTypes.PUBLISH)
|
|
23
|
+
p.MessageExpiryInterval = seconds
|
|
24
|
+
return p
|
|
25
|
+
|
|
26
|
+
|
|
13
27
|
class MqttClientInterface(metaclass=ABCMeta):
|
|
14
28
|
@abstractmethod
|
|
15
29
|
def publish(
|
|
16
|
-
self,
|
|
30
|
+
self,
|
|
31
|
+
topic: str,
|
|
32
|
+
payload: str,
|
|
33
|
+
qos: int = 0,
|
|
34
|
+
retain: bool = False,
|
|
35
|
+
properties: Properties = None,
|
|
17
36
|
) -> None:
|
|
18
37
|
"""
|
|
19
38
|
Parameters
|
|
@@ -29,7 +48,6 @@ class MqttClientInterface(metaclass=ABCMeta):
|
|
|
29
48
|
|
|
30
49
|
Returns
|
|
31
50
|
-------
|
|
32
|
-
|
|
33
51
|
"""
|
|
34
52
|
pass
|
|
35
53
|
|
|
@@ -39,9 +57,20 @@ class MqttPublisher(MqttClientInterface):
|
|
|
39
57
|
self.mqtt_queue: Queue = mqtt_queue
|
|
40
58
|
|
|
41
59
|
def publish(
|
|
42
|
-
self,
|
|
60
|
+
self,
|
|
61
|
+
topic: str,
|
|
62
|
+
payload: str,
|
|
63
|
+
qos: int = 0,
|
|
64
|
+
retain: bool = False,
|
|
65
|
+
properties: Properties = None,
|
|
43
66
|
) -> None:
|
|
44
|
-
queue_message: Tuple[str, str, int, bool] = (
|
|
67
|
+
queue_message: Tuple[str, str, int, bool, Properties] = (
|
|
68
|
+
topic,
|
|
69
|
+
payload,
|
|
70
|
+
qos,
|
|
71
|
+
retain,
|
|
72
|
+
properties,
|
|
73
|
+
)
|
|
45
74
|
self.mqtt_queue.put(queue_message)
|
|
46
75
|
|
|
47
76
|
|
|
@@ -54,6 +83,7 @@ class MqttTelemetryPublisher(MqttClientInterface):
|
|
|
54
83
|
interval: float,
|
|
55
84
|
qos: int = 0,
|
|
56
85
|
retain: bool = False,
|
|
86
|
+
properties: Properties = None,
|
|
57
87
|
) -> None:
|
|
58
88
|
self.mqtt_queue: Queue = mqtt_queue
|
|
59
89
|
self.telemetry_method: Callable = telemetry_method
|
|
@@ -61,9 +91,13 @@ class MqttTelemetryPublisher(MqttClientInterface):
|
|
|
61
91
|
self.interval: float = interval
|
|
62
92
|
self.qos: int = qos
|
|
63
93
|
self.retain: bool = retain
|
|
94
|
+
self.properties: Properties = properties
|
|
64
95
|
|
|
65
96
|
def run(self, isar_id: str, robot_name: str) -> None:
|
|
66
|
-
self.
|
|
97
|
+
self.cloud_health_topic: str = f"isar/{isar_id}/cloud_health"
|
|
98
|
+
self.battery_topic: str = f"isar/{isar_id}/battery"
|
|
99
|
+
self.pose_topic: str = f"isar/{isar_id}/pose"
|
|
100
|
+
self.pressure_topic: str = f"isar/{isar_id}/pressure"
|
|
67
101
|
topic: str
|
|
68
102
|
payload: str
|
|
69
103
|
|
|
@@ -71,19 +105,47 @@ class MqttTelemetryPublisher(MqttClientInterface):
|
|
|
71
105
|
try:
|
|
72
106
|
payload = self.telemetry_method(isar_id=isar_id, robot_name=robot_name)
|
|
73
107
|
topic = self.topic
|
|
108
|
+
except (RobotTelemetryPoseException, RobotTelemetryNoUpdateException):
|
|
109
|
+
time.sleep(self.interval)
|
|
110
|
+
continue
|
|
74
111
|
except RobotTelemetryException:
|
|
75
112
|
payload = json.dumps(
|
|
76
|
-
CloudHealthPayload(isar_id, robot_name, datetime.now(
|
|
113
|
+
CloudHealthPayload(isar_id, robot_name, datetime.now(timezone.utc)),
|
|
77
114
|
cls=EnhancedJSONEncoder,
|
|
78
115
|
)
|
|
79
|
-
topic = self.
|
|
116
|
+
topic = self.cloud_health_topic
|
|
117
|
+
|
|
118
|
+
publish_properties = self.properties
|
|
80
119
|
|
|
81
|
-
|
|
120
|
+
if topic in (
|
|
121
|
+
self.battery_topic,
|
|
122
|
+
self.pose_topic,
|
|
123
|
+
self.pressure_topic,
|
|
124
|
+
):
|
|
125
|
+
publish_properties = props_expiry(settings.MQTT_TELEMETRY_EXPIRY)
|
|
82
126
|
|
|
127
|
+
self.publish(
|
|
128
|
+
topic=topic,
|
|
129
|
+
payload=payload,
|
|
130
|
+
qos=self.qos,
|
|
131
|
+
retain=self.retain,
|
|
132
|
+
properties=publish_properties,
|
|
133
|
+
)
|
|
83
134
|
time.sleep(self.interval)
|
|
84
135
|
|
|
85
136
|
def publish(
|
|
86
|
-
self,
|
|
137
|
+
self,
|
|
138
|
+
topic: str,
|
|
139
|
+
payload: str,
|
|
140
|
+
qos: int = 0,
|
|
141
|
+
retain: bool = False,
|
|
142
|
+
properties: Properties = None,
|
|
87
143
|
) -> None:
|
|
88
|
-
queue_message: Tuple[str, str, int, bool] = (
|
|
144
|
+
queue_message: Tuple[str, str, int, bool, Properties] = (
|
|
145
|
+
topic,
|
|
146
|
+
payload,
|
|
147
|
+
qos,
|
|
148
|
+
retain,
|
|
149
|
+
properties,
|
|
150
|
+
)
|
|
89
151
|
self.mqtt_queue.put(queue_message)
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
2
|
from datetime import datetime
|
|
3
|
-
from typing import List
|
|
3
|
+
from typing import List, Optional
|
|
4
4
|
|
|
5
5
|
from alitra import Pose
|
|
6
|
-
from transitions import State
|
|
7
6
|
|
|
8
|
-
from
|
|
7
|
+
from isar.models.status import IsarStatus
|
|
8
|
+
from isar.storage.storage_interface import BlobStoragePath
|
|
9
|
+
from robot_interface.models.exceptions.robot_exceptions import ErrorReason
|
|
10
|
+
from robot_interface.models.mission.status import MissionStatus, TaskStatus
|
|
11
|
+
from robot_interface.models.mission.task import TaskTypes
|
|
12
|
+
from robot_interface.models.robots.battery_state import BatteryState
|
|
9
13
|
|
|
10
14
|
|
|
11
15
|
@dataclass
|
|
@@ -30,6 +34,7 @@ class TelemetryPosePayload(TelemetryPayload):
|
|
|
30
34
|
@dataclass
|
|
31
35
|
class TelemetryBatteryPayload(TelemetryPayload):
|
|
32
36
|
battery_level: float
|
|
37
|
+
battery_state: Optional[BatteryState] = None
|
|
33
38
|
|
|
34
39
|
|
|
35
40
|
@dataclass
|
|
@@ -43,22 +48,16 @@ class TelemetryPressurePayload(TelemetryPayload):
|
|
|
43
48
|
|
|
44
49
|
|
|
45
50
|
@dataclass
|
|
46
|
-
class
|
|
51
|
+
class DocumentInfo:
|
|
47
52
|
name: str
|
|
48
53
|
url: str
|
|
49
|
-
type: str
|
|
50
54
|
|
|
51
55
|
|
|
52
56
|
@dataclass
|
|
53
|
-
class
|
|
57
|
+
class IsarStatusPayload:
|
|
54
58
|
isar_id: str
|
|
55
59
|
robot_name: str
|
|
56
|
-
|
|
57
|
-
previous_robot_status: RobotStatus
|
|
58
|
-
current_isar_state: State
|
|
59
|
-
current_mission_id: str
|
|
60
|
-
current_task_id: str
|
|
61
|
-
current_step_id: str
|
|
60
|
+
status: IsarStatus
|
|
62
61
|
timestamp: datetime
|
|
63
62
|
|
|
64
63
|
|
|
@@ -69,7 +68,7 @@ class RobotInfoPayload:
|
|
|
69
68
|
robot_model: str
|
|
70
69
|
robot_serial_number: str
|
|
71
70
|
robot_asset: str
|
|
72
|
-
|
|
71
|
+
documentation: List[DocumentInfo]
|
|
73
72
|
host: str
|
|
74
73
|
port: int
|
|
75
74
|
capabilities: List[str]
|
|
@@ -81,3 +80,82 @@ class RobotHeartbeatPayload:
|
|
|
81
80
|
isar_id: str
|
|
82
81
|
robot_name: str
|
|
83
82
|
timestamp: datetime
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
@dataclass
|
|
86
|
+
class MissionPayload:
|
|
87
|
+
isar_id: str
|
|
88
|
+
robot_name: str
|
|
89
|
+
mission_id: Optional[str]
|
|
90
|
+
status: Optional[MissionStatus]
|
|
91
|
+
error_reason: Optional[ErrorReason]
|
|
92
|
+
error_description: Optional[str]
|
|
93
|
+
timestamp: datetime
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@dataclass
|
|
97
|
+
class MissionAbortedPayload:
|
|
98
|
+
isar_id: str
|
|
99
|
+
robot_name: str
|
|
100
|
+
mission_id: str
|
|
101
|
+
can_be_continued: bool
|
|
102
|
+
timestamp: datetime
|
|
103
|
+
reason: Optional[str]
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@dataclass
|
|
107
|
+
class TaskPayload:
|
|
108
|
+
isar_id: str
|
|
109
|
+
robot_name: str
|
|
110
|
+
mission_id: Optional[str]
|
|
111
|
+
task_id: Optional[str]
|
|
112
|
+
status: Optional[TaskStatus]
|
|
113
|
+
task_type: Optional[TaskTypes]
|
|
114
|
+
error_reason: Optional[ErrorReason]
|
|
115
|
+
error_description: Optional[str]
|
|
116
|
+
timestamp: datetime
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
@dataclass
|
|
120
|
+
class InspectionResultPayload:
|
|
121
|
+
isar_id: str
|
|
122
|
+
robot_name: str
|
|
123
|
+
inspection_id: str
|
|
124
|
+
blob_storage_data_path: BlobStoragePath
|
|
125
|
+
blob_storage_metadata_path: BlobStoragePath
|
|
126
|
+
installation_code: str
|
|
127
|
+
tag_id: Optional[str]
|
|
128
|
+
inspection_type: Optional[str]
|
|
129
|
+
inspection_description: Optional[str]
|
|
130
|
+
timestamp: datetime
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
@dataclass
|
|
134
|
+
class InspectionValuePayload:
|
|
135
|
+
isar_id: str
|
|
136
|
+
robot_name: str
|
|
137
|
+
inspection_id: str
|
|
138
|
+
installation_code: str
|
|
139
|
+
tag_id: Optional[str]
|
|
140
|
+
inspection_type: Optional[str]
|
|
141
|
+
inspection_description: Optional[str]
|
|
142
|
+
value: float
|
|
143
|
+
unit: str
|
|
144
|
+
x: float
|
|
145
|
+
y: float
|
|
146
|
+
z: float
|
|
147
|
+
timestamp: datetime
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
@dataclass
|
|
151
|
+
class StartUpMessagePayload:
|
|
152
|
+
isar_id: str
|
|
153
|
+
timestamp: datetime
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
@dataclass
|
|
157
|
+
class InterventionNeededPayload:
|
|
158
|
+
isar_id: str
|
|
159
|
+
robot_name: str
|
|
160
|
+
reason: str
|
|
161
|
+
timestamp: datetime
|
|
@@ -7,6 +7,7 @@ from uuid import UUID
|
|
|
7
7
|
|
|
8
8
|
import numpy as np
|
|
9
9
|
from alitra import Orientation
|
|
10
|
+
from pydantic import BaseModel
|
|
10
11
|
|
|
11
12
|
|
|
12
13
|
class EnhancedJSONEncoder(json.JSONEncoder):
|
|
@@ -15,8 +16,13 @@ class EnhancedJSONEncoder(json.JSONEncoder):
|
|
|
15
16
|
"""
|
|
16
17
|
|
|
17
18
|
def default(self, o):
|
|
19
|
+
if isinstance(o, BaseModel):
|
|
20
|
+
dump = getattr(o, "model_dump", None)
|
|
21
|
+
if callable(dump):
|
|
22
|
+
return dump()
|
|
23
|
+
return o.__dict__
|
|
18
24
|
if is_dataclass(o):
|
|
19
|
-
return asdict(o)
|
|
25
|
+
return asdict(o) # type: ignore
|
|
20
26
|
if isinstance(o, UUID):
|
|
21
27
|
return str(o)
|
|
22
28
|
if isinstance(o, Orientation):
|