isar 1.18.0__py3-none-any.whl → 1.19.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.
Potentially problematic release.
This version of isar might be problematic. Click here for more details.
- isar/config/settings.py +2 -4
- isar/state_machine/state_machine.py +39 -5
- isar/state_machine/states/__init__.py +1 -0
- isar/state_machine/states/idle.py +47 -2
- isar/state_machine/states/monitor.py +3 -5
- isar/state_machine/states/offline.py +62 -0
- isar/state_machine/states_enum.py +1 -0
- {isar-1.18.0.dist-info → isar-1.19.0.dist-info}/METADATA +12 -1
- {isar-1.18.0.dist-info → isar-1.19.0.dist-info}/RECORD +12 -12
- isar/services/service_connections/mqtt/robot_status_publisher.py +0 -119
- {isar-1.18.0.dist-info → isar-1.19.0.dist-info}/LICENSE +0 -0
- {isar-1.18.0.dist-info → isar-1.19.0.dist-info}/WHEEL +0 -0
- {isar-1.18.0.dist-info → isar-1.19.0.dist-info}/top_level.txt +0 -0
isar/config/settings.py
CHANGED
|
@@ -229,14 +229,13 @@ class Settings(BaseSettings):
|
|
|
229
229
|
DATA_CLASSIFICATION: str = Field(default="internal")
|
|
230
230
|
|
|
231
231
|
# List of MQTT Topics
|
|
232
|
-
|
|
232
|
+
TOPIC_ISAR_STATUS: str = Field(default="status", validate_default=True)
|
|
233
233
|
TOPIC_ISAR_MISSION: str = Field(default="mission", validate_default=True)
|
|
234
234
|
TOPIC_ISAR_TASK: str = Field(default="task", validate_default=True)
|
|
235
235
|
TOPIC_ISAR_STEP: str = Field(default="step", validate_default=True)
|
|
236
236
|
TOPIC_ISAR_INSPECTION_RESULT: str = Field(
|
|
237
237
|
default="inspection_result", validate_default=True
|
|
238
238
|
)
|
|
239
|
-
TOPIC_ISAR_ROBOT_STATUS: str = Field(default="robot_status", validate_default=True)
|
|
240
239
|
TOPIC_ISAR_ROBOT_INFO: str = Field(default="robot_info", validate_default=True)
|
|
241
240
|
TOPIC_ISAR_ROBOT_HEARTBEAT: str = Field(
|
|
242
241
|
default="robot_heartbeat", validate_default=True
|
|
@@ -284,11 +283,10 @@ class Settings(BaseSettings):
|
|
|
284
283
|
}
|
|
285
284
|
|
|
286
285
|
@field_validator(
|
|
287
|
-
"
|
|
286
|
+
"TOPIC_ISAR_STATUS",
|
|
288
287
|
"TOPIC_ISAR_MISSION",
|
|
289
288
|
"TOPIC_ISAR_TASK",
|
|
290
289
|
"TOPIC_ISAR_STEP",
|
|
291
|
-
"TOPIC_ISAR_ROBOT_STATUS",
|
|
292
290
|
"TOPIC_ISAR_ROBOT_INFO",
|
|
293
291
|
"TOPIC_ISAR_ROBOT_HEARTBEAT",
|
|
294
292
|
"TOPIC_ISAR_INSPECTION_RESULT",
|
|
@@ -24,6 +24,7 @@ from isar.state_machine.states import (
|
|
|
24
24
|
Initiate,
|
|
25
25
|
Monitor,
|
|
26
26
|
Off,
|
|
27
|
+
Offline,
|
|
27
28
|
Paused,
|
|
28
29
|
Stop,
|
|
29
30
|
)
|
|
@@ -31,7 +32,12 @@ from isar.state_machine.states_enum import States
|
|
|
31
32
|
from robot_interface.models.exceptions.robot_exceptions import ErrorMessage
|
|
32
33
|
from robot_interface.models.initialize.initialize_params import InitializeParams
|
|
33
34
|
from robot_interface.models.mission.mission import Mission
|
|
34
|
-
from robot_interface.models.mission.status import
|
|
35
|
+
from robot_interface.models.mission.status import (
|
|
36
|
+
MissionStatus,
|
|
37
|
+
RobotStatus,
|
|
38
|
+
StepStatus,
|
|
39
|
+
TaskStatus,
|
|
40
|
+
)
|
|
35
41
|
from robot_interface.models.mission.step import Step
|
|
36
42
|
from robot_interface.models.mission.task import Task
|
|
37
43
|
from robot_interface.robot_interface import RobotInterface
|
|
@@ -88,6 +94,7 @@ class StateMachine(object):
|
|
|
88
94
|
self.monitor_state: State = Monitor(self)
|
|
89
95
|
self.initiate_state: State = Initiate(self)
|
|
90
96
|
self.off_state: State = Off(self)
|
|
97
|
+
self.offline_state: State = Offline(self)
|
|
91
98
|
|
|
92
99
|
self.states: List[State] = [
|
|
93
100
|
self.off_state,
|
|
@@ -97,6 +104,7 @@ class StateMachine(object):
|
|
|
97
104
|
self.monitor_state,
|
|
98
105
|
self.stop_state,
|
|
99
106
|
self.paused_state,
|
|
107
|
+
self.offline_state,
|
|
100
108
|
]
|
|
101
109
|
|
|
102
110
|
self.machine = Machine(self, states=self.states, initial="off", queued=True)
|
|
@@ -194,6 +202,18 @@ class StateMachine(object):
|
|
|
194
202
|
"dest": self.idle_state,
|
|
195
203
|
"before": self._mission_stopped,
|
|
196
204
|
},
|
|
205
|
+
{
|
|
206
|
+
"trigger": "robot_turned_offline",
|
|
207
|
+
"source": [self.idle_state],
|
|
208
|
+
"dest": self.offline_state,
|
|
209
|
+
"before": self._offline,
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
"trigger": "robot_turned_online",
|
|
213
|
+
"source": self.offline_state,
|
|
214
|
+
"dest": self.idle_state,
|
|
215
|
+
"before": self._online,
|
|
216
|
+
},
|
|
197
217
|
]
|
|
198
218
|
)
|
|
199
219
|
|
|
@@ -239,6 +259,12 @@ class StateMachine(object):
|
|
|
239
259
|
def _off(self) -> None:
|
|
240
260
|
return
|
|
241
261
|
|
|
262
|
+
def _offline(self) -> None:
|
|
263
|
+
return
|
|
264
|
+
|
|
265
|
+
def _online(self) -> None:
|
|
266
|
+
return
|
|
267
|
+
|
|
242
268
|
def _resume(self) -> None:
|
|
243
269
|
self.logger.info(f"Resuming mission: {self.current_mission.id}")
|
|
244
270
|
self.current_mission.status = MissionStatus.InProgress
|
|
@@ -417,7 +443,7 @@ class StateMachine(object):
|
|
|
417
443
|
self.send_state_status()
|
|
418
444
|
self._log_state_transition(self.current_state)
|
|
419
445
|
self.logger.info(f"State: {self.current_state}")
|
|
420
|
-
self.
|
|
446
|
+
self.publish_status()
|
|
421
447
|
|
|
422
448
|
def reset_state_machine(self) -> None:
|
|
423
449
|
self.logger.info("Resetting state machine")
|
|
@@ -559,25 +585,33 @@ class StateMachine(object):
|
|
|
559
585
|
retain=False,
|
|
560
586
|
)
|
|
561
587
|
|
|
562
|
-
def
|
|
588
|
+
def publish_status(self) -> None:
|
|
563
589
|
if not self.mqtt_publisher:
|
|
564
590
|
return
|
|
565
591
|
payload: str = json.dumps(
|
|
566
592
|
{
|
|
567
593
|
"isar_id": settings.ISAR_ID,
|
|
568
594
|
"robot_name": settings.ROBOT_NAME,
|
|
569
|
-
"
|
|
595
|
+
"status": self._current_status(),
|
|
570
596
|
"timestamp": datetime.utcnow(),
|
|
571
597
|
},
|
|
572
598
|
cls=EnhancedJSONEncoder,
|
|
573
599
|
)
|
|
574
600
|
|
|
575
601
|
self.mqtt_publisher.publish(
|
|
576
|
-
topic=settings.
|
|
602
|
+
topic=settings.TOPIC_ISAR_STATUS,
|
|
577
603
|
payload=payload,
|
|
578
604
|
retain=False,
|
|
579
605
|
)
|
|
580
606
|
|
|
607
|
+
def _current_status(self) -> RobotStatus:
|
|
608
|
+
if self.current_state == States.Idle:
|
|
609
|
+
return RobotStatus.Available
|
|
610
|
+
elif self.current_state == States.Offline:
|
|
611
|
+
return RobotStatus.Offline
|
|
612
|
+
else:
|
|
613
|
+
return RobotStatus.Busy
|
|
614
|
+
|
|
581
615
|
def _log_state_transition(self, next_state):
|
|
582
616
|
"""Logs all state transitions that are not self-transitions."""
|
|
583
617
|
self.transitions_list.append(next_state)
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import time
|
|
3
|
-
from typing import
|
|
3
|
+
from typing import TYPE_CHECKING, Optional
|
|
4
4
|
|
|
5
5
|
from transitions import State
|
|
6
6
|
|
|
7
|
+
from isar.config.settings import settings
|
|
7
8
|
from isar.models.communication.message import StartMissionMessage
|
|
9
|
+
from isar.services.utilities.threaded_request import (
|
|
10
|
+
ThreadedRequest,
|
|
11
|
+
ThreadedRequestNotFinishedError,
|
|
12
|
+
)
|
|
13
|
+
from robot_interface.models.exceptions.robot_exceptions import RobotException
|
|
14
|
+
from robot_interface.models.mission.status import RobotStatus
|
|
8
15
|
|
|
9
16
|
if TYPE_CHECKING:
|
|
10
17
|
from isar.state_machine.state_machine import StateMachine
|
|
@@ -15,13 +22,17 @@ class Idle(State):
|
|
|
15
22
|
super().__init__(name="idle", on_enter=self.start, on_exit=self.stop)
|
|
16
23
|
self.state_machine: "StateMachine" = state_machine
|
|
17
24
|
self.logger = logging.getLogger("state_machine")
|
|
25
|
+
self.robot_status_thread: Optional[ThreadedRequest] = None
|
|
26
|
+
self.last_robot_status_poll_time: float = time.time()
|
|
18
27
|
|
|
19
28
|
def start(self) -> None:
|
|
20
29
|
self.state_machine.update_state()
|
|
21
30
|
self._run()
|
|
22
31
|
|
|
23
32
|
def stop(self) -> None:
|
|
24
|
-
|
|
33
|
+
if self.robot_status_thread:
|
|
34
|
+
self.robot_status_thread.wait_for_thread()
|
|
35
|
+
self.robot_status_thread = None
|
|
25
36
|
|
|
26
37
|
def _run(self) -> None:
|
|
27
38
|
while True:
|
|
@@ -37,4 +48,38 @@ class Idle(State):
|
|
|
37
48
|
break
|
|
38
49
|
time.sleep(self.state_machine.sleep_time)
|
|
39
50
|
|
|
51
|
+
time_from_last_robot_status_poll = (
|
|
52
|
+
time.time() - self.last_robot_status_poll_time
|
|
53
|
+
)
|
|
54
|
+
if (
|
|
55
|
+
time_from_last_robot_status_poll
|
|
56
|
+
< settings.ROBOT_API_STATUS_POLL_INTERVAL
|
|
57
|
+
):
|
|
58
|
+
continue
|
|
59
|
+
|
|
60
|
+
if not self.robot_status_thread:
|
|
61
|
+
self.robot_status_thread = ThreadedRequest(
|
|
62
|
+
request_func=self.state_machine.robot.robot_status
|
|
63
|
+
)
|
|
64
|
+
self.robot_status_thread.start_thread(
|
|
65
|
+
name="State Machine Offline Get Robot Status"
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
try:
|
|
69
|
+
robot_status: RobotStatus = self.robot_status_thread.get_output()
|
|
70
|
+
except ThreadedRequestNotFinishedError:
|
|
71
|
+
time.sleep(self.state_machine.sleep_time)
|
|
72
|
+
continue
|
|
73
|
+
|
|
74
|
+
except (RobotException,) as e:
|
|
75
|
+
self.logger.error(
|
|
76
|
+
f"Failed to get robot status because: {e.error_description}"
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
self.last_robot_status_poll_time = time.time()
|
|
80
|
+
|
|
81
|
+
if robot_status == RobotStatus.Offline:
|
|
82
|
+
transition = self.state_machine.robot_turned_offline # type: ignore
|
|
83
|
+
break
|
|
84
|
+
|
|
40
85
|
transition()
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import time
|
|
3
3
|
from copy import deepcopy
|
|
4
|
-
from typing import Callable, Optional, Sequence,
|
|
4
|
+
from typing import TYPE_CHECKING, Callable, Optional, Sequence, Tuple, Union
|
|
5
5
|
|
|
6
6
|
from injector import inject
|
|
7
7
|
from transitions import State
|
|
@@ -14,11 +14,11 @@ from isar.services.utilities.threaded_request import (
|
|
|
14
14
|
)
|
|
15
15
|
from robot_interface.models.exceptions.robot_exceptions import (
|
|
16
16
|
ErrorMessage,
|
|
17
|
+
RobotCommunicationTimeoutException,
|
|
17
18
|
RobotException,
|
|
18
19
|
RobotMissionStatusException,
|
|
19
20
|
RobotRetrieveInspectionException,
|
|
20
21
|
RobotStepStatusException,
|
|
21
|
-
RobotCommunicationTimeoutException,
|
|
22
22
|
)
|
|
23
23
|
from robot_interface.models.inspection.inspection import Inspection
|
|
24
24
|
from robot_interface.models.mission.mission import Mission
|
|
@@ -131,8 +131,6 @@ class Monitor(State):
|
|
|
131
131
|
f"Retrieving the status failed because: {e.error_description}"
|
|
132
132
|
)
|
|
133
133
|
|
|
134
|
-
self.request_status_failure_counter = 0
|
|
135
|
-
|
|
136
134
|
if isinstance(status, StepStatus):
|
|
137
135
|
self.state_machine.current_step.status = status
|
|
138
136
|
elif isinstance(status, MissionStatus):
|
|
@@ -158,6 +156,7 @@ class Monitor(State):
|
|
|
158
156
|
else:
|
|
159
157
|
if isinstance(status, StepStatus):
|
|
160
158
|
if self._step_finished(self.state_machine.current_step):
|
|
159
|
+
self.state_machine.update_current_step()
|
|
161
160
|
self.state_machine.current_task.update_task_status()
|
|
162
161
|
else: # If not all steps are done
|
|
163
162
|
self.state_machine.current_task.status = TaskStatus.InProgress
|
|
@@ -166,7 +165,6 @@ class Monitor(State):
|
|
|
166
165
|
self.state_machine.current_task
|
|
167
166
|
)
|
|
168
167
|
if self.state_machine.current_task.status == TaskStatus.Successful:
|
|
169
|
-
self.state_machine.update_remaining_steps()
|
|
170
168
|
try:
|
|
171
169
|
self.state_machine.current_task = (
|
|
172
170
|
self.state_machine.task_selector.next_task()
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import time
|
|
3
|
+
from typing import TYPE_CHECKING, Optional
|
|
4
|
+
|
|
5
|
+
from transitions import State
|
|
6
|
+
|
|
7
|
+
from isar.config.settings import settings
|
|
8
|
+
from isar.services.utilities.threaded_request import (
|
|
9
|
+
ThreadedRequest,
|
|
10
|
+
ThreadedRequestNotFinishedError,
|
|
11
|
+
)
|
|
12
|
+
from robot_interface.models.exceptions.robot_exceptions import RobotException
|
|
13
|
+
from robot_interface.models.mission.status import RobotStatus
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from isar.state_machine.state_machine import StateMachine
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Offline(State):
|
|
20
|
+
def __init__(self, state_machine: "StateMachine") -> None:
|
|
21
|
+
super().__init__(name="offline", on_enter=self.start, on_exit=self.stop)
|
|
22
|
+
self.state_machine: "StateMachine" = state_machine
|
|
23
|
+
self.logger = logging.getLogger("state_machine")
|
|
24
|
+
self.robot_status_thread: Optional[ThreadedRequest] = None
|
|
25
|
+
|
|
26
|
+
def start(self) -> None:
|
|
27
|
+
self.state_machine.update_state()
|
|
28
|
+
self._run()
|
|
29
|
+
|
|
30
|
+
def stop(self) -> None:
|
|
31
|
+
if self.robot_status_thread:
|
|
32
|
+
self.robot_status_thread.wait_for_thread()
|
|
33
|
+
self.robot_status_thread = None
|
|
34
|
+
|
|
35
|
+
def _run(self) -> None:
|
|
36
|
+
while True:
|
|
37
|
+
if not self.robot_status_thread:
|
|
38
|
+
self.robot_status_thread = ThreadedRequest(
|
|
39
|
+
request_func=self.state_machine.robot.robot_status
|
|
40
|
+
)
|
|
41
|
+
self.robot_status_thread.start_thread(
|
|
42
|
+
name="State Machine Offline Get Robot Status"
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
try:
|
|
46
|
+
robot_status: RobotStatus = self.robot_status_thread.get_output()
|
|
47
|
+
except ThreadedRequestNotFinishedError:
|
|
48
|
+
time.sleep(self.state_machine.sleep_time)
|
|
49
|
+
continue
|
|
50
|
+
|
|
51
|
+
except (RobotException,) as e:
|
|
52
|
+
self.logger.error(
|
|
53
|
+
f"Failed to get robot status because: {e.error_description}"
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
if robot_status != RobotStatus.Offline:
|
|
57
|
+
transition = self.state_machine.robot_turned_online # type: ignore
|
|
58
|
+
break
|
|
59
|
+
|
|
60
|
+
time.sleep(settings.ROBOT_API_STATUS_POLL_INTERVAL)
|
|
61
|
+
|
|
62
|
+
transition()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: isar
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.19.0
|
|
4
4
|
Summary: Integration and Supervisory control of Autonomous Robots
|
|
5
5
|
Home-page: https://github.com/equinor/isar
|
|
6
6
|
Author: Equinor ASA
|
|
@@ -415,6 +415,17 @@ ISAR_MQTT_PASSWORD
|
|
|
415
415
|
|
|
416
416
|
If not specified the password will default to an empty string.
|
|
417
417
|
|
|
418
|
+
## Running several ISAR instances locally
|
|
419
|
+
|
|
420
|
+
To run several ISAR instances in parallel locally:
|
|
421
|
+
1. Generate a guid: https://www.guidgenerator.com/
|
|
422
|
+
2. Open a new terminal in the isar folder
|
|
423
|
+
3. Run the following command before running main.py:
|
|
424
|
+
|
|
425
|
+
```
|
|
426
|
+
export ISAR_API_PORT=port_name_higher_than_1024 ISAR_ISAR_ID=guid ISAR_ROBOT_NAME=random_robot_name
|
|
427
|
+
```
|
|
428
|
+
|
|
418
429
|
# Contributions
|
|
419
430
|
|
|
420
431
|
Equinor welcomes all kinds of contributions, including code, bug reports, issues, feature requests, and documentation
|
|
@@ -14,7 +14,7 @@ isar/config/configuration_error.py,sha256=rO6WOhafX6xvVib8WxV-eY483Z0PpN-9PxGsq5
|
|
|
14
14
|
isar/config/log.py,sha256=39G_Cue0qvw0Uv-1NYXvQ83yivPIKcAv-bGNNREFw5o,2251
|
|
15
15
|
isar/config/logging.conf,sha256=mYO1xf27gAopEMHhGzY7-mwyfN16rwRLkPNMvy3zn2g,1127
|
|
16
16
|
isar/config/settings.env,sha256=-kivj0osAAKlInnY81ugySTlcImhVABbnj9kUoBDLu8,535
|
|
17
|
-
isar/config/settings.py,sha256=
|
|
17
|
+
isar/config/settings.py,sha256=Wfau6k5ZUoC0BWgWXdzj4cSU7lny8SWrwqoy3t1U4Wg,13081
|
|
18
18
|
isar/config/certs/ca-cert.pem,sha256=gSBTyY0tKSFnssyvrvbFvHpQwii0kEkBryklVmevdtc,2029
|
|
19
19
|
isar/config/keyvault/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
20
20
|
isar/config/keyvault/keyvault_error.py,sha256=zvPCsZLjboxsxthYkxpRERCTFxYV8R5WmACewAUQLwk,41
|
|
@@ -60,21 +60,21 @@ isar/services/service_connections/mqtt/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JC
|
|
|
60
60
|
isar/services/service_connections/mqtt/mqtt_client.py,sha256=Aib0lqaddxW9aVXXYD7wGL9jIpr2USCCH91SQgFdIG4,3548
|
|
61
61
|
isar/services/service_connections/mqtt/robot_heartbeat_publisher.py,sha256=TxvpLA_qfyXwwN58h2X-eomTnhbFPhqfjamPxBz084E,1000
|
|
62
62
|
isar/services/service_connections/mqtt/robot_info_publisher.py,sha256=d5HQ6Hfp5E21kaj1IJsFMWXecbzvH8iYByZhOucR004,1383
|
|
63
|
-
isar/services/service_connections/mqtt/robot_status_publisher.py,sha256=KPxRrdtu-yLQolQ7DejC1v1NCeELUzrK_ZoiEpsrqD4,4425
|
|
64
63
|
isar/services/service_connections/stid/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
65
64
|
isar/services/utilities/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
66
65
|
isar/services/utilities/queue_utilities.py,sha256=Pw3hehSwkXJNeDv-bDVDfs58VOwtt3i5hpiJ2ZpphuQ,1225
|
|
67
66
|
isar/services/utilities/scheduling_utilities.py,sha256=LFimEmacML3J9q-FNLfKPhcAr-R3f2rkYkbsoro0Gyo,8434
|
|
68
67
|
isar/services/utilities/threaded_request.py,sha256=py4G-_RjnIdHljmKFAcQ6ddqMmp-ZYV39Ece-dqRqjs,1874
|
|
69
68
|
isar/state_machine/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
70
|
-
isar/state_machine/state_machine.py,sha256=
|
|
71
|
-
isar/state_machine/states_enum.py,sha256=
|
|
72
|
-
isar/state_machine/states/__init__.py,sha256=
|
|
73
|
-
isar/state_machine/states/idle.py,sha256=
|
|
69
|
+
isar/state_machine/state_machine.py,sha256=wmjPXoavlxrLvgMr41LGZekE5D_Cnsoz5IRAreag6_8,23576
|
|
70
|
+
isar/state_machine/states_enum.py,sha256=BlrUcBWkM5K6D_UZXRwTaUgGpAagWmVZH6HhDBGzVU4,278
|
|
71
|
+
isar/state_machine/states/__init__.py,sha256=kErbKPDTwNfCLijvdyN6_AuOqDwR23nu9F0Qovsnir4,218
|
|
72
|
+
isar/state_machine/states/idle.py,sha256=_nrM17s4artaHezanl28_WcNyJod1_hkCyzAqZlPQiE,3034
|
|
74
73
|
isar/state_machine/states/initialize.py,sha256=Vx7OxWZI_xEkRwHWFh9ymxK_7mDH0Wayt09dGGqBJWk,2359
|
|
75
74
|
isar/state_machine/states/initiate.py,sha256=b0Fq3tN6TZMg5j4192OsYdGbphTXWCkftBsFX57mhrw,5652
|
|
76
|
-
isar/state_machine/states/monitor.py,sha256=
|
|
75
|
+
isar/state_machine/states/monitor.py,sha256=wJCBJo0xc42pzI8mUtOIsPsAK5V82oY3h9uiy-EkNr4,10609
|
|
77
76
|
isar/state_machine/states/off.py,sha256=jjqN_oJMpBtWuY7hP-c9f0w3p2CYCfe-NpmYHHPnmyI,544
|
|
77
|
+
isar/state_machine/states/offline.py,sha256=wEMMIwM4JWfmDjI7pe9yKce_Mfz9aXqs6WEkxn8cx5I,2125
|
|
78
78
|
isar/state_machine/states/paused.py,sha256=xVZM9WMt90FTiP5forSlA3eJ5Vt9LzZYCFDQDPIocKA,1004
|
|
79
79
|
isar/state_machine/states/stop.py,sha256=Sxdo4FGhxc2Pj91jidYtVIjpSNklbEf1sJYJ6x51HgE,3348
|
|
80
80
|
isar/storage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -107,8 +107,8 @@ robot_interface/telemetry/payloads.py,sha256=eMK7mjZPsLY6yvu7AK-OcdvkeUpChzDrySD
|
|
|
107
107
|
robot_interface/utilities/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
108
108
|
robot_interface/utilities/json_service.py,sha256=nU2Q_3P9Fq9hs6F_wtUjWtHfl_g1Siy-yDhXXSKwHwg,1018
|
|
109
109
|
robot_interface/utilities/uuid_string_factory.py,sha256=_NQIbBQ56w0qqO0MUDP6aPpHbxW7ATRhK8HnQiBSLkc,76
|
|
110
|
-
isar-1.
|
|
111
|
-
isar-1.
|
|
112
|
-
isar-1.
|
|
113
|
-
isar-1.
|
|
114
|
-
isar-1.
|
|
110
|
+
isar-1.19.0.dist-info/LICENSE,sha256=3fc2-ebLwHWwzfQbulGNRdcNob3SBQeCfEVUDYxsuqw,14058
|
|
111
|
+
isar-1.19.0.dist-info/METADATA,sha256=E_9yJSv0SF_T3xHun3k_dPsM4DqQ9ayg_E65UpXLC68,15101
|
|
112
|
+
isar-1.19.0.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
|
113
|
+
isar-1.19.0.dist-info/top_level.txt,sha256=UwIML2RtuQKCyJJkatcSnyp6-ldDjboB9k9JgKipO-U,21
|
|
114
|
+
isar-1.19.0.dist-info/RECORD,,
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
import logging
|
|
3
|
-
import time
|
|
4
|
-
from datetime import datetime
|
|
5
|
-
from logging import Logger
|
|
6
|
-
from queue import Queue
|
|
7
|
-
from threading import Thread
|
|
8
|
-
from typing import Optional
|
|
9
|
-
|
|
10
|
-
from isar.config.settings import settings
|
|
11
|
-
from isar.state_machine.state_machine import StateMachine
|
|
12
|
-
from isar.state_machine.states_enum import States
|
|
13
|
-
from robot_interface.models.exceptions.robot_exceptions import (
|
|
14
|
-
RobotAPIException,
|
|
15
|
-
RobotCommunicationException,
|
|
16
|
-
RobotException,
|
|
17
|
-
)
|
|
18
|
-
from robot_interface.models.mission.status import RobotStatus
|
|
19
|
-
from robot_interface.robot_interface import RobotInterface
|
|
20
|
-
from robot_interface.telemetry.mqtt_client import MqttPublisher
|
|
21
|
-
from robot_interface.telemetry.payloads import RobotStatusPayload
|
|
22
|
-
from robot_interface.utilities.json_service import EnhancedJSONEncoder
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
class RobotStatusPublisher:
|
|
26
|
-
def __init__(
|
|
27
|
-
self, mqtt_queue: Queue, robot: RobotInterface, state_machine: StateMachine
|
|
28
|
-
):
|
|
29
|
-
self.mqtt_publisher: MqttPublisher = MqttPublisher(mqtt_queue=mqtt_queue)
|
|
30
|
-
self.robot: RobotInterface = robot
|
|
31
|
-
self.state_machine: StateMachine = state_machine
|
|
32
|
-
|
|
33
|
-
def _get_combined_robot_status(
|
|
34
|
-
self, robot_status: RobotStatus, current_state: States
|
|
35
|
-
) -> RobotStatus:
|
|
36
|
-
if robot_status == RobotStatus.Offline:
|
|
37
|
-
return RobotStatus.Offline
|
|
38
|
-
elif current_state == States.Idle and robot_status == RobotStatus.Available:
|
|
39
|
-
return RobotStatus.Available
|
|
40
|
-
elif robot_status == RobotStatus.Blocked:
|
|
41
|
-
return RobotStatus.Blocked
|
|
42
|
-
elif current_state != States.Idle or robot_status == RobotStatus.Busy:
|
|
43
|
-
return RobotStatus.Busy
|
|
44
|
-
return None
|
|
45
|
-
|
|
46
|
-
def run(self) -> None:
|
|
47
|
-
robot_status_monitor: RobotStatusMonitor = RobotStatusMonitor(robot=self.robot)
|
|
48
|
-
robot_status_thread: Thread = Thread(
|
|
49
|
-
target=robot_status_monitor.run,
|
|
50
|
-
name="Robot Status Monitor",
|
|
51
|
-
daemon=True,
|
|
52
|
-
)
|
|
53
|
-
robot_status_thread.start()
|
|
54
|
-
|
|
55
|
-
previous_robot_status: Optional[RobotStatus] = None
|
|
56
|
-
|
|
57
|
-
while True:
|
|
58
|
-
time.sleep(settings.ROBOT_STATUS_PUBLISH_INTERVAL)
|
|
59
|
-
|
|
60
|
-
combined_status: RobotStatus = self._get_combined_robot_status(
|
|
61
|
-
robot_status=robot_status_monitor.robot_status,
|
|
62
|
-
current_state=self.state_machine.current_state,
|
|
63
|
-
)
|
|
64
|
-
|
|
65
|
-
if previous_robot_status:
|
|
66
|
-
if previous_robot_status == combined_status:
|
|
67
|
-
continue
|
|
68
|
-
|
|
69
|
-
payload: RobotStatusPayload = RobotStatusPayload(
|
|
70
|
-
isar_id=settings.ISAR_ID,
|
|
71
|
-
robot_name=settings.ROBOT_NAME,
|
|
72
|
-
robot_status=combined_status,
|
|
73
|
-
previous_robot_status=previous_robot_status,
|
|
74
|
-
current_isar_state=self.state_machine.current_state,
|
|
75
|
-
current_mission_id=(
|
|
76
|
-
self.state_machine.current_mission.id
|
|
77
|
-
if self.state_machine.current_mission
|
|
78
|
-
else None
|
|
79
|
-
),
|
|
80
|
-
current_task_id=(
|
|
81
|
-
self.state_machine.current_task.id
|
|
82
|
-
if self.state_machine.current_task
|
|
83
|
-
else None
|
|
84
|
-
),
|
|
85
|
-
current_step_id=(
|
|
86
|
-
self.state_machine.current_step.id
|
|
87
|
-
if self.state_machine.current_step
|
|
88
|
-
else None
|
|
89
|
-
),
|
|
90
|
-
timestamp=datetime.utcnow(),
|
|
91
|
-
)
|
|
92
|
-
|
|
93
|
-
self.mqtt_publisher.publish(
|
|
94
|
-
topic=settings.TOPIC_ISAR_ROBOT_STATUS,
|
|
95
|
-
payload=json.dumps(payload, cls=EnhancedJSONEncoder),
|
|
96
|
-
)
|
|
97
|
-
|
|
98
|
-
previous_robot_status = combined_status
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
class RobotStatusMonitor:
|
|
102
|
-
def __init__(self, robot: RobotInterface):
|
|
103
|
-
self.robot: RobotInterface = robot
|
|
104
|
-
self.robot_status: RobotStatus = RobotStatus.Offline
|
|
105
|
-
self.logger: Logger = logging.getLogger("robot_status_monitor")
|
|
106
|
-
|
|
107
|
-
def run(self) -> None:
|
|
108
|
-
while True:
|
|
109
|
-
try:
|
|
110
|
-
self.robot_status = self.robot.robot_status()
|
|
111
|
-
except (
|
|
112
|
-
RobotCommunicationException,
|
|
113
|
-
RobotAPIException,
|
|
114
|
-
RobotException,
|
|
115
|
-
) as e:
|
|
116
|
-
self.logger.error(
|
|
117
|
-
f"Failed to get robot status because: {e.error_description}"
|
|
118
|
-
)
|
|
119
|
-
time.sleep(settings.ROBOT_API_STATUS_POLL_INTERVAL)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|