14
14
#
15
15
16
16
import logging
17
- import time
18
- from tb_device_mqtt import TBDeviceMqttClient
17
+ from time import sleep
18
+
19
+ import paho .mqtt .client as paho
20
+
21
+ try :
22
+ from time import monotonic as time
23
+ except ImportError :
24
+ from time import time
25
+
26
+ from tb_device_mqtt import TBDeviceMqttClient , RateLimit , TBPublishInfo , TBSendMethod
19
27
20
28
GATEWAY_ATTRIBUTES_TOPIC = "v1/gateway/attributes"
21
29
GATEWAY_ATTRIBUTES_REQUEST_TOPIC = "v1/gateway/attributes/request"
@@ -37,9 +45,11 @@ def __init__(self, host, port=1883, username=None, password=None, gateway=None,
37
45
rate_limit = "DEFAULT_RATE_LIMIT" ):
38
46
super ().__init__ (host , port , username , password , quality_of_service , client_id , rate_limit = rate_limit )
39
47
self .quality_of_service = quality_of_service
48
+ self ._rate_limit = RateLimit .get_rate_limit_by_host (host , rate_limit )
40
49
self .__max_sub_id = 0
41
50
self .__sub_dict = {}
42
51
self .__connected_devices = set ("*" )
52
+ self ._devices_rate_limit = {}
43
53
self .devices_server_side_rpc_request_handler = None
44
54
self ._client .on_connect = self ._on_connect
45
55
self ._client .on_message = self ._on_message
@@ -51,44 +61,38 @@ def __init__(self, host, port=1883, username=None, password=None, gateway=None,
51
61
def _on_connect (self , client , userdata , flags , result_code , * extra_params ):
52
62
super ()._on_connect (client , userdata , flags , result_code , * extra_params )
53
63
if result_code == 0 :
54
- gateway_attributes_topic_sub_id = int (self ._subscribe_to_topic (GATEWAY_ATTRIBUTES_TOPIC , qos = 1 ,
55
- wait_for_result = True )[1 ])
56
- if gateway_attributes_topic_sub_id == 128 :
57
- log .error ("Service subscription to topic %s - failed." , GATEWAY_ATTRIBUTES_TOPIC )
58
- if gateway_attributes_topic_sub_id in self ._gw_subscriptions :
59
- del self ._gw_subscriptions [gateway_attributes_topic_sub_id ]
60
- else :
61
- self ._gw_subscriptions [gateway_attributes_topic_sub_id ] = GATEWAY_ATTRIBUTES_TOPIC
62
- gateway_attributes_resp_sub_id = int (self ._subscribe_to_topic (GATEWAY_ATTRIBUTES_RESPONSE_TOPIC , qos = 1 ,
63
- wait_for_result = True )[1 ])
64
- if gateway_attributes_resp_sub_id == 128 :
65
- log .error ("Service subscription to topic %s - failed." , GATEWAY_ATTRIBUTES_RESPONSE_TOPIC )
66
- if gateway_attributes_resp_sub_id in self ._gw_subscriptions :
67
- del self ._gw_subscriptions [gateway_attributes_resp_sub_id ]
68
- else :
69
- self ._gw_subscriptions [gateway_attributes_resp_sub_id ] = GATEWAY_ATTRIBUTES_RESPONSE_TOPIC
70
- gateway_rpc_topic_sub_id = int (self ._subscribe_to_topic (GATEWAY_RPC_TOPIC , qos = 1 ,
71
- wait_for_result = True )[1 ])
72
- if gateway_rpc_topic_sub_id == 128 :
73
- log .error ("Service subscription to topic %s - failed." , GATEWAY_RPC_TOPIC )
74
- if gateway_rpc_topic_sub_id in self ._gw_subscriptions :
75
- del self ._gw_subscriptions [gateway_rpc_topic_sub_id ]
76
- else :
77
- self ._gw_subscriptions [gateway_rpc_topic_sub_id ] = GATEWAY_RPC_TOPIC
78
- # gateway_rpc_topic_response_sub_id = int(self._client.subscribe(GATEWAY_RPC_RESPONSE_TOPIC)[1])
79
- # self._gw_subscriptions[gateway_rpc_topic_response_sub_id] = GATEWAY_RPC_RESPONSE_TOPIC
64
+ d = self ._subscribe_to_topic (GATEWAY_ATTRIBUTES_TOPIC , qos = 1 )
65
+ gateway_attributes_topic_sub_id = int (self ._subscribe_to_topic (GATEWAY_ATTRIBUTES_TOPIC , qos = 1 )[1 ])
66
+ self ._add_or_delete_subscription (GATEWAY_ATTRIBUTES_TOPIC , gateway_attributes_topic_sub_id )
67
+
68
+ gateway_attributes_resp_sub_id = int (self ._subscribe_to_topic (GATEWAY_ATTRIBUTES_RESPONSE_TOPIC , qos = 1 )[1 ])
69
+ self ._add_or_delete_subscription (GATEWAY_ATTRIBUTES_RESPONSE_TOPIC , gateway_attributes_resp_sub_id )
70
+
71
+ gateway_rpc_topic_sub_id = int (self ._subscribe_to_topic (GATEWAY_RPC_TOPIC , qos = 1 )[1 ])
72
+ self ._add_or_delete_subscription (GATEWAY_RPC_TOPIC , gateway_rpc_topic_sub_id )
80
73
81
74
def _on_subscribe (self , client , userdata , mid , reasoncodes , properties = None ):
82
75
subscription = self ._gw_subscriptions .get (mid )
83
76
if subscription is not None :
84
77
if mid == 128 :
85
- log .error ("Service subscription to topic %s - failed." , subscription )
86
- del self ._gw_subscriptions [mid ]
78
+ self ._delete_subscription (subscription , mid )
87
79
else :
88
80
log .debug ("Service subscription to topic %s - successfully completed." , subscription )
89
81
del self ._gw_subscriptions [mid ]
90
82
91
- def _on_unsubscribe (self , * args ):
83
+ def _delete_subscription (self , topic , subscription_id ):
84
+ log .error ("Service subscription to topic %s - failed." , topic )
85
+ if subscription_id in self ._gw_subscriptions :
86
+ del self ._gw_subscriptions [subscription_id ]
87
+
88
+ def _add_or_delete_subscription (self , topic , subscription_id ):
89
+ if subscription_id == 128 :
90
+ self ._delete_subscription (topic , subscription_id )
91
+ else :
92
+ self ._gw_subscriptions [subscription_id ] = topic
93
+
94
+ @staticmethod
95
+ def _on_unsubscribe (* args ):
92
96
log .debug (args )
93
97
94
98
def get_subscriptions_in_progress (self ):
@@ -140,42 +144,66 @@ def __request_attributes(self, device, keys, callback, type_is_client=False):
140
144
log .error ("There are no keys to request" )
141
145
return False
142
146
143
- ts_in_millis = int (round (time . time () * 1000 ))
147
+ ts_in_millis = int (round (time () * 1000 ))
144
148
attr_request_number = self ._add_attr_request_callback (callback )
145
149
msg = {"keys" : keys ,
146
150
"device" : device ,
147
151
"client" : type_is_client ,
148
152
"id" : attr_request_number }
149
- info = self ._publish_data (msg , GATEWAY_ATTRIBUTES_REQUEST_TOPIC , 1 , high_priority = True )
153
+ info = self ._send_device_request (TBSendMethod .PUBLISH , device , topic = GATEWAY_ATTRIBUTES_REQUEST_TOPIC , data = msg ,
154
+ qos = 1 )
150
155
self ._add_timeout (attr_request_number , ts_in_millis + 30000 )
151
156
return info
152
157
158
+ def _send_device_request (self , _type , device_name , ** kwargs ):
159
+ if _type == TBSendMethod .PUBLISH :
160
+ is_reached = self .check_device_rate_limit (device_name )
161
+ if is_reached :
162
+ return is_reached
163
+
164
+ info = self ._publish_data (** kwargs )
165
+ return info
166
+
153
167
def gw_request_shared_attributes (self , device_name , keys , callback ):
154
168
return self .__request_attributes (device_name , keys , callback , False )
155
169
156
170
def gw_request_client_attributes (self , device_name , keys , callback ):
157
171
return self .__request_attributes (device_name , keys , callback , True )
158
172
159
173
def gw_send_attributes (self , device , attributes , quality_of_service = 1 ):
160
- return self ._publish_data ({device : attributes }, GATEWAY_MAIN_TOPIC + "attributes" , quality_of_service )
174
+ return self ._send_device_request (TBSendMethod .PUBLISH ,
175
+ device ,
176
+ topic = GATEWAY_MAIN_TOPIC + "attributes" ,
177
+ data = {device : attributes },
178
+ qos = quality_of_service )
161
179
162
180
def gw_send_telemetry (self , device , telemetry , quality_of_service = 1 ):
163
181
if not isinstance (telemetry , list ) and not (isinstance (telemetry , dict ) and telemetry .get ("ts" ) is not None ):
164
182
telemetry = [telemetry ]
165
- return self ._publish_data ({device : telemetry }, GATEWAY_MAIN_TOPIC + "telemetry" , quality_of_service )
183
+
184
+ return self ._send_device_request (TBSendMethod .PUBLISH ,
185
+ device ,
186
+ topic = GATEWAY_MAIN_TOPIC + "telemetry" ,
187
+ data = {device : telemetry },
188
+ qos = quality_of_service )
166
189
167
190
def gw_connect_device (self , device_name , device_type = "default" ):
168
- info = self ._publish_data ({"device" : device_name , "type" : device_type }, GATEWAY_MAIN_TOPIC + "connect" ,
169
- self .quality_of_service )
191
+ info = self ._send_device_request (TBSendMethod .PUBLISH , device_name , topic = GATEWAY_MAIN_TOPIC + "connect" ,
192
+ data = {"device" : device_name , "type" : device_type },
193
+ qos = self .quality_of_service )
194
+
170
195
self .__connected_devices .add (device_name )
196
+
171
197
log .debug ("Connected device %s" , device_name )
172
198
return info
173
199
174
200
def gw_disconnect_device (self , device_name ):
175
- info = self ._publish_data ({"device" : device_name }, GATEWAY_MAIN_TOPIC + "disconnect" ,
176
- self .quality_of_service )
201
+ info = self ._send_device_request (TBSendMethod .PUBLISH , device_name , topic = GATEWAY_MAIN_TOPIC + "disconnect" ,
202
+ data = {"device" : device_name }, qos = self .quality_of_service )
203
+
177
204
if device_name in self .__connected_devices :
178
205
self .__connected_devices .remove (device_name )
206
+
179
207
log .debug ("Disconnected device %s" , device_name )
180
208
return info
181
209
@@ -217,8 +245,10 @@ def gw_send_rpc_reply(self, device, req_id, resp, quality_of_service=None):
217
245
if quality_of_service not in (0 , 1 ):
218
246
log .error ("Quality of service (qos) value must be 0 or 1" )
219
247
return None
220
- info = self ._publish_data ({"device" : device , "id" : req_id , "data" : resp }, GATEWAY_RPC_TOPIC ,
221
- quality_of_service )
248
+
249
+ info = self ._send_device_request (TBSendMethod .PUBLISH , device , topic = GATEWAY_RPC_TOPIC ,
250
+ data = {"device" : device , "id" : req_id , "data" : resp },
251
+ qos = quality_of_service )
222
252
return info
223
253
224
254
def gw_claim (self , device_name , secret_key , duration , claiming_request = None ):
@@ -229,5 +259,30 @@ def gw_claim(self, device_name, secret_key, duration, claiming_request=None):
229
259
"durationMs" : duration
230
260
}
231
261
}
232
- info = self ._publish_data (claiming_request , GATEWAY_CLAIMING_TOPIC , self .quality_of_service )
262
+
263
+ info = self ._send_device_request (TBSendMethod .PUBLISH , device_name , topic = GATEWAY_CLAIMING_TOPIC ,
264
+ data = claiming_request , qos = self .quality_of_service )
233
265
return info
266
+
267
+ def _add_device_rate_limit (self , device_name ):
268
+ rate_limit = RateLimit (self ._rate_limit )
269
+ self ._devices_rate_limit [device_name ] = {'rate_limit' : rate_limit }
270
+
271
+ def check_device_rate_limit (self , device_name ):
272
+ if self ._devices_rate_limit .get (device_name ) is None :
273
+ self ._add_device_rate_limit (device_name )
274
+
275
+ is_reached = self ._check_device_rate_limit (device_name )
276
+ if is_reached :
277
+ return is_reached
278
+
279
+ self ._devices_rate_limit [device_name ]['rate_limit' ].add_counter ()
280
+
281
+ def _check_device_rate_limit (self , device_name ):
282
+ start_time = time ()
283
+ timeout = self ._devices_rate_limit [device_name ]['rate_limit' ].get_minimal_timeout ()
284
+ while self ._devices_rate_limit [device_name ]['rate_limit' ].check_limit_reached ():
285
+ if time () >= timeout + start_time :
286
+ log .error ("Timeout while waiting for rate limit to be released!" )
287
+ return TBPublishInfo (paho .MQTTMessageInfo (None ))
288
+ sleep (0.001 )
0 commit comments