Option to ignore SNMP response type cast errors
This commit is contained in:
parent
09b866c41f
commit
854e059435
@ -22,6 +22,7 @@ import com.google.gson.JsonPrimitive;
|
||||
import lombok.Data;
|
||||
import lombok.SneakyThrows;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.core.context.SecurityContext;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
@ -57,7 +58,7 @@ import org.thingsboard.server.service.security.permission.Operation;
|
||||
import org.thingsboard.server.service.security.permission.Resource;
|
||||
import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
|
||||
import org.thingsboard.server.utils.CsvUtils;
|
||||
import org.thingsboard.server.utils.TypeCastUtil;
|
||||
import org.thingsboard.server.common.data.util.TypeCastUtil;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.PostConstruct;
|
||||
@ -269,7 +270,7 @@ public abstract class AbstractBulkImportService<E extends HasId<? extends Entity
|
||||
if (!entry.getKey().getType().isKv()) {
|
||||
entityData.getFields().put(entry.getKey().getType(), entry.getValue());
|
||||
} else {
|
||||
Map.Entry<DataType, Object> castResult = TypeCastUtil.castValue(entry.getValue());
|
||||
Pair<DataType, Object> castResult = TypeCastUtil.castValue(entry.getValue());
|
||||
entityData.getKvs().put(entry.getKey(), new ParsedValue(castResult.getValue(), castResult.getKey()));
|
||||
}
|
||||
});
|
||||
|
||||
@ -975,6 +975,8 @@ transport:
|
||||
# to configure SNMP to work over UDP or TCP
|
||||
underlying_protocol: "${SNMP_UNDERLYING_PROTOCOL:udp}"
|
||||
max_request_oids: "${SNMP_MAX_REQUEST_OIDS:100}"
|
||||
response:
|
||||
ignore_type_cast_errors: "${SNMP_RESPONSE_IGNORE_TYPE_CAST_ERRORS:false}"
|
||||
stats:
|
||||
enabled: "${TB_TRANSPORT_STATS_ENABLED:true}"
|
||||
print-interval-ms: "${TB_TRANSPORT_STATS_PRINT_INTERVAL_MS:60000}"
|
||||
|
||||
@ -163,6 +163,15 @@ public class StringUtils {
|
||||
return false;
|
||||
}
|
||||
|
||||
public static boolean equalsAnyIgnoreCase(String string, String... otherStrings) {
|
||||
for (String otherString : otherStrings) {
|
||||
if (equalsIgnoreCase(string, otherString)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static String substringAfterLast(String str, String sep) {
|
||||
return org.apache.commons.lang3.StringUtils.substringAfterLast(str, sep);
|
||||
}
|
||||
|
||||
@ -13,35 +13,53 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.thingsboard.server.utils;
|
||||
package org.thingsboard.server.common.data.util;
|
||||
|
||||
import org.apache.commons.lang3.math.NumberUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.thingsboard.server.common.data.kv.DataType;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Map;
|
||||
|
||||
public class TypeCastUtil {
|
||||
|
||||
private TypeCastUtil() {}
|
||||
|
||||
public static Map.Entry<DataType, Object> castValue(String value) {
|
||||
public static Pair<DataType, Object> castValue(String value) {
|
||||
if (isNumber(value)) {
|
||||
String formattedValue = value.replace(',', '.');
|
||||
try {
|
||||
BigDecimal bd = new BigDecimal(formattedValue);
|
||||
if (bd.stripTrailingZeros().scale() > 0 || isSimpleDouble(formattedValue)) {
|
||||
if (bd.scale() <= 16) {
|
||||
return Map.entry(DataType.DOUBLE, bd.doubleValue());
|
||||
return Pair.of(DataType.DOUBLE, bd.doubleValue());
|
||||
}
|
||||
} else {
|
||||
return Map.entry(DataType.LONG, bd.longValueExact());
|
||||
return Pair.of(DataType.LONG, bd.longValueExact());
|
||||
}
|
||||
} catch (RuntimeException ignored) {}
|
||||
} else if (value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false")) {
|
||||
return Map.entry(DataType.BOOLEAN, Boolean.parseBoolean(value));
|
||||
return Pair.of(DataType.BOOLEAN, Boolean.parseBoolean(value));
|
||||
}
|
||||
return Pair.of(DataType.STRING, value);
|
||||
}
|
||||
|
||||
public static Pair<DataType, Number> castToNumber(String value) {
|
||||
if (isNumber(value)) {
|
||||
String formattedValue = value.replace(',', '.');
|
||||
BigDecimal bd = new BigDecimal(formattedValue);
|
||||
if (bd.stripTrailingZeros().scale() > 0 || isSimpleDouble(formattedValue)) {
|
||||
if (bd.scale() <= 16) {
|
||||
return Pair.of(DataType.DOUBLE, bd.doubleValue());
|
||||
} else {
|
||||
return Pair.of(DataType.DOUBLE, bd);
|
||||
}
|
||||
} else {
|
||||
return Pair.of(DataType.LONG, bd.longValueExact());
|
||||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("'" + value + "' can't be parsed as number");
|
||||
}
|
||||
return Map.entry(DataType.STRING, value);
|
||||
}
|
||||
|
||||
private static boolean isNumber(String value) {
|
||||
@ -17,6 +17,7 @@ package org.thingsboard.server.transport.snmp.service;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.gson.JsonObject;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.snmp4j.PDU;
|
||||
import org.snmp4j.ScopedPDU;
|
||||
@ -28,12 +29,14 @@ import org.snmp4j.smi.Variable;
|
||||
import org.snmp4j.smi.VariableBinding;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.thingsboard.server.common.data.StringUtils;
|
||||
import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
|
||||
import org.thingsboard.server.common.data.kv.DataType;
|
||||
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.SnmpProtocolVersion;
|
||||
import org.thingsboard.server.common.data.transport.snmp.config.SnmpCommunicationConfig;
|
||||
import org.thingsboard.server.common.data.util.TypeCastUtil;
|
||||
import org.thingsboard.server.queue.util.TbSnmpTransportComponent;
|
||||
import org.thingsboard.server.transport.snmp.session.DeviceSessionContext;
|
||||
|
||||
@ -48,11 +51,15 @@ import java.util.stream.Collectors;
|
||||
@TbSnmpTransportComponent
|
||||
@Service
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class PduService {
|
||||
|
||||
@Value("${transport.snmp.max_request_oids:100}")
|
||||
private int maxRequestOids;
|
||||
|
||||
@Value("${transport.snmp.response.ignore_type_cast_errors:false}")
|
||||
private boolean ignoreTypeCastErrors;
|
||||
|
||||
public List<PDU> createPdus(DeviceSessionContext sessionContext, SnmpCommunicationConfig communicationConfig, Map<String, String> values) {
|
||||
List<PDU> pdus = new ArrayList<>();
|
||||
List<SnmpMapping> allMappings = communicationConfig.getAllMappings();
|
||||
@ -165,20 +172,31 @@ public class PduService {
|
||||
}
|
||||
|
||||
public void processValue(String key, DataType dataType, String value, JsonObject result) {
|
||||
switch (dataType) {
|
||||
case LONG:
|
||||
result.addProperty(key, Long.parseLong(value));
|
||||
break;
|
||||
case BOOLEAN:
|
||||
result.addProperty(key, Boolean.parseBoolean(value));
|
||||
break;
|
||||
case DOUBLE:
|
||||
result.addProperty(key, Double.parseDouble(value));
|
||||
break;
|
||||
case STRING:
|
||||
case JSON:
|
||||
default:
|
||||
result.addProperty(key, value);
|
||||
try {
|
||||
switch (dataType) {
|
||||
case STRING:
|
||||
case JSON:
|
||||
result.addProperty(key, value);
|
||||
break;
|
||||
case LONG:
|
||||
case DOUBLE:
|
||||
result.addProperty(key, TypeCastUtil.castToNumber(value).getValue());
|
||||
break;
|
||||
case BOOLEAN:
|
||||
if (StringUtils.equalsAnyIgnoreCase(value, "true", "false")) {
|
||||
result.addProperty(key, Boolean.parseBoolean(value));
|
||||
} else {
|
||||
throw new IllegalArgumentException("Can't parse '" + value + "' as boolean");
|
||||
}
|
||||
break;
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
if (ignoreTypeCastErrors) {
|
||||
log.debug("Ignoring value '{}' for key '{}' because of data type mismatch ({} required)", value, key, dataType);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -104,6 +104,8 @@ transport:
|
||||
# to configure SNMP to work over UDP or TCP
|
||||
underlying_protocol: "${SNMP_UNDERLYING_PROTOCOL:udp}"
|
||||
max_request_oids: "${SNMP_MAX_REQUEST_OIDS:100}"
|
||||
response:
|
||||
ignore_type_cast_errors: "${SNMP_RESPONSE_IGNORE_TYPE_CAST_ERRORS:false}"
|
||||
sessions:
|
||||
inactivity_timeout: "${TB_TRANSPORT_SESSIONS_INACTIVITY_TIMEOUT:300000}"
|
||||
report_timeout: "${TB_TRANSPORT_SESSIONS_REPORT_TIMEOUT:3000}"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user