wandelbots-api-client 26.1.0.dev42__py3-none-any.whl → 26.1.0.dev63__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 (32) hide show
  1. wandelbots_api_client/__init__.py +1 -1
  2. wandelbots_api_client/api_client.py +1 -1
  3. wandelbots_api_client/configuration.py +1 -1
  4. wandelbots_api_client/models/__init__.py +3 -1
  5. wandelbots_api_client/models/virtual_controller_types.py +4 -4
  6. wandelbots_api_client/v2/__init__.py +1 -1
  7. wandelbots_api_client/v2/api/__init__.py +2 -0
  8. wandelbots_api_client/v2/api/jogging_api.py +1 -1
  9. wandelbots_api_client/v2/api/motion_group_models_api.py +265 -0
  10. wandelbots_api_client/v2/api/robot_configurations_api.py +291 -0
  11. wandelbots_api_client/v2/api_client.py +1 -1
  12. wandelbots_api_client/v2/configuration.py +1 -1
  13. wandelbots_api_client/v2/models/__init__.py +5 -3
  14. wandelbots_api_client/v2/models/initialize_jogging_request.py +2 -4
  15. wandelbots_api_client/v2/models/inverse_kinematics_request.py +6 -4
  16. wandelbots_api_client/v2/models/kinematic_model.py +110 -0
  17. wandelbots_api_client/v2/models/virtual_controller.py +1 -2
  18. wandelbots_api_client/v2/models/virtual_controller_types.py +1 -1
  19. wandelbots_api_client/v2_pydantic/__init__.py +1 -1
  20. wandelbots_api_client/v2_pydantic/api/__init__.py +2 -0
  21. wandelbots_api_client/v2_pydantic/api/jogging_api.py +1 -1
  22. wandelbots_api_client/v2_pydantic/api/motion_group_models_api.py +268 -0
  23. wandelbots_api_client/v2_pydantic/api/robot_configurations_api.py +294 -0
  24. wandelbots_api_client/v2_pydantic/api_client.py +1 -1
  25. wandelbots_api_client/v2_pydantic/configuration.py +1 -1
  26. wandelbots_api_client/v2_pydantic/models.py +78 -161
  27. wandelbots_api_client/v2_pydantic/virtual_controller_types.py +126 -0
  28. {wandelbots_api_client-26.1.0.dev42.dist-info → wandelbots_api_client-26.1.0.dev63.dist-info}/METADATA +2 -2
  29. {wandelbots_api_client-26.1.0.dev42.dist-info → wandelbots_api_client-26.1.0.dev63.dist-info}/RECORD +32 -28
  30. {wandelbots_api_client-26.1.0.dev42.dist-info → wandelbots_api_client-26.1.0.dev63.dist-info}/WHEEL +0 -0
  31. {wandelbots_api_client-26.1.0.dev42.dist-info → wandelbots_api_client-26.1.0.dev63.dist-info}/licenses/LICENSE +0 -0
  32. {wandelbots_api_client-26.1.0.dev42.dist-info → wandelbots_api_client-26.1.0.dev63.dist-info}/top_level.txt +0 -0
@@ -13,7 +13,7 @@
13
13
  Do not edit the class manually.
14
14
  """ # noqa: E501
15
15
 
16
- __version__ = "26.1.0.dev42"
16
+ __version__ = "26.1.0.dev63"
17
17
 
18
18
  from . import models
19
19
  from . import api
@@ -91,7 +91,7 @@ class ApiClient:
91
91
  self.default_headers[header_name] = header_value
92
92
  self.cookie = cookie
93
93
  # Set default User-Agent.
94
- self.user_agent = 'Wandelbots-Nova-API-Python-Client/26.1.0.dev42'
94
+ self.user_agent = 'Wandelbots-Nova-API-Python-Client/26.1.0.dev63'
95
95
  self.client_side_validation = configuration.client_side_validation
96
96
 
97
97
  async def __aenter__(self):
@@ -535,7 +535,7 @@ conf = wandelbots_api_client.Configuration(
535
535
  "OS: {env}\n"\
536
536
  "Python Version: {pyversion}\n"\
537
537
  "Version of the API: 1.2.0 dev\n"\
538
- "SDK Package Version: 26.1.0.dev42".\
538
+ "SDK Package Version: 26.1.0.dev63".\
539
539
  format(env=sys.platform, pyversion=sys.version)
540
540
 
541
541
  def get_host_settings(self) -> List[HostSetting]:
@@ -304,6 +304,7 @@ from .virtual_controller import VirtualController
304
304
  from .virtual_controller_types import VirtualControllerTypes
305
305
  from .virtual_robot_configuration import VirtualRobotConfiguration
306
306
  from .yaskawa_controller import YaskawaController
307
+ from .virtual_controller_types import VirtualControllerTypes
307
308
 
308
309
  __all__ = [
309
310
  "AbbController",
@@ -596,5 +597,6 @@ __all__ = [
596
597
  "VirtualController",
597
598
  "VirtualControllerTypes",
598
599
  "VirtualRobotConfiguration",
600
+ "VirtualControllerTypes",
599
601
  "YaskawaController"
600
- ]
602
+ ]
@@ -3,9 +3,9 @@
3
3
  """
