From a99b5bfe1ee158754e29492eff2bd981be93fe9a Mon Sep 17 00:00:00 2001 From: cpoder Date: Sun, 10 Nov 2024 09:27:28 +0100 Subject: [PATCH] Added support for gateways custom attributes --- .../lora/ns/kerlink/KerlinkConnector.java | 73 ++++++++++--------- .../main/java/lora/ns/gateway/Gateway.java | 3 +- .../lora/ns/gateway/LNSGatewayService.java | 5 +- .../lora/ns/objenious/ObjeniousConnector.java | 55 +++++++------- .../main/java/lora/ns/ttn/TTNConnector.java | 32 +++++++- 5 files changed, 101 insertions(+), 67 deletions(-) diff --git a/java/lora-ns-kerlink/src/main/java/lora/ns/kerlink/KerlinkConnector.java b/java/lora-ns-kerlink/src/main/java/lora/ns/kerlink/KerlinkConnector.java index c4e1d4c8b..1f7b780a1 100644 --- a/java/lora-ns-kerlink/src/main/java/lora/ns/kerlink/KerlinkConnector.java +++ b/java/lora-ns-kerlink/src/main/java/lora/ns/kerlink/KerlinkConnector.java @@ -1,6 +1,7 @@ package lora.ns.kerlink; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; import java.util.Properties; @@ -84,8 +85,8 @@ private void login() { // HttpEntity("", headers), new // ParameterizedTypeReference>(){}); ResponseEntity> users = restTemplate.exchange(baseUrl + "/users", HttpMethod.GET, - new HttpEntity("", headers), new ParameterizedTypeReference>() { - }); + new HttpEntity("", headers), new ParameterizedTypeReference>() { + }); if (users.getStatusCode() == HttpStatus.OK) { for (UserDto user : users.getBody().getList()) { logger.info("Testing user {}", user.getLogin()); @@ -108,13 +109,13 @@ public List getDevices() { headers.set("Authorization", jwt.getTokenType() + " " + jwt.getToken()); RestTemplate restTemplate = new RestTemplate(); ResponseEntity> response = restTemplate.exchange(baseUrl + "/endDevices", - HttpMethod.GET, new HttpEntity("", headers), - new ParameterizedTypeReference>() { - }); + HttpMethod.GET, new HttpEntity("", headers), + new ParameterizedTypeReference>() { + }); if (response.hasBody()) { for (EndDeviceDto endDeviceDto : response.getBody().getList()) { result.add(new EndDevice(endDeviceDto.getDevEui(), endDeviceDto.getName(), - endDeviceDto.getClassType())); + endDeviceDto.getClassType())); } } return result; @@ -126,10 +127,10 @@ public String sendDownlink(DownlinkData operation) { login(); } String request = String.format( - "{\n" + " \"endDevice\": {\n" + " \"devEui\": \"%s\"\n" + " },\n" - + " \"fPort\": %d,\n" + " \"payload\": \"%s\",\n" + " \"confirmed\": false,\n" - + " \"contentType\": \"HEXA\"\n" + "}", - operation.getDevEui(), operation.getFport(), operation.getPayload()); + "{\n" + " \"endDevice\": {\n" + " \"devEui\": \"%s\"\n" + " },\n" + + " \"fPort\": %d,\n" + " \"payload\": \"%s\",\n" + " \"confirmed\": false,\n" + + " \"contentType\": \"HEXA\"\n" + "}", + operation.getDevEui(), operation.getFport(), operation.getPayload()); logger.info("Request: {}", request); HttpHeaders headers = new HttpHeaders(); headers.set("Authorization", jwt.getTokenType() + " " + jwt.getToken()); @@ -140,7 +141,7 @@ public String sendDownlink(DownlinkData operation) { RestTemplate restTemplate = new RestTemplate(); logger.info("Will send data to {}", baseUrl + "/dataDown"); String response = restTemplate.exchange(baseUrl + "/dataDown", HttpMethod.POST, - new HttpEntity(request, headers), String.class).getHeaders().getLocation().getPath(); + new HttpEntity(request, headers), String.class).getHeaders().getLocation().getPath(); return response.substring(response.lastIndexOf('/') + 1); } @@ -154,7 +155,7 @@ public EndDevice getDevice(String devEui) { RestTemplate restTemplate = new RestTemplate(); logger.info("Will get device info on URL: {}", baseUrl + "/endDevices/" + devEui); EndDeviceDto endDeviceDto = restTemplate.exchange(baseUrl + "/endDevices/" + devEui, HttpMethod.GET, - new HttpEntity("", headers), EndDeviceDto.class).getBody(); + new HttpEntity("", headers), EndDeviceDto.class).getBody(); return new EndDevice(devEui, endDeviceDto.getName(), endDeviceDto.getClassType()); } @@ -176,7 +177,7 @@ public void provisionDevice(DeviceProvisioning deviceProvisioning) { dto.setAppEui(deviceProvisioning.getAppEUI()); dto.setAppKey(deviceProvisioning.getAppKey()); restTemplate.exchange(baseUrl + "/endDevices/" + deviceProvisioning.getDevEUI(), HttpMethod.PUT, - new HttpEntity(dto, headers), String.class); + new HttpEntity(dto, headers), String.class); } @Override @@ -198,10 +199,10 @@ public void configureRoutings(String url, String tenant, String login, String pa headers.set("Authorization", jwt.getTokenType() + " " + jwt.getToken()); RestTemplate restTemplate = new RestTemplate(); PaginatedDto pushConfigurationDtos = restTemplate - .exchange(baseUrl + "/pushConfigurations", HttpMethod.GET, new HttpEntity("", headers), - new ParameterizedTypeReference>() { - }) - .getBody(); + .exchange(baseUrl + "/pushConfigurations", HttpMethod.GET, new HttpEntity("", headers), + new ParameterizedTypeReference>() { + }) + .getBody(); for (PushConfigurationDto pushConfigurationDto : pushConfigurationDtos.getList()) { if (pushConfigurationDto.getName().equals(routingName)) { currentPushConfigurationDto = pushConfigurationDto; @@ -244,10 +245,10 @@ public void configureRoutings(String url, String tenant, String login, String pa // } if (currentPushConfigurationDto == null) { currentPushConfigurationDto = new PushConfigurationDto(new CustomerDto(customerId), routingName, - PushConfigurationType.HTTP, PushConfigurationMSgDetailLevel.NETWORK, - new PushConfigurationHeaderDto[] { - new PushConfigurationHeaderDto("Content-Type", "application/json") }, - "/downlink", "/uplink", url, tenant + "/" + login, password); + PushConfigurationType.HTTP, PushConfigurationMSgDetailLevel.NETWORK, + new PushConfigurationHeaderDto[] { + new PushConfigurationHeaderDto("Content-Type", "application/json") }, + "/downlink", "/uplink", url, tenant + "/" + login, password); logger.info("Will create a new push configuration: {}", currentPushConfigurationDto.toString()); headers.setContentType(MediaType.MULTIPART_FORM_DATA); List mediaTypes = new ArrayList<>(); @@ -264,7 +265,7 @@ public void configureRoutings(String url, String tenant, String login, String pa } HttpEntity> request = new HttpEntity<>(map, headers); ResponseEntity response = restTemplate.exchange(baseUrl + "/pushConfigurations", HttpMethod.POST, - request, String.class); + request, String.class); String[] tokens = response.getHeaders().getLocation().getPath().split("/"); configId = Integer.parseInt(tokens[tokens.length - 1]); } @@ -273,14 +274,14 @@ public void configureRoutings(String url, String tenant, String login, String pa headers = new HttpHeaders(); headers.set("Authorization", jwt.getTokenType() + " " + jwt.getToken()); ClusterDto cluster = restTemplate.exchange(baseUrl + "/clusters/" + clusterId, HttpMethod.GET, - new HttpEntity("", headers), ClusterDto.class).getBody(); + new HttpEntity("", headers), ClusterDto.class).getBody(); cluster.setPushConfiguration(currentPushConfigurationDto); cluster.setGeolocEnabled(true); cluster.setHexa(true); cluster.setPushEnabled(true); headers.setContentType(MediaType.APPLICATION_JSON); ResponseEntity response = restTemplate.exchange(baseUrl + "/clusters/" + clusterId, - HttpMethod.PATCH, new HttpEntity(cluster, headers), String.class); + HttpMethod.PATCH, new HttpEntity(cluster, headers), String.class); } } @@ -294,7 +295,7 @@ public void removeRoutings() { headers.set("Authorization", jwt.getTokenType() + " " + jwt.getToken()); RestTemplate restTemplate = new RestTemplate(); restTemplate.exchange(baseUrl + "/pushConfigurations/" + configId, HttpMethod.DELETE, - new HttpEntity("", headers), String.class); + new HttpEntity("", headers), String.class); }); } @@ -308,7 +309,7 @@ public void deprovisionDevice(String deveui) { headers.set("Accept", "application/json,application/vnd.kerlink.iot-v1+json"); RestTemplate restTemplate = new RestTemplate(); restTemplate.exchange(baseUrl + "/endDevices/" + deveui, HttpMethod.DELETE, new HttpEntity("", headers), - String.class); + String.class); } public List getClusters() { @@ -320,10 +321,10 @@ public List getClusters() { headers.set("Authorization", jwt.getTokenType() + " " + jwt.getToken()); RestTemplate restTemplate = new RestTemplate(); PaginatedDto clusterDtos = restTemplate - .exchange(baseUrl + "/clusters", HttpMethod.GET, new HttpEntity("", headers), - new ParameterizedTypeReference>() { - }) - .getBody(); + .exchange(baseUrl + "/clusters", HttpMethod.GET, new HttpEntity("", headers), + new ParameterizedTypeReference>() { + }) + .getBody(); for (ClusterDto clusterDto : clusterDtos.getList()) { result.add(clusterDto); } @@ -341,15 +342,15 @@ public List getGateways() { headers.set("Authorization", jwt.getTokenType() + " " + jwt.getToken()); RestTemplate restTemplate = new RestTemplate(); PaginatedDto gatewaysDto = restTemplate - .exchange(baseUrl + "/gateways", HttpMethod.GET, new HttpEntity("", headers), - new ParameterizedTypeReference>() { - }) - .getBody(); + .exchange(baseUrl + "/gateways", HttpMethod.GET, new HttpEntity("", headers), + new ParameterizedTypeReference>() { + }) + .getBody(); C8YData c8yData = new C8YData(); for (GatewayDto gatewayDto : gatewaysDto.getList()) { result.add(new Gateway(gatewayDto.getEui(), gatewayDto.getEui(), gatewayDto.getName(), - gatewayDto.getLatitude(), gatewayDto.getLongitude(), gatewayDto.getDescription(), - ConnectionState.AVAILABLE, c8yData)); + gatewayDto.getLatitude(), gatewayDto.getLongitude(), gatewayDto.getDescription(), + ConnectionState.AVAILABLE, c8yData, new HashMap<>())); } return result; } diff --git a/java/lora-ns-ms/src/main/java/lora/ns/gateway/Gateway.java b/java/lora-ns-ms/src/main/java/lora/ns/gateway/Gateway.java index 18f377690..896702391 100644 --- a/java/lora-ns-ms/src/main/java/lora/ns/gateway/Gateway.java +++ b/java/lora-ns-ms/src/main/java/lora/ns/gateway/Gateway.java @@ -1,6 +1,7 @@ package lora.ns.gateway; import java.math.BigDecimal; +import java.util.Map; import c8y.ConnectionState; import lora.codec.uplink.C8YData; @@ -9,7 +10,6 @@ import lombok.Data; import lombok.NoArgsConstructor; - @Data @Builder @AllArgsConstructor @@ -23,4 +23,5 @@ public class Gateway { private String type; private ConnectionState status; private C8YData data; + private Map properties; } \ No newline at end of file diff --git a/java/lora-ns-ms/src/main/java/lora/ns/gateway/LNSGatewayService.java b/java/lora-ns-ms/src/main/java/lora/ns/gateway/LNSGatewayService.java index 82192f88d..f5e6dc07d 100644 --- a/java/lora-ns-ms/src/main/java/lora/ns/gateway/LNSGatewayService.java +++ b/java/lora-ns-ms/src/main/java/lora/ns/gateway/LNSGatewayService.java @@ -1,5 +1,6 @@ package lora.ns.gateway; +import java.util.HashMap; import java.util.List; import java.util.Optional; @@ -63,6 +64,7 @@ private MicroserviceCredentials createContextWithoutAppKey(MicroserviceCredentia } public void upsertGateways(LNSConnector connector) { + log.info("Upserting gateways in connector {}", connector.getName()); List gateways = connector.getGateways(); for (Gateway gateway : gateways) { var gw = getGateway(gateway.getGwEUI()); @@ -72,6 +74,7 @@ public void upsertGateways(LNSConnector connector) { } else { mor.setId(gw.getId()); } + mor.setProperty("gatewayProperties", gateway.getProperties()); mor.setProperty("gatewayAvailability", gateway.getStatus()); if (gateway.getLat() != null && gateway.getLng() != null) { loraContextService.log("Updating position of gateway {}: {}, {}", gateway.getName(), gateway.getLat(), @@ -122,7 +125,7 @@ public ManagedObjectRepresentation createGateway(String lnsConnectorId, GatewayP new Gateway(gatewayProvisioning.getGwEUI(), gatewayProvisioning.getSerial(), gatewayProvisioning.getName(), gatewayProvisioning.getLat(), gatewayProvisioning.getLng(), gatewayProvisioning.getType(), - gatewayProvisioning.getStatus(), null)); + gatewayProvisioning.getStatus(), new C8YData(), new HashMap<>())); } public ManagedObjectRepresentation getGateway(String id) { diff --git a/java/lora-ns-objenious/src/main/java/lora/ns/objenious/ObjeniousConnector.java b/java/lora-ns-objenious/src/main/java/lora/ns/objenious/ObjeniousConnector.java index bf4de18cc..2254002d0 100644 --- a/java/lora-ns-objenious/src/main/java/lora/ns/objenious/ObjeniousConnector.java +++ b/java/lora-ns-objenious/src/main/java/lora/ns/objenious/ObjeniousConnector.java @@ -3,6 +3,7 @@ import java.math.BigDecimal; import java.util.ArrayList; import java.util.Base64; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; @@ -59,20 +60,20 @@ public ObjeniousConnector(ManagedObjectRepresentation instance) { } private static final ObjectMapper objectMapper = new ObjectMapper().registerModule(new JodaModule()) - .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) - .configure(SerializationFeature.INDENT_OUTPUT, true).setSerializationInclusion(Include.NON_NULL); + .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) + .configure(SerializationFeature.INDENT_OUTPUT, true).setSerializationInclusion(Include.NON_NULL); @Override protected void init() { final ch.qos.logback.classic.Logger serviceLogger = (ch.qos.logback.classic.Logger) LoggerFactory - .getLogger("lora.ns.objenious"); + .getLogger("lora.ns.objenious"); serviceLogger.setLevel(ch.qos.logback.classic.Level.DEBUG); var feignBuilder = Feign.builder().decoder(new JacksonDecoder(objectMapper)) - .encoder(new JacksonEncoder(objectMapper)).logger(new Slf4jLogger("lora.ns.objenious")) - .logLevel(Level.FULL) - .requestInterceptor(template -> template.headers(Map.of("apikey", - List.of(properties.getProperty("apikey")), "Content-Type", - List.of("application/json"), "Accept", List.of("application/json")))); + .encoder(new JacksonEncoder(objectMapper)).logger(new Slf4jLogger("lora.ns.objenious")) + .logLevel(Level.FULL) + .requestInterceptor(template -> template.headers(Map.of("apikey", + List.of(properties.getProperty("apikey")), "Content-Type", + List.of("application/json"), "Accept", List.of("application/json")))); objeniousService = feignBuilder.target(ObjeniousService.class, "https://api.objenious.com/v1/"); } @@ -81,7 +82,7 @@ protected void init() { public List getDevices() { var devices = objeniousService.getDevices(); return devices.stream().map(device -> new EndDevice(device.getProperties().getDeveui(), device.getLabel(), "")) - .collect(Collectors.toList()); + .collect(Collectors.toList()); } public Profile getProfile(int id) { @@ -119,18 +120,18 @@ public void provisionDevice(DeviceProvisioning deviceProvisioning) { deviceCreate.setLng(deviceProvisioning.getLng()); deviceCreate.setGroupId(Integer.parseInt(properties.getProperty("groupId"))); deviceCreate.setProfileId( - Integer.valueOf(deviceProvisioning.getAdditionalProperties().getProperty("deviceProfile"))); + Integer.valueOf(deviceProvisioning.getAdditionalProperties().getProperty("deviceProfile"))); objeniousService.createDevice(deviceCreate); } public void configureRouting(String url, String tenant, String login, String password, String name, - MessageTypeEnum messageType) { + MessageTypeEnum messageType) { RoutingHttp routingHttp = new RoutingHttp(); routingHttp.setUrl(url); routingHttp.setMethod(RoutingHttp.MethodEnum.POST); Headers headers = new Headers(); headers.put("Authorization", "Basic " - + Base64.getEncoder().encodeToString((tenant + "/" + login + ":" + password).getBytes())); + + Base64.getEncoder().encodeToString((tenant + "/" + login + ":" + password).getBytes())); routingHttp.setHeaders(headers); ScenarioRoutingCreateUpdate scenarioRoutingCreateUpdate = new ScenarioRoutingCreateUpdate(); scenarioRoutingCreateUpdate.setHttp(routingHttp); @@ -163,9 +164,9 @@ public void configureRoutings(String url, String tenant, String login, String pa }); configureRouting(url + "/downlink", tenant, login, password, tenant + "-" + this.getId() + "-downlink", - MessageTypeEnum.DOWNLINK); + MessageTypeEnum.DOWNLINK); configureRouting(url + "/uplink", tenant, login, password, tenant + "-" + this.getId() + "-uplink", - MessageTypeEnum.UPLINK); + MessageTypeEnum.UPLINK); } private void removeRouting(String id) { @@ -204,21 +205,21 @@ public List getGateways() { C8YData data = new C8YData(); ConnectionState state = ConnectionState.AVAILABLE; switch (g.getStatus()) { - case ACTIVE: - state = ConnectionState.AVAILABLE; - break; - case ALERT: - state = ConnectionState.AVAILABLE; - break; - case INACTIVE: - state = ConnectionState.UNAVAILABLE; - break; - default: - break; + case ACTIVE: + state = ConnectionState.AVAILABLE; + break; + case ALERT: + state = ConnectionState.AVAILABLE; + break; + case INACTIVE: + state = ConnectionState.UNAVAILABLE; + break; + default: + break; } Gateway gateway = new Gateway(g.getGatewayId(), g.getSerialNumber(), g.getGatewayName(), - BigDecimal.valueOf(g.getLat()), BigDecimal.valueOf(g.getLng()), g.getGatewayType(), - state, data); + BigDecimal.valueOf(g.getLat()), BigDecimal.valueOf(g.getLng()), g.getGatewayType(), + state, data, new HashMap<>()); result.add(gateway); }); } catch (FeignClientException e) { diff --git a/java/lora-ns-ttn/src/main/java/lora/ns/ttn/TTNConnector.java b/java/lora-ns-ttn/src/main/java/lora/ns/ttn/TTNConnector.java index ac9a2269d..7a4c5f923 100644 --- a/java/lora-ns-ttn/src/main/java/lora/ns/ttn/TTNConnector.java +++ b/java/lora-ns-ttn/src/main/java/lora/ns/ttn/TTNConnector.java @@ -1,22 +1,32 @@ package lora.ns.ttn; +import java.math.BigDecimal; import java.util.Base64; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Properties; import java.util.UUID; import java.util.stream.Collectors; import javax.net.ssl.SSLException; +import org.joda.time.DateTime; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.converter.protobuf.ProtobufJsonFormatHttpMessageConverter; import com.cumulocity.microservice.subscription.service.MicroserviceSubscriptionsService; import com.cumulocity.rest.representation.inventory.ManagedObjectRepresentation; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.io.BaseEncoding; import com.google.protobuf.ByteString; import com.google.protobuf.FieldMask; +import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.util.JsonFormat; import c8y.ConnectionState; import io.grpc.ManagedChannel; @@ -354,21 +364,39 @@ public List getGateways() { return gateways.getGatewaysList().stream().map(g -> { // logger.info("Retrieved gateway: {}", g.toString()); boolean connected = true; + Map properties = new HashMap<>(); + var c8yData = new C8YData(); try { GatewayConnectionStats stats = gatewayServer .getGatewayConnectionStats(g.getIds()); // logger.info("Gateway status: {}", stats.toString()); connected = stats.hasConnectedAt(); + String statsJson = JsonFormat.printer().print(stats); + var objectMapper = new ObjectMapper(); + properties.put("stats", objectMapper.readValue(statsJson, Map.class)); + if (stats.hasLastUplinkReceivedAt()) { + c8yData.addMeasurement(null, "uplink", "count", "", + BigDecimal.valueOf(stats.getUplinkCount()), DateTime.now()); + } + if (stats.hasLastDownlinkReceivedAt()) { + c8yData.addMeasurement(null, "downlink", "count", "", + BigDecimal.valueOf(stats.getDownlinkCount()), DateTime.now()); + } } catch (StatusRuntimeException e) { - // e.printStackTrace(); connected = false; + } catch (InvalidProtocolBufferException e) { + e.printStackTrace(); + } catch (JsonMappingException e) { + e.printStackTrace(); + } catch (JsonProcessingException e) { + e.printStackTrace(); } return new Gateway(g.getIds().getGatewayId(), g.getIds().getGatewayId(), g.getName(), null, null, g.getVersionIds().getBrandId(), connected ? ConnectionState.AVAILABLE : ConnectionState.UNAVAILABLE, - new C8YData()); + c8yData, properties); }).collect(Collectors.toList()); }