Browse Source

[WIP] SNMP initial implementation (#3755)

* Add SNMP type to transport profiles

* Update reference config for transport profiles

* Initial implementation to retrieve a value using SNMP GET message

* Fix license header

* Move config files

* Use async handler for SNMP response

* Send SNMP agent response to attributes and telemetry

* UI: add SNMP option for device profile

* UI: use JSON field to set SNMP profile configuration

* Handle device profile update event

* Use concurrent hash map for SNMP device sessions

* UI: Add device transport configuration control

* Cancel async request to avoid memory leak and timeout handling

* Start SNMP pooling after application started

* Move OID per profile mapping to SNMP transport context

* Fix build after merge with 3.2.1-SNAPSHOT

* Init device sessions on TB start

* Fix build error, refactoring

* Update session context on device update

* Set device info on session context creating

* Refresh pooling params on device or profile update

* Update license header

* Process device and profile transport config update

* Process SNMP response asynchronously

* Change polling implementation
pull/4248/head
vkukhtyn 5 years ago
committed by GitHub
parent
commit
5a72479951
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      application/pom.xml
  2. 2
      application/src/main/resources/thingsboard.yml
  3. 4
      common/data/pom.xml
  4. 3
      common/data/src/main/java/org/thingsboard/server/common/data/DeviceTransportType.java
  5. 39
      common/data/src/main/java/org/thingsboard/server/common/data/device/JsonBasedTransportConfiguration.java
  6. 3
      common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceTransportConfiguration.java
  7. 43
      common/data/src/main/java/org/thingsboard/server/common/data/device/data/SnmpDeviceTransportConfiguration.java
  8. 8
      common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileTransportConfiguration.java
  9. 28
      common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpDeviceProfileKvMapping.java
  10. 44
      common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpProfileTransportConfiguration.java
  11. 1
      common/transport/pom.xml
  12. 66
      common/transport/snmp/pom.xml
  13. 142
      common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java
  14. 147
      common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportService.java
  15. 206
      common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionCtx.java
  16. 173
      common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/SnmpSessionListener.java
  17. 3
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java
  18. 1
      msa/transport/pom.xml
  19. 33
      msa/transport/snmp/docker/Dockerfile
  20. 33
      msa/transport/snmp/docker/start-tb-snmp-transport.sh
  21. 190
      msa/transport/snmp/pom.xml
  22. 11
      pom.xml
  23. 1
      transport/pom.xml
  24. 112
      transport/snmp/pom.xml
  25. 48
      transport/snmp/src/main/java/org/thingsboard/server/snmp/ThingsboardSnmpTransportApplication.java
  26. 9
      transport/snmp/src/main/resources/device-data-config.json
  27. 21
      transport/snmp/src/main/resources/device-profile-config.json
  28. 3
      ui-ngx/src/app/modules/home/components/home-components.module.ts
  29. 6
      ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.html
  30. 23
      ui-ngx/src/app/modules/home/components/profile/device/snmp-device-profile-transport-configuration.component.html
  31. 100
      ui-ngx/src/app/modules/home/components/profile/device/snmp-device-profile-transport-configuration.component.ts
  32. 6
      ui-ngx/src/app/modules/home/pages/device/data/device-transport-configuration.component.html
  33. 24
      ui-ngx/src/app/modules/home/pages/device/data/snmp-device-transport-configuration.component.html
  34. 100
      ui-ngx/src/app/modules/home/pages/device/data/snmp-device-transport-configuration.component.ts
  35. 2
      ui-ngx/src/app/modules/home/pages/device/device.module.ts
  36. 42
      ui-ngx/src/app/shared/models/device.models.ts
  37. 9
      ui-ngx/src/assets/locale/locale.constant-en_US.json

4
application/pom.xml

@ -85,6 +85,10 @@
<groupId>org.thingsboard.common.transport</groupId>
<artifactId>coap</artifactId>
</dependency>
<dependency>
<groupId>org.thingsboard.common.transport</groupId>
<artifactId>snmp</artifactId>
</dependency>
<dependency>
<groupId>org.thingsboard</groupId>
<artifactId>dao</artifactId>

2
application/src/main/resources/thingsboard.yml

@ -566,6 +566,8 @@ transport:
bind_address: "${COAP_BIND_ADDRESS:0.0.0.0}"
bind_port: "${COAP_BIND_PORT:5683}"
timeout: "${COAP_TIMEOUT:10000}"
snmp:
enabled: "${SNMP_ENABLED:true}"
swagger:
api_path_regex: "${SWAGGER_API_PATH_REGEX:/api.*}"

4
common/data/pom.xml

@ -79,6 +79,10 @@
<groupId>org.thingsboard</groupId>
<artifactId>protobuf-dynamic</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
</dependencies>
<build>

3
common/data/src/main/java/org/thingsboard/server/common/data/DeviceTransportType.java

@ -18,5 +18,6 @@ package org.thingsboard.server.common.data;
public enum DeviceTransportType {
DEFAULT,
MQTT,
LWM2M
LWM2M,
SNMP
}

39
common/data/src/main/java/org/thingsboard/server/common/data/device/JsonBasedTransportConfiguration.java

@ -0,0 +1,39 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.device;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import java.util.HashMap;
import java.util.Map;
public abstract class JsonBasedTransportConfiguration {
@JsonIgnore
private final Map<String, Object> properties = new HashMap<>();
@JsonAnyGetter
public Map<String, Object> properties() {
return this.properties;
}
@JsonAnySetter
public void put(String name, Object value) {
this.properties.put(name, value);
}
}

3
common/data/src/main/java/org/thingsboard/server/common/data/device/data/DeviceTransportConfiguration.java

@ -29,7 +29,8 @@ import org.thingsboard.server.common.data.DeviceTransportType;
@JsonSubTypes({
@JsonSubTypes.Type(value = DefaultDeviceTransportConfiguration.class, name = "DEFAULT"),
@JsonSubTypes.Type(value = MqttDeviceTransportConfiguration.class, name = "MQTT"),
@JsonSubTypes.Type(value = Lwm2mDeviceTransportConfiguration.class, name = "LWM2M")})
@JsonSubTypes.Type(value = Lwm2mDeviceTransportConfiguration.class, name = "LWM2M"),
@JsonSubTypes.Type(value = SnmpDeviceTransportConfiguration.class, name = "SNMP")})
public interface DeviceTransportConfiguration {
@JsonIgnore

43
common/data/src/main/java/org/thingsboard/server/common/data/device/data/SnmpDeviceTransportConfiguration.java

@ -0,0 +1,43 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.device.data;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.thingsboard.server.common.data.DeviceTransportType;
@Data
public class SnmpDeviceTransportConfiguration implements DeviceTransportConfiguration {
private String address;
private int port;
private String community;
private String protocolVersion;
@Override
public DeviceTransportType getType() {
return DeviceTransportType.SNMP;
}
@JsonIgnore
public boolean isValid() {
return StringUtils.isNotEmpty(this.address)
&& this.port > 0
&& StringUtils.isNotEmpty(this.community)
&& StringUtils.isNotEmpty(this.protocolVersion);
}
}

8
common/data/src/main/java/org/thingsboard/server/common/data/device/profile/DeviceProfileTransportConfiguration.java

@ -19,7 +19,6 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import org.thingsboard.server.common.data.DeviceProfileType;
import org.thingsboard.server.common.data.DeviceTransportType;
@JsonIgnoreProperties(ignoreUnknown = true)
@ -28,9 +27,10 @@ import org.thingsboard.server.common.data.DeviceTransportType;
include = JsonTypeInfo.As.PROPERTY,
property = "type")
@JsonSubTypes({
@JsonSubTypes.Type(value = DefaultDeviceProfileTransportConfiguration.class, name = "DEFAULT"),
@JsonSubTypes.Type(value = MqttDeviceProfileTransportConfiguration.class, name = "MQTT"),
@JsonSubTypes.Type(value = Lwm2mDeviceProfileTransportConfiguration.class, name = "LWM2M")})
@JsonSubTypes.Type(value = DefaultDeviceProfileTransportConfiguration.class, name = "DEFAULT"),
@JsonSubTypes.Type(value = MqttDeviceProfileTransportConfiguration.class, name = "MQTT"),
@JsonSubTypes.Type(value = Lwm2mDeviceProfileTransportConfiguration.class, name = "LWM2M"),
@JsonSubTypes.Type(value = SnmpProfileTransportConfiguration.class, name = "SNMP")})
public interface DeviceProfileTransportConfiguration {
@JsonIgnore

28
common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpDeviceProfileKvMapping.java

@ -0,0 +1,28 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.device.profile;
import lombok.Data;
import org.thingsboard.server.common.data.kv.DataType;
//TODO: rename class
@Data
public class SnmpDeviceProfileKvMapping {
private String key;
private DataType type;
private String method;
private String oid;
}

44
common/data/src/main/java/org/thingsboard/server/common/data/device/profile/SnmpProfileTransportConfiguration.java

@ -0,0 +1,44 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.common.data.device.profile;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import org.thingsboard.server.common.data.DeviceTransportType;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@Data
public class SnmpProfileTransportConfiguration implements DeviceProfileTransportConfiguration {
private int poolPeriodMs;
private int timeoutMs;
private int retries;
private List<SnmpDeviceProfileKvMapping> attributes;
private List<SnmpDeviceProfileKvMapping> telemetry;
@Override
public DeviceTransportType getType() {
return DeviceTransportType.SNMP;
}
@JsonIgnore
public List<SnmpDeviceProfileKvMapping> getKvMappings() {
return Stream.concat(attributes.stream(), telemetry.stream()).collect(Collectors.toList());
}
}

1
common/transport/pom.xml

@ -39,6 +39,7 @@
<module>mqtt</module>
<module>http</module>
<module>coap</module>
<module>snmp</module>
</modules>
</project>

66
common/transport/snmp/pom.xml

@ -0,0 +1,66 @@
<!--
Copyright © 2016-2021 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard.common</groupId>
<version>3.2.1-SNAPSHOT</version>
<artifactId>transport</artifactId>
</parent>
<groupId>org.thingsboard.common.transport</groupId>
<artifactId>snmp</artifactId>
<packaging>jar</packaging>
<name>Thingsboard SNMP Transport Common</name>
<url>https://thingsboard.io</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<main.dir>${basedir}/../../..</main.dir>
</properties>
<dependencies>
<dependency>
<groupId>org.thingsboard.common.transport</groupId>
<artifactId>transport-api</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.snmp4j</groupId>
<artifactId>snmp4j</artifactId>
</dependency>
<dependency>
<groupId>org.thingsboard.common</groupId>
<artifactId>dao-api</artifactId>
</dependency>
</dependencies>
</project>

142
common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportContext.java

@ -0,0 +1,142 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.transport.snmp;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.snmp4j.PDU;
import org.snmp4j.Snmp;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.VariableBinding;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.SnmpDeviceProfileKvMapping;
import org.thingsboard.server.common.data.device.profile.SnmpProfileTransportConfiguration;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
import org.thingsboard.server.common.transport.TransportContext;
import org.thingsboard.server.dao.device.DeviceCredentialsService;
import org.thingsboard.server.transport.snmp.session.DeviceSessionCtx;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;
@Service("SnmpTransportContext")
@ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.snmp.enabled}'=='true')")
@Slf4j
public class SnmpTransportContext extends TransportContext {
@Autowired
DeviceCredentialsService deviceCredentialsService;
@Autowired
SnmpTransportService snmpTransportService;
@Getter
private final Map<DeviceProfileId, SnmpProfileTransportConfiguration> profileTransportConfig = new ConcurrentHashMap<>();
@Getter
private final Map<DeviceProfileId, List<PDU>> pdusPerProfile = new ConcurrentHashMap<>();
@Getter
private final Map<DeviceId, DeviceSessionCtx> deviceSessions = new ConcurrentHashMap<>();
public Optional<SnmpDeviceProfileKvMapping> findAttributesMapping(DeviceProfileId deviceProfileId, OID responseOid) {
if (profileTransportConfig.containsKey(deviceProfileId)) {
return findMapping(responseOid, profileTransportConfig.get(deviceProfileId).getAttributes());
}
return Optional.empty();
}
public Optional<SnmpDeviceProfileKvMapping> findTelemetryMapping(DeviceProfileId deviceProfileId, OID responseOid) {
if (profileTransportConfig.containsKey(deviceProfileId)) {
return findMapping(responseOid, profileTransportConfig.get(deviceProfileId).getTelemetry());
}
return Optional.empty();
}
private Optional<SnmpDeviceProfileKvMapping> findMapping(OID responseOid, List<SnmpDeviceProfileKvMapping> mappings) {
return mappings.stream()
.filter(kvMapping -> new OID(kvMapping.getOid()).equals(responseOid))
//TODO: OID shouldn't be duplicated in the config, add backend and UI verification
.findFirst();
}
public void initPduListPerProfile() {
profileTransportConfig.forEach(this::updatePduListPerProfile);
}
public void updatePduListPerProfile(DeviceProfileId id, SnmpProfileTransportConfiguration config) {
pdusPerProfile.put(id, createPduList(config));
}
public void updateDeviceSessionCtx(Device device, DeviceProfile deviceProfile, Snmp snmp) {
DeviceCredentials credentials = deviceCredentialsService.findDeviceCredentialsByDeviceId(device.getTenantId(), device.getId());
if (DeviceCredentialsType.ACCESS_TOKEN.equals(credentials.getCredentialsType())) {
SnmpDeviceTransportConfiguration snmpDeviceTransportConfiguration = (SnmpDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration();
if (snmpDeviceTransportConfiguration.isValid()) {
DeviceSessionCtx deviceSessionCtx = new DeviceSessionCtx(this, credentials.getCredentialsId(), snmpDeviceTransportConfiguration, snmp, device.getId(), deviceProfile);
deviceSessionCtx.createSessionInfo(ctx -> getTransportService().registerAsyncSession(deviceSessionCtx.getSessionInfo(), deviceSessionCtx));
this.deviceSessions.put(device.getId(), deviceSessionCtx);
}
} else {
log.warn("[{}] Expected credentials type is {} but found {}", device.getId(), DeviceCredentialsType.ACCESS_TOKEN, credentials.getCredentialsType());
}
}
public ExecutorService getSnmpCallbackExecutor() {
return snmpTransportService.getSnmpCallbackExecutor();
}
private List<PDU> createPduList(SnmpProfileTransportConfiguration deviceProfileConfig) {
Map<String, List<VariableBinding>> varBindingPerMethod = new HashMap<>();
deviceProfileConfig.getKvMappings().forEach(mapping -> varBindingPerMethod
.computeIfAbsent(mapping.getMethod(), v -> new ArrayList<>())
.add(new VariableBinding(new OID(mapping.getOid()))));
return varBindingPerMethod.keySet().stream()
.map(method -> {
PDU request = new PDU();
request.setType(getSnmpMethod(method));
request.addAll(varBindingPerMethod.get(method));
return request;
})
.collect(Collectors.toList());
}
//TODO: Extract SNMP methods to enum
private int getSnmpMethod(String configMethod) {
switch (configMethod) {
case "get":
return PDU.GET;
case "getNext":
case "response":
case "set":
default:
return -1;
}
}
}

147
common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/SnmpTransportService.java

@ -0,0 +1,147 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.transport.snmp;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.snmp4j.Snmp;
import org.snmp4j.transport.DefaultUdpTransportMapping;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;
import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.server.common.data.DeviceInfo;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.device.profile.SnmpProfileTransportConfiguration;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageDataIterable;
import org.thingsboard.server.dao.device.DeviceProfileService;
import org.thingsboard.server.dao.device.DeviceService;
import org.thingsboard.server.dao.tenant.TenantService;
import org.thingsboard.server.transport.snmp.session.DeviceSessionCtx;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@Service("SnmpTransportService")
@ConditionalOnExpression("'${service.type:null}'=='tb-transport' || ('${service.type:null}'=='monolith' && '${transport.api_enabled:true}'=='true' && '${transport.snmp.enabled}'=='true')")
@Slf4j
public class SnmpTransportService {
private static final int ENTITY_PACK_LIMIT = 1024;
@Autowired
private SnmpTransportContext snmpTransportContext;
@Autowired
DeviceProfileService deviceProfileService;
@Autowired
TenantService tenantService;
@Autowired
DeviceService deviceService;
@Getter
private ExecutorService snmpCallbackExecutor;
private Snmp snmp;
private ScheduledExecutorService pollingExecutor;
@PostConstruct
public void init() {
log.info("Starting SNMP transport...");
pollingExecutor = Executors.newScheduledThreadPool(1, ThingsBoardThreadFactory.forName("snmp-polling"));
//TODO: Set parallelism value in the config
snmpCallbackExecutor = Executors.newWorkStealingPool(20);
initializeSnmp();
log.info("SNMP transport started!");
}
@PreDestroy
public void shutdown() {
log.info("Stopping SNMP transport!");
if (pollingExecutor != null) {
pollingExecutor.shutdownNow();
}
if (snmpCallbackExecutor != null) {
snmpCallbackExecutor.shutdownNow();
}
if (snmp != null) {
try {
snmp.close();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
log.info("SNMP transport stopped!");
}
@EventListener(ApplicationReadyEvent.class)
@Order(value = 2)
public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {
log.info("Received application ready event. Starting SNMP polling.");
initSessionCtxList();
startPolling();
}
private void initializeSnmp() {
try {
this.snmp = new Snmp(new DefaultUdpTransportMapping());
this.snmp.listen();
} catch (IOException e) {
//TODO: what should be done if transport wasn't initialized?
log.error(e.getMessage(), e);
}
}
private void initSessionCtxList() {
//TODO: This approach works for monolith, in cluster the same data will be fetched by each node.
for (Tenant tenant : new PageDataIterable<>(tenantService::findTenants, ENTITY_PACK_LIMIT)) {
TenantId tenantId = tenant.getTenantId();
for (DeviceProfile deviceProfile : new PageDataIterable<>(pageLink -> deviceProfileService.findDeviceProfiles(tenantId, pageLink), ENTITY_PACK_LIMIT)) {
if (DeviceTransportType.SNMP.equals(deviceProfile.getTransportType())) {
snmpTransportContext.getProfileTransportConfig().put(deviceProfile.getId(),
(SnmpProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration());
initDeviceSessions(deviceProfile);
}
}
}
snmpTransportContext.initPduListPerProfile();
}
private void initDeviceSessions(DeviceProfile deviceProfile) {
for (DeviceInfo deviceInfo : new PageDataIterable<>(pageLink -> deviceService.findDeviceInfosByTenantIdAndDeviceProfileId(deviceProfile.getTenantId(), deviceProfile.getId(), pageLink), ENTITY_PACK_LIMIT)) {
snmpTransportContext.updateDeviceSessionCtx(deviceInfo, deviceProfile, snmp);
}
}
private void startPolling() {
//TODO: Get poll period from configuration;
int poolPeriodSeconds = 1;
pollingExecutor.scheduleAtFixedRate(() -> snmpTransportContext.getDeviceSessions().values().forEach(DeviceSessionCtx::executeSnmpRequest),
0, poolPeriodSeconds, TimeUnit.SECONDS);
}
}

206
common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/DeviceSessionCtx.java

@ -0,0 +1,206 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.transport.snmp.session;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.snmp4j.CommunityTarget;
import org.snmp4j.Snmp;
import org.snmp4j.Target;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.smi.GenericAddress;
import org.snmp4j.smi.OctetString;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
import org.thingsboard.server.common.data.device.profile.SnmpProfileTransportConfiguration;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.transport.SessionMsgListener;
import org.thingsboard.server.common.transport.TransportServiceCallback;
import org.thingsboard.server.common.transport.auth.SessionInfoCreator;
import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
import org.thingsboard.server.common.transport.session.DeviceAwareSessionContext;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.gen.transport.TransportProtos.AttributeUpdateNotificationMsg;
import org.thingsboard.server.gen.transport.TransportProtos.GetAttributeResponseMsg;
import org.thingsboard.server.gen.transport.TransportProtos.SessionCloseNotificationProto;
import org.thingsboard.server.gen.transport.TransportProtos.ToDeviceRpcRequestMsg;
import org.thingsboard.server.gen.transport.TransportProtos.ToServerRpcResponseMsg;
import org.thingsboard.server.transport.snmp.SnmpTransportContext;
import java.io.IOException;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;
@Slf4j
public class DeviceSessionCtx extends DeviceAwareSessionContext implements SessionMsgListener {
private final AtomicInteger msgIdSeq = new AtomicInteger(0);
@Getter
@Setter
private SnmpDeviceTransportConfiguration deviceTransportConfig;
@Getter
@Setter
private SnmpSessionListener snmpSessionListener;
@Getter
@Setter
private Target target;
@Getter
@Setter
private volatile TransportProtos.SessionInfoProto sessionInfo;
private Snmp snmp;
private SnmpProfileTransportConfiguration snmpProfileTransportConfiguration;
private long previousRequestExecutedAt = 0;
public DeviceSessionCtx(SnmpTransportContext transportContext, String token, SnmpDeviceTransportConfiguration deviceTransportConfig,
Snmp snmp, DeviceId deviceId, DeviceProfile deviceProfile) {
super(UUID.randomUUID());
this.snmpSessionListener = new SnmpSessionListener(transportContext, token);
super.setDeviceId(deviceId);
super.setDeviceProfile(deviceProfile);
//TODO: What should be done if snmp null?
if (snmp != null) {
this.snmp = snmp;
}
this.snmpProfileTransportConfiguration = (SnmpProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
initTarget(this.snmpProfileTransportConfiguration, deviceTransportConfig);
}
@Override
public int nextMsgId() {
return msgIdSeq.incrementAndGet();
}
@Override
public void onGetAttributesResponse(GetAttributeResponseMsg getAttributesResponse) {
}
@Override
public void onAttributeUpdate(AttributeUpdateNotificationMsg attributeUpdateNotification) {
}
@Override
public void onRemoteSessionCloseCommand(SessionCloseNotificationProto sessionCloseNotification) {
}
@Override
public void onToDeviceRpcRequest(ToDeviceRpcRequestMsg toDeviceRequest) {
}
@Override
public void onToServerRpcResponse(ToServerRpcResponseMsg toServerResponse) {
}
@Override
public void onDeviceProfileUpdate(TransportProtos.SessionInfoProto newSessionInfo, DeviceProfile deviceProfile) {
super.onDeviceProfileUpdate(sessionInfo, deviceProfile);
if (DeviceTransportType.SNMP.equals(deviceProfile.getTransportType())) {
snmpProfileTransportConfiguration = (SnmpProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
snmpSessionListener.getSnmpTransportContext().getProfileTransportConfig().put(
deviceProfile.getId(),
snmpProfileTransportConfiguration);
snmpSessionListener.getSnmpTransportContext().updatePduListPerProfile(deviceProfile.getId(), snmpProfileTransportConfiguration);
} else {
//TODO: should the context be removed from the map?
}
}
@Override
public void onDeviceUpdate(TransportProtos.SessionInfoProto sessionInfo, Device device, Optional<DeviceProfile> deviceProfileOpt) {
super.onDeviceUpdate(sessionInfo, device, deviceProfileOpt);
if (super.getDeviceProfile() != null && DeviceTransportType.SNMP.equals(super.getDeviceProfile().getTransportType())) {
snmpSessionListener.getSnmpTransportContext().updateDeviceSessionCtx(device, deviceProfile, null);
SnmpProfileTransportConfiguration profileTransportConfig = (SnmpProfileTransportConfiguration) deviceProfile.getProfileData().getTransportConfiguration();
SnmpDeviceTransportConfiguration deviceTransportConfig = (SnmpDeviceTransportConfiguration) device.getDeviceData().getTransportConfiguration();
initTarget(profileTransportConfig, deviceTransportConfig);
} else {
//TODO: should the context be removed from the map?
}
}
public void createSessionInfo(Consumer<TransportProtos.SessionInfoProto> registerSession) {
getSnmpSessionListener().getSnmpTransportContext().getTransportService().process(DeviceTransportType.SNMP,
TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(getSnmpSessionListener().getToken()).build(),
new TransportServiceCallback<ValidateDeviceCredentialsResponse>() {
@Override
public void onSuccess(ValidateDeviceCredentialsResponse msg) {
if (msg.hasDeviceInfo()) {
sessionInfo = SessionInfoCreator.create(msg, getSnmpSessionListener().getSnmpTransportContext(), UUID.randomUUID());
registerSession.accept(sessionInfo);
setDeviceInfo(msg.getDeviceInfo());
} else {
log.warn("[{}] Failed to process device auth", getDeviceId());
}
}
@Override
public void onError(Throwable e) {
log.warn("[{}] Failed to process device auth", getDeviceId(), e);
}
});
}
public void executeSnmpRequest() {
long timeNow = System.currentTimeMillis();
long nextRequestExecutionTime = previousRequestExecutedAt + snmpProfileTransportConfiguration.getPoolPeriodMs();
if (nextRequestExecutionTime < timeNow) {
previousRequestExecutedAt = timeNow;
snmpSessionListener.getSnmpTransportContext().getPdusPerProfile().get(deviceProfile.getId()).forEach(pdu -> {
try {
log.debug("[{}] Sending SNMP message...", pdu.getRequestID());
snmp.send(pdu,
target,
deviceProfile.getId(),
snmpSessionListener);
} catch (IOException e) {
log.error(e.getMessage(), e);
}
});
}
}
private void initTarget(SnmpProfileTransportConfiguration profileTransportConfig, SnmpDeviceTransportConfiguration deviceTransportConfig) {
this.deviceTransportConfig = deviceTransportConfig;
CommunityTarget communityTarget = new CommunityTarget();
communityTarget.setAddress(GenericAddress.parse(GenericAddress.TYPE_UDP + ":" + this.deviceTransportConfig.getAddress() + "/" + this.deviceTransportConfig.getPort()));
communityTarget.setVersion(getSnmpVersion(this.deviceTransportConfig.getProtocolVersion()));
communityTarget.setCommunity(new OctetString(this.deviceTransportConfig.getCommunity()));
communityTarget.setTimeout(profileTransportConfig.getTimeoutMs());
communityTarget.setRetries(profileTransportConfig.getRetries());
this.target = communityTarget;
log.info("SNMP target initialized: {}", this.target);
}
//TODO: replace with enum, wtih preliminary discussion of type version in config (string or integer)
private int getSnmpVersion(String configSnmpVersion) {
switch (configSnmpVersion) {
case ("v1"):
return SnmpConstants.version1;
case ("v2c"):
return SnmpConstants.version2c;
case ("v3"):
return SnmpConstants.version3;
default:
return -1;
}
}
}

173
common/transport/snmp/src/main/java/org/thingsboard/server/transport/snmp/session/SnmpSessionListener.java

@ -0,0 +1,173 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.transport.snmp.session;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonSyntaxException;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.snmp4j.PDU;
import org.snmp4j.Snmp;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.event.ResponseListener;
import org.snmp4j.smi.VariableBinding;
import org.thingsboard.server.common.data.DeviceTransportType;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.kv.DataType;
import org.thingsboard.server.common.transport.TransportContext;
import org.thingsboard.server.common.transport.TransportService;
import org.thingsboard.server.common.transport.TransportServiceCallback;
import org.thingsboard.server.common.transport.adaptor.AdaptorException;
import org.thingsboard.server.common.transport.adaptor.JsonConverter;
import org.thingsboard.server.common.transport.auth.SessionInfoCreator;
import org.thingsboard.server.common.transport.auth.ValidateDeviceCredentialsResponse;
import org.thingsboard.server.gen.transport.TransportProtos;
import org.thingsboard.server.transport.snmp.SnmpTransportContext;
import java.util.UUID;
import java.util.function.Consumer;
@Slf4j
@AllArgsConstructor
public class SnmpSessionListener implements ResponseListener {
@Getter
private final SnmpTransportContext snmpTransportContext;
@Getter
private final String token;
@Override
public void onResponse(ResponseEvent event) {
((Snmp) event.getSource()).cancel(event.getRequest(), this);
snmpTransportContext.getSnmpCallbackExecutor().submit(() -> processSnmpResponse(event));
}
private void processSnmpResponse(ResponseEvent event) {
PDU response = event.getResponse();
if (event.getError() != null) {
log.warn("Response error: {}", event.getError().getMessage(), event.getError());
}
if (response != null) {
log.debug("[{}] Processing SNMP response: {}", response.getRequestID(), response);
DeviceProfileId deviceProfileId = (DeviceProfileId) event.getUserObject();
TransportService transportService = snmpTransportContext.getTransportService();
for (int i = 0; i < response.size(); i++) {
VariableBinding vb = response.get(i);
snmpTransportContext.findAttributesMapping(deviceProfileId, vb.getOid()).ifPresent(kvMapping -> transportService.process(DeviceTransportType.DEFAULT,
TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(token).build(),
new DeviceAuthCallback(snmpTransportContext, sessionInfo -> {
try {
transportService.process(sessionInfo,
convertToPostAttributes(kvMapping.getKey(), kvMapping.getType(), vb.toValueString()),
TransportServiceCallback.EMPTY);
reportActivity(sessionInfo);
} catch (Exception e) {
log.warn("Failed to process SNMP response: {}", e.getMessage(), e);
}
})));
snmpTransportContext.findTelemetryMapping(deviceProfileId, vb.getOid()).ifPresent(kvMapping -> transportService.process(DeviceTransportType.DEFAULT,
TransportProtos.ValidateDeviceTokenRequestMsg.newBuilder().setToken(token).build(),
new DeviceAuthCallback(snmpTransportContext, sessionInfo -> {
try {
transportService.process(sessionInfo,
convertToPostTelemetry(kvMapping.getKey(), kvMapping.getType(), vb.toValueString()),
TransportServiceCallback.EMPTY);
reportActivity(sessionInfo);
} catch (Exception e) {
log.warn("Failed to process SNMP response: {}", e.getMessage(), e);
}
})));
}
} else {
log.warn("No SNMP response, requestId: {}", event.getRequest().getRequestID());
}
}
private TransportProtos.PostAttributeMsg convertToPostAttributes(String keyName, DataType dataType, String payload) throws AdaptorException {
try {
return JsonConverter.convertToAttributesProto(getKvJson(keyName, dataType, payload));
} catch (IllegalStateException | JsonSyntaxException ex) {
//TODO: change the exception type
throw new AdaptorException(ex);
}
}
private TransportProtos.PostTelemetryMsg convertToPostTelemetry(String keyName, DataType dataType, String payload) throws AdaptorException {
try {
return JsonConverter.convertToTelemetryProto(getKvJson(keyName, dataType, payload));
} catch (IllegalStateException | JsonSyntaxException ex) {
//TODO: change the exception type
throw new AdaptorException(ex);
}
}
private JsonElement getKvJson(String keyName, DataType dataType, String payload) throws AdaptorException {
JsonObject result = new JsonObject();
switch (dataType) {
case LONG:
result.addProperty(keyName, Long.parseLong(payload));
break;
case BOOLEAN:
result.addProperty(keyName, Boolean.parseBoolean(payload));
break;
case DOUBLE:
result.addProperty(keyName, Double.parseDouble(payload));
break;
case STRING:
result.addProperty(keyName, payload);
break;
default:
//TODO: change the exception type
throw new AdaptorException("Unsupported data type");
}
return new JsonParser().parse(result.toString());
}
private void reportActivity(TransportProtos.SessionInfoProto sessionInfo) {
snmpTransportContext.getTransportService().process(sessionInfo, TransportProtos.SubscriptionInfoProto.newBuilder()
.setAttributeSubscription(false)
.setRpcSubscription(false)
.setLastActivityTime(System.currentTimeMillis())
.build(), TransportServiceCallback.EMPTY);
}
@AllArgsConstructor
private static class DeviceAuthCallback implements TransportServiceCallback<ValidateDeviceCredentialsResponse> {
private final TransportContext transportContext;
private final Consumer<TransportProtos.SessionInfoProto> onSuccess;
@Override
public void onSuccess(ValidateDeviceCredentialsResponse msg) {
if (msg.hasDeviceInfo()) {
onSuccess.accept(SessionInfoCreator.create(msg, transportContext, UUID.randomUUID()));
} else {
log.warn("Failed to process device auth");
}
}
@Override
public void onError(Throwable e) {
log.warn("Failed to process device auth", e);
}
}
}

3
dao/src/main/java/org/thingsboard/server/dao/device/DeviceServiceImpl.java

@ -47,6 +47,7 @@ import org.thingsboard.server.common.data.device.data.DefaultDeviceTransportConf
import org.thingsboard.server.common.data.device.data.DeviceData;
import org.thingsboard.server.common.data.device.data.Lwm2mDeviceTransportConfiguration;
import org.thingsboard.server.common.data.device.data.MqttDeviceTransportConfiguration;
import org.thingsboard.server.common.data.device.data.SnmpDeviceTransportConfiguration;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.DeviceProfileId;
@ -240,6 +241,8 @@ public class DeviceServiceImpl extends AbstractEntityService implements DeviceSe
case LWM2M:
deviceData.setTransportConfiguration(new Lwm2mDeviceTransportConfiguration());
break;
case SNMP:
deviceData.setTransportConfiguration(new SnmpDeviceTransportConfiguration());
}
}
return deviceData;

1
msa/transport/pom.xml

@ -38,6 +38,7 @@
<module>mqtt</module>
<module>http</module>
<module>coap</module>
<module>snmp</module>
</modules>
<build>

33
msa/transport/snmp/docker/Dockerfile

@ -0,0 +1,33 @@
#
# Copyright © 2016-2021 The Thingsboard Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
FROM thingsboard/openjdk8
COPY start-tb-snmp-transport.sh ${pkg.name}.deb /tmp/
RUN chmod a+x /tmp/*.sh \
&& mv /tmp/start-tb-snmp-transport.sh /usr/bin
RUN yes | dpkg -i /tmp/${pkg.name}.deb
RUN rm /tmp/${pkg.name}.deb
RUN systemctl --no-reload disable --now ${pkg.name}.service > /dev/null 2>&1 || :
RUN chmod 555 ${pkg.installFolder}/bin/${pkg.name}.jar
USER ${pkg.user}
CMD ["start-tb-snmp-transport.sh"]

33
msa/transport/snmp/docker/start-tb-snmp-transport.sh

@ -0,0 +1,33 @@
#!/bin/bash
#
# Copyright © 2016-2021 The Thingsboard Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
CONF_FOLDER="/config"
jarfile=${pkg.installFolder}/bin/${pkg.name}.jar
configfile=${pkg.name}.conf
source "${CONF_FOLDER}/${configfile}"
export LOADER_PATH=/config,${LOADER_PATH}
echo "Starting '${project.name}' ..."
cd ${pkg.installFolder}/bin
exec java -cp ${jarfile} $JAVA_OPTS -Dloader.main=org.thingsboard.server.snmp.ThingsboardSnmpTransportApplication \
-Dspring.jpa.hibernate.ddl-auto=none \
-Dlogging.config=/config/logback.xml \
org.springframework.boot.loader.PropertiesLauncher

190
msa/transport/snmp/pom.xml

@ -0,0 +1,190 @@
<!--
Copyright © 2016-2021 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard.msa</groupId>
<artifactId>transport</artifactId>
<version>3.2.1-SNAPSHOT</version>
</parent>
<groupId>org.thingsboard.msa.transport</groupId>
<artifactId>snmp</artifactId>
<packaging>pom</packaging>
<name>ThingsBoard SNMP Transport Microservice</name>
<url>https://thingsboard.io</url>
<description>ThingsBoard SNMP Transport Microservice</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<main.dir>${basedir}/../../..</main.dir>
<pkg.name>tb-snmp-transport</pkg.name>
<docker.name>tb-snmp-transport</docker.name>
<pkg.logFolder>/var/log/${pkg.name}</pkg.logFolder>
<pkg.installFolder>/usr/share/${pkg.name}</pkg.installFolder>
</properties>
<dependencies>
<dependency>
<groupId>org.thingsboard.transport</groupId>
<artifactId>snmp</artifactId>
<version>${project.version}</version>
<classifier>deb</classifier>
<type>deb</type>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-tb-snmp-transport-deb</id>
<phase>package</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>org.thingsboard.transport</groupId>
<artifactId>snmp</artifactId>
<classifier>deb</classifier>
<type>deb</type>
<destFileName>${pkg.name}.deb</destFileName>
<outputDirectory>${project.build.directory}</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<executions>
<execution>
<id>copy-docker-config</id>
<phase>process-resources</phase>
<goals>
<goal>copy-resources</goal>
</goals>
<configuration>
<outputDirectory>${project.build.directory}</outputDirectory>
<resources>
<resource>
<directory>docker</directory>
<filtering>true</filtering>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<executions>
<execution>
<id>build-docker-image</id>
<phase>pre-integration-test</phase>
<goals>
<goal>build</goal>
</goals>
<configuration>
<skip>${dockerfile.skip}</skip>
<repository>${docker.repo}/${docker.name}</repository>
<verbose>true</verbose>
<googleContainerRegistryEnabled>false</googleContainerRegistryEnabled>
<contextDirectory>${project.build.directory}</contextDirectory>
</configuration>
</execution>
<execution>
<id>tag-docker-image</id>
<phase>pre-integration-test</phase>
<goals>
<goal>tag</goal>
</goals>
<configuration>
<skip>${dockerfile.skip}</skip>
<repository>${docker.repo}/${docker.name}</repository>
<tag>${project.version}</tag>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>push-docker-image</id>
<activation>
<property>
<name>push-docker-image</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<executions>
<execution>
<id>push-latest-docker-image</id>
<phase>pre-integration-test</phase>
<goals>
<goal>push</goal>
</goals>
<configuration>
<tag>latest</tag>
<repository>${docker.repo}/${docker.name}</repository>
</configuration>
</execution>
<execution>
<id>push-version-docker-image</id>
<phase>pre-integration-test</phase>
<goals>
<goal>push</goal>
</goals>
<configuration>
<tag>${project.version}</tag>
<repository>${docker.repo}/${docker.name}</repository>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<repositories>
<repository>
<id>jenkins</id>
<name>Jenkins Repository</name>
<url>https://repo.jenkins-ci.org/releases</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>

11
pom.xml

@ -109,6 +109,7 @@
<protobuf-dynamic.version>1.0.2TB</protobuf-dynamic.version>
<wire-schema.version>3.4.0</wire-schema.version>
<twilio.version>7.54.2</twilio.version>
<snmp4j.version>2.8.5</snmp4j.version>
</properties>
<modules>
@ -838,6 +839,11 @@
<artifactId>coap</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.thingsboard.common.transport</groupId>
<artifactId>snmp</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.thingsboard</groupId>
<artifactId>dao</artifactId>
@ -1408,6 +1414,11 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.snmp4j</groupId>
<artifactId>snmp4j</artifactId>
<version>${snmp4j.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

1
transport/pom.xml

@ -37,6 +37,7 @@
<module>http</module>
<module>mqtt</module>
<module>coap</module>
<module>snmp</module>
</modules>
</project>

112
transport/snmp/pom.xml

@ -0,0 +1,112 @@
<!--
Copyright © 2016-2021 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>3.2.1-SNAPSHOT</version>
<artifactId>transport</artifactId>
</parent>
<groupId>org.thingsboard.transport</groupId>
<artifactId>snmp</artifactId>
<packaging>jar</packaging>
<name>Thingsboard SNMP Transport Service</name>
<url>https://thingsboard.io</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<main.dir>${basedir}/../..</main.dir>
<pkg.type>java</pkg.type>
<pkg.disabled>false</pkg.disabled>
<pkg.process-resources.phase>process-resources</pkg.process-resources.phase>
<pkg.package.phase>package</pkg.package.phase>
<pkg.name>tb-snmp-transport</pkg.name>
<pkg.copyInstallScripts>false</pkg.copyInstallScripts>
<pkg.win.dist>${project.build.directory}/windows</pkg.win.dist>
<pkg.implementationTitle>ThingsBoard SNMP Transport Service</pkg.implementationTitle>
<pkg.mainClass>org.thingsboard.server.snmp.ThingsboardSnmpTransportApplication</pkg.mainClass>
</properties>
<dependencies>
<dependency>
<groupId>org.thingsboard.common.transport</groupId>
<artifactId>snmp</artifactId>
</dependency>
<dependency>
<groupId>org.thingsboard.common</groupId>
<artifactId>queue</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<build>
<finalName>${pkg.name}-${project.version}</finalName>
<resources>
<resource>
<directory>${project.basedir}/src/main/resources</directory>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.thingsboard</groupId>
<artifactId>gradle-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>jenkins</id>
<name>Jenkins Repository</name>
<url>https://repo.jenkins-ci.org/releases</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
</project>

48
transport/snmp/src/main/java/org/thingsboard/server/snmp/ThingsboardSnmpTransportApplication.java

@ -0,0 +1,48 @@
/**
* Copyright © 2016-2021 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.snmp;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import java.util.Arrays;
@SpringBootConfiguration
@EnableAsync
@EnableScheduling
@ComponentScan({"org.thingsboard.server.snmp", "org.thingsboard.server.common", "org.thingsboard.server.transport.snmp", "org.thingsboard.server.queue"})
public class ThingsboardSnmpTransportApplication {
private static final String SPRING_CONFIG_NAME_KEY = "--spring.config.name";
private static final String DEFAULT_SPRING_CONFIG_PARAM = SPRING_CONFIG_NAME_KEY + "=" + "tb-snmp-transport";
public static void main(String[] args) {
SpringApplication.run(ThingsboardSnmpTransportApplication.class, updateArguments(args));
}
private static String[] updateArguments(String[] args) {
if (Arrays.stream(args).noneMatch(arg -> arg.startsWith(SPRING_CONFIG_NAME_KEY))) {
String[] modifiedArgs = new String[args.length + 1];
System.arraycopy(args, 0, modifiedArgs, 0, args.length);
modifiedArgs[args.length] = DEFAULT_SPRING_CONFIG_PARAM;
return modifiedArgs;
}
return args;
}
}

9
transport/snmp/src/main/resources/device-data-config.json

@ -0,0 +1,9 @@
{
"deviceName": "Thermostat T1",
"snmpConfig": {
"address": "192.168.1.2",
"port": 161,
"community": "U5J=$HWj6f@7",
"protocolVersion": "v2c"
}
}

21
transport/snmp/src/main/resources/device-profile-config.json

@ -0,0 +1,21 @@
{
"poolPeriodMs": 10000,
"timeoutMs": 5000,
"retries": 5,
"attributes": [
{
"key": "snmpNodeManagerEmail",
"type": "STRING",
"method": "get",
"oid": ".1.3.6.1.2.1.1.4.0"
}
],
"telemetry": [
{
"key": "snmpNodeSysUpTime",
"type": "LONG",
"method": "get",
"oid": ".1.3.6.1.2.1.1.3.0"
}
]
}

3
ui-ngx/src/app/modules/home/components/home-components.module.ts

@ -97,6 +97,7 @@ import { DeviceProfileDialogComponent } from '@home/components/profile/device-pr
import { DeviceProfileAutocompleteComponent } from '@home/components/profile/device-profile-autocomplete.component';
import { MqttDeviceProfileTransportConfigurationComponent } from '@home/components/profile/device/mqtt-device-profile-transport-configuration.component';
import { Lwm2mDeviceProfileTransportConfigurationComponent } from '@home/components/profile/device/lwm2m-device-profile-transport-configuration.component';
import { SnmpDeviceProfileTransportConfigurationComponent } from './profile/device/snmp-device-profile-transport-configuration.component';
import { DeviceProfileAlarmsComponent } from '@home/components/profile/alarm/device-profile-alarms.component';
import { DeviceProfileAlarmComponent } from '@home/components/profile/alarm/device-profile-alarm.component';
import { CreateAlarmRulesComponent } from '@home/components/profile/alarm/create-alarm-rules.component';
@ -199,6 +200,7 @@ import { CopyDeviceCredentialsComponent } from '@home/components/device/copy-dev
DefaultDeviceProfileTransportConfigurationComponent,
MqttDeviceProfileTransportConfigurationComponent,
Lwm2mDeviceProfileTransportConfigurationComponent,
SnmpDeviceProfileTransportConfigurationComponent,
DeviceProfileTransportConfigurationComponent,
CreateAlarmRulesComponent,
AlarmRuleComponent,
@ -288,6 +290,7 @@ import { CopyDeviceCredentialsComponent } from '@home/components/device/copy-dev
DefaultDeviceProfileTransportConfigurationComponent,
MqttDeviceProfileTransportConfigurationComponent,
Lwm2mDeviceProfileTransportConfigurationComponent,
SnmpDeviceProfileTransportConfigurationComponent,
DeviceProfileTransportConfigurationComponent,
CreateAlarmRulesComponent,
AlarmRuleComponent,

6
ui-ngx/src/app/modules/home/components/profile/device/device-profile-transport-configuration.component.html

@ -35,5 +35,11 @@
formControlName="configuration">
</tb-lwm2m-device-profile-transport-configuration>
</ng-template-->
<ng-template [ngSwitchCase]="deviceTransportType.SNMP">
<tb-snmp-device-profile-transport-configuration
[required]="required"
formControlName="configuration">
</tb-snmp-device-profile-transport-configuration>
</ng-template>
</div>
</div>

23
ui-ngx/src/app/modules/home/components/profile/device/snmp-device-profile-transport-configuration.component.html

@ -0,0 +1,23 @@
<!--
Copyright © 2016-2021 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<form [formGroup]="snmpDeviceProfileTransportConfigurationFormGroup" style="padding-bottom: 16px;">
<tb-json-object-edit
required
formControlName="configuration">
</tb-json-object-edit>
</form>

100
ui-ngx/src/app/modules/home/components/profile/device/snmp-device-profile-transport-configuration.component.ts

@ -0,0 +1,100 @@
///
/// Copyright © 2016-2021 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import {Component, forwardRef, Input, OnInit} from '@angular/core';
import {ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators} from '@angular/forms';
import {Store} from '@ngrx/store';
import {AppState} from '@app/core/core.state';
import {coerceBooleanProperty} from '@angular/cdk/coercion';
import {
DeviceProfileTransportConfiguration,
DeviceTransportType,
SnmpDeviceProfileTransportConfiguration
} from '@shared/models/device.models';
import {isDefinedAndNotNull} from "@core/utils";
export interface OidMappingConfiguration {
isAttribute: boolean;
key: string;
type: string;
method: string;
oid: string;
}
@Component({
selector: 'tb-snmp-device-profile-transport-configuration',
templateUrl: './snmp-device-profile-transport-configuration.component.html',
styleUrls: [],
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => SnmpDeviceProfileTransportConfigurationComponent),
multi: true
}]
})
export class SnmpDeviceProfileTransportConfigurationComponent implements ControlValueAccessor, OnInit {
snmpDeviceProfileTransportConfigurationFormGroup: FormGroup;
private requiredValue: boolean;
private configuration = [];
get required(): boolean {
return this.requiredValue;
}
@Input()
set required(value: boolean) {
this.requiredValue = coerceBooleanProperty(value);
}
@Input()
disabled: boolean;
private propagateChange = (v: any) => {
}
constructor(private store: Store<AppState>, private fb: FormBuilder) {
}
ngOnInit(): void {
this.snmpDeviceProfileTransportConfigurationFormGroup = this.fb.group({
configuration: [null, Validators.required]
});
this.snmpDeviceProfileTransportConfigurationFormGroup.valueChanges.subscribe(() => {
this.updateModel();
});
}
registerOnChange(fn: any): void {
this.propagateChange = fn;
}
registerOnTouched(fn: any): void {
}
writeValue(value: SnmpDeviceProfileTransportConfiguration | null): void {
if (isDefinedAndNotNull(value)) {
this.snmpDeviceProfileTransportConfigurationFormGroup.patchValue({configuration: value}, {emitEvent: false});
}
}
private updateModel() {
let configuration: DeviceProfileTransportConfiguration = null;
if (this.snmpDeviceProfileTransportConfigurationFormGroup.valid) {
configuration = this.snmpDeviceProfileTransportConfigurationFormGroup.getRawValue().configuration;
configuration.type = DeviceTransportType.SNMP;
}
this.propagateChange(configuration);
}
}

6
ui-ngx/src/app/modules/home/pages/device/data/device-transport-configuration.component.html

@ -35,5 +35,11 @@
formControlName="configuration">
</tb-lwm2m-device-transport-configuration>
</ng-template-->
<ng-template [ngSwitchCase]="deviceTransportType.SNMP">
<tb-snmp-device-transport-configuration
[required]="required"
formControlName="configuration">
</tb-snmp-device-transport-configuration>
</ng-template>
</div>
</div>

24
ui-ngx/src/app/modules/home/pages/device/data/snmp-device-transport-configuration.component.html

@ -0,0 +1,24 @@
<!--
Copyright © 2016-2021 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<form [formGroup]="snmpDeviceTransportConfigurationFormGroup" style="padding-bottom: 16px;">
<tb-json-object-edit
[required]="required"
label="{{ 'device-profile.transport-type-snmp-hint' | translate }}"
formControlName="configuration">
</tb-json-object-edit>
</form>

100
ui-ngx/src/app/modules/home/pages/device/data/snmp-device-transport-configuration.component.ts

@ -0,0 +1,100 @@
///
/// Copyright © 2016-2021 The Thingsboard Authors
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License is distributed on an "AS IS" BASIS,
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
/// See the License for the specific language governing permissions and
/// limitations under the License.
///
import {Component, forwardRef, Input, OnInit} from '@angular/core';
import {ControlValueAccessor, FormBuilder, FormGroup, NG_VALUE_ACCESSOR, Validators} from '@angular/forms';
import {Store} from '@ngrx/store';
import {AppState} from '@app/core/core.state';
import {coerceBooleanProperty} from '@angular/cdk/coercion';
import {
DeviceTransportConfiguration,
DeviceTransportType,
SnmpDeviceTransportConfiguration
} from '@shared/models/device.models';
@Component({
selector: 'tb-snmp-device-transport-configuration',
templateUrl: './snmp-device-transport-configuration.component.html',
styleUrls: [],
providers: [{
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => SnmpDeviceTransportConfigurationComponent),
multi: true
}]
})
export class SnmpDeviceTransportConfigurationComponent implements ControlValueAccessor, OnInit {
snmpDeviceTransportConfigurationFormGroup: FormGroup;
private requiredValue: boolean;
get required(): boolean {
return this.requiredValue;
}
@Input()
set required(value: boolean) {
this.requiredValue = coerceBooleanProperty(value);
}
@Input()
disabled: boolean;
private propagateChange = (v: any) => {
};
constructor(private store: Store<AppState>,
private fb: FormBuilder) {
}
registerOnChange(fn: any): void {
this.propagateChange = fn;
}
registerOnTouched(fn: any): void {
}
ngOnInit() {
this.snmpDeviceTransportConfigurationFormGroup = this.fb.group({
configuration: [null, Validators.required]
});
this.snmpDeviceTransportConfigurationFormGroup.valueChanges.subscribe(() => {
this.updateModel();
});
}
setDisabledState(isDisabled: boolean): void {
this.disabled = isDisabled;
if (this.disabled) {
this.snmpDeviceTransportConfigurationFormGroup.disable({emitEvent: false});
} else {
this.snmpDeviceTransportConfigurationFormGroup.enable({emitEvent: false});
}
}
writeValue(value: SnmpDeviceTransportConfiguration | null): void {
this.snmpDeviceTransportConfigurationFormGroup.patchValue({configuration: value}, {emitEvent: false});
}
private updateModel() {
let configuration: DeviceTransportConfiguration = null;
if (this.snmpDeviceTransportConfigurationFormGroup.valid) {
configuration = this.snmpDeviceTransportConfigurationFormGroup.getRawValue().configuration;
configuration.type = DeviceTransportType.SNMP;
}
this.propagateChange(configuration);
}
}

2
ui-ngx/src/app/modules/home/pages/device/device.module.ts

@ -31,6 +31,7 @@ import { DefaultDeviceTransportConfigurationComponent } from './data/default-dev
import { DeviceTransportConfigurationComponent } from './data/device-transport-configuration.component';
import { MqttDeviceTransportConfigurationComponent } from './data/mqtt-device-transport-configuration.component';
import { Lwm2mDeviceTransportConfigurationComponent } from './data/lwm2m-device-transport-configuration.component';
import { SnmpDeviceTransportConfigurationComponent } from './data/snmp-device-transport-configuration.component';
@NgModule({
declarations: [
@ -39,6 +40,7 @@ import { Lwm2mDeviceTransportConfigurationComponent } from './data/lwm2m-device-
DefaultDeviceTransportConfigurationComponent,
MqttDeviceTransportConfigurationComponent,
Lwm2mDeviceTransportConfigurationComponent,
SnmpDeviceTransportConfigurationComponent,
DeviceTransportConfigurationComponent,
DeviceDataComponent,
DeviceComponent,

42
ui-ngx/src/app/shared/models/device.models.ts

@ -29,13 +29,15 @@ import * as _moment from 'moment';
import { AbstractControl, ValidationErrors } from '@angular/forms';
export enum DeviceProfileType {
DEFAULT = 'DEFAULT'
DEFAULT = 'DEFAULT',
SNMP = 'SNMP'
}
export enum DeviceTransportType {
DEFAULT = 'DEFAULT',
MQTT = 'MQTT',
// LWM2M = 'LWM2M'
SNMP = 'SNMP'
}
export enum MqttTransportPayloadType {
@ -68,6 +70,13 @@ export const deviceProfileTypeConfigurationInfoMap = new Map<DeviceProfileType,
hasProfileConfiguration: false,
hasDeviceConfiguration: false,
}
],
[
DeviceProfileType.SNMP,
{
hasProfileConfiguration: true,
hasDeviceConfiguration: true,
}
]
]
);
@ -77,6 +86,7 @@ export const deviceTransportTypeTranslationMap = new Map<DeviceTransportType, st
[DeviceTransportType.DEFAULT, 'device-profile.transport-type-default'],
[DeviceTransportType.MQTT, 'device-profile.transport-type-mqtt'],
// [DeviceTransportType.LWM2M, 'device-profile.transport-type-lwm2m']
[DeviceTransportType.SNMP, 'device-profile.transport-type-snmp'],
]
);
@ -94,6 +104,7 @@ export const deviceTransportTypeHintMap = new Map<DeviceTransportType, string>(
[DeviceTransportType.DEFAULT, 'device-profile.transport-type-default-hint'],
[DeviceTransportType.MQTT, 'device-profile.transport-type-mqtt-hint'],
// [DeviceTransportType.LWM2M, 'device-profile.transport-type-lwm2m-hint']
[DeviceTransportType.SNMP, 'device-profile.transport-type-snmp-hint'],
]
);
@ -128,6 +139,13 @@ export const deviceTransportTypeConfigurationInfoMap = new Map<DeviceTransportTy
hasDeviceConfiguration: false,
}
]*/
[
DeviceTransportType.SNMP,
{
hasProfileConfiguration: true,
hasDeviceConfiguration: true
}
]
]
);
@ -158,9 +176,14 @@ export interface Lwm2mDeviceProfileTransportConfiguration {
[key: string]: any;
}
export interface SnmpDeviceProfileTransportConfiguration {
[key: string]: any;
}
export type DeviceProfileTransportConfigurations = DefaultDeviceProfileTransportConfiguration &
MqttDeviceProfileTransportConfiguration &
Lwm2mDeviceProfileTransportConfiguration;
Lwm2mDeviceProfileTransportConfiguration &
SnmpDeviceProfileTransportConfiguration;
export interface DeviceProfileTransportConfiguration extends DeviceProfileTransportConfigurations {
type: DeviceTransportType;
@ -218,6 +241,10 @@ export function createDeviceProfileTransportConfiguration(type: DeviceTransportT
const lwm2mTransportConfiguration: Lwm2mDeviceProfileTransportConfiguration = {};
transportConfiguration = {...lwm2mTransportConfiguration, type: DeviceTransportType.LWM2M};
break;*/
case DeviceTransportType.SNMP:
const snmpTransportConfiguration: SnmpDeviceProfileTransportConfiguration = {};
transportConfiguration = {...snmpTransportConfiguration, type: DeviceTransportType.SNMP};
break;
}
}
return transportConfiguration;
@ -239,6 +266,10 @@ export function createDeviceTransportConfiguration(type: DeviceTransportType): D
const lwm2mTransportConfiguration: Lwm2mDeviceTransportConfiguration = {};
transportConfiguration = {...lwm2mTransportConfiguration, type: DeviceTransportType.LWM2M};
break;*/
case DeviceTransportType.SNMP:
const snmpTransportConfiguration: SnmpDeviceTransportConfiguration = {};
transportConfiguration = {...snmpTransportConfiguration, type: DeviceTransportType.SNMP};
break;
}
}
return transportConfiguration;
@ -403,9 +434,14 @@ export interface Lwm2mDeviceTransportConfiguration {
[key: string]: any;
}
export interface SnmpDeviceTransportConfiguration {
[key: string]: any;
}
export type DeviceTransportConfigurations = DefaultDeviceTransportConfiguration &
MqttDeviceTransportConfiguration &
Lwm2mDeviceTransportConfiguration;
Lwm2mDeviceTransportConfiguration &
SnmpDeviceTransportConfiguration;
export interface DeviceTransportConfiguration extends DeviceTransportConfigurations {
type: DeviceTransportType;

9
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -956,6 +956,8 @@
"transport-type-mqtt-hint": "Enables advanced MQTT transport settings",
"transport-type-lwm2m": "LWM2M",
"transport-type-lwm2m-hint": "LWM2M transport type",
"transport-type-snmp": "SNMP",
"transport-type-snmp-hint": "Specify SNMP transport configuration",
"description": "Description",
"default": "Default",
"profile-configuration": "Profile configuration",
@ -975,6 +977,13 @@
"mqtt-device-payload-type": "MQTT device payload",
"mqtt-device-payload-type-json": "JSON",
"mqtt-device-payload-type-proto": "Protobuf",
"snmp-add-mapping": "Add SNMP mapping",
"snmp-mapping-not-configured": "No mapping for OID to timeseries/telemetry configured",
"snmp-timseries-or-attribute-name": "Timeseries/attribute name for mapping",
"snmp-timseries-or-attribute-type": "Timeseries/attribute type for mapping",
"snmp-method-pdu-type-get-request": "GetRequest",
"snmp-method-pdu-type-get-next-request": "GetNextRequest",
"snmp-oid": "OID",
"mqtt-payload-type-required": "Payload type is required.",
"support-level-wildcards": "Single <code>[+]</code> and multi-level <code>[#]</code> wildcards supported.",
"telemetry-topic-filter": "Telemetry topic filter",

Loading…
Cancel
Save