4
4
  Wandelbots NOVA API
5
5
 
6
- Interact with robots in an easy and intuitive way.
6
+ Interact with robots in an easy and intuitive way.
7
7
 
8
- The version of the OpenAPI document: 1.2.0 dev
8
+ The version of the OpenAPI document: 2.1.0 dev
9
9
  Generated by OpenAPI Generator (https://openapi-generator.tech)
10
10
 
11
11
  Do not edit the class manually.
@@ -80,14 +80,14 @@ class VirtualControllerTypes(str, Enum):
80
80
  KUKA_MINUS_KR10_R900 = 'kuka-kr10_r900'
81
81
  KUKA_MINUS_KR10_R900_2 = 'kuka-kr10_r900_2'
82
82
  KUKA_MINUS_KR120_R2700_2 = 'kuka-kr120_r2700_2'
83
- KUKA_MINUS_KR120_R3100_2 = 'kuka-kr120_r3100_2'
84
- KUKA_MINUS_KR120_R3900_2_K = 'kuka-kr120_r3900_2_k'
85
83
  KUKA_MINUS_KR12_R1810_2 = 'kuka-kr12_r1810_2'
86
84
  KUKA_MINUS_KR150_R2 = 'kuka-kr150_r2'
87
85
  KUKA_MINUS_KR16_R1610_2 = 'kuka-kr16_r1610_2'
88
86
  KUKA_MINUS_KR16_R2010_2 = 'kuka-kr16_r2010_2'
89
87
  KUKA_MINUS_KR20_R1810 = 'kuka-kr20_r1810'
90
88
  KUKA_MINUS_KR20_R1810_2 = 'kuka-kr20_r1810_2'
89
+ KUKA_MINUS_KR120_R3100_2 = 'kuka-kr120_r3100_2'
90
+ KUKA_MINUS_KR120_R3900_2_K = 'kuka-kr120_r3900_2_k'
91
91
  KUKA_MINUS_KR210_R2700_EXTRA = 'kuka-kr210_r2700_extra'
92
92
  KUKA_MINUS_KR210_R2700_2 = 'kuka-kr210_r2700_2'
93
93
  KUKA_MINUS_KR210_R3100_2 = 'kuka-kr210_r3100_2'
@@ -13,7 +13,7 @@
13
13
  Do not edit the class manually.
14
14
  """ # noqa: E501
15
15
 
16
- __version__ = "26.1.0.dev42"
16
+ __version__ = "26.1.0.dev63"
17
17
 
18
18
  from . import models
19
19
  from . import api
@@ -12,6 +12,7 @@ from .license_api import LicenseApi
12
12
  from .motion_group_api import MotionGroupApi
13
13
  from .motion_group_models_api import MotionGroupModelsApi
14
14
  from .program_api import ProgramApi
15
+ from .robot_configurations_api import RobotConfigurationsApi
15
16
  from .store_collision_components_api import StoreCollisionComponentsApi
16
17
  from .store_collision_setups_api import StoreCollisionSetupsApi
17
18
  from .store_object_api import StoreObjectApi
@@ -37,6 +38,7 @@ __all__ = [
37
38
  "MotionGroupApi",
38
39
  "MotionGroupModelsApi",
39
40
  "ProgramApi",
41
+ "RobotConfigurationsApi",
40
42
  "StoreCollisionComponentsApi",
41
43
  "StoreCollisionSetupsApi",
42
44
  "StoreObjectApi",
@@ -47,7 +47,7 @@ class JoggingApi:
47
47
  async def execute_jogging(self, cell: Annotated[StrictStr, Field(description="Unique identifier addressing a cell in all API calls. ")], controller: Annotated[StrictStr, Field(description="Unique identifier to address a controller in the cell.")], client_request_generator: Callable[[AsyncGenerator[ExecuteJoggingResponse, None]], AsyncGenerator[ExecuteJoggingRequest, None]]) -> None: # noqa: E501
48
48
  """Execute Jogging # noqa: E501
49
49
 
50
- <!-- theme: success --> > Websocket endpoint Provides execution control over a dynamically adaptable jogging motion for a motion group. Jogging describes controlling a motion group by sending real-time commands to move either its joints or the TCP. The commands contain target velocities that may change at any time during execution, so the resulting motion cannot be computed upfront. ### Preconditions The motion group is not moved by any other endpoint. ### Requests #### 1. Send `InitializeJoggingRequest` to configure the jogging. - Sets the robot controller mode to control mode. - Sets rate and coordinate system for the jogging response. #### 2. Send `JointVelocityRequest` or `TcpVelocityRequest` to start the jogging motion. - Commands can only be processed in the cycle rate of the controller - Sending commands faster will not increase the responsiveness of the jogging motion, it will lead to dropped commands - It is recommended to couple sending commands with the (state stream)[streamMotionGroupState], which can be subscribed to via nats as well. #### 3. Change or stop the jogging motion - Change the jogging direction and/or velocity during the jogging motion with `JointVelocityRequest` or `TcpVelocityRequest`. - To stop the jogging motion, send zero velocities via either request. ### Responses - `InitializeJoggingResponse` is sent to signal the success or failure of the `InitializeJoggingRequest`. - Jogging responses are streamed continuously after an `InitializeJoggingRequest` is processed. Jogging responses contain the robot controller state and the state of the jogging control. - `JoggingErrorResponse` with error details is sent in case of an unexpected error, e.g., controller disconnects during jogging. ### Tips and Tricks - In the `JoggingResponse`, verify that the robot control is in the desired state, e.g., standstill, with JoggingState. - Ensure that the websocket connection remains open until the jogging motion is stopped to avoid unexpected stops. # noqa: E501
50
+ <!-- theme: success --> > Websocket endpoint Provides execution control over a dynamically adaptable jogging motion for a motion group. Jogging describes controlling a motion group by sending real-time commands to move either its joints or the TCP. The commands contain target velocities that may change at any time during execution, so the resulting motion cannot be computed upfront. ### Preconditions The motion group is not moved by any other endpoint. ### Requests #### 1. Send `InitializeJoggingRequest` to configure the jogging. - Sets the robot controller mode to control mode. - Claims the motion group for jogging. For robotic arms, TCP is required to ensure that limits, including TCP limits, are respected. #### 2. Send `JointVelocityRequest` or `TcpVelocityRequest` to start the jogging motion. - Commands can only be processed in the cycle rate of the controller - Sending commands faster will not increase the responsiveness of the jogging motion, it will lead to dropped commands - It is recommended to couple sending commands with the [state stream](streamMotionGroupState), which can be subscribed to via nats as well. #### 3. Change or stop the jogging motion - Change the jogging direction and/or velocity during the jogging motion with `JointVelocityRequest` or `TcpVelocityRequest`. - To stop the jogging motion, send zero velocities via either request or `PauseJoggingRequest`. ### Responses - Each request is acknowledged with a corresponding response: - `InitializeJoggingResponse` after `InitializeJoggingRequest` - `JointVelocityResponse` after `JointVelocityRequest` - `TcpVelocityResponse` after `TcpVelocityRequest` - `PauseJoggingResponse` after `PauseJoggingRequest` The responses confirm that the requests were received. They do not signal that the operation was successful; check the [motion group state](streamMotionGroupState) for that. - `MovementErrorResponse` with error details is sent in case of an unexpected error, e.g., controller disconnects during jogging. ### Tips and Tricks - Ensure that the websocket connection remains open until the jogging motion is stopped to avoid unexpected stops. # noqa: E501
51
51
  :param client_request_generator: An AsyncGenerator that yields request of type ExecuteJoggingRequest and takes an AsyncGenerator of ExecuteJoggingResponse as an input argument (required)
52
52
  :info All responses from the server will be yielded to client_request_generator through the (AsyncGenerator[ExecuteJoggingResponse, None])
53
53
  :type AsyncGenerator[ExecuteJoggingRequest, None]
@@ -26,6 +26,7 @@ from pydantic import Field, StrictBytes, StrictStr
26
26
  from typing import Dict, List, Tuple, Union
27
27
  from typing_extensions import Annotated
28
28
  from wandelbots_api_client.v2.models.collider import Collider
29
+ from wandelbots_api_client.v2.models.kinematic_model import KinematicModel
29
30
 
30
31
  from wandelbots_api_client.v2.api_client import ApiClient, RequestSerialized
31
32
  from wandelbots_api_client.v2.api_response import ApiResponse
@@ -571,6 +572,270 @@ class MotionGroupModelsApi:
571
572
 
572
573
 
573
574
 
575
+ @validate_call
576
+ async def get_motion_group_kinematic_model(
577
+ self,
578
+ motion_group_model: Annotated[StrictStr, Field(description="Unique identifier for the model of a motion group, e.g., `UniversalRobots_UR10e`. Get the `model` of a configured motion group with [getOptimizerConfiguration](getOptimizerConfiguration). ")],
579
+ _request_timeout: Union[
580
+ None,
581
+ Annotated[StrictFloat, Field(gt=0)],
582
+ Tuple[
583
+ Annotated[StrictFloat, Field(gt=0)],
584
+ Annotated[StrictFloat, Field(gt=0)]
585
+ ]
586
+ ] = None,
587
+ _request_auth: Optional[Dict[StrictStr, Any]] = None,
588
+ _content_type: Optional[StrictStr] = None,
589
+ _headers: Optional[Dict[StrictStr, Any]] = None,
590
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
591
+ ) -> KinematicModel:
592
+ """Get Kinematics
593
+
594
+ Returns the kinematics model (DH parameters) for the given motion group model. See [getMotionGroupModels](getMotionGroupModels) for supported motion group models.
595
+
596
+ :param motion_group_model: Unique identifier for the model of a motion group, e.g., `UniversalRobots_UR10e`. Get the `model` of a configured motion group with [getOptimizerConfiguration](getOptimizerConfiguration). (required)
597
+ :type motion_group_model: str
598
+ :param _request_timeout: timeout setting for this request. If one
599
+ number provided, it will be total request
600
+ timeout. It can also be a pair (tuple) of
601
+ (connection, read) timeouts.
602
+ :type _request_timeout: int, tuple(int, int), optional
603
+ :param _request_auth: set to override the auth_settings for an a single
604
+ request; this effectively ignores the
605
+ authentication in the spec for a single request.
606
+ :type _request_auth: dict, optional
607
+ :param _content_type: force content-type for the request.
608
+ :type _content_type: str, Optional
609
+ :param _headers: set to override the headers for a single
610
+ request; this effectively ignores the headers
611
+ in the spec for a single request.
612
+ :type _headers: dict, optional
613
+ :param _host_index: set to override the host_index for a single
614
+ request; this effectively ignores the host_index
615
+ in the spec for a single request.
616
+ :type _host_index: int, optional
617
+ :return: Returns the result object.
618
+ """ # noqa: E501
619
+
620
+ _param = self._get_motion_group_kinematic_model_serialize(
621
+ motion_group_model=motion_group_model,
622
+ _request_auth=_request_auth,
623
+ _content_type=_content_type,
624
+ _headers=_headers,
625
+ _host_index=_host_index
626
+ )
627
+
628
+ _response_types_map: Dict[str, Optional[str]] = {
629
+ '200': "KinematicModel",
630
+ '404': None,
631
+ '500': None,
632
+ }
633
+ response_data = await self.api_client.call_api(
634
+ *_param,
635
+ _request_timeout=_request_timeout
636
+ )
637
+ await response_data.read()
638
+ return self.api_client.response_deserialize(
639
+ response_data=response_data,
640
+ response_types_map=_response_types_map,
641
+ ).data
642
+
643
+
644
+ @validate_call
645
+ async def get_motion_group_kinematic_model_with_http_info(
646
+ self,
647
+ motion_group_model: Annotated[StrictStr, Field(description="Unique identifier for the model of a motion group, e.g., `UniversalRobots_UR10e`. Get the `model` of a configured motion group with [getOptimizerConfiguration](getOptimizerConfiguration). ")],
648
+ _request_timeout: Union[
649
+ None,
650
+ Annotated[StrictFloat, Field(gt=0)],
651
+ Tuple[
652
+ Annotated[StrictFloat, Field(gt=0)],
653
+ Annotated[StrictFloat, Field(gt=0)]
654
+ ]
655
+ ] = None,
656
+ _request_auth: Optional[Dict[StrictStr, Any]] = None,
657
+ _content_type: Optional[StrictStr] = None,
658
+ _headers: Optional[Dict[StrictStr, Any]] = None,
659
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
660
+ ) -> ApiResponse[KinematicModel]:
661
+ """Get Kinematics
662
+
663
+ Returns the kinematics model (DH parameters) for the given motion group model. See [getMotionGroupModels](getMotionGroupModels) for supported motion group models.
664
+
665
+ :param motion_group_model: Unique identifier for the model of a motion group, e.g., `UniversalRobots_UR10e`. Get the `model` of a configured motion group with [getOptimizerConfiguration](getOptimizerConfiguration). (required)
666
+ :type motion_group_model: str
667
+ :param _request_timeout: timeout setting for this request. If one
668
+ number provided, it will be total request
669
+ timeout. It can also be a pair (tuple) of
670
+ (connection, read) timeouts.
671
+ :type _request_timeout: int, tuple(int, int), optional
672
+ :param _request_auth: set to override the auth_settings for an a single
673
+ request; this effectively ignores the
674
+ authentication in the spec for a single request.
675
+ :type _request_auth: dict, optional
676
+ :param _content_type: force content-type for the request.
677
+ :type _content_type: str, Optional
678
+ :param _headers: set to override the headers for a single
679
+ request; this effectively ignores the headers
680
+ in the spec for a single request.
681
+ :type _headers: dict, optional
682
+ :param _host_index: set to override the host_index for a single
683
+ request; this effectively ignores the host_index
684
+ in the spec for a single request.
685
+ :type _host_index: int, optional
686
+ :return: Returns the result object.
687
+ """ # noqa: E501
688
+
689
+ _param = self._get_motion_group_kinematic_model_serialize(
690
+ motion_group_model=motion_group_model,
691
+ _request_auth=_request_auth,
692
+ _content_type=_content_type,
693
+ _headers=_headers,
694
+ _host_index=_host_index
695
+ )
696
+
697
+ _response_types_map: Dict[str, Optional[str]] = {
698
+ '200': "KinematicModel",
699
+ '404': None,
700
+ '500': None,
701
+ }
702
+ response_data = await self.api_client.call_api(
703
+ *_param,
704
+ _request_timeout=_request_timeout
705
+ )
706
+ await response_data.read()
707
+ return self.api_client.response_deserialize(
708
+ response_data=response_data,
709
+ response_types_map=_response_types_map,
710
+ )
711
+
712
+
713
+ @validate_call
714
+ async def get_motion_group_kinematic_model_without_preload_content(
715
+ self,
716
+ motion_group_model: Annotated[StrictStr, Field(description="Unique identifier for the model of a motion group, e.g., `UniversalRobots_UR10e`. Get the `model` of a configured motion group with [getOptimizerConfiguration](getOptimizerConfiguration). ")],
717
+ _request_timeout: Union[
718
+ None,
719
+ Annotated[StrictFloat, Field(gt=0)],
720
+ Tuple[
721
+ Annotated[StrictFloat, Field(gt=0)],
722
+ Annotated[StrictFloat, Field(gt=0)]
723
+ ]
724
+ ] = None,
725
+ _request_auth: Optional[Dict[StrictStr, Any]] = None,
726
+ _content_type: Optional[StrictStr] = None,
727
+ _headers: Optional[Dict[StrictStr, Any]] = None,
728
+ _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0,
729
+ ) -> RESTResponseType:
730
+ """Get Kinematics
731
+
732
+ Returns the kinematics model (DH parameters) for the given motion group model. See [getMotionGroupModels](getMotionGroupModels) for supported motion group models.
733
+
734
+ :param motion_group_model: Unique identifier for the model of a motion group, e.g., `UniversalRobots_UR10e`. Get the `model` of a configured motion group with [getOptimizerConfiguration](getOptimizerConfiguration). (required)
735
+ :type motion_group_model: str
736
+ :param _request_timeout: timeout setting for this request. If one
737
+ number provided, it will be total request
738
+ timeout. It can also be a pair (tuple) of
739
+ (connection, read) timeouts.
740
+ :type _request_timeout: int, tuple(int, int), optional
741
+ :param _request_auth: set to override the auth_settings for an a single
742
+ request; this effectively ignores the
743
+ authentication in the spec for a single request.
744
+ :type _request_auth: dict, optional
745
+ :param _content_type: force content-type for the request.
746
+ :type _content_type: str, Optional
747
+ :param _headers: set to override the headers for a single
748
+ request; this effectively ignores the headers
749
+ in the spec for a single request.
750
+ :type _headers: dict, optional
751
+ :param _host_index: set to override the host_index for a single
752
+ request; this effectively ignores the host_index
753
+ in the spec for a single request.
754
+ :type _host_index: int, optional
755
+ :return: Returns the result object.
756
+ """ # noqa: E501
757
+
758
+ _param = self._get_motion_group_kinematic_model_serialize(
759
+ motion_group_model=motion_group_model,
760
+ _request_auth=_request_auth,
761
+ _content_type=_content_type,
762
+ _headers=_headers,
763
+ _host_index=_host_index
764
+ )
765
+
766
+ _response_types_map: Dict[str, Optional[str]] = {
767
+ '200': "KinematicModel",
768
+ '404': None,
769
+ '500': None,
770
+ }
771
+ response_data = await self.api_client.call_api(
772
+ *_param,
773
+ _request_timeout=_request_timeout
774
+ )
775
+ return response_data.response
776
+
777
+
778
+ def _get_motion_group_kinematic_model_serialize(
779
+ self,
780
+ motion_group_model,
781
+ _request_auth,
782
+ _content_type,
783
+ _headers,
784
+ _host_index,
785
+ ) -> RequestSerialized:
786
+
787
+ _host = None
788
+
789
+ _collection_formats: Dict[str, str] = {
790
+ }
791
+
792
+ _path_params: Dict[str, str] = {}
793
+ _query_params: List[Tuple[str, str]] = []
794
+ _header_params: Dict[str, Optional[str]] = _headers or {}
795
+ _form_params: List[Tuple[str, str]] = []
796
+ _files: Dict[str, Union[str, bytes]] = {}
797
+ _body_params: Optional[bytes] = None
798
+
799
+ # process the path parameters
800
+ if motion_group_model is not None:
801
+ _path_params['motion-group-model'] = motion_group_model
802
+ # process the query parameters
803
+ # process the header parameters
804
+ # process the form parameters
805
+ # process the body parameter
806
+
807
+
808
+ # set the HTTP header `Accept`
809
+ _header_params['Accept'] = self.api_client.select_header_accept(
810
+ [
811
+ 'application/json'
812
+ ]
813
+ )
814
+
815
+
816
+ # authentication setting
817
+ _auth_settings: List[str] = [
818
+ 'BasicAuth',
819
+ 'BearerAuth'
820
+ ]
821
+
822
+ return self.api_client.param_serialize(
823
+ method='GET',
824
+ resource_path='/motion-group-models/{motion-group-model}/kinematic',
825
+ path_params=_path_params,
826
+ query_params=_query_params,
827
+ header_params=_header_params,
828
+ body=_body_params,
829
+ post_params=_form_params,
830
+ files=_files,
831
+ auth_settings=_auth_settings,
832
+ collection_formats=_collection_formats,
833
+ _host=_host,
834
+ _request_auth=_request_auth
835
+ )
836
+
837
+
838
+
574
839
  @validate_call
575
840
  async def get_motion_group_models(
576
841
  self,