@ -36,7 +36,9 @@ import org.thingsboard.server.service.ws.WebSocketService;
import org.thingsboard.server.service.ws.WebSocketSessionRef ;
import org.thingsboard.server.service.ws.telemetry.cmd.v2.AlarmCountUpdate ;
import java.util.List ;
import java.util.LinkedHashSet ;
import java.util.Map ;
import java.util.concurrent.ConcurrentHashMap ;
@Slf4j
@ToString ( callSuper = true )
@ -44,56 +46,120 @@ public class TbAlarmCountSubCtx extends TbAbstractEntityQuerySubCtx<AlarmCountQu
private final AlarmService alarmService ;
protected final Map < Integer , EntityId > subToEntityIdMap ;
private LinkedHashSet < EntityId > entitiesIds ;
private final int maxEntitiesPerAlarmSubscription ;
private final int maxAlarmQueriesPerRefreshInterval ;
@Getter
@Setter
private volatile int result ;
@Getter
@Setter
private boolean tooManyEntities ;
private int alarmCountInvocationAttempts ;
public TbAlarmCountSubCtx ( String serviceId , WebSocketService wsService ,
EntityService entityService , TbLocalSubscriptionService localSubscriptionService ,
AttributesService attributesService , SubscriptionServiceStatistics stats , AlarmService alarmService ,
WebSocketSessionRef sessionRef , int cmdId , int maxEntitiesPerAlarmSubscription ) {
WebSocketSessionRef sessionRef , int cmdId , int maxEntitiesPerAlarmSubscription , int maxAlarmQueriesPerRefreshInterval ) {
super ( serviceId , wsService , entityService , localSubscriptionService , attributesService , stats , sessionRef , cmdId ) ;
this . alarmService = alarmService ;
this . subToEntityIdMap = new ConcurrentHashMap < > ( ) ;
this . maxEntitiesPerAlarmSubscription = maxEntitiesPerAlarmSubscription ;
this . maxAlarmQueriesPerRefreshInterval = maxAlarmQueriesPerRefreshInterval ;
this . entitiesIds = null ;
}
@Override
public void clearSubscriptions ( ) {
clearAlarmSubscriptions ( ) ;
}
@Override
public void fetchData ( ) {
result = countAlarms ( ) ;
sendWsMsg ( new AlarmCountUpdate ( cmdId , result ) ) ;
resetInvocationCounter ( ) ;
if ( query . getEntityFilter ( ) ! = null ) {
entitiesIds = new LinkedHashSet < > ( ) ;
log . trace ( "[{}] Fetching data: {}" , cmdId , alarmCountInvocationAttempts ) ;
PageData < EntityData > data = entityService . findEntityDataByQuery ( getTenantId ( ) , getCustomerId ( ) , buildEntityDataQuery ( ) ) ;
entitiesIds . clear ( ) ;
tooManyEntities = data . hasNext ( ) ;
for ( EntityData entityData : data . getData ( ) ) {
entitiesIds . add ( entityData . getEntityId ( ) ) ;
}
}
}
@Override
protected void update ( ) {
int newCount = countAlarms ( ) ;
if ( newCount ! = result ) {
result = newCount ;
sendWsMsg ( new AlarmCountUpdate ( cmdId , result ) ) ;
resetInvocationCounter ( ) ;
fetchAlarmCount ( ) ;
}
public void fetchAlarmCount ( ) {
alarmCountInvocationAttempts + + ;
log . trace ( "[{}] Fetching alarms: {}" , cmdId , alarmCountInvocationAttempts ) ;
if ( alarmCountInvocationAttempts < = maxAlarmQueriesPerRefreshInterval ) {
doFetchAlarmCount ( ) ;
} else {
log . trace ( "[{}] Ignore alarm count fetch due to rate limit: [{}] of maximum [{}]" , cmdId , alarmCountInvocationAttempts , maxAlarmQueriesPerRefreshInterval ) ;
}
}
private void doFetchAlarmCount ( ) {
result = ( int ) alarmService . countAlarmsByQuery ( getTenantId ( ) , getCustomerId ( ) , query , entitiesIds ) ;
sendWsMsg ( new AlarmCountUpdate ( cmdId , result ) ) ;
}
@Override
public boolean isDynamic ( ) {
return true ;
}
private int countAlarms ( ) {
List < EntityId > entityIds = null ;
if ( query . getEntityFilter ( ) ! = null ) {
PageData < EntityData > data = entityService . findEntityDataByQuery ( getTenantId ( ) , getCustomerId ( ) , buildEntityDataQuery ( ) ) ;
if ( data . getData ( ) . isEmpty ( ) ) {
return 0 ;
}
entityIds = data . getData ( ) . stream ( ) . map ( EntityData : : getEntityId ) . toList ( ) ;
}
return ( int ) alarmService . countAlarmsByQuery ( getTenantId ( ) , getCustomerId ( ) , query , entityIds ) ;
}
private EntityDataQuery buildEntityDataQuery ( ) {
EntityDataPageLink edpl = new EntityDataPageLink ( maxEntitiesPerAlarmSubscription , 0 , null ,
new EntityDataSortOrder ( new EntityKey ( EntityKeyType . ENTITY_FIELD , ModelConstants . CREATED_TIME_PROPERTY ) ) ) ;
return new EntityDataQuery ( query . getEntityFilter ( ) , edpl , null , null , query . getKeyFilters ( ) ) ;
}
private void resetInvocationCounter ( ) {
alarmCountInvocationAttempts = 0 ;
}
public void createAlarmSubscriptions ( ) {
for ( EntityId entityId : entitiesIds ) {
createAlarmSubscriptionForEntity ( entityId ) ;
}
}
private void createAlarmSubscriptionForEntity ( EntityId entityId ) {
int subIdx = sessionRef . getSessionSubIdSeq ( ) . incrementAndGet ( ) ;
subToEntityIdMap . put ( subIdx , entityId ) ;
log . trace ( "[{}][{}][{}] Creating alarms subscription for [{}] " , serviceId , cmdId , subIdx , entityId ) ;
TbAlarmsSubscription subscription = TbAlarmsSubscription . builder ( )
. serviceId ( serviceId )
. sessionId ( sessionRef . getSessionId ( ) )
. subscriptionId ( subIdx )
. tenantId ( sessionRef . getSecurityCtx ( ) . getTenantId ( ) )
. entityId ( entityId )
. updateProcessor ( ( sub , update ) - > fetchAlarmCount ( ) )
. build ( ) ;
localSubscriptionService . addSubscription ( subscription , sessionRef ) ;
}
public void clearAlarmSubscriptions ( ) {
if ( subToEntityIdMap ! = null ) {
for ( Integer subId : subToEntityIdMap . keySet ( ) ) {
localSubscriptionService . cancelSubscription ( getTenantId ( ) , getSessionId ( ) , subId ) ;
}
subToEntityIdMap . clear ( ) ;
}
}
}