|
|
|
@ -15,6 +15,7 @@ |
|
|
|
*/ |
|
|
|
package org.thingsboard.server.transport.snmp.service; |
|
|
|
|
|
|
|
import com.google.gson.JsonElement; |
|
|
|
import com.google.gson.JsonObject; |
|
|
|
import lombok.Data; |
|
|
|
import lombok.Getter; |
|
|
|
@ -35,11 +36,12 @@ import org.springframework.beans.factory.annotation.Value; |
|
|
|
import org.springframework.stereotype.Service; |
|
|
|
import org.thingsboard.common.util.ThingsBoardThreadFactory; |
|
|
|
import org.thingsboard.server.common.data.TbTransportService; |
|
|
|
import org.thingsboard.server.common.data.id.DeviceProfileId; |
|
|
|
import org.thingsboard.server.common.data.kv.DataType; |
|
|
|
import org.thingsboard.server.common.data.transport.snmp.SnmpCommunicationSpec; |
|
|
|
import org.thingsboard.server.common.data.transport.snmp.SnmpMapping; |
|
|
|
import org.thingsboard.server.common.data.transport.snmp.SnmpMethod; |
|
|
|
import org.thingsboard.server.common.data.transport.snmp.config.RepeatingQueryingSnmpCommunicationConfig; |
|
|
|
import org.thingsboard.server.common.data.transport.snmp.config.SnmpCommunicationConfig; |
|
|
|
import org.thingsboard.server.common.data.transport.snmp.config.impl.ToDeviceRpcResponseQueryingSnmpCommunicationConfig; |
|
|
|
import org.thingsboard.server.common.transport.TransportService; |
|
|
|
import org.thingsboard.server.common.transport.TransportServiceCallback; |
|
|
|
import org.thingsboard.server.common.transport.adaptor.JsonConverter; |
|
|
|
@ -50,10 +52,12 @@ import org.thingsboard.server.transport.snmp.session.DeviceSessionContext; |
|
|
|
import javax.annotation.PostConstruct; |
|
|
|
import javax.annotation.PreDestroy; |
|
|
|
import java.io.IOException; |
|
|
|
import java.util.Arrays; |
|
|
|
import java.util.Collections; |
|
|
|
import java.util.EnumMap; |
|
|
|
import java.util.List; |
|
|
|
import java.util.Map; |
|
|
|
import java.util.Optional; |
|
|
|
import java.util.concurrent.ExecutorService; |
|
|
|
import java.util.concurrent.Executors; |
|
|
|
import java.util.concurrent.ScheduledExecutorService; |
|
|
|
@ -67,13 +71,14 @@ import java.util.stream.Collectors; |
|
|
|
@RequiredArgsConstructor |
|
|
|
public class SnmpTransportService implements TbTransportService { |
|
|
|
private final TransportService transportService; |
|
|
|
private final PduMapper pduMapper; |
|
|
|
private final PduService pduService; |
|
|
|
|
|
|
|
@Getter |
|
|
|
private Snmp snmp; |
|
|
|
private ScheduledExecutorService queryingExecutor; |
|
|
|
private ExecutorService responseProcessingExecutor; |
|
|
|
|
|
|
|
private final Map<SnmpCommunicationSpec, ResponseDataMapper> responseDataMappers = new EnumMap<>(SnmpCommunicationSpec.class); |
|
|
|
private final Map<SnmpCommunicationSpec, ResponseProcessor> responseProcessors = new EnumMap<>(SnmpCommunicationSpec.class); |
|
|
|
|
|
|
|
@Value("${transport.snmp.response_processing.parallelism_level}") |
|
|
|
@ -87,6 +92,7 @@ public class SnmpTransportService implements TbTransportService { |
|
|
|
responseProcessingExecutor = Executors.newWorkStealingPool(responseProcessingParallelismLevel); |
|
|
|
|
|
|
|
initializeSnmp(); |
|
|
|
configureResponseDataMappers(); |
|
|
|
configureResponseProcessors(); |
|
|
|
|
|
|
|
log.info("SNMP transport service initialized"); |
|
|
|
@ -138,16 +144,19 @@ public class SnmpTransportService implements TbTransportService { |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public void sendRequest(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig) { |
|
|
|
private void sendRequest(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig) { |
|
|
|
sendRequest(sessionContext, communicationConfig, Collections.emptyMap()); |
|
|
|
} |
|
|
|
|
|
|
|
public void sendRequest(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig, Map<String, String> values) { |
|
|
|
PDU request = pduMapper.createPdu(sessionContext, communicationConfig, values); |
|
|
|
private void sendRequest(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig, Map<String, String> values) { |
|
|
|
PDU request = pduService.createPdu(sessionContext, communicationConfig, values); |
|
|
|
RequestInfo requestInfo = new RequestInfo(communicationConfig.getSpec(), communicationConfig.getAllMappings()); |
|
|
|
sendRequest(sessionContext, request, requestInfo); |
|
|
|
} |
|
|
|
|
|
|
|
private void sendRequest(DeviceSessionContext sessionContext, PDU request, RequestInfo requestInfo) { |
|
|
|
if (request.size() > 0) { |
|
|
|
log.trace("Executing SNMP request for device {}. Variables bindings: {}", sessionContext.getDeviceId(), request.getVariableBindings()); |
|
|
|
RequestInfo requestInfo = new RequestInfo(sessionContext.getDeviceProfile().getId(), communicationConfig); |
|
|
|
try { |
|
|
|
snmp.send(request, sessionContext.getTarget(), requestInfo, sessionContext); |
|
|
|
} catch (IOException e) { |
|
|
|
@ -156,6 +165,39 @@ public class SnmpTransportService implements TbTransportService { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public void onAttributeUpdate(DeviceSessionContext sessionContext, TransportProtos.AttributeUpdateNotificationMsg attributeUpdateNotification) { |
|
|
|
sessionContext.getProfileTransportConfiguration().getCommunicationConfigs().stream() |
|
|
|
.filter(config -> config.getSpec() == SnmpCommunicationSpec.SHARED_ATTRIBUTES_SETTING) |
|
|
|
.findFirst() |
|
|
|
.ifPresent(communicationConfig -> { |
|
|
|
Map<String, String> sharedAttributes = JsonConverter.toJson(attributeUpdateNotification).entrySet().stream() |
|
|
|
.collect(Collectors.toMap( |
|
|
|
Map.Entry::getKey, |
|
|
|
entry -> entry.getValue().isJsonPrimitive() ? entry.getValue().getAsString() : entry.getValue().toString() |
|
|
|
)); |
|
|
|
sendRequest(sessionContext, communicationConfig, sharedAttributes); |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
public void onToDeviceRpcRequest(DeviceSessionContext sessionContext, TransportProtos.ToDeviceRpcRequestMsg toDeviceRpcRequestMsg) { |
|
|
|
SnmpMethod snmpMethod = SnmpMethod.valueOf(toDeviceRpcRequestMsg.getMethodName()); |
|
|
|
JsonObject params = JsonConverter.parse(toDeviceRpcRequestMsg.getParams()).getAsJsonObject(); |
|
|
|
|
|
|
|
String oid = Optional.ofNullable(params.get("oid")).map(JsonElement::getAsString).orElse(null); |
|
|
|
String value = Optional.ofNullable(params.get("value")).map(JsonElement::getAsString).orElse(null); |
|
|
|
DataType dataType = Optional.ofNullable(params.get("dataType")).map(e -> DataType.valueOf(e.getAsString())).orElse(DataType.STRING); |
|
|
|
|
|
|
|
if (oid == null || oid.isEmpty()) { |
|
|
|
throw new IllegalArgumentException("OID in to-device RPC request is not specified"); |
|
|
|
} |
|
|
|
if (value == null && snmpMethod == SnmpMethod.SET) { |
|
|
|
throw new IllegalArgumentException("Value must be specified for SNMP method 'SET'"); |
|
|
|
} |
|
|
|
|
|
|
|
PDU request = pduService.createSingleVariablePdu(sessionContext, snmpMethod, oid, value, dataType); |
|
|
|
sendRequest(sessionContext, request, new RequestInfo(toDeviceRpcRequestMsg.getRequestId(), SnmpCommunicationSpec.TO_DEVICE_RPC_REQUEST)); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public void processResponseEvent(DeviceSessionContext sessionContext, ResponseEvent event) { |
|
|
|
((Snmp) event.getSource()).cancel(event.getRequest(), sessionContext); |
|
|
|
@ -178,47 +220,59 @@ public class SnmpTransportService implements TbTransportService { |
|
|
|
} |
|
|
|
|
|
|
|
private void processResponse(DeviceSessionContext sessionContext, PDU response, RequestInfo requestInfo) { |
|
|
|
ResponseProcessor responseProcessor = responseProcessors.get(requestInfo.getCommunicationConfig().getSpec()); |
|
|
|
if (responseProcessor == null) { |
|
|
|
return; |
|
|
|
} |
|
|
|
ResponseProcessor responseProcessor = responseProcessors.get(requestInfo.getCommunicationSpec()); |
|
|
|
if (responseProcessor == null) return; |
|
|
|
|
|
|
|
JsonObject responseData = responseDataMappers.get(requestInfo.getCommunicationSpec()).map(response, requestInfo); |
|
|
|
|
|
|
|
JsonObject responseData = pduMapper.processPdu(response, sessionContext, requestInfo.getCommunicationConfig()); |
|
|
|
if (responseData.entrySet().isEmpty()) { |
|
|
|
log.debug("No values is the SNMP response for device {}. Request id: {}", sessionContext.getDeviceId(), response.getRequestID()); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
responseProcessor.process(responseData, sessionContext); |
|
|
|
responseProcessor.process(responseData, requestInfo, sessionContext); |
|
|
|
reportActivity(sessionContext.getSessionInfo()); |
|
|
|
} |
|
|
|
|
|
|
|
private void configureResponseDataMappers() { |
|
|
|
responseDataMappers.put(SnmpCommunicationSpec.TO_DEVICE_RPC_REQUEST, (pdu, requestInfo) -> { |
|
|
|
JsonObject responseData = new JsonObject(); |
|
|
|
pduService.processPdu(pdu).forEach((oid, value) -> { |
|
|
|
responseData.addProperty(oid.toDottedString(), value); |
|
|
|
}); |
|
|
|
return responseData; |
|
|
|
}); |
|
|
|
|
|
|
|
ResponseDataMapper defaultResponseDataMapper = (pdu, requestInfo) -> { |
|
|
|
return pduService.processPdu(pdu, requestInfo.getResponseMappings()); |
|
|
|
}; |
|
|
|
Arrays.stream(SnmpCommunicationSpec.values()) |
|
|
|
.forEach(communicationSpec -> { |
|
|
|
responseDataMappers.putIfAbsent(communicationSpec, defaultResponseDataMapper); |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
private void configureResponseProcessors() { |
|
|
|
responseProcessors.put(SnmpCommunicationSpec.TELEMETRY_QUERYING, (response, sessionContext) -> { |
|
|
|
TransportProtos.PostTelemetryMsg postTelemetryMsg = JsonConverter.convertToTelemetryProto(response); |
|
|
|
responseProcessors.put(SnmpCommunicationSpec.TELEMETRY_QUERYING, (responseData, requestInfo, sessionContext) -> { |
|
|
|
TransportProtos.PostTelemetryMsg postTelemetryMsg = JsonConverter.convertToTelemetryProto(responseData); |
|
|
|
transportService.process(sessionContext.getSessionInfo(), postTelemetryMsg, null); |
|
|
|
log.debug("Posted telemetry for SNMP device {}: {}", sessionContext.getDeviceId(), response); |
|
|
|
log.debug("Posted telemetry for SNMP device {}: {}", sessionContext.getDeviceId(), responseData); |
|
|
|
}); |
|
|
|
|
|
|
|
responseProcessors.put(SnmpCommunicationSpec.CLIENT_ATTRIBUTES_QUERYING, (response, sessionContext) -> { |
|
|
|
TransportProtos.PostAttributeMsg postAttributesMsg = JsonConverter.convertToAttributesProto(response); |
|
|
|
responseProcessors.put(SnmpCommunicationSpec.CLIENT_ATTRIBUTES_QUERYING, (responseData, requestInfo, sessionContext) -> { |
|
|
|
TransportProtos.PostAttributeMsg postAttributesMsg = JsonConverter.convertToAttributesProto(responseData); |
|
|
|
transportService.process(sessionContext.getSessionInfo(), postAttributesMsg, null); |
|
|
|
log.debug("Posted attributes for SNMP device {}: {}", sessionContext.getDeviceId(), response); |
|
|
|
log.debug("Posted attributes for SNMP device {}: {}", sessionContext.getDeviceId(), responseData); |
|
|
|
}); |
|
|
|
|
|
|
|
responseProcessors.put(SnmpCommunicationSpec.TO_DEVICE_RPC_RESPONSE_QUERYING, (response, sessionContext) -> { |
|
|
|
String rpcResponse = response.get(ToDeviceRpcResponseQueryingSnmpCommunicationConfig.RPC_RESPONSE_KEY_NAME).getAsString(); |
|
|
|
responseProcessors.put(SnmpCommunicationSpec.TO_DEVICE_RPC_REQUEST, (responseData, requestInfo, sessionContext) -> { |
|
|
|
TransportProtos.ToDeviceRpcResponseMsg rpcResponseMsg = TransportProtos.ToDeviceRpcResponseMsg.newBuilder() |
|
|
|
.setPayload(rpcResponse) |
|
|
|
.setRequestId(requestInfo.getRequestId()) |
|
|
|
.setPayload(JsonConverter.toJson(responseData)) |
|
|
|
.build(); |
|
|
|
transportService.process(sessionContext.getSessionInfo(), rpcResponseMsg, null); |
|
|
|
log.debug("Processed RPC response from device {}: {}", sessionContext.getDeviceId(), rpcResponse); |
|
|
|
log.debug("Posted RPC response {} for device {}", responseData, sessionContext.getDeviceId()); |
|
|
|
}); |
|
|
|
|
|
|
|
// responseProcessors.put(, (response, sessionContext) -> {
|
|
|
|
// TransportProtos.ClaimDeviceMsg claimDeviceMsg = JsonConverter.convertToClaimDeviceProto(sessionContext.getDeviceId(), response);
|
|
|
|
// transportService.process(sessionContext.getSessionInfo(), claimDeviceMsg, null);
|
|
|
|
// });
|
|
|
|
} |
|
|
|
|
|
|
|
private void reportActivity(TransportProtos.SessionInfoProto sessionInfo) { |
|
|
|
@ -256,12 +310,31 @@ public class SnmpTransportService implements TbTransportService { |
|
|
|
|
|
|
|
@Data |
|
|
|
private static class RequestInfo { |
|
|
|
private final DeviceProfileId deviceProfileId; |
|
|
|
private final SnmpCommunicationConfig communicationConfig; |
|
|
|
private Integer requestId; |
|
|
|
private SnmpCommunicationSpec communicationSpec; |
|
|
|
private List<SnmpMapping> responseMappings; |
|
|
|
|
|
|
|
public RequestInfo(Integer requestId, SnmpCommunicationSpec communicationSpec) { |
|
|
|
this.requestId = requestId; |
|
|
|
this.communicationSpec = communicationSpec; |
|
|
|
} |
|
|
|
|
|
|
|
public RequestInfo(SnmpCommunicationSpec communicationSpec) { |
|
|
|
this.communicationSpec = communicationSpec; |
|
|
|
} |
|
|
|
|
|
|
|
public RequestInfo(SnmpCommunicationSpec communicationSpec, List<SnmpMapping> responseMappings) { |
|
|
|
this.communicationSpec = communicationSpec; |
|
|
|
this.responseMappings = responseMappings; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
private interface ResponseDataMapper { |
|
|
|
JsonObject map(PDU pdu, RequestInfo requestInfo); |
|
|
|
} |
|
|
|
|
|
|
|
private interface ResponseProcessor { |
|
|
|
void process(JsonObject responseData, DeviceSessionContext sessionContext); |
|
|
|
void process(JsonObject responseData, RequestInfo requestInfo, DeviceSessionContext sessionContext); |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|