@@ -42,6 +42,172 @@ def __str__(self):
42
42
return self .name
43
43
44
44
45
+ class ControlMode (Enum ):
46
+ """
47
+ Control mode
48
+
49
+ Values
50
+ ------
51
+ NONE
52
+ Indicates that the component does not have control over the gimbal
53
+
54
+ PRIMARY
55
+ To take primary control over the gimbal
56
+
57
+ SECONDARY
58
+ To take secondary control over the gimbal
59
+
60
+ """
61
+
62
+
63
+ NONE = 0
64
+ PRIMARY = 1
65
+ SECONDARY = 2
66
+
67
+ def translate_to_rpc (self ):
68
+ if self == ControlMode .NONE :
69
+ return gimbal_pb2 .CONTROL_MODE_NONE
70
+ if self == ControlMode .PRIMARY :
71
+ return gimbal_pb2 .CONTROL_MODE_PRIMARY
72
+ if self == ControlMode .SECONDARY :
73
+ return gimbal_pb2 .CONTROL_MODE_SECONDARY
74
+
75
+ @staticmethod
76
+ def translate_from_rpc (rpc_enum_value ):
77
+ """ Parses a gRPC response """
78
+ if rpc_enum_value == gimbal_pb2 .CONTROL_MODE_NONE :
79
+ return ControlMode .NONE
80
+ if rpc_enum_value == gimbal_pb2 .CONTROL_MODE_PRIMARY :
81
+ return ControlMode .PRIMARY
82
+ if rpc_enum_value == gimbal_pb2 .CONTROL_MODE_SECONDARY :
83
+ return ControlMode .SECONDARY
84
+
85
+ def __str__ (self ):
86
+ return self .name
87
+
88
+
89
+ class ControlStatus :
90
+ """
91
+ Control status
92
+
93
+ Parameters
94
+ ----------
95
+ control_mode : ControlMode
96
+ Control mode (none, primary or secondary)
97
+
98
+ sysid_primary_control : int32_t
99
+ Sysid of the component that has primary control over the gimbal (0 if no one is in control)
100
+
101
+ compid_primary_control : int32_t
102
+ Compid of the component that has primary control over the gimbal (0 if no one is in control)
103
+
104
+ sysid_secondary_control : int32_t
105
+ Sysid of the component that has secondary control over the gimbal (0 if no one is in control)
106
+
107
+ compid_secondary_control : int32_t
108
+ Compid of the component that has secondary control over the gimbal (0 if no one is in control)
109
+
110
+ """
111
+
112
+
113
+
114
+ def __init__ (
115
+ self ,
116
+ control_mode ,
117
+ sysid_primary_control ,
118
+ compid_primary_control ,
119
+ sysid_secondary_control ,
120
+ compid_secondary_control ):
121
+ """ Initializes the ControlStatus object """
122
+ self .control_mode = control_mode
123
+ self .sysid_primary_control = sysid_primary_control
124
+ self .compid_primary_control = compid_primary_control
125
+ self .sysid_secondary_control = sysid_secondary_control
126
+ self .compid_secondary_control = compid_secondary_control
127
+
128
+ def __equals__ (self , to_compare ):
129
+ """ Checks if two ControlStatus are the same """
130
+ try :
131
+ # Try to compare - this likely fails when it is compared to a non
132
+ # ControlStatus object
133
+ return \
134
+ (self .control_mode == to_compare .control_mode ) and \
135
+ (self .sysid_primary_control == to_compare .sysid_primary_control ) and \
136
+ (self .compid_primary_control == to_compare .compid_primary_control ) and \
137
+ (self .sysid_secondary_control == to_compare .sysid_secondary_control ) and \
138
+ (self .compid_secondary_control == to_compare .compid_secondary_control )
139
+
140
+ except AttributeError :
141
+ return False
142
+
143
+ def __str__ (self ):
144
+ """ ControlStatus in string representation """
145
+ struct_repr = ", " .join ([
146
+ "control_mode: " + str (self .control_mode ),
147
+ "sysid_primary_control: " + str (self .sysid_primary_control ),
148
+ "compid_primary_control: " + str (self .compid_primary_control ),
149
+ "sysid_secondary_control: " + str (self .sysid_secondary_control ),
150
+ "compid_secondary_control: " + str (self .compid_secondary_control )
151
+ ])
152
+
153
+ return f"ControlStatus: [{ struct_repr } ]"
154
+
155
+ @staticmethod
156
+ def translate_from_rpc (rpcControlStatus ):
157
+ """ Translates a gRPC struct to the SDK equivalent """
158
+ return ControlStatus (
159
+
160
+ ControlMode .translate_from_rpc (rpcControlStatus .control_mode ),
161
+
162
+
163
+ rpcControlStatus .sysid_primary_control ,
164
+
165
+
166
+ rpcControlStatus .compid_primary_control ,
167
+
168
+
169
+ rpcControlStatus .sysid_secondary_control ,
170
+
171
+
172
+ rpcControlStatus .compid_secondary_control
173
+ )
174
+
175
+ def translate_to_rpc (self , rpcControlStatus ):
176
+ """ Translates this SDK object into its gRPC equivalent """
177
+
178
+
179
+
180
+
181
+ rpcControlStatus .control_mode = self .control_mode .translate_to_rpc ()
182
+
183
+
184
+
185
+
186
+
187
+ rpcControlStatus .sysid_primary_control = self .sysid_primary_control
188
+
189
+
190
+
191
+
192
+
193
+ rpcControlStatus .compid_primary_control = self .compid_primary_control
194
+
195
+
196
+
197
+
198
+
199
+ rpcControlStatus .sysid_secondary_control = self .sysid_secondary_control
200
+
201
+
202
+
203
+
204
+
205
+ rpcControlStatus .compid_secondary_control = self .compid_secondary_control
206
+
207
+
208
+
209
+
210
+
45
211
class GimbalResult :
46
212
"""
47
213
Result type.
@@ -243,6 +409,40 @@ async def set_pitch_and_yaw(self, pitch_deg, yaw_deg):
243
409
raise GimbalError (result , "set_pitch_and_yaw()" , pitch_deg , yaw_deg )
244
410
245
411
412
+ async def set_pitch_rate_and_yaw_rate (self , pitch_rate_deg_s , yaw_rate_deg_s ):
413
+ """
414
+ Set gimbal angular rates around pitch and yaw axes.
415
+
416
+ This sets the desired angular rates around pitch and yaw axes of a gimbal.
417
+ Will return when the command is accepted, however, it might
418
+ take the gimbal longer to actually reach the angular rate.
419
+
420
+ Parameters
421
+ ----------
422
+ pitch_rate_deg_s : float
423
+ Angular rate around pitch axis in degrees/second (negative downward)
424
+
425
+ yaw_rate_deg_s : float
426
+ Angular rate around yaw axis in degrees/second (positive is clock-wise)
427
+
428
+ Raises
429
+ ------
430
+ GimbalError
431
+ If the request fails. The error contains the reason for the failure.
432
+ """
433
+
434
+ request = gimbal_pb2 .SetPitchRateAndYawRateRequest ()
435
+ request .pitch_rate_deg_s = pitch_rate_deg_s
436
+ request .yaw_rate_deg_s = yaw_rate_deg_s
437
+ response = await self ._stub .SetPitchRateAndYawRate (request )
438
+
439
+
440
+ result = self ._extract_result (response )
441
+
442
+ if result .result is not GimbalResult .Result .SUCCESS :
443
+ raise GimbalError (result , "set_pitch_rate_and_yaw_rate()" , pitch_rate_deg_s , yaw_rate_deg_s )
444
+
445
+
246
446
async def set_mode (self , gimbal_mode ):
247
447
"""
248
448
Set gimbal mode.
@@ -314,4 +514,91 @@ async def set_roi_location(self, latitude_deg, longitude_deg, altitude_m):
314
514
315
515
if result .result is not GimbalResult .Result .SUCCESS :
316
516
raise GimbalError (result , "set_roi_location()" , latitude_deg , longitude_deg , altitude_m )
317
-
517
+
518
+
519
+ async def take_control (self , control_mode ):
520
+ """
521
+ Take control.
522
+
523
+ There can be only two components in control of a gimbal at any given time.
524
+ One with "primary" control, and one with "secondary" control. The way the
525
+ secondary control is implemented is not specified and hence depends on the
526
+ vehicle.
527
+
528
+ Components are expected to be cooperative, which means that they can
529
+ override each other and should therefore do it carefully.
530
+
531
+ Parameters
532
+ ----------
533
+ control_mode : ControlMode
534
+ Control mode (primary or secondary)
535
+
536
+ Raises
537
+ ------
538
+ GimbalError
539
+ If the request fails. The error contains the reason for the failure.
540
+ """
541
+
542
+ request = gimbal_pb2 .TakeControlRequest ()
543
+
544
+ request .control_mode = control_mode .translate_to_rpc ()
545
+
546
+
547
+ response = await self ._stub .TakeControl (request )
548
+
549
+
550
+ result = self ._extract_result (response )
551
+
552
+ if result .result is not GimbalResult .Result .SUCCESS :
553
+ raise GimbalError (result , "take_control()" , control_mode )
554
+
555
+
556
+ async def release_control (self ):
557
+ """
558
+ Release control.
559
+
560
+ Release control, such that other components can control the gimbal.
561
+
562
+ Raises
563
+ ------
564
+ GimbalError
565
+ If the request fails. The error contains the reason for the failure.
566
+ """
567
+
568
+ request = gimbal_pb2 .ReleaseControlRequest ()
569
+ response = await self ._stub .ReleaseControl (request )
570
+
571
+
572
+ result = self ._extract_result (response )
573
+
574
+ if result .result is not GimbalResult .Result .SUCCESS :
575
+ raise GimbalError (result , "release_control()" )
576
+
577
+
578
+ async def control (self ):
579
+ """
580
+ Subscribe to control status updates.
581
+
582
+ This allows a component to know if it has primary, secondary or
583
+ no control over the gimbal. Also, it gives the system and component ids
584
+ of the other components in control (if any).
585
+
586
+ Yields
587
+ -------
588
+ control_status : ControlStatus
589
+ Control status
590
+
591
+
592
+ """
593
+
594
+ request = gimbal_pb2 .SubscribeControlRequest ()
595
+ control_stream = self ._stub .SubscribeControl (request )
596
+
597
+ try :
598
+ async for response in control_stream :
599
+
600
+
601
+
602
+ yield ControlStatus .translate_from_rpc (response .control_status )
603
+ finally :
604
+ control_stream .cancel ()
0 commit comments