diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/GeoLocationBasedService.java b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/GeoLocationBasedService.java index 094781fe798..503b45c4ede 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/GeoLocationBasedService.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/GeoLocationBasedService.java @@ -81,7 +81,7 @@ import javax.ws.rs.core.Response; ) @Path("/geo-services") @Api(value = "Geo Service", - description = "This carries all the resources related to the geo service functionalities.") + description = "This carries all the resources related to the geo service functionalities.") public interface GeoLocationBasedService { /** * Retrieve Analytics for the device type @@ -152,6 +152,112 @@ public interface GeoLocationBasedService { required = true) @QueryParam("to") long to); + @GET + @Path("stats/groups/{groupId}") + @ApiOperation( + consumes = "application/json", + produces = "application/json", + httpMethod = "GET", + value = "Retrieve Analytics for the device group", + notes = "", + response = Response.class, + tags = "Geo Service Management", + extensions = { + @Extension(properties = { + @ExtensionProperty(name = Constants.SCOPE, value = "perm:geo-service:analytics-view") + }) + } + ) + @ApiResponses(value = { + @ApiResponse( + code = 200, + message = "OK.", + response = Response.class, + responseHeaders = { + @ResponseHeader( + name = "Content-Type", + description = "The content type of the body"), + @ResponseHeader( + name = "Last-Modified", + description = "Date and time the resource was last modified.\n" + + "Used by caches, or in conditional requests."), + }), + @ApiResponse( + code = 400, + message = "Bad Request. \n Invalid Device Identifiers found.", + response = Response.class), + @ApiResponse( + code = 401, + message = "Unauthorized. \n Unauthorized request."), + @ApiResponse( + code = 500, + message = "Internal Server Error. \n Error on retrieving stats", + response = Response.class) + }) + Response getGeoGroupStats( + @ApiParam( + name = "deviceId", + value = "The registered device Id.", + required = true) + @PathParam("groupId") int groupId); + /** + * Get data to show device locations in a map + */ + @GET + @Path("stats/deviceLocations") + @ApiOperation( + consumes = "application/json", + produces = "application/json", + httpMethod = "GET", + value = "Retrieve Analytics for the device group", + notes = "", + response = Response.class, + tags = "Geo Service Management", + extensions = { + @Extension(properties = { + @ExtensionProperty(name = Constants.SCOPE, value = "perm:geo-service:analytics-view") + }) + } + ) + @ApiResponses(value = { + @ApiResponse( + code = 200, + message = "OK.", + response = Response.class, + responseHeaders = { + @ResponseHeader( + name = "Content-Type", + description = "The content type of the body"), + @ResponseHeader( + name = "Last-Modified", + description = "Date and time the resource was last modified.\n" + + "Used by caches, or in conditional requests."), + }), + @ApiResponse( + code = 400, + message = "Bad Request. \n Invalid Device Identifiers found.", + response = Response.class), + @ApiResponse( + code = 401, + message = "Unauthorized. \n Unauthorized request."), + @ApiResponse( + code = 500, + message = "Internal Server Error. \n Error on retrieving stats", + response = Response.class) + }) + Response getGeoDeviceLocations( + @ApiParam( + name = "deviceId", + value = "The registered device Id.", + required = true) + @QueryParam("horizontalDivisions") int horizontalDivisions, + @QueryParam("verticalDivisions") int verticalDivisions, + @QueryParam("minLat") double minLat, + @QueryParam("maxLat") double maxLat, + @QueryParam("minLong") double minLong, + @QueryParam("maxLong") double maxLong); + + /** * Create Geo alerts */ diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/GeoLocationBasedServiceImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/GeoLocationBasedServiceImpl.java index fc34cfba0da..3d05ffe9b88 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/GeoLocationBasedServiceImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/GeoLocationBasedServiceImpl.java @@ -29,18 +29,30 @@ import org.wso2.carbon.analytics.dataservice.commons.SortType; import org.wso2.carbon.analytics.datasource.commons.Record; import org.wso2.carbon.analytics.datasource.commons.exception.AnalyticsException; import org.wso2.carbon.context.CarbonContext; +import org.wso2.carbon.device.mgt.common.Device; import org.wso2.carbon.device.mgt.common.DeviceIdentifier; import org.wso2.carbon.device.mgt.common.DeviceManagementConstants.GeoServices; import org.wso2.carbon.device.mgt.common.DeviceManagementException; import org.wso2.carbon.device.mgt.common.authorization.DeviceAccessAuthorizationException; +import org.wso2.carbon.device.mgt.common.device.details.DeviceLocation; import org.wso2.carbon.device.mgt.common.geo.service.Alert; import org.wso2.carbon.device.mgt.common.geo.service.Event; import org.wso2.carbon.device.mgt.common.geo.service.GeoFence; import org.wso2.carbon.device.mgt.common.geo.service.GeoLocationBasedServiceException; import org.wso2.carbon.device.mgt.common.geo.service.GeoLocationProviderService; import org.wso2.carbon.device.mgt.common.group.mgt.DeviceGroupConstants; +import org.wso2.carbon.device.mgt.common.group.mgt.GroupManagementException; +import org.wso2.carbon.device.mgt.core.device.details.mgt.DeviceDetailsMgtException; +import org.wso2.carbon.device.mgt.core.device.details.mgt.DeviceInformationManager; +import org.wso2.carbon.device.mgt.core.geo.GeoGrid; +import org.wso2.carbon.device.mgt.core.geo.GeoRectangle; +import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService; +import org.wso2.carbon.device.mgt.core.service.GroupManagementProviderService; +import org.wso2.carbon.device.mgt.core.service.GroupManagementProviderServiceImpl; import org.wso2.carbon.device.mgt.core.util.DeviceManagerUtil; +import org.wso2.carbon.device.mgt.jaxrs.service.api.DeviceManagementService; import org.wso2.carbon.device.mgt.jaxrs.service.api.GeoLocationBasedService; +import org.wso2.carbon.device.mgt.jaxrs.service.api.GroupManagementService; import org.wso2.carbon.device.mgt.jaxrs.util.Constants; import org.wso2.carbon.device.mgt.jaxrs.util.DeviceMgtAPIUtils; import org.wso2.carbon.device.mgt.jaxrs.util.DeviceMgtUtil; @@ -110,11 +122,11 @@ public class GeoLocationBasedServiceImpl implements GeoLocationBasedService { int tenantId = DeviceMgtAPIUtils.getRealmService().getTenantManager().getTenantId(tenantDomain); AnalyticsDataAPI analyticsDataAPI = DeviceMgtAPIUtils.getAnalyticsDataAPI(); List searchResults = analyticsDataAPI.search(tenantId, tableName, query, - 0, - 100, - sortByFields); + 0, + 100, + sortByFields); List events = getEventBeans(analyticsDataAPI, tenantId, tableName, new ArrayList(), - searchResults); + searchResults); return Response.ok().entity(events).build(); } catch (AnalyticsException | UserStoreException e) { log.error("Failed to perform search on table: " + tableName + " : " + e.getMessage(), e); @@ -127,6 +139,102 @@ public class GeoLocationBasedServiceImpl implements GeoLocationBasedService { } } + @Path("stats/groups/{groupId}") + @GET + @Consumes("application/json") + @Produces("application/json") + public Response getGeoGroupStats(@PathParam("groupId") int groupId) { + + try { + if (!DeviceManagerUtil.isPublishOperationResponseEnabled()) { + return Response.status(Response.Status.BAD_REQUEST.getStatusCode()) + .entity("Operation publishing does not exists").build(); + } + } catch (DeviceManagementException e) { + return Response.status(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()).entity(e.getMessage()).build(); + } + + GroupManagementProviderService groupManagementProviderService = DeviceMgtAPIUtils.getGroupManagementProviderService(); + DeviceManagementProviderService deviceManagementService = DeviceMgtAPIUtils.getDeviceManagementService(); + DeviceInformationManager deviceInformationManagerService = DeviceMgtAPIUtils.getDeviceInformationManagerService(); + try { + int deviceCount = groupManagementProviderService.getDeviceCount(groupId); + List devices = groupManagementProviderService.getDevices(groupId, 0, deviceCount); + Map locationHashMap = new HashMap<>(); + + for (Device device : devices) { + DeviceIdentifier deviceIdentifier = new DeviceIdentifier(device.getDeviceIdentifier(),device.getType()); + + locationHashMap.put(device.getId(), deviceInformationManagerService.getDeviceLocation(deviceIdentifier)); + } + return Response.ok().entity(locationHashMap).build(); + } catch (GroupManagementException e) { + String msg = "Error occurred in getDeviceCount or getDevices for groupId: " + groupId; + log.error(msg, e); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()).build(); + } catch (DeviceDetailsMgtException e) { + String msg = "Exception occurred while retrieving device location."+groupId; + log.error(msg,e); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()).build(); + } + + } + + @Path("stats/deviceLocations") + @GET + @Consumes("application/json") + @Produces("application/json") + public Response getGeoDeviceLocations(@QueryParam("horizontalDivisions") int horizontalDivisions, + @QueryParam("verticalDivisions") int verticalDivisions, + @QueryParam("minLat") double minLat, + @QueryParam("maxLat") double maxLat, + @QueryParam("minLong") double minLong, + @QueryParam("maxLong") double maxLong) { + + try { + if (!DeviceManagerUtil.isPublishOperationResponseEnabled()) { + return Response.status(Response.Status.BAD_REQUEST.getStatusCode()) + .entity("Operation publishing does not exists").build(); + } + } catch (DeviceManagementException e) { + return Response.status(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()).entity(e.getMessage()).build(); + } + DeviceManagementProviderService deviceManagementService = DeviceMgtAPIUtils.getDeviceManagementService(); + DeviceInformationManager deviceInformationManagerService = DeviceMgtAPIUtils.getDeviceInformationManagerService(); + GeoGrid geoGrid = new GeoGrid(horizontalDivisions,verticalDivisions,minLat,maxLat,minLong,maxLong); + + try { + List devices =deviceManagementService.getAllDevices(); + ArrayList devicesInGeoGrid =geoGrid.getDevicesInGeoGrid(devices); + List geoRectangles=geoGrid.placeDevicesInGeoRectangles(devicesInGeoGrid); + Map,Map> details = new HashMap<>(); + for(GeoRectangle geoRectangle:geoRectangles){ + Map center = geoRectangle.getCenter(); + if(geoRectangle.getDeviceCount()==0){ + details.put(center,null); + }else if(geoRectangle.getDeviceCount()==1){ + Map deviceDetails = new HashMap<>(); + Device device=geoRectangle.getDevices().get(0); + deviceDetails.put("deviceID",device.getDeviceIdentifier()); + details.put(center,deviceDetails); + }else{ + Map deviceCountDetails = new HashMap<>(); + int deviceCount=geoRectangle.getDeviceCount(); + deviceCountDetails.put("count",Integer.toString(deviceCount)); + details.put(center,deviceCountDetails); + } + + } + return Response.ok().entity(details).build(); + } catch (DeviceManagementException e) { + String msg = "Error occurred "; + log.error(msg, e); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()).build(); + } + + } + + @Path("alerts/{alertType}/{deviceType}/{deviceId}") @POST @Consumes("application/json") @@ -312,11 +420,11 @@ public class GeoLocationBasedServiceImpl implements GeoLocationBasedService { int tenantId = DeviceMgtAPIUtils.getRealmService().getTenantManager().getTenantId(tenantDomain); AnalyticsDataAPI analyticsDataAPI = DeviceMgtAPIUtils.getAnalyticsDataAPI(); List searchResults = analyticsDataAPI.search(tenantId, tableName, query, - 0, - 100, - sortByFields); + 0, + 100, + sortByFields); List events = getEventBeans(analyticsDataAPI, tenantId, tableName, new ArrayList(), - searchResults); + searchResults); return Response.ok().entity(events).build(); } catch (AnalyticsException | UserStoreException e) { log.error("Failed to perform search on table: " + tableName + " : " + e.getMessage(), e); diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/geo/GeoGrid.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/geo/GeoGrid.java new file mode 100644 index 00000000000..86753673acf --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/geo/GeoGrid.java @@ -0,0 +1,110 @@ +package org.wso2.carbon.device.mgt.core.geo; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.device.mgt.common.Device; +import org.wso2.carbon.device.mgt.common.DeviceIdentifier; +import org.wso2.carbon.device.mgt.common.device.details.DeviceLocation; +import org.wso2.carbon.device.mgt.core.device.details.mgt.DeviceDetailsMgtException; +import org.wso2.carbon.device.mgt.core.device.details.mgt.DeviceInformationManager; +import org.wso2.carbon.device.mgt.core.device.details.mgt.impl.DeviceInformationManagerImpl; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public class GeoGrid { + private static Log log = LogFactory.getLog(GeoRectangle.class); + DeviceInformationManager deviceInformationManagerService = new DeviceInformationManagerImpl(); + private int horizontalDivisions; + private int verticalDivisions; + private double minLat; + private double maxLat; + private double minLong; + private double maxLong; + private double latDistance; + private double longitudeDistance; + private double latIncrement; + private double longIncrement; + private ArrayList geoRectangles = new ArrayList<>(); + + public GeoGrid(int horizontalDivisions, int verticalDivisions, double minLat, double maxLat, double minLong, + double maxLong) { + this.horizontalDivisions = horizontalDivisions; + this.verticalDivisions = verticalDivisions; + this.minLat = minLat; + this.maxLat = maxLat; + this.minLong = minLong; + this.maxLong = maxLong; + this.latDistance = maxLat - minLat; + this.longitudeDistance = maxLong - minLong; + this.latIncrement = this.latDistance / this.horizontalDivisions; + this.longIncrement = this.longitudeDistance / this.verticalDivisions; + this.createGeoRectangles(); + } + + private void createGeoRectangles() { + double minRectangleLat; + double maxRectangleLat; + double minRectangleLong; + double maxRectangleLong; + + for (int i = 0; i < verticalDivisions; i++) { + minRectangleLong = this.minLong + i * longIncrement; + if (i + 1 == verticalDivisions) { + maxRectangleLong = this.maxLong; + } else { + maxRectangleLong = this.minLong + (i + 1) * longIncrement; + } + + for (int m = 0; m < horizontalDivisions; m++) { + minRectangleLat = this.minLat + m * latIncrement; + maxRectangleLat = this.minLat + (m + 1) * latIncrement; + geoRectangles.add(new GeoRectangle(minRectangleLat, maxRectangleLat, minRectangleLong, maxRectangleLong)); + } + } + + } + + public ArrayList getGeoRectangles() { + return geoRectangles; + } + + public ArrayList getDevicesInGeoGrid(List devices) { + ArrayList devicesInGeoGrid = new ArrayList<>(); + for (Device device : devices) { + DeviceLocation location = null; + DeviceIdentifier deviceIdentifier = new DeviceIdentifier(device.getDeviceIdentifier(), device.getType()); + try { + location = deviceInformationManagerService.getDeviceLocation(deviceIdentifier); + } catch (DeviceDetailsMgtException e) { + String msg = "Exception occurred while retrieving device location." + deviceIdentifier; + log.error(msg, e); + return devicesInGeoGrid; + } + double locationLat = location.getLatitude(); + double locationLong = location.getLongitude(); + if (locationLat >= minLat && locationLat < maxLat && locationLong >= minLong && locationLong < maxLong) { + devicesInGeoGrid.add(device); + + } + } + return devicesInGeoGrid; + } + + public ArrayList placeDevicesInGeoRectangles(ArrayList devicesInGeoGrid) { + ArrayList remainingDevicesInGeoGrid = devicesInGeoGrid; + for (GeoRectangle geoRectangle : geoRectangles) { + Iterator remainingDevicesIterator = remainingDevicesInGeoGrid.iterator(); + while (remainingDevicesIterator.hasNext()) { + Device currentDevice = remainingDevicesIterator.next(); + if (geoRectangle.isDeviceInGeoRectangle(currentDevice)) { + geoRectangle.addDevice(currentDevice); + } + } + + } + return geoRectangles; + } + +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/geo/GeoRectangle.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/geo/GeoRectangle.java new file mode 100644 index 00000000000..bdc36234fa3 --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/geo/GeoRectangle.java @@ -0,0 +1,129 @@ +/* + * Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you 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.wso2.carbon.device.mgt.core.geo; + + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.wso2.carbon.device.mgt.common.Device; +import org.wso2.carbon.device.mgt.common.DeviceIdentifier; +import org.wso2.carbon.device.mgt.common.device.details.DeviceLocation; +import org.wso2.carbon.device.mgt.core.device.details.mgt.DeviceDetailsMgtException; +import org.wso2.carbon.device.mgt.core.device.details.mgt.DeviceInformationManager; +import org.wso2.carbon.device.mgt.core.device.details.mgt.impl.DeviceInformationManagerImpl; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class GeoRectangle{ + + private double minLat; + private double maxLat; + private double minLong; + private double maxLong; + private ArrayList devices=new ArrayList<>(); + + + DeviceInformationManager deviceInformationManagerService = new DeviceInformationManagerImpl(); + private static Log log = LogFactory.getLog(GeoRectangle.class); + + public GeoRectangle(double minLat, double maxLat, double minLong, double maxLong){ + this.minLat=minLat; + this.maxLat=maxLat; + this.minLong=minLong; + this.maxLong=maxLong; + } + + public Map getCenter(){ + Map centerCordinates = new HashMap(); + double centerLat = (minLat+maxLat)/2; + double centerLong = (minLong+maxLong)/2; + centerCordinates.put("Lat",centerLat); + centerCordinates.put("Long",centerLong); + return centerCordinates; + } + + public boolean isDeviceInGeoRectangle(Device device){ + DeviceIdentifier deviceIdentifier = new DeviceIdentifier(device.getDeviceIdentifier(),device.getType()); + try { + DeviceLocation location = deviceInformationManagerService.getDeviceLocation(deviceIdentifier); + double locationLat = location.getLatitude(); + double locationLong = location.getLongitude(); + if(locationLat >= minLat && locationLat < maxLat && locationLong >= minLong && locationLong getDevices(){ + return devices; + } + + public int getDeviceCount(){ + return devices.size(); + } + + public double getMinLat() { + return minLat; + } + + public void setMinLat(double minLat) { + this.minLat = minLat; + } + + public double getMaxLat() { + return maxLat; + } + + public void setMaxLat(double maxLat) { + this.maxLat = maxLat; + } + + public double getMinLong() { + return minLong; + } + + public void setMinLong(double minLong) { + this.minLong = minLong; + } + + public double getMaxLong() { + return maxLong; + } + + public void setMaxLong(double maxLong) { + this.maxLong = maxLong; + } + +} \ No newline at end of file