@ -18,6 +18,7 @@ package org.thingsboard.rule.engine.action;
import com.fasterxml.jackson.databind.node.ArrayNode ;
import com.fasterxml.jackson.databind.node.ObjectNode ;
import lombok.extern.slf4j.Slf4j ;
import org.junit.Ignore ;
import org.junit.jupiter.api.AfterEach ;
import org.junit.jupiter.api.Assertions ;
import org.junit.jupiter.api.BeforeEach ;
@ -31,9 +32,9 @@ import org.thingsboard.rule.engine.api.TbContext;
import org.thingsboard.rule.engine.api.TbNodeConfiguration ;
import org.thingsboard.rule.engine.api.TbNodeException ;
import org.thingsboard.rule.engine.api.TbRelationTypes ;
import org.thingsboard.rule.engine.delay .DeDuplicateStrategy ;
import org.thingsboard.rule.engine.delay .TbMsgDeDuplicateNode ;
import org.thingsboard.rule.engine.delay .TbMsgDeDuplicateNodeConfiguration ;
import org.thingsboard.rule.engine.deduplicate .DeDuplicateStrategy ;
import org.thingsboard.rule.engine.deduplicate .TbMsgDeDuplicateNode ;
import org.thingsboard.rule.engine.deduplicate .TbMsgDeDuplicateNodeConfiguration ;
import org.thingsboard.server.common.data.id.DeviceId ;
import org.thingsboard.server.common.data.id.EntityId ;
import org.thingsboard.server.common.data.id.RuleNodeId ;
@ -43,12 +44,18 @@ import org.thingsboard.server.common.msg.TbMsgMetaData;
import org.thingsboard.server.common.msg.session.SessionMsgType ;
import java.util.ArrayList ;
import java.util.Collections ;
import java.util.Comparator ;
import java.util.List ;
import java.util.Random ;
import java.util.UUID ;
import java.util.concurrent.CountDownLatch ;
import java.util.concurrent.ExecutionException ;
import java.util.concurrent.ExecutorService ;
import java.util.concurrent.Executors ;
import java.util.concurrent.ScheduledExecutorService ;
import java.util.concurrent.TimeUnit ;
import java.util.concurrent.atomic.AtomicInteger ;
import java.util.concurrent.atomic.AtomicLong ;
import java.util.function.Consumer ;
import static org.mockito.ArgumentMatchers.any ;
@ -57,6 +64,7 @@ import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.ArgumentMatchers.nullable ;
import static org.mockito.Mockito.doAnswer ;
import static org.mockito.Mockito.mock ;
import static org.mockito.Mockito.spy ;
import static org.mockito.Mockito.times ;
import static org.mockito.Mockito.verify ;
import static org.mockito.Mockito.when ;
@ -70,7 +78,9 @@ public class TbMsgDeDuplicateNodeTest {
private TbContext ctx ;
private final ExecutorService executorService = Executors . newSingleThreadExecutor ( ThingsBoardThreadFactory . forName ( "de-duplication-node-test" ) ) ;
private final ThingsBoardThreadFactory factory = ThingsBoardThreadFactory . forName ( "de-duplication-node-test" ) ;
private final ScheduledExecutorService executorService = Executors . newSingleThreadScheduledExecutor ( factory ) ;
private final int deDuplicationInterval = 1 ;
private TenantId tenantId ;
@ -79,14 +89,10 @@ public class TbMsgDeDuplicateNodeTest {
private TbNodeConfiguration nodeConfiguration ;
private CountDownLatch awaitTellSelfLatch ;
private CountDownLatch awaitAllMsgsProcessedLatch ;
private int scheduleCount ;
@BeforeEach
public void init ( ) throws TbNodeException {
ctx = mock ( TbContext . class ) ;
awaitTellSelfLatch = new CountDownLatch ( 1 ) ;
awaitAllMsgsProcessedLatch = new CountDownLatch ( 1 ) ;
tenantId = TenantId . fromUUID ( UUID . randomUUID ( ) ) ;
RuleNodeId ruleNodeId = new RuleNodeId ( UUID . randomUUID ( ) ) ;
@ -101,28 +107,32 @@ public class TbMsgDeDuplicateNodeTest {
String data = ( String ) ( invocationOnMock . getArguments ( ) ) [ 4 ] ;
return TbMsg . newMsg ( type , originator , metaData . copy ( ) , data ) ;
} ) . when ( ctx ) . newMsg ( isNull ( ) , eq ( TB_MSG_DEDUPLICATION_TIMEOUT_MSG ) , nullable ( EntityId . class ) , any ( TbMsgMetaData . class ) , any ( String . class ) ) ;
node = spy ( new TbMsgDeDuplicateNode ( ) ) ;
config = new TbMsgDeDuplicateNodeConfiguration ( ) . defaultConfiguration ( ) ;
}
scheduleCount = 0 ;
private void invokeTellSelf ( int maxNumberOfInvocation , boolean incrementScheduleTimeout ) {
AtomicLong scheduleTimeout = new AtomicLong ( deDuplicationInterval ) ;
AtomicInteger scheduleCount = new AtomicInteger ( 0 ) ;
doAnswer ( ( Answer < Void > ) invocationOnMock - > {
scheduleCount + + ;
if ( scheduleCount = = 1 ) {
scheduleCount . getAndIncrement ( ) ;
if ( scheduleCount . get ( ) < = maxNumberOfInvocation ) {
TbMsg msg = ( TbMsg ) ( invocationOnMock . getArguments ( ) ) [ 0 ] ;
executorService . submit ( ( ) - > {
executorService . schedule ( ( ) - > {
try {
awaitAllMsgsProcessedLatch . await ( ) ;
node . onMsg ( ctx , msg ) ;
awaitTellSelfLatch . countDown ( ) ;
} catch ( ExecutionException | InterruptedException | TbNodeException e ) {
log . error ( "Failed to execute tellSelf method call due to: " , e ) ;
}
} ) ;
} , scheduleTimeout . get ( ) , TimeUnit . SECONDS ) ;
if ( incrementScheduleTimeout ) {
scheduleTimeout . set ( scheduleTimeout . get ( ) * 3 ) ;
}
}
return null ;
} ) . when ( ctx ) . tellSelf ( ArgumentMatchers . any ( TbMsg . class ) , ArgumentMatchers . anyLong ( ) ) ;
node = new TbMsgDeDuplicateNode ( ) ;
config = new TbMsgDeDuplicateNodeConfiguration ( ) . defaultConfiguration ( ) ;
}
@AfterEach
@ -132,170 +142,189 @@ public class TbMsgDeDuplicateNodeTest {
}
@Test
public void given_multipleMessages_thenVerifyOutputFirst ( ) throws TbNodeException , ExecutionException , InterruptedException {
public void given_100_messages_then_verifyOutputFirst ( ) throws TbNodeException , ExecutionException , InterruptedException {
int wantedNumberOfTellSelfInvocation = 2 ;
int msgCount = 100 ;
awaitTellSelfLatch = new CountDownLatch ( wantedNumberOfTellSelfInvocation ) ;
invokeTellSelf ( wantedNumberOfTellSelfInvocation , false ) ;
config . setDelay ( deDuplicationInterval ) ;
config . setMaxPendingMsgs ( msgCount ) ;
nodeConfiguration = new TbNodeConfiguration ( JacksonUtil . valueToTree ( config ) ) ;
node . init ( ctx , nodeConfiguration ) ;
DeviceId deviceId = new DeviceId ( UUID . randomUUID ( ) ) ;
List < TbMsg > inputMsgs = createTbMsgs ( deviceId ) ;
long currentTimeMillis = System . currentTimeMillis ( ) ;
List < TbMsg > inputMsgs = getTbMsgs ( deviceId , msgCount , currentTimeMillis , 500 ) ;
for ( TbMsg msg : inputMsgs ) {
node . onMsg ( ctx , msg ) ;
}
awaitAllMsgsProcessedLatch . countDown ( ) ;
awaitTellSelfLatch . await ( ) ;
verify ( ctx , times ( inputMsgs . size ( ) ) ) . ack ( any ( ) ) ;
ArgumentCaptor < TbMsg > newMsgCaptor = ArgumentCaptor . forClass ( TbMsg . class ) ;
ArgumentCaptor < Runnable > successCaptor = ArgumentCaptor . forClass ( Runnable . class ) ;
ArgumentCaptor < Consumer < Throwable > > failureCaptor = ArgumentCaptor . forClass ( Consumer . class ) ;
verify ( ctx , times ( 1 ) ) . enqueueForTellNext ( newMsgCaptor . capture ( ) , eq ( TbRelationTypes . SUCCESS ) , successCaptor . capture ( ) , failureCaptor . capture ( ) ) ;
Assertions . assertEquals ( inputMsgs . get ( 0 ) , newMsgCaptor . getValue ( ) ) ;
}
TbMsg msgToReject = createMsg ( deviceId , inputMsgs . get ( inputMsgs . size ( ) - 1 ) . getMetaDataTs ( ) + 2 ) ;
node . onMsg ( ctx , msgToReject ) ;
@Test
public void given_moreMessagesThenAllowedInConfiguration_thenVerifyTellFailure ( ) throws TbNodeException , ExecutionException , InterruptedException {
nodeConfiguration = new TbNodeConfiguration ( JacksonUtil . valueToTree ( config ) ) ;
node . init ( ctx , nodeConfiguration ) ;
DeviceId deviceId = new DeviceId ( UUID . randomUUID ( ) ) ;
List < TbMsg > inputMsgs = createTbMsgs ( deviceId , 101 ) ;
for ( TbMsg msg : inputMsgs ) {
node . onMsg ( ctx , msg ) ;
}
awaitAllMsgsProcessedLatch . countDown ( ) ;
awaitTellSelfLatch . await ( ) ;
verify ( ctx , times ( inputMsgs . size ( ) - 1 ) ) . ack ( any ( ) ) ;
verify ( ctx , times ( 1 ) ) . tellFailure ( eq ( inputMsgs . get ( inputMsgs . size ( ) - 1 ) ) , any ( ) ) ;
ArgumentCaptor < TbMsg > newMsgCaptor = ArgumentCaptor . forClass ( TbMsg . class ) ;
ArgumentCaptor < Runnable > successCaptor = ArgumentCaptor . forClass ( Runnable . class ) ;
ArgumentCaptor < Consumer < Throwable > > failureCaptor = ArgumentCaptor . forClass ( Consumer . class ) ;
verify ( ctx , times ( msgCount ) ) . ack ( any ( ) ) ;
verify ( ctx , times ( 1 ) ) . tellFailure ( eq ( msgToReject ) , any ( ) ) ;
verify ( node , times ( msgCount + wantedNumberOfTellSelfInvocation + 1 ) ) . onMsg ( eq ( ctx ) , any ( ) ) ;
verify ( ctx , times ( 1 ) ) . enqueueForTellNext ( newMsgCaptor . capture ( ) , eq ( TbRelationTypes . SUCCESS ) , successCaptor . capture ( ) , failureCaptor . capture ( ) ) ;
Assertions . assertEquals ( inputMsgs . get ( 0 ) , newMsgCaptor . getValue ( ) ) ;
}
@Test
public void given_multipleMessages_thenVerifyOutputLast ( ) throws TbNodeException , ExecutionException , InterruptedException {
public void given_100_messages_then_verifyOutputLast ( ) throws TbNodeException , ExecutionException , InterruptedException {
int wantedNumberOfTellSelfInvocation = 2 ;
int msgCount = 100 ;
awaitTellSelfLatch = new CountDownLatch ( wantedNumberOfTellSelfInvocation ) ;
invokeTellSelf ( wantedNumberOfTellSelfInvocation , false ) ;
config . setStrategy ( DeDuplicateStrategy . LAST ) ;
config . setDelay ( deDuplicationInterval ) ;
config . setMaxPendingMsgs ( msgCount ) ;
nodeConfiguration = new TbNodeConfiguration ( JacksonUtil . valueToTree ( config ) ) ;
node . init ( ctx , nodeConfiguration ) ;
DeviceId deviceId = new DeviceId ( UUID . randomUUID ( ) ) ;
List < TbMsg > inputMsgs = createTbMsgs ( deviceId ) ;
long currentTimeMillis = System . currentTimeMillis ( ) ;
List < TbMsg > inputMsgs = getTbMsgs ( deviceId , msgCount , currentTimeMillis , 500 ) ;
int indexOfLastMsgInArray = inputMsgs . size ( ) - 1 ;
int indexToSetMaxTs = new Random ( ) . nextInt ( indexOfLastMsgInArray ) + 1 ;
TbMsg currentMaxTsMsg = inputMsgs . get ( indexOfLastMsgInArray ) ;
TbMsg newLastMsgOfArray = inputMsgs . get ( indexToSetMaxTs ) ;
inputMsgs . set ( indexOfLastMsgInArray , newLastMsgOfArray ) ;
inputMsgs . set ( indexToSetMaxTs , currentMaxTsMsg ) ;
for ( TbMsg msg : inputMsgs ) {
node . onMsg ( ctx , msg ) ;
}
awaitAllMsgsProcessedLatch . countDown ( ) ;
TbMsg msgToReject = createMsg ( deviceId , inputMsgs . get ( indexOfLastMsgInArray ) . getMetaDataTs ( ) + 2 ) ;
node . onMsg ( ctx , msgToReject ) ;
awaitTellSelfLatch . await ( ) ;
verify ( ctx , times ( inputMsgs . size ( ) ) ) . ack ( any ( ) ) ;
ArgumentCaptor < TbMsg > newMsgCaptor = ArgumentCaptor . forClass ( TbMsg . class ) ;
ArgumentCaptor < Runnable > successCaptor = ArgumentCaptor . forClass ( Runnable . class ) ;
ArgumentCaptor < Consumer < Throwable > > failureCaptor = ArgumentCaptor . forClass ( Consumer . class ) ;
verify ( ctx , times ( msgCount ) ) . ack ( any ( ) ) ;
verify ( ctx , times ( 1 ) ) . tellFailure ( eq ( msgToReject ) , any ( ) ) ;
verify ( node , times ( msgCount + wantedNumberOfTellSelfInvocation + 1 ) ) . onMsg ( eq ( ctx ) , any ( ) ) ;
verify ( ctx , times ( 1 ) ) . enqueueForTellNext ( newMsgCaptor . capture ( ) , eq ( TbRelationTypes . SUCCESS ) , successCaptor . capture ( ) , failureCaptor . capture ( ) ) ;
Assertions . assertEquals ( inputMsgs . get ( inputMsgs . size ( ) - 1 ) , newMsgCaptor . getValue ( ) ) ;
Assertions . assertEquals ( currentMaxTsMsg , newMsgCaptor . getValue ( ) ) ;
}
@Test
public void given_multipleMessagesFromTwoOriginators_thenVerifyOutputAllForEachOriginator ( ) throws TbNodeException , ExecutionException , InterruptedException {
public void given_100_messages_then_verifyOutputAll ( ) throws TbNodeException , ExecutionException , InterruptedException {
int wantedNumberOfTellSelfInvocation = 2 ;
int msgCount = 100 ;
awaitTellSelfLatch = new CountDownLatch ( wantedNumberOfTellSelfInvocation ) ;
invokeTellSelf ( wantedNumberOfTellSelfInvocation , false ) ;
config . setDelay ( deDuplicationInterval ) ;
config . setMaxPendingMsgs ( msgCount ) ;
config . setStrategy ( DeDuplicateStrategy . ALL ) ;
nodeConfiguration = new TbNodeConfiguration ( JacksonUtil . valueToTree ( config ) ) ;
node . init ( ctx , nodeConfiguration ) ;
DeviceId firstDeviceId = new DeviceId ( UUID . randomUUID ( ) ) ;
List < TbMsg > firstDeviceInputMsgs = createTbMsgs ( firstDeviceId ) ;
DeviceId secondDeviceId = new DeviceId ( UUID . randomUUID ( ) ) ;
List < TbMsg > secondDeviceInputMsgs = createTbMsgs ( secondDeviceId ) ;
List < TbMsg > inputMsgs = new ArrayList < > ( ) ;
inputMsgs . addAll ( firstDeviceInputMsgs ) ;
inputMsgs . addAll ( secondDeviceInputMsgs ) ;
DeviceId deviceId = new DeviceId ( UUID . randomUUID ( ) ) ;
long currentTimeMillis = System . currentTimeMillis ( ) ;
List < TbMsg > inputMsgs = getTbMsgs ( deviceId , msgCount , currentTimeMillis , 500 ) ;
for ( TbMsg msg : inputMsgs ) {
node . onMsg ( ctx , msg ) ;
}
awaitAllMsgsProcessedLatch . countDown ( ) ;
awaitTellSelfLatch . await ( ) ;
verify ( ctx , times ( inputMsgs . size ( ) ) ) . ack ( any ( ) ) ;
ArgumentCaptor < TbMsg > newMsgCaptor = ArgumentCaptor . forClass ( TbMsg . class ) ;
ArgumentCaptor < Runnable > successCaptor = ArgumentCaptor . forClass ( Runnable . class ) ;
ArgumentCaptor < Consumer < Throwable > > failureCaptor = ArgumentCaptor . forClass ( Consumer . class ) ;
verify ( ctx , times ( 2 ) ) . enqueueForTellNext ( newMsgCaptor . capture ( ) , eq ( TbRelationTypes . SUCCESS ) , successCaptor . capture ( ) , failureCaptor . capture ( ) ) ;
List < TbMsg > outMessages = newMsgCaptor . getAllValues ( ) ;
Assertions . assertEquals ( 2 , outMessages . size ( ) ) ;
for ( TbMsg tbMsg : outMessages ) {
if ( tbMsg . getOriginator ( ) . equals ( firstDeviceId ) ) {
Assertions . assertEquals ( getMergedData ( firstDeviceInputMsgs ) , tbMsg . getData ( ) ) ;
} else {
Assertions . assertEquals ( getMergedData ( secondDeviceInputMsgs ) , tbMsg . getData ( ) ) ;
Assertions . assertEquals ( secondDeviceId , tbMsg . getOriginator ( ) ) ;
}
}
verify ( ctx , times ( msgCount ) ) . ack ( any ( ) ) ;
verify ( node , times ( msgCount + wantedNumberOfTellSelfInvocation ) ) . onMsg ( eq ( ctx ) , any ( ) ) ;
verify ( ctx , times ( 1 ) ) . enqueueForTellNext ( newMsgCaptor . capture ( ) , eq ( TbRelationTypes . SUCCESS ) , successCaptor . capture ( ) , failureCaptor . capture ( ) ) ;
Assertions . assertEquals ( 1 , newMsgCaptor . getAllValues ( ) . size ( ) ) ;
TbMsg outMessage = newMsgCaptor . getAllValues ( ) . get ( 0 ) ;
Assertions . assertEquals ( getMergedData ( inputMsgs ) , outMessage . getData ( ) ) ;
Assertions . assertEquals ( deviceId , outMessage . getOriginator ( ) ) ;
}
@Test
public void given_multipleMessagesFromTwoOriginators_thenVerifyOutputAll ( ) throws TbNodeException , ExecutionException , InterruptedException {
public void given_100_messages_then_verifyOutput_2_packs ( ) throws TbNodeException , ExecutionException , InterruptedException {
int wantedNumberOfTellSelfInvocation = 2 ;
int msgCount = 100 ;
awaitTellSelfLatch = new CountDownLatch ( wantedNumberOfTellSelfInvocation ) ;
invokeTellSelf ( wantedNumberOfTellSelfInvocation , true ) ;
config . setDelay ( deDuplicationInterval ) ;
config . setMaxPendingMsgs ( msgCount ) ;
config . setStrategy ( DeDuplicateStrategy . ALL ) ;
config . setDeDuplicateByOriginator ( false ) ;
config . setQueueName ( HIGH_PRIORITY_QUEUE_NAME ) ;
config . setOutMsgType ( SessionMsgType . POST_ATTRIBUTES_REQUEST . name ( ) ) ;
nodeConfiguration = new TbNodeConfiguration ( JacksonUtil . valueToTree ( config ) ) ;
node . init ( ctx , nodeConfiguration ) ;
DeviceId firstDeviceId = new DeviceId ( UUID . randomUUID ( ) ) ;
List < TbMsg > firstDeviceInputMsgs = createTbMsgs ( firstDeviceId , 50 ) ;
DeviceId secondDeviceId = new DeviceId ( UUID . randomUUID ( ) ) ;
List < TbMsg > secondDeviceInputMsgs = createTbMsgs ( secondDeviceId , 50 ) ;
DeviceId deviceId = new DeviceId ( UUID . randomUUID ( ) ) ;
long currentTimeMillis = System . currentTimeMillis ( ) ;
List < TbMsg > inputMsgs = new ArrayList < > ( ) ;
inputMsgs . addAll ( firstDeviceInputMsgs ) ;
inputMsgs . addAll ( secondDeviceInputMsgs ) ;
List < TbMsg > firstMsgPack = getTbMsgs ( deviceId , msgCount / 2 , currentTimeMillis , 500 ) ;
for ( TbMsg msg : firstMsgPack ) {
node . onMsg ( ctx , msg ) ;
}
TbMsg firstMsgFromFirstPack = firstMsgPack . get ( 0 ) ;
long firstPackDeDuplicationPackEndTs = firstMsgFromFirstPack . getMetaDataTs ( ) + TimeUnit . SECONDS . toMillis ( deDuplicationInterval ) ;
for ( TbMsg msg : inputMsgs ) {
List < TbMsg > secondMsgPack = getTbMsgs ( deviceId , msgCount / 2 , firstPackDeDuplicationPackEndTs , 500 ) ;
for ( TbMsg msg : secondMsgPack ) {
node . onMsg ( ctx , msg ) ;
}
awaitAllMsgsProcessedLatch . countDown ( ) ;
awaitTellSelfLatch . await ( ) ;
verify ( ctx , times ( inputMsgs . size ( ) ) ) . ack ( any ( ) ) ;
ArgumentCaptor < TbMsg > newMsgCaptor = ArgumentCaptor . forClass ( TbMsg . class ) ;
ArgumentCaptor < Runnable > successCaptor = ArgumentCaptor . forClass ( Runnable . class ) ;
ArgumentCaptor < Consumer < Throwable > > failureCaptor = ArgumentCaptor . forClass ( Consumer . class ) ;
verify ( ctx , times ( 1 ) ) . enqueueForTellNext ( newMsgCaptor . capture ( ) , eq ( TbRelationTypes . SUCCESS ) , successCaptor . capture ( ) , failureCaptor . capture ( ) ) ;
Assertions . assertEquals ( 1 , newMsgCaptor . getAllValues ( ) . size ( ) ) ;
TbMsg outMessage = newMsgCaptor . getAllValues ( ) . get ( 0 ) ;
Assertions . assertEquals ( getMergedData ( inputMsgs ) , outMessage . getData ( ) ) ;
Assertions . assertEquals ( tenantId , outMessage . getOriginator ( ) ) ;
Assertions . assertEquals ( config . getQueueName ( ) , outMessage . getQueueName ( ) ) ;
Assertions . assertEquals ( config . getOutMsgType ( ) , outMessage . getType ( ) ) ;
}
verify ( ctx , times ( msgCount ) ) . ack ( any ( ) ) ;
verify ( node , times ( msgCount + wantedNumberOfTellSelfInvocation ) ) . onMsg ( eq ( ctx ) , any ( ) ) ;
verify ( ctx , times ( 2 ) ) . enqueueForTellNext ( newMsgCaptor . capture ( ) , eq ( TbRelationTypes . SUCCESS ) , successCaptor . capture ( ) , failureCaptor . capture ( ) ) ;
private List < TbMsg > createTbMsgs ( DeviceId deviceId ) {
return createTbMsgs ( deviceId , 100 ) ;
Assertions . assertEquals ( 2 , newMsgCaptor . getAllValues ( ) . size ( ) ) ;
Assertions . assertEquals ( getMergedData ( firstMsgPack ) , newMsgCaptor . getAllValues ( ) . get ( 0 ) . getData ( ) ) ;
Assertions . assertEquals ( getMergedData ( secondMsgPack ) , newMsgCaptor . getAllValues ( ) . get ( 1 ) . getData ( ) ) ;
}
private List < TbMsg > create TbMsgs( DeviceId deviceId , int msgCount ) {
private List < TbMsg > get TbMsgs( DeviceId deviceId , int msgCount , long currentTimeMillis , int initTsStep ) {
List < TbMsg > inputMsgs = new ArrayList < > ( ) ;
var ts = currentTimeMillis + initTsStep ;
for ( int i = 0 ; i < msgCount ; i + + ) {
ObjectNode dataNode = JacksonUtil . newObjectNode ( ) ;
dataNode . put ( "msgId" , i ) ;
dataNode . put ( "deviceId" , deviceId . getId ( ) . toString ( ) ) ;
TbMsg tbMsg = TbMsg . newMsg (
MAIN_QUEUE_NAME ,
SessionMsgType . POST_TELEMETRY_REQUEST . name ( ) ,
deviceId ,
new TbMsgMetaData ( ) ,
JacksonUtil . toString ( dataNode ) ) ;
inputMsgs . add ( tbMsg ) ;
inputMsgs . add ( createMsg ( deviceId , ts ) ) ;
ts + = 2 ;
}
return inputMsgs ;
}
private TbMsg createMsg ( DeviceId deviceId , long ts ) {
ObjectNode dataNode = JacksonUtil . newObjectNode ( ) ;
dataNode . put ( "deviceId" , deviceId . getId ( ) . toString ( ) ) ;
TbMsgMetaData metaData = new TbMsgMetaData ( ) ;
metaData . putValue ( "ts" , String . valueOf ( ts ) ) ;
return TbMsg . newMsg (
MAIN_QUEUE_NAME ,
SessionMsgType . POST_TELEMETRY_REQUEST . name ( ) ,
deviceId ,
metaData ,
JacksonUtil . toString ( dataNode ) ) ;
}
private String getMergedData ( List < TbMsg > msgs ) {
ArrayNode mergedData = JacksonUtil . OBJECT_MAPPER . createArrayNode ( ) ;
msgs . forEach ( msg - > {