Commit 3a7a97dd9171296d246c524d44e55960a3540c4c
Committed by
Andrew Shvayka
1 parent
4364755e
PSM and eDRX implementation draft
Showing
17 changed files
with
452 additions
and
63 deletions
... | ... | @@ -228,57 +228,57 @@ cassandra: |
228 | 228 | |
229 | 229 | # SQL configuration parameters |
230 | 230 | sql: |
231 | - # Specify batch size for persisting attribute updates | |
232 | - attributes: | |
233 | - batch_size: "${SQL_ATTRIBUTES_BATCH_SIZE:10000}" | |
234 | - batch_max_delay: "${SQL_ATTRIBUTES_BATCH_MAX_DELAY_MS:100}" | |
235 | - stats_print_interval_ms: "${SQL_ATTRIBUTES_BATCH_STATS_PRINT_MS:10000}" | |
236 | - batch_threads: "${SQL_ATTRIBUTES_BATCH_THREADS:4}" | |
237 | - ts: | |
238 | - batch_size: "${SQL_TS_BATCH_SIZE:10000}" | |
239 | - batch_max_delay: "${SQL_TS_BATCH_MAX_DELAY_MS:100}" | |
240 | - stats_print_interval_ms: "${SQL_TS_BATCH_STATS_PRINT_MS:10000}" | |
241 | - batch_threads: "${SQL_TS_BATCH_THREADS:4}" | |
242 | - ts_latest: | |
243 | - batch_size: "${SQL_TS_LATEST_BATCH_SIZE:10000}" | |
244 | - batch_max_delay: "${SQL_TS_LATEST_BATCH_MAX_DELAY_MS:100}" | |
245 | - stats_print_interval_ms: "${SQL_TS_LATEST_BATCH_STATS_PRINT_MS:10000}" | |
246 | - batch_threads: "${SQL_TS_LATEST_BATCH_THREADS:4}" | |
247 | - update_by_latest_ts: "${SQL_TS_UPDATE_BY_LATEST_TIMESTAMP:true}" | |
248 | - # Specify whether to sort entities before batch update. Should be enabled for cluster mode to avoid deadlocks | |
249 | - batch_sort: "${SQL_BATCH_SORT:false}" | |
250 | - # Specify whether to remove null characters from strValue of attributes and timeseries before insert | |
251 | - remove_null_chars: "${SQL_REMOVE_NULL_CHARS:true}" | |
231 | + # Specify batch size for persisting attribute updates | |
232 | + attributes: | |
233 | + batch_size: "${SQL_ATTRIBUTES_BATCH_SIZE:10000}" | |
234 | + batch_max_delay: "${SQL_ATTRIBUTES_BATCH_MAX_DELAY_MS:100}" | |
235 | + stats_print_interval_ms: "${SQL_ATTRIBUTES_BATCH_STATS_PRINT_MS:10000}" | |
236 | + batch_threads: "${SQL_ATTRIBUTES_BATCH_THREADS:4}" | |
237 | + ts: | |
238 | + batch_size: "${SQL_TS_BATCH_SIZE:10000}" | |
239 | + batch_max_delay: "${SQL_TS_BATCH_MAX_DELAY_MS:100}" | |
240 | + stats_print_interval_ms: "${SQL_TS_BATCH_STATS_PRINT_MS:10000}" | |
241 | + batch_threads: "${SQL_TS_BATCH_THREADS:4}" | |
242 | + ts_latest: | |
243 | + batch_size: "${SQL_TS_LATEST_BATCH_SIZE:10000}" | |
244 | + batch_max_delay: "${SQL_TS_LATEST_BATCH_MAX_DELAY_MS:100}" | |
245 | + stats_print_interval_ms: "${SQL_TS_LATEST_BATCH_STATS_PRINT_MS:10000}" | |
246 | + batch_threads: "${SQL_TS_LATEST_BATCH_THREADS:4}" | |
247 | + update_by_latest_ts: "${SQL_TS_UPDATE_BY_LATEST_TIMESTAMP:true}" | |
248 | + # Specify whether to sort entities before batch update. Should be enabled for cluster mode to avoid deadlocks | |
249 | + batch_sort: "${SQL_BATCH_SORT:false}" | |
250 | + # Specify whether to remove null characters from strValue of attributes and timeseries before insert | |
251 | + remove_null_chars: "${SQL_REMOVE_NULL_CHARS:true}" | |
252 | 252 | # Specify whether to log database queries and their parameters generated by entity query repository |
253 | - log_queries: "${SQL_LOG_QUERIES:false}" | |
254 | - log_queries_threshold: "${SQL_LOG_QUERIES_THRESHOLD:5000}" | |
255 | - postgres: | |
256 | - # Specify partitioning size for timestamp key-value storage. Example: DAYS, MONTHS, YEARS, INDEFINITE. | |
257 | - ts_key_value_partitioning: "${SQL_POSTGRES_TS_KV_PARTITIONING:MONTHS}" | |
258 | - timescale: | |
259 | - # Specify Interval size for new data chunks storage. | |
260 | - chunk_time_interval: "${SQL_TIMESCALE_CHUNK_TIME_INTERVAL:604800000}" | |
261 | - batch_threads: "${SQL_TIMESCALE_BATCH_THREADS:4}" | |
262 | - ttl: | |
263 | - ts: | |
264 | - enabled: "${SQL_TTL_TS_ENABLED:true}" | |
265 | - execution_interval_ms: "${SQL_TTL_TS_EXECUTION_INTERVAL:86400000}" # Number of milliseconds. The current value corresponds to one day | |
266 | - ts_key_value_ttl: "${SQL_TTL_TS_TS_KEY_VALUE_TTL:0}" # Number of seconds | |
267 | - events: | |
268 | - enabled: "${SQL_TTL_EVENTS_ENABLED:true}" | |
269 | - execution_interval_ms: "${SQL_TTL_EVENTS_EXECUTION_INTERVAL:2220000}" # Number of milliseconds (max random initial delay and fixed period). # 37minutes to avoid common interval spikes | |
270 | - events_ttl: "${SQL_TTL_EVENTS_EVENTS_TTL:0}" # Number of seconds | |
271 | - debug_events_ttl: "${SQL_TTL_EVENTS_DEBUG_EVENTS_TTL:604800}" # Number of seconds. The current value corresponds to one week | |
272 | - edge_events: | |
273 | - enabled: "${SQL_TTL_EDGE_EVENTS_ENABLED:true}" | |
274 | - execution_interval_ms: "${SQL_TTL_EDGE_EVENTS_EXECUTION_INTERVAL:86400000}" # Number of milliseconds. The current value corresponds to one day | |
275 | - edge_events_ttl: "${SQL_TTL_EDGE_EVENTS_TTL:2628000}" # Number of seconds. The current value corresponds to one month | |
276 | - alarms: | |
277 | - checking_interval: "${SQL_ALARMS_TTL_CHECKING_INTERVAL:7200000}" # Number of milliseconds. The current value corresponds to two hours | |
278 | - removal_batch_size: "${SQL_ALARMS_TTL_REMOVAL_BATCH_SIZE:3000}" # To delete outdated alarms not all at once but in batches | |
279 | - rpc: | |
280 | - enabled: "${SQL_TTL_RPC_ENABLED:true}" | |
281 | - checking_interval: "${SQL_RPC_TTL_CHECKING_INTERVAL:7200000}" # Number of milliseconds. The current value corresponds to two hours | |
253 | + log_queries: "${SQL_LOG_QUERIES:false}" | |
254 | + log_queries_threshold: "${SQL_LOG_QUERIES_THRESHOLD:5000}" | |
255 | + postgres: | |
256 | + # Specify partitioning size for timestamp key-value storage. Example: DAYS, MONTHS, YEARS, INDEFINITE. | |
257 | + ts_key_value_partitioning: "${SQL_POSTGRES_TS_KV_PARTITIONING:MONTHS}" | |
258 | + timescale: | |
259 | + # Specify Interval size for new data chunks storage. | |
260 | + chunk_time_interval: "${SQL_TIMESCALE_CHUNK_TIME_INTERVAL:604800000}" | |
261 | + batch_threads: "${SQL_TIMESCALE_BATCH_THREADS:4}" | |
262 | + ttl: | |
263 | + ts: | |
264 | + enabled: "${SQL_TTL_TS_ENABLED:true}" | |
265 | + execution_interval_ms: "${SQL_TTL_TS_EXECUTION_INTERVAL:86400000}" # Number of milliseconds. The current value corresponds to one day | |
266 | + ts_key_value_ttl: "${SQL_TTL_TS_TS_KEY_VALUE_TTL:0}" # Number of seconds | |
267 | + events: | |
268 | + enabled: "${SQL_TTL_EVENTS_ENABLED:true}" | |
269 | + execution_interval_ms: "${SQL_TTL_EVENTS_EXECUTION_INTERVAL:2220000}" # Number of milliseconds (max random initial delay and fixed period). # 37minutes to avoid common interval spikes | |
270 | + events_ttl: "${SQL_TTL_EVENTS_EVENTS_TTL:0}" # Number of seconds | |
271 | + debug_events_ttl: "${SQL_TTL_EVENTS_DEBUG_EVENTS_TTL:604800}" # Number of seconds. The current value corresponds to one week | |
272 | + edge_events: | |
273 | + enabled: "${SQL_TTL_EDGE_EVENTS_ENABLED:true}" | |
274 | + execution_interval_ms: "${SQL_TTL_EDGE_EVENTS_EXECUTION_INTERVAL:86400000}" # Number of milliseconds. The current value corresponds to one day | |
275 | + edge_events_ttl: "${SQL_TTL_EDGE_EVENTS_TTL:2628000}" # Number of seconds. The current value corresponds to one month | |
276 | + alarms: | |
277 | + checking_interval: "${SQL_ALARMS_TTL_CHECKING_INTERVAL:7200000}" # Number of milliseconds. The current value corresponds to two hours | |
278 | + removal_batch_size: "${SQL_ALARMS_TTL_REMOVAL_BATCH_SIZE:3000}" # To delete outdated alarms not all at once but in batches | |
279 | + rpc: | |
280 | + enabled: "${SQL_TTL_RPC_ENABLED:true}" | |
281 | + checking_interval: "${SQL_RPC_TTL_CHECKING_INTERVAL:7200000}" # Number of milliseconds. The current value corresponds to two hours | |
282 | 282 | |
283 | 283 | # Actor system parameters |
284 | 284 | actors: |
... | ... | @@ -573,6 +573,9 @@ transport: |
573 | 573 | timeout: "${CLIENT_SIDE_RPC_TIMEOUT:60000}" |
574 | 574 | # Enable/disable http/mqtt/coap transport protocols (has higher priority than certain protocol's 'enabled' property) |
575 | 575 | api_enabled: "${TB_TRANSPORT_API_ENABLED:true}" |
576 | + log: | |
577 | + enabled: "${TB_TRANSPORT_LOG_ENABLED:true}" | |
578 | + max_length: "${TB_TRANSPORT_LOG_MAX_LENGTH:1024}" | |
576 | 579 | # Local HTTP transport parameters |
577 | 580 | http: |
578 | 581 | enabled: "${HTTP_ENABLED:true}" |
... | ... | @@ -618,6 +621,8 @@ transport: |
618 | 621 | bind_address: "${COAP_BIND_ADDRESS:0.0.0.0}" |
619 | 622 | bind_port: "${COAP_BIND_PORT:5683}" |
620 | 623 | timeout: "${COAP_TIMEOUT:10000}" |
624 | + psm_activity_timer: "${COAP_PSM_ACTIVITY_TIMER:10000}" | |
625 | + paging_transmission_window: "${COAP_PAGING_TRANSMISSION_WINDOW:10000}" | |
621 | 626 | dtls: |
622 | 627 | # Enable/disable DTLS 1.2 support |
623 | 628 | enabled: "${COAP_DTLS_ENABLED:false}" | ... | ... |
... | ... | @@ -39,6 +39,14 @@ public class CoapServerContext { |
39 | 39 | private Long timeout; |
40 | 40 | |
41 | 41 | @Getter |
42 | + @Value("${transport.coap.psm_activity_timer:10000}") | |
43 | + private long psmActivityTimer; | |
44 | + | |
45 | + @Getter | |
46 | + @Value("${transport.coap.paging_transmission_window:10000}") | |
47 | + private long pagingTransmissionWindow; | |
48 | + | |
49 | + @Getter | |
42 | 50 | @Autowired(required = false) |
43 | 51 | private TbCoapDtlsSettings dtlsSettings; |
44 | 52 | ... | ... |
... | ... | @@ -17,6 +17,7 @@ package org.thingsboard.server.common.data.device.data.lwm2m; |
17 | 17 | |
18 | 18 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; |
19 | 19 | import lombok.Data; |
20 | +import org.thingsboard.server.common.data.device.data.PowerMode; | |
20 | 21 | import org.thingsboard.server.common.data.device.data.PowerSavingConfiguration; |
21 | 22 | |
22 | 23 | @Data |
... | ... | @@ -26,6 +27,10 @@ public class OtherConfiguration extends PowerSavingConfiguration { |
26 | 27 | private Integer fwUpdateStrategy; |
27 | 28 | private Integer swUpdateStrategy; |
28 | 29 | private Integer clientOnlyObserveAfterConnect; |
30 | + private PowerMode powerMode; | |
31 | + private Long psmActivityTimer; | |
32 | + private Long edrxCycle; | |
33 | + private Long pagingTransmissionWindow; | |
29 | 34 | private String fwUpdateResource; |
30 | 35 | private String swUpdateResource; |
31 | 36 | private boolean compositeOperationsSupport; | ... | ... |
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/CoapTransportResource.java
... | ... | @@ -234,7 +234,7 @@ public class CoapTransportResource extends AbstractCoapTransportResource { |
234 | 234 | TbCoapClientState clientState = null; |
235 | 235 | try { |
236 | 236 | clientState = clients.getOrCreateClient(type, deviceCredentials, deviceProfile); |
237 | - clientState.updateLastUplinkTime(); | |
237 | + clients.awake(clientState); | |
238 | 238 | switch (type) { |
239 | 239 | case POST_ATTRIBUTES_REQUEST: |
240 | 240 | handlePostAttributesRequest(clientState, exchange, request); | ... | ... |
common/transport/coap/src/main/java/org/thingsboard/server/transport/coap/TbCoapMessageObserver.java
... | ... | @@ -27,6 +27,7 @@ public class TbCoapMessageObserver implements MessageObserver { |
27 | 27 | |
28 | 28 | private final int msgId; |
29 | 29 | private final Consumer<Integer> onAcknowledge; |
30 | + private final Consumer<Integer> onTimeout; | |
30 | 31 | |
31 | 32 | @Override |
32 | 33 | public void onRetransmission() { |
... | ... | @@ -50,7 +51,9 @@ public class TbCoapMessageObserver implements MessageObserver { |
50 | 51 | |
51 | 52 | @Override |
52 | 53 | public void onTimeout() { |
53 | - | |
54 | + if (onTimeout != null) { | |
55 | + onTimeout.accept(msgId); | |
56 | + } | |
54 | 57 | } |
55 | 58 | |
56 | 59 | @Override | ... | ... |
... | ... | @@ -22,8 +22,13 @@ import org.eclipse.californium.core.coap.Response; |
22 | 22 | import org.eclipse.californium.core.observe.ObserveRelation; |
23 | 23 | import org.eclipse.californium.core.server.resources.CoapExchange; |
24 | 24 | import org.springframework.stereotype.Service; |
25 | +import org.thingsboard.server.coapserver.CoapServerContext; | |
25 | 26 | import org.thingsboard.server.coapserver.TbCoapServerComponent; |
27 | +import org.thingsboard.server.common.data.Device; | |
26 | 28 | import org.thingsboard.server.common.data.DeviceProfile; |
29 | +import org.thingsboard.server.common.data.DeviceTransportType; | |
30 | +import org.thingsboard.server.common.data.device.data.PowerMode; | |
31 | +import org.thingsboard.server.common.data.device.data.PowerSavingConfiguration; | |
27 | 32 | import org.thingsboard.server.common.data.device.profile.CoapDeviceProfileTransportConfiguration; |
28 | 33 | import org.thingsboard.server.common.data.device.profile.CoapDeviceTypeConfiguration; |
29 | 34 | import org.thingsboard.server.common.data.device.profile.DefaultCoapDeviceTypeConfiguration; |
... | ... | @@ -33,9 +38,11 @@ import org.thingsboard.server.common.data.device.profile.JsonTransportPayloadCon |
33 | 38 | import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration; |
34 | 39 | import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; |
35 | 40 | import org.thingsboard.server.common.data.id.DeviceId; |
41 | +import org.thingsboard.server.common.data.id.DeviceProfileId; | |
36 | 42 | import org.thingsboard.server.common.msg.session.FeatureType; |
37 | 43 | import org.thingsboard.server.common.msg.session.SessionMsgType; |
38 | 44 | import org.thingsboard.server.common.transport.SessionMsgListener; |
45 | +import org.thingsboard.server.common.transport.TransportDeviceProfileCache; | |
39 | 46 | import org.thingsboard.server.common.transport.TransportService; |
40 | 47 | import org.thingsboard.server.common.transport.TransportServiceCallback; |
41 | 48 | import org.thingsboard.server.common.transport.adaptor.AdaptorException; |
... | ... | @@ -50,9 +57,11 @@ import org.thingsboard.server.transport.coap.callback.AbstractSyncSessionCallbac |
50 | 57 | import org.thingsboard.server.transport.coap.callback.CoapNoOpCallback; |
51 | 58 | import org.thingsboard.server.transport.coap.callback.CoapOkCallback; |
52 | 59 | |
60 | +import java.util.Optional; | |
53 | 61 | import java.util.UUID; |
54 | 62 | import java.util.concurrent.ConcurrentHashMap; |
55 | 63 | import java.util.concurrent.ConcurrentMap; |
64 | +import java.util.concurrent.Future; | |
56 | 65 | import java.util.concurrent.ThreadLocalRandom; |
57 | 66 | import java.util.concurrent.TimeUnit; |
58 | 67 | import java.util.concurrent.atomic.AtomicInteger; |
... | ... | @@ -66,8 +75,10 @@ import static org.eclipse.californium.core.coap.Message.NONE; |
66 | 75 | @TbCoapServerComponent |
67 | 76 | public class DefaultCoapClientContext implements CoapClientContext { |
68 | 77 | |
78 | + private final CoapServerContext config; | |
69 | 79 | private final CoapTransportContext transportContext; |
70 | 80 | private final TransportService transportService; |
81 | + private final TransportDeviceProfileCache profileCache; | |
71 | 82 | private final ConcurrentMap<DeviceId, TbCoapClientState> clients = new ConcurrentHashMap<>(); |
72 | 83 | private final ConcurrentMap<String, TbCoapClientState> clientsByToken = new ConcurrentHashMap<>(); |
73 | 84 | |
... | ... | @@ -148,6 +159,65 @@ public class DefaultCoapClientContext implements CoapClientContext { |
148 | 159 | } |
149 | 160 | } |
150 | 161 | |
162 | + private void onUplink(TbCoapClientState client) { | |
163 | + PowerMode powerMode = client.getPowerMode(); | |
164 | + PowerSavingConfiguration profileSettings = null; | |
165 | + if (powerMode == null) { | |
166 | + var clientProfile = getProfile(client.getProfileId()); | |
167 | + if (clientProfile.isPresent()) { | |
168 | + profileSettings = clientProfile.get().getClientSettings(); | |
169 | + powerMode = profileSettings.getPowerMode(); | |
170 | + if (powerMode == null) { | |
171 | + powerMode = PowerMode.DRX; | |
172 | + } | |
173 | + } | |
174 | + } | |
175 | + if (PowerMode.DRX.equals(powerMode)) { | |
176 | + client.updateLastUplinkTime(); | |
177 | + return; | |
178 | + } | |
179 | + client.lock(); | |
180 | + try { | |
181 | + long uplinkTime = client.updateLastUplinkTime(); | |
182 | + long timeout; | |
183 | + if (PowerMode.PSM.equals(powerMode)) { | |
184 | + Long psmActivityTimer = client.getPsmActivityTimer(); | |
185 | + if (psmActivityTimer == null && profileSettings != null) { | |
186 | + psmActivityTimer = profileSettings.getPsmActivityTimer(); | |
187 | + | |
188 | + } | |
189 | + if (psmActivityTimer == null || psmActivityTimer == 0L) { | |
190 | + psmActivityTimer = config.getPsmActivityTimer(); | |
191 | + } | |
192 | + | |
193 | + timeout = psmActivityTimer; | |
194 | + } else { | |
195 | + Long pagingTransmissionWindow = client.getPagingTransmissionWindow(); | |
196 | + if (pagingTransmissionWindow == null && profileSettings != null) { | |
197 | + pagingTransmissionWindow = profileSettings.getPagingTransmissionWindow(); | |
198 | + | |
199 | + } | |
200 | + if (pagingTransmissionWindow == null || pagingTransmissionWindow == 0L) { | |
201 | + pagingTransmissionWindow = config.getPagingTransmissionWindow(); | |
202 | + } | |
203 | + timeout = pagingTransmissionWindow; | |
204 | + } | |
205 | + Future<Void> sleepTask = client.getSleepTask(); | |
206 | + if (sleepTask != null) { | |
207 | + sleepTask.cancel(false); | |
208 | + } | |
209 | + Future<Void> task = transportContext.getScheduler().schedule(() -> { | |
210 | + if (uplinkTime == client.getLastUplinkTime()) { | |
211 | + asleep(client); | |
212 | + } | |
213 | + return null; | |
214 | + }, timeout, TimeUnit.MILLISECONDS); | |
215 | + client.setSleepTask(task); | |
216 | + } finally { | |
217 | + client.unlock(); | |
218 | + } | |
219 | + } | |
220 | + | |
151 | 221 | private boolean registerFeatureObservation(TbCoapClientState state, String token, CoapExchange exchange, FeatureType featureType) { |
152 | 222 | state.lock(); |
153 | 223 | try { |
... | ... | @@ -182,7 +252,9 @@ public class DefaultCoapClientContext implements CoapClientContext { |
182 | 252 | if (state.getSession() == null) { |
183 | 253 | TransportProtos.SessionInfoProto session = SessionInfoCreator.create(state.getCredentials(), transportContext, UUID.randomUUID()); |
184 | 254 | state.setSession(session); |
185 | - transportService.registerAsyncSession(session, new CoapSessionListener(state)); | |
255 | + CoapSessionListener listener = new CoapSessionListener(state); | |
256 | + state.setListener(listener); | |
257 | + transportService.registerAsyncSession(session, state.getListener()); | |
186 | 258 | transportService.process(session, getSessionEventMsg(TransportProtos.SessionEvent.OPEN), null); |
187 | 259 | } |
188 | 260 | if (FeatureType.ATTRIBUTES.equals(featureType)) { |
... | ... | @@ -261,7 +333,7 @@ public class DefaultCoapClientContext implements CoapClientContext { |
261 | 333 | state.setAdaptor(getCoapTransportAdaptor(state.getConfiguration().isJsonPayload())); |
262 | 334 | } |
263 | 335 | if (state.getCredentials() == null) { |
264 | - state.setCredentials(deviceCredentials); | |
336 | + state.init(deviceCredentials); | |
265 | 337 | } |
266 | 338 | } finally { |
267 | 339 | state.unlock(); |
... | ... | @@ -278,10 +350,6 @@ public class DefaultCoapClientContext implements CoapClientContext { |
278 | 350 | return clients.computeIfAbsent(deviceId, TbCoapClientState::new); |
279 | 351 | } |
280 | 352 | |
281 | - private static DeviceId toDeviceId(TransportProtos.SessionInfoProto s) { | |
282 | - return new DeviceId(new UUID(s.getDeviceIdMSB(), s.getDeviceIdLSB())); | |
283 | - } | |
284 | - | |
285 | 353 | private static TransportProtos.SessionEventMsg getSessionEventMsg(TransportProtos.SessionEvent event) { |
286 | 354 | return TransportProtos.SessionEventMsg.newBuilder() |
287 | 355 | .setSessionType(TransportProtos.SessionType.ASYNC) |
... | ... | @@ -331,7 +399,7 @@ public class DefaultCoapClientContext implements CoapClientContext { |
331 | 399 | } |
332 | 400 | |
333 | 401 | @RequiredArgsConstructor |
334 | - private class CoapSessionListener implements SessionMsgListener { | |
402 | + public class CoapSessionListener implements SessionMsgListener { | |
335 | 403 | |
336 | 404 | private final TbCoapClientState state; |
337 | 405 | |
... | ... | @@ -355,13 +423,28 @@ public class DefaultCoapClientContext implements CoapClientContext { |
355 | 423 | |
356 | 424 | @Override |
357 | 425 | public void onAttributeUpdate(UUID sessionId, TransportProtos.AttributeUpdateNotificationMsg msg) { |
426 | + if (!isDownlinkAllowed(state)) { | |
427 | + log.trace("[{}] ignore downlink request cause client is sleeping.", state.getDeviceId()); | |
428 | + state.lock(); | |
429 | + try { | |
430 | + state.addQueuedNotification(msg); | |
431 | + } finally { | |
432 | + state.unlock(); | |
433 | + } | |
434 | + return; | |
435 | + } | |
358 | 436 | log.trace("[{}] Received attributes update notification to device", sessionId); |
359 | 437 | TbCoapObservationState attrs = state.getAttrs(); |
360 | 438 | if (attrs != null) { |
361 | 439 | try { |
362 | 440 | boolean conRequest = AbstractSyncSessionCallback.isConRequest(state.getAttrs()); |
441 | + int requestId = getNextMsgId(); | |
363 | 442 | Response response = state.getAdaptor().convertToPublish(conRequest, msg); |
443 | + response.setMID(requestId); | |
364 | 444 | attrs.getExchange().respond(response); |
445 | + if (conRequest) { | |
446 | + response.addMessageObserver(new TbCoapMessageObserver(requestId, id -> awake(state), id -> asleep(state))); | |
447 | + } | |
365 | 448 | } catch (AdaptorException e) { |
366 | 449 | log.trace("[{}] Failed to reply due to error", state.getDeviceId(), e); |
367 | 450 | cancelObserveRelation(attrs); |
... | ... | @@ -373,6 +456,17 @@ public class DefaultCoapClientContext implements CoapClientContext { |
373 | 456 | } |
374 | 457 | |
375 | 458 | @Override |
459 | + public void onDeviceUpdate(TransportProtos.SessionInfoProto sessionInfo, Device device, Optional<DeviceProfile> deviceProfileOpt) { | |
460 | + state.onDeviceUpdate(device); | |
461 | + } | |
462 | + | |
463 | + @Override | |
464 | + public void onDeviceDeleted(DeviceId deviceId) { | |
465 | + cancelRpcSubscription(state); | |
466 | + cancelAttributeSubscription(state); | |
467 | + } | |
468 | + | |
469 | + @Override | |
376 | 470 | public void onRemoteSessionCloseCommand(UUID sessionId, TransportProtos.SessionCloseNotificationProto sessionCloseNotification) { |
377 | 471 | log.trace("[{}] Received the remote command to close the session: {}", sessionId, sessionCloseNotification.getMessage()); |
378 | 472 | cancelRpcSubscription(state); |
... | ... | @@ -382,6 +476,10 @@ public class DefaultCoapClientContext implements CoapClientContext { |
382 | 476 | @Override |
383 | 477 | public void onToDeviceRpcRequest(UUID sessionId, TransportProtos.ToDeviceRpcRequestMsg msg) { |
384 | 478 | log.trace("[{}] Received RPC command to device", sessionId); |
479 | + if (!isDownlinkAllowed(state)) { | |
480 | + log.trace("[{}] ignore downlink request cause client is sleeping.", state.getDeviceId()); | |
481 | + return; | |
482 | + } | |
385 | 483 | boolean sent = false; |
386 | 484 | boolean conRequest = AbstractSyncSessionCallback.isConRequest(state.getRpc()); |
387 | 485 | try { |
... | ... | @@ -401,7 +499,10 @@ public class DefaultCoapClientContext implements CoapClientContext { |
401 | 499 | if (rpcRequestMsg != null) { |
402 | 500 | transportService.process(state.getSession(), rpcRequestMsg, false, TransportServiceCallback.EMPTY); |
403 | 501 | } |
404 | - })); | |
502 | + }, null)); | |
503 | + } | |
504 | + if (conRequest) { | |
505 | + response.addMessageObserver(new TbCoapMessageObserver(requestId, id -> awake(state), id -> asleep(state))); | |
405 | 506 | } |
406 | 507 | state.getRpc().getExchange().respond(response); |
407 | 508 | sent = true; |
... | ... | @@ -428,6 +529,143 @@ public class DefaultCoapClientContext implements CoapClientContext { |
428 | 529 | } |
429 | 530 | } |
430 | 531 | |
532 | + private boolean asleep(TbCoapClientState client) { | |
533 | + boolean changed = compareAndSetSleepFlag(client, true); | |
534 | + if (changed) { | |
535 | + log.debug("[{}] client is sleeping", client.getDeviceId()); | |
536 | + transportService.log(client.getSession(), "Info: Client is sleeping!"); | |
537 | + } | |
538 | + return changed; | |
539 | + } | |
540 | + | |
541 | + @Override | |
542 | + public boolean awake(TbCoapClientState client) { | |
543 | + onUplink(client); | |
544 | + boolean changed = compareAndSetSleepFlag(client, false); | |
545 | + if (changed) { | |
546 | + log.debug("[{}] client is awake", client.getDeviceId()); | |
547 | + transportService.log(client.getSession(), "Info: Client is awake!"); | |
548 | + sendMsgsAfterSleeping(client); | |
549 | + } | |
550 | + return changed; | |
551 | + } | |
552 | + | |
553 | + private void sendMsgsAfterSleeping(TbCoapClientState client) { | |
554 | + if (client.getRpc() != null) { | |
555 | + TransportProtos.TransportToDeviceActorMsg persistentRpcRequestMsg = TransportProtos.TransportToDeviceActorMsg | |
556 | + .newBuilder() | |
557 | + .setSessionInfo(client.getSession()) | |
558 | + .setSendPendingRPC(TransportProtos.SendPendingRPCMsg.newBuilder().build()) | |
559 | + .build(); | |
560 | + transportService.process(persistentRpcRequestMsg, TransportServiceCallback.EMPTY); | |
561 | + } | |
562 | + if (client.getAttrs() != null && client.getMissedAttributeUpdates() != null) { | |
563 | + client.getListener().onAttributeUpdate(new UUID(client.getSession().getSessionIdMSB(), client.getSession().getSessionIdLSB()), client.getAndClearMissedUpdates()); | |
564 | + } | |
565 | + } | |
566 | + | |
567 | + private boolean compareAndSetSleepFlag(TbCoapClientState client, boolean sleeping) { | |
568 | + if (sleeping == client.isAsleep()) { | |
569 | + log.trace("[{}] Client is already at sleeping: {}, ignoring event: {}", client.getDeviceId(), client.isAsleep(), sleeping); | |
570 | + return false; | |
571 | + } | |
572 | + client.lock(); | |
573 | + try { | |
574 | + if (sleeping == client.isAsleep()) { | |
575 | + log.trace("[{}] Client is already at sleeping: {}, ignoring event: {}", client.getDeviceId(), client.isAsleep(), sleeping); | |
576 | + return false; | |
577 | + } else { | |
578 | + PowerMode powerMode = getPowerMode(client); | |
579 | + if (PowerMode.PSM.equals(powerMode) || PowerMode.E_DRX.equals(powerMode)) { | |
580 | + log.trace("[{}] Switch sleeping from: {} to: {}", client.getDeviceId(), client.isAsleep(), sleeping); | |
581 | + client.setAsleep(sleeping); | |
582 | + // TODO: persist changes. | |
583 | + // update(client); | |
584 | + return true; | |
585 | + } else { | |
586 | + return false; | |
587 | + } | |
588 | + } | |
589 | + } finally { | |
590 | + client.unlock(); | |
591 | + } | |
592 | + } | |
593 | + | |
594 | + private boolean isDownlinkAllowed(TbCoapClientState client) { | |
595 | + PowerMode powerMode = client.getPowerMode(); | |
596 | + PowerSavingConfiguration profileSettings = null; | |
597 | + if (powerMode == null) { | |
598 | + var clientProfile = getProfile(client.getProfileId()); | |
599 | + if (clientProfile.isPresent()) { | |
600 | + profileSettings = clientProfile.get().getClientSettings(); | |
601 | + powerMode = profileSettings.getPowerMode(); | |
602 | + if (powerMode == null) { | |
603 | + powerMode = PowerMode.DRX; | |
604 | + } | |
605 | + } | |
606 | + } | |
607 | + if (PowerMode.DRX.equals(powerMode)) { | |
608 | + return true; | |
609 | + } | |
610 | + client.lock(); | |
611 | + long timeSinceLastUplink = System.currentTimeMillis() - client.getLastUplinkTime(); | |
612 | + try { | |
613 | + if (PowerMode.PSM.equals(powerMode)) { | |
614 | + Long psmActivityTimer = client.getPsmActivityTimer(); | |
615 | + if (psmActivityTimer == null && profileSettings != null) { | |
616 | + psmActivityTimer = profileSettings.getPsmActivityTimer(); | |
617 | + | |
618 | + } | |
619 | + if (psmActivityTimer == null || psmActivityTimer == 0L) { | |
620 | + psmActivityTimer = config.getPsmActivityTimer(); | |
621 | + } | |
622 | + return timeSinceLastUplink <= psmActivityTimer; | |
623 | + } else { | |
624 | + Long pagingTransmissionWindow = client.getPagingTransmissionWindow(); | |
625 | + if (pagingTransmissionWindow == null && profileSettings != null) { | |
626 | + pagingTransmissionWindow = profileSettings.getPagingTransmissionWindow(); | |
627 | + | |
628 | + } | |
629 | + if (pagingTransmissionWindow == null || pagingTransmissionWindow == 0L) { | |
630 | + pagingTransmissionWindow = config.getPagingTransmissionWindow(); | |
631 | + } | |
632 | + boolean allowed = timeSinceLastUplink <= pagingTransmissionWindow; | |
633 | + if (!allowed) { | |
634 | + return client.checkFirstDownlink(); | |
635 | + } else { | |
636 | + return true; | |
637 | + } | |
638 | + } | |
639 | + } finally { | |
640 | + client.unlock(); | |
641 | + } | |
642 | + } | |
643 | + | |
644 | + private PowerMode getPowerMode(TbCoapClientState client) { | |
645 | + PowerMode powerMode = client.getPowerMode(); | |
646 | + if (powerMode == null) { | |
647 | + Optional<CoapDeviceProfileTransportConfiguration> deviceProfile = getProfile(client.getProfileId()); | |
648 | + if (deviceProfile.isPresent()) { | |
649 | + powerMode = deviceProfile.get().getClientSettings().getPowerMode(); | |
650 | + } else { | |
651 | + powerMode = PowerMode.PSM; | |
652 | + } | |
653 | + } | |
654 | + return powerMode; | |
655 | + } | |
656 | + | |
657 | + public Optional<CoapDeviceProfileTransportConfiguration> getProfile(DeviceProfileId profileId) { | |
658 | + DeviceProfile deviceProfile = profileCache.get(profileId); | |
659 | + if (deviceProfile.getTransportType().equals(DeviceTransportType.COAP)) { | |
660 | + return Optional.of((CoapDeviceProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration()); | |
661 | + } else if (deviceProfile.getTransportType().equals(DeviceTransportType.DEFAULT)) { | |
662 | + return Optional.empty(); | |
663 | + } else { | |
664 | + log.warn("[{}] Invalid device profile type: {}", profileId, deviceProfile.getTransportType()); | |
665 | + throw new IllegalArgumentException("Invalid device profile type: " + deviceProfile.getTransportType()); | |
666 | + } | |
667 | + } | |
668 | + | |
431 | 669 | protected int getNextMsgId() { |
432 | 670 | return ThreadLocalRandom.current().nextInt(NONE, MAX_MID + 1); |
433 | 671 | } | ... | ... |
... | ... | @@ -18,12 +18,26 @@ package org.thingsboard.server.transport.coap.client; |
18 | 18 | import lombok.Data; |
19 | 19 | import lombok.Getter; |
20 | 20 | import lombok.Setter; |
21 | +import org.eclipse.leshan.server.registration.Registration; | |
22 | +import org.thingsboard.server.common.data.Device; | |
23 | +import org.thingsboard.server.common.data.DeviceTransportType; | |
24 | +import org.thingsboard.server.common.data.device.data.CoapDeviceTransportConfiguration; | |
25 | +import org.thingsboard.server.common.data.device.data.PowerMode; | |
21 | 26 | import org.thingsboard.server.common.data.id.DeviceId; |
27 | +import org.thingsboard.server.common.data.id.DeviceProfileId; | |
28 | +import org.thingsboard.server.common.data.id.TenantId; | |
22 | 29 | import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse; |
23 | 30 | import org.thingsboard.server.gen.transport.TransportProtos; |
24 | 31 | import org.thingsboard.server.transport.coap.TransportConfigurationContainer; |
25 | 32 | import org.thingsboard.server.transport.coap.adaptors.CoapTransportAdaptor; |
26 | 33 | |
34 | +import java.util.ArrayList; | |
35 | +import java.util.HashMap; | |
36 | +import java.util.HashSet; | |
37 | +import java.util.List; | |
38 | +import java.util.Map; | |
39 | +import java.util.Set; | |
40 | +import java.util.UUID; | |
27 | 41 | import java.util.concurrent.Future; |
28 | 42 | import java.util.concurrent.locks.Lock; |
29 | 43 | import java.util.concurrent.locks.ReentrantLock; |
... | ... | @@ -37,12 +51,22 @@ public class TbCoapClientState { |
37 | 51 | private volatile TransportConfigurationContainer configuration; |
38 | 52 | private volatile CoapTransportAdaptor adaptor; |
39 | 53 | private volatile ValidateDeviceCredentialsResponse credentials; |
40 | - | |
41 | 54 | private volatile TransportProtos.SessionInfoProto session; |
42 | - | |
55 | + private volatile DefaultCoapClientContext.CoapSessionListener listener; | |
43 | 56 | private volatile TbCoapObservationState attrs; |
44 | 57 | private volatile TbCoapObservationState rpc; |
58 | + private TransportProtos.AttributeUpdateNotificationMsg missedAttributeUpdates; | |
45 | 59 | |
60 | + private DeviceProfileId profileId; | |
61 | + | |
62 | + @Getter | |
63 | + private PowerMode powerMode; | |
64 | + @Getter | |
65 | + private Long psmActivityTimer; | |
66 | + @Getter | |
67 | + private Long edrxCycle; | |
68 | + @Getter | |
69 | + private Long pagingTransmissionWindow; | |
46 | 70 | @Getter |
47 | 71 | @Setter |
48 | 72 | private boolean asleep; |
... | ... | @@ -59,6 +83,14 @@ public class TbCoapClientState { |
59 | 83 | this.lock = new ReentrantLock(); |
60 | 84 | } |
61 | 85 | |
86 | + public void init(ValidateDeviceCredentialsResponse credentials) { | |
87 | + this.profileId = credentials.getDeviceInfo().getDeviceProfileId(); | |
88 | + this.powerMode = credentials.getDeviceInfo().getPowerMode(); | |
89 | + this.edrxCycle = credentials.getDeviceInfo().getEdrxCycle(); | |
90 | + this.psmActivityTimer = credentials.getDeviceInfo().getPsmActivityTimer(); | |
91 | + this.pagingTransmissionWindow = credentials.getDeviceInfo().getPagingTransmissionWindow(); | |
92 | + } | |
93 | + | |
62 | 94 | public void lock() { |
63 | 95 | lock.lock(); |
64 | 96 | } |
... | ... | @@ -67,10 +99,54 @@ public class TbCoapClientState { |
67 | 99 | lock.unlock(); |
68 | 100 | } |
69 | 101 | |
70 | - public long updateLastUplinkTime(){ | |
102 | + public long updateLastUplinkTime() { | |
71 | 103 | this.lastUplinkTime = System.currentTimeMillis(); |
72 | 104 | this.firstEdrxDownlink = true; |
73 | 105 | return lastUplinkTime; |
74 | 106 | } |
75 | 107 | |
108 | + public boolean checkFirstDownlink() { | |
109 | + boolean result = firstEdrxDownlink; | |
110 | + firstEdrxDownlink = false; | |
111 | + return result; | |
112 | + } | |
113 | + | |
114 | + public void onDeviceUpdate(Device device) { | |
115 | + this.profileId = device.getDeviceProfileId(); | |
116 | + var data = device.getDeviceData(); | |
117 | + if (data.getTransportConfiguration() != null && data.getTransportConfiguration().getType().equals(DeviceTransportType.COAP)) { | |
118 | + CoapDeviceTransportConfiguration configuration = (CoapDeviceTransportConfiguration) data.getTransportConfiguration(); | |
119 | + this.powerMode = configuration.getPowerMode(); | |
120 | + this.edrxCycle = configuration.getEdrxCycle(); | |
121 | + this.psmActivityTimer = configuration.getPsmActivityTimer(); | |
122 | + this.pagingTransmissionWindow = configuration.getPagingTransmissionWindow(); | |
123 | + } | |
124 | + } | |
125 | + | |
126 | + public void addQueuedNotification(TransportProtos.AttributeUpdateNotificationMsg msg) { | |
127 | + if (missedAttributeUpdates == null) { | |
128 | + missedAttributeUpdates = msg; | |
129 | + } else { | |
130 | + Map<String, TransportProtos.TsKvProto> updatedAttrs = new HashMap<>(missedAttributeUpdates.getSharedUpdatedCount() + msg.getSharedUpdatedCount()); | |
131 | + Set<String> deletedKeys = new HashSet<>(missedAttributeUpdates.getSharedDeletedCount() + msg.getSharedDeletedCount()); | |
132 | + for (TransportProtos.TsKvProto oldUpdatedAttrs : missedAttributeUpdates.getSharedUpdatedList()) { | |
133 | + updatedAttrs.put(oldUpdatedAttrs.getKv().getKey(), oldUpdatedAttrs); | |
134 | + } | |
135 | + deletedKeys.addAll(msg.getSharedDeletedList()); | |
136 | + for (TransportProtos.TsKvProto newUpdatedAttrs : msg.getSharedUpdatedList()) { | |
137 | + updatedAttrs.put(newUpdatedAttrs.getKv().getKey(), newUpdatedAttrs); | |
138 | + } | |
139 | + deletedKeys.addAll(msg.getSharedDeletedList()); | |
140 | + for (String deletedKey : msg.getSharedDeletedList()) { | |
141 | + updatedAttrs.remove(deletedKey); | |
142 | + } | |
143 | + missedAttributeUpdates = TransportProtos.AttributeUpdateNotificationMsg.newBuilder().addAllSharedUpdated(updatedAttrs.values()).addAllSharedDeleted(deletedKeys).build(); | |
144 | + } | |
145 | + } | |
146 | + | |
147 | + public TransportProtos.AttributeUpdateNotificationMsg getAndClearMissedUpdates() { | |
148 | + var result = this.missedAttributeUpdates; | |
149 | + this.missedAttributeUpdates = null; | |
150 | + return result; | |
151 | + } | |
76 | 152 | } | ... | ... |
... | ... | @@ -119,6 +119,12 @@ public class DefaultTransportService implements TransportService { |
119 | 119 | |
120 | 120 | public static final String OVERWRITE_ACTIVITY_TIME = "overwriteActivityTime"; |
121 | 121 | |
122 | + private final AtomicInteger atomicTs = new AtomicInteger(0); | |
123 | + | |
124 | + @Value("${transport.log.enabled:true}") | |
125 | + private boolean logEnabled; | |
126 | + @Value("${transport.log.max_length:1024}") | |
127 | + private int logMaxLength; | |
122 | 128 | @Value("${transport.sessions.inactivity_timeout}") |
123 | 129 | private long sessionInactivityTimeout; |
124 | 130 | @Value("${transport.sessions.report_timeout}") |
... | ... | @@ -444,6 +450,8 @@ public class DefaultTransportService implements TransportService { |
444 | 450 | if (StringUtils.isNotEmpty(di.getPowerMode())) { |
445 | 451 | tdi.setPowerMode(PowerMode.valueOf(di.getPowerMode())); |
446 | 452 | tdi.setEdrxCycle(di.getEdrxCycle()); |
453 | + tdi.setPsmActivityTimer(di.getPsmActivityTimer()); | |
454 | + tdi.setPagingTransmissionWindow(di.getPagingTransmissionWindow()); | |
447 | 455 | } |
448 | 456 | return tdi; |
449 | 457 | } |
... | ... | @@ -745,6 +753,26 @@ public class DefaultTransportService implements TransportService { |
745 | 753 | sessions.remove(toSessionId(sessionInfo)); |
746 | 754 | } |
747 | 755 | |
756 | + @Override | |
757 | + public void log(TransportProtos.SessionInfoProto sessionInfo, String msg) { | |
758 | + if (!logEnabled || sessionInfo == null || StringUtils.isEmpty(msg)) { | |
759 | + return; | |
760 | + } | |
761 | + if (msg.length() > logMaxLength) { | |
762 | + msg = msg.substring(0, logMaxLength); | |
763 | + } | |
764 | + TransportProtos.PostTelemetryMsg.Builder request = TransportProtos.PostTelemetryMsg.newBuilder(); | |
765 | + TransportProtos.TsKvListProto.Builder builder = TransportProtos.TsKvListProto.newBuilder(); | |
766 | + builder.setTs(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) * 1000L + (atomicTs.getAndIncrement() % 1000)); | |
767 | + builder.addKv(TransportProtos.KeyValueProto.newBuilder() | |
768 | + .setKey("transportLog") | |
769 | + .setType(TransportProtos.KeyValueType.STRING_V) | |
770 | + .setStringV(msg).build()); | |
771 | + request.addTsKvList(builder.build()); | |
772 | + TransportProtos.PostTelemetryMsg postTelemetryMsg = request.build(); | |
773 | + process(sessionInfo, postTelemetryMsg, TransportServiceCallback.EMPTY); | |
774 | + } | |
775 | + | |
748 | 776 | private boolean checkLimits(TransportProtos.SessionInfoProto sessionInfo, Object msg, TransportServiceCallback<?> callback) { |
749 | 777 | return checkLimits(sessionInfo, msg, callback, 0); |
750 | 778 | } | ... | ... |
... | ... | @@ -89,6 +89,8 @@ transport: |
89 | 89 | bind_address: "${COAP_BIND_ADDRESS:0.0.0.0}" |
90 | 90 | bind_port: "${COAP_BIND_PORT:5683}" |
91 | 91 | timeout: "${COAP_TIMEOUT:10000}" |
92 | + psm_activity_timer: "${COAP_PSM_ACTIVITY_TIMER:10000}" | |
93 | + paging_transmission_window: "${COAP_PAGING_TRANSMISSION_WINDOW:10000}" | |
92 | 94 | dtls: |
93 | 95 | # Enable/disable DTLS 1.2 support |
94 | 96 | enabled: "${COAP_DTLS_ENABLED:false}" |
... | ... | @@ -117,6 +119,9 @@ transport: |
117 | 119 | type_cast_enabled: "${JSON_TYPE_CAST_ENABLED:true}" |
118 | 120 | # Maximum allowed string value length when processing Telemetry/Attributes JSON (0 value disables string value length check) |
119 | 121 | max_string_value_length: "${JSON_MAX_STRING_VALUE_LENGTH:0}" |
122 | + log: | |
123 | + enabled: "${TB_TRANSPORT_LOG_ENABLED:true}" | |
124 | + max_length: "${TB_TRANSPORT_LOG_MAX_LENGTH:1024}" | |
120 | 125 | |
121 | 126 | queue: |
122 | 127 | type: "${TB_QUEUE_TYPE:kafka}" # kafka (Apache Kafka) or aws-sqs (AWS SQS) or pubsub (PubSub) or service-bus (Azure Service Bus) or rabbitmq (RabbitMQ) | ... | ... |
... | ... | @@ -91,6 +91,9 @@ transport: |
91 | 91 | type_cast_enabled: "${JSON_TYPE_CAST_ENABLED:true}" |
92 | 92 | # Maximum allowed string value length when processing Telemetry/Attributes JSON (0 value disables string value length check) |
93 | 93 | max_string_value_length: "${JSON_MAX_STRING_VALUE_LENGTH:0}" |
94 | + log: | |
95 | + enabled: "${TB_TRANSPORT_LOG_ENABLED:true}" | |
96 | + max_length: "${TB_TRANSPORT_LOG_MAX_LENGTH:1024}" | |
94 | 97 | |
95 | 98 | queue: |
96 | 99 | type: "${TB_QUEUE_TYPE:kafka}" # kafka (Apache Kafka) or aws-sqs (AWS SQS) or pubsub (PubSub) or service-bus (Azure Service Bus) or rabbitmq (RabbitMQ) | ... | ... |
... | ... | @@ -98,6 +98,9 @@ transport: |
98 | 98 | # Enable/disable http/mqtt/coap transport protocols (has higher priority than certain protocol's 'enabled' property) |
99 | 99 | api_enabled: "${TB_TRANSPORT_API_ENABLED:true}" |
100 | 100 | # Local LwM2M transport parameters |
101 | + log: | |
102 | + enabled: "${TB_TRANSPORT_LOG_ENABLED:true}" | |
103 | + max_length: "${TB_TRANSPORT_LOG_MAX_LENGTH:1024}" | |
101 | 104 | lwm2m: |
102 | 105 | # Enable/disable lvm2m transport protocol. |
103 | 106 | enabled: "${LWM2M_ENABLED:true}" | ... | ... |
... | ... | @@ -123,6 +123,10 @@ transport: |
123 | 123 | type_cast_enabled: "${JSON_TYPE_CAST_ENABLED:true}" |
124 | 124 | # Maximum allowed string value length when processing Telemetry/Attributes JSON (0 value disables string value length check) |
125 | 125 | max_string_value_length: "${JSON_MAX_STRING_VALUE_LENGTH:0}" |
126 | + log: | |
127 | + enabled: "${TB_TRANSPORT_LOG_ENABLED:true}" | |
128 | + max_length: "${TB_TRANSPORT_LOG_MAX_LENGTH:1024}" | |
129 | + | |
126 | 130 | |
127 | 131 | queue: |
128 | 132 | type: "${TB_QUEUE_TYPE:kafka}" # kafka (Apache Kafka) or aws-sqs (AWS SQS) or pubsub (PubSub) or service-bus (Azure Service Bus) or rabbitmq (RabbitMQ) | ... | ... |
... | ... | @@ -56,6 +56,9 @@ transport: |
56 | 56 | type_cast_enabled: "${JSON_TYPE_CAST_ENABLED:true}" |
57 | 57 | # Maximum allowed string value length when processing Telemetry/Attributes JSON (0 value disables string value length check) |
58 | 58 | max_string_value_length: "${JSON_MAX_STRING_VALUE_LENGTH:0}" |
59 | + log: | |
60 | + enabled: "${TB_TRANSPORT_LOG_ENABLED:true}" | |
61 | + max_length: "${TB_TRANSPORT_LOG_MAX_LENGTH:1024}" | |
59 | 62 | |
60 | 63 | queue: |
61 | 64 | type: "${TB_QUEUE_TYPE:kafka}" # kafka (Apache Kafka) or aws-sqs (AWS SQS) or pubsub (PubSub) or service-bus (Azure Service Bus) or rabbitmq (RabbitMQ) | ... | ... |