isar 1.15.0__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.
Files changed (129) hide show
  1. isar/__init__.py +2 -5
  2. isar/apis/api.py +159 -66
  3. isar/apis/models/__init__.py +0 -1
  4. isar/apis/models/models.py +22 -12
  5. isar/apis/models/start_mission_definition.py +128 -123
  6. isar/apis/robot_control/robot_controller.py +41 -0
  7. isar/apis/schedule/scheduling_controller.py +135 -121
  8. isar/apis/security/authentication.py +5 -5
  9. isar/config/certs/ca-cert.pem +32 -32
  10. isar/config/keyvault/keyvault_service.py +1 -2
  11. isar/config/log.py +47 -39
  12. isar/config/logging.conf +16 -31
  13. isar/config/open_telemetry.py +102 -0
  14. isar/config/predefined_mission_definition/default_exr.json +49 -0
  15. isar/config/predefined_mission_definition/default_mission.json +1 -5
  16. isar/config/predefined_mission_definition/default_turtlebot.json +4 -11
  17. isar/config/predefined_missions/default.json +67 -87
  18. isar/config/predefined_missions/default_extra_capabilities.json +107 -0
  19. isar/config/settings.py +119 -142
  20. isar/eventhandlers/eventhandler.py +123 -0
  21. isar/mission_planner/local_planner.py +6 -20
  22. isar/mission_planner/mission_planner_interface.py +1 -1
  23. isar/models/events.py +184 -0
  24. isar/models/status.py +18 -0
  25. isar/modules.py +118 -205
  26. isar/robot/robot.py +377 -0
  27. isar/robot/robot_battery.py +60 -0
  28. isar/robot/robot_monitor_mission.py +357 -0
  29. isar/robot/robot_pause_mission.py +74 -0
  30. isar/robot/robot_resume_mission.py +67 -0
  31. isar/robot/robot_start_mission.py +66 -0
  32. isar/robot/robot_status.py +61 -0
  33. isar/robot/robot_stop_mission.py +68 -0
  34. isar/robot/robot_upload_inspection.py +75 -0
  35. isar/script.py +171 -0
  36. isar/services/service_connections/mqtt/mqtt_client.py +47 -11
  37. isar/services/service_connections/mqtt/robot_heartbeat_publisher.py +32 -0
  38. isar/services/service_connections/mqtt/robot_info_publisher.py +4 -3
  39. isar/services/service_connections/persistent_memory.py +69 -0
  40. isar/services/utilities/mqtt_utilities.py +93 -0
  41. isar/services/utilities/robot_utilities.py +20 -0
  42. isar/services/utilities/scheduling_utilities.py +393 -65
  43. isar/state_machine/state_machine.py +227 -486
  44. isar/state_machine/states/__init__.py +0 -7
  45. isar/state_machine/states/await_next_mission.py +114 -0
  46. isar/state_machine/states/blocked_protective_stop.py +60 -0
  47. isar/state_machine/states/going_to_lockdown.py +95 -0
  48. isar/state_machine/states/going_to_recharging.py +92 -0
  49. isar/state_machine/states/home.py +115 -0
  50. isar/state_machine/states/intervention_needed.py +77 -0
  51. isar/state_machine/states/lockdown.py +38 -0
  52. isar/state_machine/states/maintenance.py +36 -0
  53. isar/state_machine/states/monitor.py +137 -166
  54. isar/state_machine/states/offline.py +60 -0
  55. isar/state_machine/states/paused.py +92 -23
  56. isar/state_machine/states/pausing.py +48 -0
  57. isar/state_machine/states/pausing_return_home.py +48 -0
  58. isar/state_machine/states/recharging.py +80 -0
  59. isar/state_machine/states/resuming.py +57 -0
  60. isar/state_machine/states/resuming_return_home.py +64 -0
  61. isar/state_machine/states/return_home_paused.py +109 -0
  62. isar/state_machine/states/returning_home.py +217 -0
  63. isar/state_machine/states/stopping.py +61 -0
  64. isar/state_machine/states/stopping_due_to_maintenance.py +61 -0
  65. isar/state_machine/states/stopping_go_to_lockdown.py +60 -0
  66. isar/state_machine/states/stopping_go_to_recharge.py +51 -0
  67. isar/state_machine/states/stopping_return_home.py +77 -0
  68. isar/state_machine/states/unknown_status.py +72 -0
  69. isar/state_machine/states_enum.py +22 -5
  70. isar/state_machine/transitions/mission.py +192 -0
  71. isar/state_machine/transitions/return_home.py +106 -0
  72. isar/state_machine/transitions/robot_status.py +80 -0
  73. isar/state_machine/utils/common_event_handlers.py +73 -0
  74. isar/storage/blob_storage.py +71 -45
  75. isar/storage/local_storage.py +28 -14
  76. isar/storage/storage_interface.py +28 -6
  77. isar/storage/uploader.py +184 -55
  78. isar/storage/utilities.py +35 -27
  79. isar-1.34.9.dist-info/METADATA +496 -0
  80. isar-1.34.9.dist-info/RECORD +135 -0
  81. {isar-1.15.0.dist-info → isar-1.34.9.dist-info}/WHEEL +1 -1
  82. isar-1.34.9.dist-info/entry_points.txt +3 -0
  83. robot_interface/models/exceptions/__init__.py +0 -7
  84. robot_interface/models/exceptions/robot_exceptions.py +274 -4
  85. robot_interface/models/initialize/__init__.py +0 -1
  86. robot_interface/models/inspection/__init__.py +0 -13
  87. robot_interface/models/inspection/inspection.py +43 -34
  88. robot_interface/models/mission/mission.py +18 -14
  89. robot_interface/models/mission/status.py +20 -25
  90. robot_interface/models/mission/task.py +156 -92
  91. robot_interface/models/robots/battery_state.py +6 -0
  92. robot_interface/models/robots/media.py +13 -0
  93. robot_interface/models/robots/robot_model.py +7 -7
  94. robot_interface/robot_interface.py +135 -66
  95. robot_interface/telemetry/mqtt_client.py +84 -12
  96. robot_interface/telemetry/payloads.py +111 -12
  97. robot_interface/utilities/json_service.py +7 -1
  98. isar/config/predefined_missions/default_turtlebot.json +0 -110
  99. isar/config/predefined_poses/__init__.py +0 -0
  100. isar/config/predefined_poses/predefined_poses.py +0 -616
  101. isar/config/settings.env +0 -26
  102. isar/mission_planner/sequential_task_selector.py +0 -23
  103. isar/mission_planner/task_selector_interface.py +0 -31
  104. isar/models/communication/__init__.py +0 -0
  105. isar/models/communication/message.py +0 -12
  106. isar/models/communication/queues/__init__.py +0 -4
  107. isar/models/communication/queues/queue_io.py +0 -12
  108. isar/models/communication/queues/queue_timeout_error.py +0 -2
  109. isar/models/communication/queues/queues.py +0 -19
  110. isar/models/communication/queues/status_queue.py +0 -20
  111. isar/models/mission_metadata/__init__.py +0 -0
  112. isar/services/readers/__init__.py +0 -0
  113. isar/services/readers/base_reader.py +0 -37
  114. isar/services/service_connections/mqtt/robot_status_publisher.py +0 -93
  115. isar/services/service_connections/stid/__init__.py +0 -0
  116. isar/services/service_connections/stid/stid_service.py +0 -45
  117. isar/services/utilities/queue_utilities.py +0 -39
  118. isar/state_machine/states/idle.py +0 -40
  119. isar/state_machine/states/initialize.py +0 -60
  120. isar/state_machine/states/initiate.py +0 -129
  121. isar/state_machine/states/off.py +0 -18
  122. isar/state_machine/states/stop.py +0 -78
  123. isar/storage/slimm_storage.py +0 -181
  124. isar-1.15.0.dist-info/METADATA +0 -417
  125. isar-1.15.0.dist-info/RECORD +0 -113
  126. robot_interface/models/initialize/initialize_params.py +0 -9
  127. robot_interface/models/mission/step.py +0 -211
  128. {isar-1.15.0.dist-info → isar-1.34.9.dist-info/licenses}/LICENSE +0 -0
  129. {isar-1.15.0.dist-info → isar-1.34.9.dist-info}/top_level.txt +0 -0
@@ -1,181 +0,0 @@
1
- import json
2
- import logging
3
-
4
- from azure.identity import DefaultAzureCredential
5
- from injector import inject
6
- from requests import HTTPError, RequestException
7
- from requests_toolbelt import MultipartEncoder
8
-
9
- from isar.config.settings import settings
10
- from robot_interface.models.mission.mission import Mission
11
- from isar.services.auth.azure_credentials import AzureCredentials
12
- from isar.services.service_connections.request_handler import RequestHandler
13
- from isar.storage.storage_interface import StorageException, StorageInterface
14
- from isar.storage.utilities import get_filename
15
- from robot_interface.models.inspection.inspection import Inspection, ThermalVideo, Video
16
-
17
-
18
- class SlimmStorage(StorageInterface):
19
- @inject
20
- def __init__(self, request_handler: RequestHandler) -> None:
21
- self.request_handler: RequestHandler = request_handler
22
- self.logger = logging.getLogger("uploader")
23
-
24
- self.credentials: DefaultAzureCredential = (
25
- AzureCredentials.get_azure_credentials()
26
- )
27
-
28
- client_id: str = settings.SLIMM_CLIENT_ID
29
- scope: str = settings.SLIMM_APP_SCOPE
30
- self.request_scope: str = f"{client_id}/{scope}"
31
-
32
- self.url: str = settings.SLIMM_API_URL
33
-
34
- def store(self, inspection: Inspection, mission: Mission) -> str:
35
- filename: str = get_filename(
36
- inspection=inspection,
37
- )
38
- filename = f"{filename}.{inspection.metadata.file_type}"
39
- if type(inspection) in [Video, ThermalVideo]:
40
- inspection_path = self._store_video(filename, inspection, mission)
41
- else:
42
- inspection_path = self._store_image(filename, inspection, mission)
43
- return inspection_path
44
-
45
- def _store_image(
46
- self, filename: str, inspection: Inspection, mission: Mission
47
- ) -> str:
48
- multiform_body: MultipartEncoder = self._construct_multiform_request_image(
49
- filename=filename, inspection=inspection, mission=mission
50
- )
51
- request_url: str = f"{self.url}/UploadSingleImage"
52
- inspection_path = self._ingest(
53
- inspection=inspection,
54
- multiform_body=multiform_body,
55
- request_url=request_url,
56
- )
57
- return inspection_path
58
-
59
- def _store_video(
60
- self, filename: str, inspection: Inspection, mission: Mission
61
- ) -> str:
62
- multiform_body: MultipartEncoder = self._construct_multiform_request_video(
63
- filename=filename, inspection=inspection, mission=mission
64
- )
65
- request_url = f"{self.url}/UploadSingleVideo"
66
- inspection_path = self._ingest(
67
- inspection=inspection,
68
- multiform_body=multiform_body,
69
- request_url=request_url,
70
- )
71
- return inspection_path
72
-
73
- def _ingest(
74
- self, inspection: Inspection, multiform_body: MultipartEncoder, request_url: str
75
- ) -> str:
76
- token: str = self.credentials.get_token(self.request_scope).token
77
- try:
78
- response = self.request_handler.post(
79
- url=request_url,
80
- data=multiform_body,
81
- headers={
82
- "Authorization": f"Bearer {token}",
83
- "Content-Type": multiform_body.content_type,
84
- },
85
- )
86
- guid = json.loads(response.text)["guid"]
87
- self.logger.info(f"SLIMM upload GUID: {guid}")
88
- except (RequestException, HTTPError) as e:
89
- self.logger.warning(
90
- f"Failed to upload inspection: {inspection.id} to SLIMM due to a "
91
- f"request exception"
92
- )
93
- raise StorageException from e
94
- data = json.loads(response.content)
95
- return data["guid"]
96
-
97
- @staticmethod
98
- def _construct_multiform_request_image(
99
- filename: str, inspection: Inspection, mission: Mission
100
- ):
101
- array_of_orientation = (
102
- inspection.metadata.pose.orientation.to_quat_array().tolist()
103
- )
104
- multiform_body: MultipartEncoder = MultipartEncoder(
105
- fields={
106
- "PlantFacilitySAPCode": settings.PLANT_CODE,
107
- "InstCode": settings.PLANT_SHORT_NAME,
108
- "InternalClassification": settings.DATA_CLASSIFICATION,
109
- "IsoCountryCode": "NO",
110
- "Geodetic.CoordinateReferenceSystemCode": settings.COORDINATE_REFERENCE_SYSTEM, # noqa: E501
111
- "Geodetic.VerticalCoordinateReferenceSystemCode": settings.VERTICAL_REFERENCE_SYSTEM, # noqa: E501
112
- "Geodetic.OrientationReferenceSystem": settings.MEDIA_ORIENTATION_REFERENCE_SYSTEM, # noqa: E501
113
- "SensorCarrier.SensorCarrierId": settings.ISAR_ID,
114
- "SensorCarrier.ModelName": settings.ROBOT_TYPE,
115
- "Mission.MissionId": mission.id,
116
- "Mission.Client": "Equinor",
117
- "ImageMetadata.Timestamp": inspection.metadata.start_time.isoformat(), # noqa: E501
118
- "ImageMetadata.X": str(inspection.metadata.pose.position.x),
119
- "ImageMetadata.Y": str(inspection.metadata.pose.position.y),
120
- "ImageMetadata.Z": str(inspection.metadata.pose.position.z),
121
- "ImageMetadata.CameraOrientation1": str(array_of_orientation[0]),
122
- "ImageMetadata.CameraOrientation2": str(array_of_orientation[1]),
123
- "ImageMetadata.CameraOrientation3": str(array_of_orientation[2]),
124
- "ImageMetadata.CameraOrientation4": str(array_of_orientation[3]),
125
- "ImageMetadata.AnalysisMethods": inspection.metadata.analysis
126
- if inspection.metadata.analysis
127
- else "N/A",
128
- "ImageMetadata.Description": str(inspection.metadata.additional),
129
- "ImageMetadata.FunctionalLocation": inspection.metadata.tag_id # noqa: E501
130
- if inspection.metadata.tag_id
131
- else "N/A",
132
- "Filename": filename,
133
- "AttachedFile": (filename, inspection.data),
134
- }
135
- )
136
- return multiform_body
137
-
138
- @staticmethod
139
- def _construct_multiform_request_video(
140
- filename: str,
141
- inspection: Inspection,
142
- mission: Mission,
143
- ):
144
- array_of_orientation = (
145
- inspection.metadata.pose.orientation.to_quat_array().tolist()
146
- )
147
- multiform_body: MultipartEncoder = MultipartEncoder(
148
- fields={
149
- "PlantFacilitySAPCode": settings.PLANT_CODE,
150
- "InstCode": settings.PLANT_SHORT_NAME,
151
- "InternalClassification": settings.DATA_CLASSIFICATION,
152
- "IsoCountryCode": "NO",
153
- "Geodetic.CoordinateReferenceSystemCode": settings.COORDINATE_REFERENCE_SYSTEM, # noqa: E501
154
- "Geodetic.VerticalCoordinateReferenceSystemCode": settings.VERTICAL_REFERENCE_SYSTEM, # noqa: E501
155
- "Geodetic.OrientationReferenceSystem": settings.MEDIA_ORIENTATION_REFERENCE_SYSTEM, # noqa: E501
156
- "SensorCarrier.SensorCarrierId": settings.ISAR_ID,
157
- "SensorCarrier.ModelName": settings.ROBOT_TYPE,
158
- "Mission.MissionId": mission.id,
159
- "Mission.Client": "Equinor",
160
- "VideoMetadata.Timestamp": inspection.metadata.start_time.isoformat(), # noqa: E501
161
- # Converting to int because SLIMM expects an int, while we use floats in operations.
162
- "VideoMetadata.Duration": str(int(inspection.metadata.duration)), # type: ignore
163
- "VideoMetadata.X": str(inspection.metadata.pose.position.x),
164
- "VideoMetadata.Y": str(inspection.metadata.pose.position.y),
165
- "VideoMetadata.Z": str(inspection.metadata.pose.position.z),
166
- "VideoMetadata.CameraOrientation1": str(array_of_orientation[0]),
167
- "VideoMetadata.CameraOrientation2": str(array_of_orientation[1]),
168
- "VideoMetadata.CameraOrientation3": str(array_of_orientation[2]),
169
- "VideoMetadata.CameraOrientation4": str(array_of_orientation[3]),
170
- "VideoMetadata.AnalysisMethods": inspection.metadata.analysis
171
- if inspection.metadata.analysis
172
- else "N/A",
173
- "VideoMetadata.Description": str(inspection.metadata.additional),
174
- "VideoMetadata.FunctionalLocation": inspection.metadata.tag_id # noqa: E501
175
- if inspection.metadata.tag_id
176
- else "N/A",
177
- "Filename": filename,
178
- "AttachedFile": (filename, inspection.data),
179
- }
180
- )
181
- return multiform_body
@@ -1,417 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: isar
3
- Version: 1.15.0
4
- Summary: Integration and Supervisory control of Autonomous Robots
5
- Home-page: https://github.com/equinor/isar
6
- Author: Equinor ASA
7
- Author-email: fg_robots_dev@equinor.com
8
- Classifier: Environment :: Other Environment
9
- Classifier: Intended Audience :: Developers
10
- Classifier: Intended Audience :: Science/Research
11
- Classifier: Programming Language :: Python
12
- Classifier: Topic :: Scientific/Engineering
13
- Classifier: Topic :: Scientific/Engineering :: Physics
14
- Classifier: Topic :: Software Development :: Libraries
15
- Requires-Python: >=3.8
16
- Description-Content-Type: text/markdown
17
- License-File: LICENSE
18
- Requires-Dist: alitra (>=1.1.0)
19
- Requires-Dist: azure-identity
20
- Requires-Dist: azure-keyvault-secrets
21
- Requires-Dist: azure-storage-blob
22
- Requires-Dist: backoff
23
- Requires-Dist: click
24
- Requires-Dist: dacite
25
- Requires-Dist: fastapi-azure-auth
26
- Requires-Dist: fastapi
27
- Requires-Dist: injector
28
- Requires-Dist: opencensus-ext-logging
29
- Requires-Dist: opencensus-ext-requests
30
- Requires-Dist: opencensus-ext-azure
31
- Requires-Dist: numpy
32
- Requires-Dist: paho-mqtt
33
- Requires-Dist: pydantic
34
- Requires-Dist: PyJWT
35
- Requires-Dist: python-dotenv
36
- Requires-Dist: PyYAML
37
- Requires-Dist: requests-toolbelt
38
- Requires-Dist: requests
39
- Requires-Dist: transitions
40
- Requires-Dist: uvicorn
41
- Provides-Extra: dev
42
- Requires-Dist: black ; extra == 'dev'
43
- Requires-Dist: flake8 ; extra == 'dev'
44
- Requires-Dist: mypy ; extra == 'dev'
45
- Requires-Dist: myst-parser ; extra == 'dev'
46
- Requires-Dist: pre-commit ; extra == 'dev'
47
- Requires-Dist: pytest-dotenv ; extra == 'dev'
48
- Requires-Dist: pytest-mock ; extra == 'dev'
49
- Requires-Dist: pytest-xdist ; extra == 'dev'
50
- Requires-Dist: pytest ; extra == 'dev'
51
- Requires-Dist: requests-mock ; extra == 'dev'
52
- Requires-Dist: sphinx ; extra == 'dev'
53
-
54
- # ISAR
55
-
56
- [![Python package](https://github.com/equinor/isar/actions/workflows/pythonpackage.yml/badge.svg)](https://github.com/equinor/isar/actions/workflows/pythonpackage.yml)
57
- [![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
58
- [![License](https://img.shields.io/badge/License-EPL_2.0-blue.svg)](https://opensource.org/licenses/EPL-2.0)
59
-
60
- ISAR - Integration and Supervisory control of Autonomous Robots - is a tool for integrating robot applications into
61
- operator systems. Through the ISAR API you can send commands to a robot to do missions and collect results from the
62
- missions.
63
-
64
- ## Getting started
65
-
66
- Steps:
67
-
68
- - Install
69
- - Integrate a robot
70
- - Run the ISAR server
71
- - Run a robot mission
72
-
73
- ### Install
74
-
75
- For local development, please fork the repository. Then, clone and install in the repository root folder:
76
-
77
- ```
78
- git clone https://github.com/<path_to_parent>/isar
79
- cd isar
80
- pip install -e .[dev]
81
- ```
82
-
83
- For `zsh` you might have to type `".[dev]"`
84
-
85
- Verify that you can run the tests:
86
-
87
- ```bash
88
- pytest .
89
- ```
90
-
91
- The repository contains a configuration file for installing pre-commit hooks. Currently, [black](https://github.com/psf/black) and a mirror of [mypy](https://github.com/pre-commit/mirrors-mypy) are configured hooks. Install with:
92
-
93
- ```
94
- pre-commit install
95
- ```
96
-
97
- Verify that pre-commit runs:
98
-
99
- ```
100
- pre-commit
101
- ```
102
-
103
- pre-commit will now run the installed hooks before code is commited to git. To turn pre-commit off, run:
104
-
105
- ```
106
- pre-commit uninstall
107
- ```
108
-
109
- ### Robot integration
110
-
111
- To connect the state machine to a robot in a separate repository, it is required that the separate repository implements
112
- the [robot interface](https://github.com/equinor/isar/blob/main/src/robot_interface/robot_interface.py). A mocked robot
113
- can be found in [this repository](https://github.com/equinor/isar-robot). Install the repo, i.e:
114
-
115
- ```bash
116
- pip install isar-robot
117
- ```
118
-
119
- Then, ensure the `ISAR_ROBOT_PACKAGE` variable in [settings.env](./src/isar/config/settings.env)
120
- is set to the name of the package you installed. `isar_robot` is set by default. See the section
121
- for [configuration](#configuration) for overwriting configuration.
122
-
123
- If you have the robot repository locally, you can simply install through
124
-
125
- ```bash
126
- pip install -e /path/to/robot/repo/
127
- ```
128
-
129
- #### Running ISAR with a robot simulator
130
-
131
- A simulator based on the open source robot Turtlebot3 has been implemented for use with ISAR and may be
132
- found [here](https://github.com/equinor/isar-turtlebot). Follow the installation instructions for the simulator and
133
- install `isar-turtlebot` in the same manner as given in the [robot integration](#robot-integration) section. Overwrite
134
- the following configuration variables:
135
-
136
- ```bash
137
- ISAR_ROBOT_PACKAGE = isar_turtlebot
138
- ISAR_DEFAULT_MAP = turtleworld
139
- ```
140
-
141
- ### Run ISAR server
142
-
143
- To run ISAR:
144
-
145
- ```bash
146
- python main.py
147
- ```
148
-
149
- Note, running the full system requires that an implementation of a robot has been installed. See
150
- this [section](#robot-integration) for installing a mocked robot or a Turtlebot3 simulator.
151
-
152
- ### Running a robot mission
153
-
154
- Once the application has been started the swagger site may be accessed at
155
-
156
- ```
157
- http://localhost:3000/docs
158
- ```
159
-
160
- Execute the `/schedule/start-mission` endpoint with `mission_id=1` to run a mission.
161
-
162
- In [this](./src/isar/config/predefined_missions) folder there are predefined default missions, for example the mission
163
- corresponding to `mission_id=1`. A new mission may be added by adding a new json-file with a mission description. Note,
164
- the mission IDs must be unique.
165
-
166
- ### Running with docker-compose
167
-
168
- ISAR may be started with an instance of the [isar-robot](https://github.com/equinor/isar-robot) package by
169
-
170
- ```shell
171
- docker-compose up --build
172
- ```
173
-
174
- Provided that the simulator from [isar-turtlebot](https://github.com/equinor/isar-turtlebot) is running ISAR may be
175
- started with the turtlebot by
176
-
177
- ```shell
178
- docker-compose -f docker-compose-turtlebot.yml up --build
179
- ```
180
-
181
- ### Configuration
182
-
183
- The system consists of many configuration variables which may alter the functionality. As an example, it is possible to
184
- change mission planners or add multiple storage handlers as described in the [mission planner](#mission-planner)
185
- and [storage](#storage) sections.
186
-
187
- There are two methods of specifying configuration.
188
-
189
- 1. Override the default value by setting an environment variable.
190
-
191
- Every configuration variable is defined in [settings.py](./src/isar/config/settings.py), and they may all be
192
- overwritten by specifying the variables in the environment instead. Note that the configuration variable must be
193
- prefixed with `ISAR_` when specified in the environment. So for the `ROBOT_PACKAGE`configuration variable:
194
-
195
- ```shell
196
- export ISAR_ROBOT_PACKAGE=isar_turtlebot
197
- ```
198
-
199
- This means ISAR will connect to `isar_turtlebot` robot package.
200
-
201
- 2. Adding environment variables through [settings.env](./src/isar/config/settings.env).
202
-
203
- By adding environment variables with the prefix `ISAR_` to the [settings.env](./src/isar/config/settings.env) file
204
- the configuration variables will be overwritten by the values in this file.
205
-
206
- ### Running tests
207
-
208
- After following the steps in [Development](#install), you can run the tests:
209
-
210
- ```bash
211
- pytest .
212
- ```
213
-
214
- To create an interface test in your robot repository, use the function `interface_test` from `robot_interface`. The
215
- argument should be an interface object from your robot specific implementation.
216
- See [isar-robot](https://github.com/equinor/isar-robot/blob/main/tests/interfaces/test_robotinterface.py) for example.
217
-
218
- #### Integration tests
219
-
220
- Integration tests can be found [here](https://github.com/equinor/isar/tree/main/tests/integration) and have been created
221
- with a simulator in mind. The integration tests will not run as part of `pytest .` or as part of the CI/CD pipeline. To
222
- run the integration tests please follow the instructions in [this section](#running-isar-with-a-robot-simulator) for
223
- setting up the `isar-turtlebot` implementation with simulator and run the following command once the simulation has been
224
- launched.
225
-
226
- ```bash
227
- pytest tests/integration
228
- ```
229
-
230
- Note that these tests will run towards the actual simulation (you may monitor it through Gazebo and RVIZ) and it will
231
- take a long time.
232
-
233
- ### Documentation
234
-
235
- To build the project documentation, run the following commands:
236
-
237
- ```bash
238
- cd docs
239
- make docs
240
- ```
241
-
242
- The documentation can now be viewed at `docs/build/html/index.html`.
243
-
244
- ### Contributing
245
-
246
- We welcome all kinds of contributions, including code, bug reports, issues, feature requests, and documentation. The
247
- preferred way of submitting a contribution is to either make an [issue](https://github.com/equinor/isar/issues) on
248
- GitHub or by forking the project on GitHub and making a pull requests.
249
-
250
- ## Components
251
-
252
- The system consists of two main components.
253
-
254
- 1. State machine
255
- 1. FastAPI
256
-
257
- ### State machine
258
-
259
- The state machine handles interaction with the robots API and monitors the execution of missions. It also enables
260
- interacting with the robot before, during and after missions.
261
-
262
- The state machine is based on the [transitions](https://github.com/pytransitions/transitions) package for Python. An visualization of the state machine can be seen below:
263
- ![State Machine](./docs/state_machine_diagram.png)
264
-
265
- In general the states
266
-
267
- ```
268
- States.Off,
269
- States.Initialize,
270
- States.Initiate,
271
- States.Stop,
272
- States.Monitor,
273
- States.Paused,
274
- ```
275
-
276
- indicates that the state machine is already running. For running a mission the state machine need to be in the state
277
-
278
- ```
279
- States.Idle
280
- ```
281
-
282
- ### FastAPI
283
-
284
- The FastAPI establishes an interface to the state machine for the user. As the API and state machine are separate
285
- threads, they communicate through python queues. FastAPI runs on an ASGI-server, specifically uvicorn. The
286
- FastAPI-framework is split into routers where the endpoint operations are defined.
287
-
288
- ## Mission planner
289
-
290
- The mission planner that is currently in use is a local mission planner, where missions are specified in a json file. You can create your own mission planner by implementing
291
- the [mission planner interface](./src/isar/mission_planner/mission_planner_interface.py) and adding your planner to the
292
- selection [here](./src/isar/modules.py). Note that you must add your module as an option in the dictionary.
293
-
294
- ## Storage
295
-
296
- The storage modules that are used is defined by the `ISAR_STORAGE` configuration variable. This can be changed by
297
- overriding the configuration through an environment variable. It accepts a json encoded list and will use each element
298
- in the list to retrieve the corresponding handler. The current options are
299
-
300
- ```
301
- ISAR_STORAGE = '["local", "blob", "slimm"]'
302
- ```
303
-
304
- Note that the `blob` and `slimm` options require special configuration to authenticate to these endpoints.
305
-
306
- ### Implement your own storage module
307
-
308
- You can create your own storage module by implementing the [storage interface](./src/isar/storage/storage_interface.py)
309
- and adding your storage module to the selection [here](./src/isar/modules.py). Note that you must add your module as an
310
- option in the dictionary.
311
-
312
- ## Task selection
313
-
314
- The tasks of a mission are selected based on a task selector module, defined by the `TASK_SELECTOR` configuration variable. The default task selector is `sequential`. When using the default module, tasks are executed in sequential order defined by the current input mission.
315
-
316
- ### Implement you own task selector module
317
-
318
- Custom task selector modules may be added by implementing additional versions of the [task selector interface](./src/isar/mission_planner/task_selector_interface.py).
319
-
320
- For every custom module, the interface function `next_task()` must be implemented. All interface implementations by default have access to the list of tasks in the current mission through the member `self.tasks`, however additional variables may be supplied by adding arguments to `next_task()`. To comply with the interface definition, the function should return the next task upon every call, and raise the `TaskSelectorStop` exception when all tasks in the current mission have been completed:
321
-
322
- ```python
323
- class CustomTaskSelector(TaskSelectorInterface):
324
- ...
325
- def next_task(...) -> Task:
326
-
327
- # Add code here
328
- ...
329
-
330
- # Raise `TaskSelectorStop` when all tasks have been completed
331
- ...
332
- ```
333
-
334
- Optionally, the `initialize()` function may be extended by supplementing the parameter list or function body:
335
-
336
- ```python
337
- class CustomTaskSelector(TaskSelectorInterface):
338
- ...
339
- def initialize(self, tasks: List[Task], ...) -> None:
340
- super.initialize(tasks=tasks)
341
-
342
- # Add supplementary code here
343
- ...
344
- ```
345
-
346
- A custom task selector may be made available during [module selection](./src/isar/modules.py) by adding it to the series of options in the dictionary of injector modules. It can then be activated by overriding the task selector configuration variable:
347
-
348
- ```python
349
- # Add custom task selector module to `modules.py`
350
-
351
- class CustomTaskSelectorModule(Module):
352
- @provider
353
- @singleton
354
- def provide_task_selector(self) -> TaskSelectorInterface:
355
- return CustomTaskSelector()
356
-
357
- ...
358
-
359
- # Make it available to select during injector instantiation
360
-
361
- modules: dict[str, tuple[Module, Union[str, bool]]] = {
362
- ...
363
- "task_selector": (
364
- {
365
- "sequential": SequentialTaskSelectorModule,
366
- "custom": CustomTaskSelectorModule
367
- }
368
- ...
369
- )
370
- ...
371
- }
372
- ```
373
-
374
- ## API authentication
375
-
376
- The API has an option to include user authentication. This can be enabled by setting the environment variable
377
-
378
- ```
379
- ISAR_AUTHENTICATION_ENABLED = true
380
- ```
381
-
382
- By default, the `local` storage module is used and API authentication is disabled. If using Azure Blob Storage a set of
383
- environment variables must be available which gives access to an app registration that may use the storage account.
384
- Enabling API authentication also requires the same environment variables. The required variables are
385
-
386
- ```
387
- AZURE_CLIENT_ID
388
- AZURE_TENANT_ID
389
- AZURE_CLIENT_SECRET
390
- ```
391
-
392
- ## MQTT communication
393
-
394
- ISAR is able to publish parts of its internal state to topics on an MQTT broker whenever they change. This is by default
395
- turned off but may be activated by setting the environment variable
396
-
397
- ```
398
- ISAR_MQTT_ENABLED = true
399
- ```
400
-
401
- The connection to the broker will be determined by the following configuration values in `settings.py`
402
-
403
- ```
404
- ISAR_MQTT_USERNAME
405
- ISAR_MQTT_HOST
406
- ISAR_MQTT_PORT
407
- ```
408
-
409
- The default values of these are overwritten by the environment in `settings.env`.
410
-
411
- To specify broker password, add the following environment variable to a .env file in the root of the repository:
412
-
413
- ```
414
- ISAR_MQTT_PASSWORD
415
- ```
416
-
417
- If not specified the password will default to an empty string.