Device Location history view feature

feature/appm-store/pbac
achala piyarathna 5 years ago committed by Dharmakeerthi Lasantha
parent 852518f9af
commit 5b50cddea6

@ -11,13 +11,18 @@
"bootstrap": "^4.3.1",
"javascript-time-ago": "^2.0.1",
"keymirror": "^0.1.1",
"leaflet": "^1.3.4",
"leaflet-routing-machine": "^3.2.12",
"lodash.debounce": "^4.0.8",
"moment": "^2.24.0",
"moment": "latest",
"prop-types": "latest",
"rc-viewer": "0.0.9",
"react-bootstrap": "^1.0.0-beta.12",
"react-advanced-datetimerange-picker": "^1.0.8",
"react-highlight-words": "^0.16.0",
"react-image-viewer-zoom": "^1.0.36",
"react-infinite-scroller": "^1.2.4",
"react-leaflet": "^2.4.0",
"react-bootstrap": "^1.0.0-beta.12",
"react-moment": "^0.9.2",
"react-router": "^5.0.1",
"react-router-config": "^5.0.1",

@ -33,5 +33,10 @@
"color": "#008cc4",
"theme": "filled"
}
},
"geoMap": {
"url": "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
"attribution": "&copy; <a href=&quot;http://osm.org/copyright&quot;>OpenStreetMap</a> contributors",
"defaultZoomLevel": 16
}
}

@ -0,0 +1,636 @@
/* required styles */
.leaflet-pane,
.leaflet-tile,
.leaflet-marker-icon,
.leaflet-marker-shadow,
.leaflet-tile-container,
.leaflet-pane > svg,
.leaflet-pane > canvas,
.leaflet-zoom-box,
.leaflet-image-layer,
.leaflet-layer {
position: absolute;
left: 0;
top: 0;
}
.leaflet-container {
overflow: hidden;
}
.leaflet-tile,
.leaflet-marker-icon,
.leaflet-marker-shadow {
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
-webkit-user-drag: none;
}
/* Safari renders non-retina tile on retina better with this, but Chrome is worse */
.leaflet-safari .leaflet-tile {
image-rendering: -webkit-optimize-contrast;
}
/* hack that prevents hw layers "stretching" when loading new tiles */
.leaflet-safari .leaflet-tile-container {
width: 1600px;
height: 1600px;
-webkit-transform-origin: 0 0;
}
.leaflet-marker-icon,
.leaflet-marker-shadow {
display: block;
}
/* .leaflet-container svg: reset svg max-width decleration shipped in Joomla! (joomla.org) 3.x */
/* .leaflet-container img: map is broken in FF if you have max-width: 100% on tiles */
.leaflet-container .leaflet-overlay-pane svg,
.leaflet-container .leaflet-marker-pane img,
.leaflet-container .leaflet-shadow-pane img,
.leaflet-container .leaflet-tile-pane img,
.leaflet-container img.leaflet-image-layer {
max-width: none !important;
max-height: none !important;
}
.leaflet-container.leaflet-touch-zoom {
-ms-touch-action: pan-x pan-y;
touch-action: pan-x pan-y;
}
.leaflet-container.leaflet-touch-drag {
-ms-touch-action: pinch-zoom;
/* Fallback for FF which doesn't support pinch-zoom */
touch-action: none;
touch-action: pinch-zoom;
}
.leaflet-container.leaflet-touch-drag.leaflet-touch-zoom {
-ms-touch-action: none;
touch-action: none;
}
.leaflet-container {
-webkit-tap-highlight-color: transparent;
}
.leaflet-container a {
-webkit-tap-highlight-color: rgba(51, 181, 229, 0.4);
}
.leaflet-tile {
filter: inherit;
visibility: hidden;
}
.leaflet-tile-loaded {
visibility: inherit;
}
.leaflet-zoom-box {
width: 0;
height: 0;
-moz-box-sizing: border-box;
box-sizing: border-box;
z-index: 800;
}
/* workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=888319 */
.leaflet-overlay-pane svg {
-moz-user-select: none;
}
.leaflet-pane { z-index: 400; }
.leaflet-tile-pane { z-index: 200; }
.leaflet-overlay-pane { z-index: 400; }
.leaflet-shadow-pane { z-index: 500; }
.leaflet-marker-pane { z-index: 600; }
.leaflet-tooltip-pane { z-index: 650; }
.leaflet-popup-pane { z-index: 700; }
.leaflet-map-pane canvas { z-index: 100; }
.leaflet-map-pane svg { z-index: 200; }
.leaflet-vml-shape {
width: 1px;
height: 1px;
}
.lvml {
behavior: url(#default#VML);
display: inline-block;
position: absolute;
}
/* control positioning */
.leaflet-control {
position: relative;
z-index: 800;
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
pointer-events: auto;
}
.leaflet-top,
.leaflet-bottom {
position: absolute;
z-index: 1000;
pointer-events: none;
}
.leaflet-top {
top: 0;
}
.leaflet-right {
right: 0;
}
.leaflet-bottom {
bottom: 0;
}
.leaflet-left {
left: 0;
}
.leaflet-control {
float: left;
clear: both;
}
.leaflet-right .leaflet-control {
float: right;
}
.leaflet-top .leaflet-control {
margin-top: 10px;
}
.leaflet-bottom .leaflet-control {
margin-bottom: 10px;
}
.leaflet-left .leaflet-control {
margin-left: 10px;
}
.leaflet-right .leaflet-control {
margin-right: 10px;
}
/* zoom and fade animations */
.leaflet-fade-anim .leaflet-tile {
will-change: opacity;
}
.leaflet-fade-anim .leaflet-popup {
opacity: 0;
-webkit-transition: opacity 0.2s linear;
-moz-transition: opacity 0.2s linear;
-o-transition: opacity 0.2s linear;
transition: opacity 0.2s linear;
}
.leaflet-fade-anim .leaflet-map-pane .leaflet-popup {
opacity: 1;
}
.leaflet-zoom-animated {
-webkit-transform-origin: 0 0;
-ms-transform-origin: 0 0;
transform-origin: 0 0;
}
.leaflet-zoom-anim .leaflet-zoom-animated {
will-change: transform;
}
.leaflet-zoom-anim .leaflet-zoom-animated {
-webkit-transition: -webkit-transform 0.25s cubic-bezier(0,0,0.25,1);
-moz-transition: -moz-transform 0.25s cubic-bezier(0,0,0.25,1);
-o-transition: -o-transform 0.25s cubic-bezier(0,0,0.25,1);
transition: transform 0.25s cubic-bezier(0,0,0.25,1);
}
.leaflet-zoom-anim .leaflet-tile,
.leaflet-pan-anim .leaflet-tile {
-webkit-transition: none;
-moz-transition: none;
-o-transition: none;
transition: none;
}
.leaflet-zoom-anim .leaflet-zoom-hide {
visibility: hidden;
}
/* cursors */
.leaflet-interactive {
cursor: pointer;
}
.leaflet-grab {
cursor: -webkit-grab;
cursor: -moz-grab;
}
.leaflet-crosshair,
.leaflet-crosshair .leaflet-interactive {
cursor: crosshair;
}
.leaflet-popup-pane,
.leaflet-control {
cursor: auto;
}
.leaflet-dragging .leaflet-grab,
.leaflet-dragging .leaflet-grab .leaflet-interactive,
.leaflet-dragging .leaflet-marker-draggable {
cursor: move;
cursor: -webkit-grabbing;
cursor: -moz-grabbing;
}
/* marker & overlays interactivity */
.leaflet-marker-icon,
.leaflet-marker-shadow,
.leaflet-image-layer,
.leaflet-pane > svg path,
.leaflet-tile-container {
pointer-events: none;
}
.leaflet-marker-icon.leaflet-interactive,
.leaflet-image-layer.leaflet-interactive,
.leaflet-pane > svg path.leaflet-interactive {
pointer-events: visiblePainted; /* IE 9-10 doesn't have auto */
pointer-events: auto;
}
/* visual tweaks */
.leaflet-container {
background: #ddd;
outline: 0;
}
.leaflet-container a {
color: #0078A8;
}
.leaflet-container a.leaflet-active {
outline: 2px solid orange;
}
.leaflet-zoom-box {
border: 2px dotted #38f;
background: rgba(255,255,255,0.5);
}
/* general typography */
.leaflet-container {
font: 12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif;
}
/* general toolbar styles */
.leaflet-bar {
box-shadow: 0 1px 5px rgba(0,0,0,0.65);
border-radius: 4px;
}
.leaflet-bar a,
.leaflet-bar a:hover {
background-color: #fff;
border-bottom: 1px solid #ccc;
width: 26px;
height: 26px;
line-height: 26px;
display: block;
text-align: center;
text-decoration: none;
color: black;
}
.leaflet-bar a,
.leaflet-control-layers-toggle {
background-position: 50% 50%;
background-repeat: no-repeat;
display: block;
}
.leaflet-bar a:hover {
background-color: #f4f4f4;
}
.leaflet-bar a:first-child {
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
.leaflet-bar a:last-child {
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
border-bottom: none;
}
.leaflet-bar a.leaflet-disabled {
cursor: default;
background-color: #f4f4f4;
color: #bbb;
}
.leaflet-touch .leaflet-bar a {
width: 30px;
height: 30px;
line-height: 30px;
}
.leaflet-touch .leaflet-bar a:first-child {
border-top-left-radius: 2px;
border-top-right-radius: 2px;
}
.leaflet-touch .leaflet-bar a:last-child {
border-bottom-left-radius: 2px;
border-bottom-right-radius: 2px;
}
/* zoom control */
.leaflet-control-zoom-in,
.leaflet-control-zoom-out {
font: bold 18px 'Lucida Console', Monaco, monospace;
text-indent: 1px;
}
.leaflet-touch .leaflet-control-zoom-in, .leaflet-touch .leaflet-control-zoom-out {
font-size: 22px;
}
/* layers control */
.leaflet-control-layers {
box-shadow: 0 1px 5px rgba(0,0,0,0.4);
background: #fff;
border-radius: 5px;
}
.leaflet-control-layers-toggle {
background-image: url(images/layers.png);
width: 36px;
height: 36px;
}
.leaflet-retina .leaflet-control-layers-toggle {
background-image: url(images/layers-2x.png);
background-size: 26px 26px;
}
.leaflet-touch .leaflet-control-layers-toggle {
width: 44px;
height: 44px;
}
.leaflet-control-layers .leaflet-control-layers-list,
.leaflet-control-layers-expanded .leaflet-control-layers-toggle {
display: none;
}
.leaflet-control-layers-expanded .leaflet-control-layers-list {
display: block;
position: relative;
}
.leaflet-control-layers-expanded {
padding: 6px 10px 6px 6px;
color: #333;
background: #fff;
}
.leaflet-control-layers-scrollbar {
overflow-y: scroll;
overflow-x: hidden;
padding-right: 5px;
}
.leaflet-control-layers-selector {
margin-top: 2px;
position: relative;
top: 1px;
}
.leaflet-control-layers label {
display: block;
}
.leaflet-control-layers-separator {
height: 0;
border-top: 1px solid #ddd;
margin: 5px -10px 5px -6px;
}
/* Default icon URLs */
.leaflet-default-icon-path {
background-image: url(images/marker-icon.png);
}
/* attribution and scale controls */
.leaflet-container .leaflet-control-attribution {
background: #fff;
background: rgba(255, 255, 255, 0.7);
margin: 0;
}
.leaflet-control-attribution,
.leaflet-control-scale-line {
padding: 0 5px;
color: #333;
}
.leaflet-control-attribution a {
text-decoration: none;
}
.leaflet-control-attribution a:hover {
text-decoration: underline;
}
.leaflet-container .leaflet-control-attribution,
.leaflet-container .leaflet-control-scale {
font-size: 11px;
}
.leaflet-left .leaflet-control-scale {
margin-left: 5px;
}
.leaflet-bottom .leaflet-control-scale {
margin-bottom: 5px;
}
.leaflet-control-scale-line {
border: 2px solid #777;
border-top: none;
line-height: 1.1;
padding: 2px 5px 1px;
font-size: 11px;
white-space: nowrap;
overflow: hidden;
-moz-box-sizing: border-box;
box-sizing: border-box;
background: #fff;
background: rgba(255, 255, 255, 0.5);
}
.leaflet-control-scale-line:not(:first-child) {
border-top: 2px solid #777;
border-bottom: none;
margin-top: -2px;
}
.leaflet-control-scale-line:not(:first-child):not(:last-child) {
border-bottom: 2px solid #777;
}
.leaflet-touch .leaflet-control-attribution,
.leaflet-touch .leaflet-control-layers,
.leaflet-touch .leaflet-bar {
box-shadow: none;
}
.leaflet-touch .leaflet-control-layers,
.leaflet-touch .leaflet-bar {
border: 2px solid rgba(0,0,0,0.2);
background-clip: padding-box;
}
/* popup */
.leaflet-popup {
position: absolute;
text-align: center;
margin-bottom: 20px;
}
.leaflet-popup-content-wrapper {
padding: 1px;
text-align: left;
border-radius: 12px;
}
.leaflet-popup-content {
margin: 13px 19px;
line-height: 1.4;
}
.leaflet-popup-content p {
margin: 18px 0;
}
.leaflet-popup-tip-container {
width: 40px;
height: 20px;
position: absolute;
left: 50%;
margin-left: -20px;
overflow: hidden;
pointer-events: none;
}
.leaflet-popup-tip {
width: 17px;
height: 17px;
padding: 1px;
margin: -10px auto 0;
-webkit-transform: rotate(45deg);
-moz-transform: rotate(45deg);
-ms-transform: rotate(45deg);
-o-transform: rotate(45deg);
transform: rotate(45deg);
}
.leaflet-popup-content-wrapper,
.leaflet-popup-tip {
background: white;
color: #333;
box-shadow: 0 3px 14px rgba(0,0,0,0.4);
}
.leaflet-container a.leaflet-popup-close-button {
position: absolute;
top: 0;
right: 0;
padding: 4px 4px 0 0;
border: none;
text-align: center;
width: 18px;
height: 14px;
font: 16px/14px Tahoma, Verdana, sans-serif;
color: #c3c3c3;
text-decoration: none;
font-weight: bold;
background: transparent;
}
.leaflet-container a.leaflet-popup-close-button:hover {
color: #999;
}
.leaflet-popup-scrolled {
overflow: auto;
border-bottom: 1px solid #ddd;
border-top: 1px solid #ddd;
}
.leaflet-oldie .leaflet-popup-content-wrapper {
zoom: 1;
}
.leaflet-oldie .leaflet-popup-tip {
width: 24px;
margin: 0 auto;
-ms-filter: "progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";
filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678);
}
.leaflet-oldie .leaflet-popup-tip-container {
margin-top: -1px;
}
.leaflet-oldie .leaflet-control-zoom,
.leaflet-oldie .leaflet-control-layers,
.leaflet-oldie .leaflet-popup-content-wrapper,
.leaflet-oldie .leaflet-popup-tip {
border: 1px solid #999;
}
/* div icon */
.leaflet-div-icon {
background: #fff;
border: 1px solid #666;
}
/* Tooltip */
/* Base styles for the element that has a tooltip */
.leaflet-tooltip {
position: absolute;
padding: 6px;
background-color: #fff;
border: 1px solid #fff;
border-radius: 3px;
color: #222;
white-space: nowrap;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
pointer-events: none;
box-shadow: 0 1px 3px rgba(0,0,0,0.4);
}
.leaflet-tooltip.leaflet-clickable {
cursor: pointer;
pointer-events: auto;
}
.leaflet-tooltip-top:before,
.leaflet-tooltip-bottom:before,
.leaflet-tooltip-left:before,
.leaflet-tooltip-right:before {
position: absolute;
pointer-events: none;
border: 6px solid transparent;
background: transparent;
content: "";
}
/* Directions */
.leaflet-tooltip-bottom {
margin-top: 6px;
}
.leaflet-tooltip-top {
margin-top: -6px;
}
.leaflet-tooltip-bottom:before,
.leaflet-tooltip-top:before {
left: 50%;
margin-left: -6px;
}
.leaflet-tooltip-top:before {
bottom: 0;
margin-bottom: -12px;
border-top-color: #fff;
}
.leaflet-tooltip-bottom:before {
top: 0;
margin-top: -12px;
margin-left: -6px;
border-bottom-color: #fff;
}
.leaflet-tooltip-left {
margin-left: -6px;
}
.leaflet-tooltip-right {
margin-left: 6px;
}
.leaflet-tooltip-left:before,
.leaflet-tooltip-right:before {
top: 50%;
margin-top: -6px;
}
.leaflet-tooltip-left:before {
right: 0;
margin-right: -12px;
border-left-color: #fff;
}
.leaflet-tooltip-right:before {
left: 0;
margin-left: -12px;
border-right-color: #fff;
}

@ -0,0 +1,81 @@
/*
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
*
* Entgra (pvt) Ltd. 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.
*/
import React, {Component, Fragment} from "react";
import {
Map,
TileLayer,
Marker,
Polyline, Popup, Tooltip
} from "react-leaflet";
import {withConfigContext} from "../../../context/ConfigContext";
class GeoCustomMap extends Component {
constructor(props) {
super(props);
}
/**
* Polyline draw for historical locations
* @param locationData - location data object
* @returns content
*/
polylineMarker = (locationData) => {
const polyMarkers = locationData
.map(locationPoint => {
return [locationPoint.latitude, locationPoint.longitude]
});
return (
<div style={{display: "none"}}>{
<Polyline color="green" positions={polyMarkers}>
<Popup>on the way</Popup>
</Polyline>
}</div>
);
};
render() {
const locationData = this.props.locationData;
const config = this.props.context;
const attribution = config.geoMap.attribution;
const url = config.geoMap.url;
const startingPoint = [locationData[0].latitude, locationData[0].longitude];
const zoom = config.geoMap.defaultZoomLevel;
return (
<div style={{backgroundColor: "#ffffff", borderRadius: 5, padding: 5}}>
<Map center={startingPoint} zoom={zoom}>
<TileLayer
url={url}
attribution={attribution}
/>
<Fragment>
{this.polylineMarker(locationData)}
<Marker position={startingPoint}>
<Tooltip>Starting Location</Tooltip>
</Marker>
</Fragment>
</Map>
</div>
);
}
}
export default withConfigContext(GeoCustomMap);

@ -0,0 +1,10 @@
.leaflet-container {
align-content: center;
padding-top: 80px;
height: 550px;
width: 100%;
}
.controllerDiv {
padding-bottom: 30px;
}

@ -0,0 +1,292 @@
/*
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
*
* Entgra (pvt) Ltd. 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.
*/
import React from "react";
import moment from "moment";
import DateTimeRangeContainer from "react-advanced-datetimerange-picker";
import {Button, Select, message, notification, Tag, Tooltip, Empty} from "antd";
import axios from "axios";
import {withConfigContext} from "../../../context/ConfigContext";
import GeoCustomMap from "../geo-custom-map/GeoCustomMap";
import "./GeoDashboard.css";
class GeoDashboard extends React.Component {
constructor(props) {
super(props);
let start = moment(new Date());
let end = moment(start)
.add(5, "days")
.subtract(1, "minute");
this.state = {
deviceData: [],
selectedDevice: '',
locationData: [],
// currentLocation: [],
loading: false,
start: start,
end: end,
buttonTooltip: "Fetch Locations",
};
}
componentDidMount() {
this.fetchDevices();
// this.fetchCurrentLocation();
}
/**
* Call back on apply button in the date time picker
* @param startDate - start date
* @param endDate - end date
*/
applyCallback = (startDate, endDate) => {
console.log("Apply Callback");
this.setState({
start: startDate,
end: endDate
});
};
/**
* Api call handle on fetch location date button
*/
handleApiCall = () => {
if (this.state.selectedDevice && this.state.start && this.state.end) {
const toInMills = moment(this.state.end);
const fromInMills = moment(this.state.start);
const deviceType = this.state.selectedDevice.type;
const deviceId = this.state.selectedDevice.deviceIdentifier;
const config = this.props.context;
this.setState({loading: true});
axios.get(window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.deviceMgt
+ "/devices/" + deviceType + "/" + deviceId + "/location-history?" + "from=" + fromInMills + "&to=" +
toInMills,).then(res => {
if (res.status === 200) {
const locationData = JSON.parse(res.data.data);
this.setState({
loading: false,
locationData,
});
}
}).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) {
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to fetch locations......",
});
}
this.setState({loading: false});
console.log(error);
});
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Please provide a date range and a device.",
});
}
};
/**
* Device dropdown list handler
* @param e - selected device data
*/
handleDeviceList = (e) => {
let selectedDevice = this.state.deviceData[e];
this.setState({selectedDevice})
};
/**
* render fetch location button
*/
fetchLocationButton = () => {
let flag;
let toolTip = "";
if (this.state.selectedDevice === "") {
flag = true;
toolTip = "Please select a Device";
}
return (
<Tooltip placement="rightBottom" title={toolTip}>
<Button disabled={flag}
onClick={this.handleApiCall}>
Fetch Locations
</Button>
</Tooltip>);
};
/**
* fetches device data to populate the dropdown list
*/
fetchDevices = () => {
const config = this.props.context;
this.setState({loading: true});
axios.get(
window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.deviceMgt +
"/devices?excludeStatus=REMOVED",).then(res => {
if (res.status === 200) {
this.setState({
loading: false,
deviceData: res.data.data.devices,
});
}
}).catch((error) => {
if (error.hasOwnProperty("response") && error.response.status === 401) {
message.error('You are not logged in');
window.location.href = window.location.origin + '/entgra/login';
} else {
notification["error"]({
message: "There was a problem",
duration: 0,
description:
"Error occurred while trying to load devices.",
});
}
this.setState({loading: false});
});
};
/**
* Geo Dashboard controller
*/
controllerBar = () => {
let now = new Date();
let start = moment(
new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0, 0)
);
let end = moment(start)
.add(1, "days")
.subtract(1, "seconds");
let ranges = {
"Today Only": [moment(start), moment(end)],
"Yesterday Only": [
moment(start).subtract(1, "days"),
moment(end).subtract(1, "days")
],
"3 Days": [moment(start).subtract(3, "days"), moment(end)],
"5 Days": [moment(start).subtract(5, "days"), moment(end)],
"1 Week": [moment(start).subtract(7, "days"), moment(end)],
"2 Weeks": [moment(start).subtract(14, "days"), moment(end)],
"1 Month": [moment(start).subtract(1, "months"), moment(end)],
};
let local = {
format: "DD-MM-YYYY HH:mm",
sundayFirst: false
};
let maxDate = moment(start).add(24, "hour");
let value =
`
${this.state.start.format("DD-MM-YYYY HH:mm")} - ${this.state.end.format("DD-MM-YYYY HH:mm")}
`;
let {deviceData} = this.state;
return (
<div className="controllerDiv">
<Button style={{marginRight: 20}}>
<DateTimeRangeContainer
ranges={ranges}
start={this.state.start}
end={this.state.end}
local={local}
maxDate={maxDate}
applyCallback={this.applyCallback}
>
{value}
</DateTimeRangeContainer>
</Button>
<Select
showSearch
style={{width: 220, marginRight: 20}}
placeholder="Select a Device"
optionFilterProp="children"
onChange={this.handleDeviceList}
filterOption={(input, option) =>
option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
>
{deviceData.map((device, index) =>
<Select.Option key={index} value={index}>
{device.name + " "}{this.statusTag(device)}
</Select.Option>)}
</Select>
{this.fetchLocationButton()}
</div>
);
};
/**
* Creates color based tags on device status
* @param device - device object
*/
statusTag = (device) => {
const status = device.enrolmentInfo.status.toLowerCase();
let color = "#f9ca24";
switch (status) {
case "active":
color = "#badc58";
break;
case "created":
color = "#6ab04c";
break;
case "inactive":
color = "#f9ca24";
break;
case "blocked":
color = "#636e72";
break;
}
return <Tag color={color}>{status}</Tag>
};
render() {
const locationData = [...this.state.locationData];
return (
<div className="container">
{this.controllerBar()}
{(locationData.length > 0) ?
<GeoCustomMap locationData={locationData}/>
:
<Empty/>
}
</div>
);
}
}
export default withConfigContext(GeoDashboard);

@ -21,6 +21,7 @@
<head>
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=0"/>
<title>Entgra Device Management</title>
<link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.3.3/leaflet.css'>
</head>
<div id="root"></div>
</html>

@ -25,7 +25,7 @@ import {
Card
} from "antd";
import {Link} from "react-router-dom";
import DeviceTable from "../../../components/Devices/DevicesTable";
import GeoDashboard from "../../../components/Geo/geo-dashboard/GeoDashboard";
const {Paragraph} = Typography;
@ -50,12 +50,11 @@ class Geo extends React.Component {
</Breadcrumb>
<div className="wrap">
<h3>Geo</h3>
<Paragraph>Lorem ipsum dolor sit amet, est similique constituto at, quot inermis id mel, an
illud incorrupte nam.</Paragraph>
<Paragraph>Geo Location Service</Paragraph>
</div>
</PageHeader>
<div style={{background: '#f0f2f5', padding: 24, minHeight: 720}}>
<div style={{background: '#f0f2f5', padding: 24, minHeight: 720, alignItems: "center"}}>
<GeoDashboard/>
</div>
</div>
);

@ -269,6 +269,13 @@ public interface DeviceManagementService {
@QueryParam("ownership")
@Size(max = 45)
String ownership,
@ApiParam(
name = "excludeStatus",
value = "Provide the devices that excludes the given status",
required = false)
@QueryParam("excludeStatus")
@Size(max = 45)
String excludeStatus,
@ApiParam(
name = "status",
value = "Provide the device status details, such as active or inactive.",
@ -476,6 +483,78 @@ public interface DeviceManagementService {
@HeaderParam("If-Modified-Since")
String ifModifiedSince);
@GET
@Path("/{deviceType}/{deviceId}/location-history")
@ApiOperation(
consumes = "application/json",
produces = "application/json",
httpMethod = "GET",
value = "Getting the Location Details of a Device",
notes = "Get the location details of a device during a define time period.",
response = Response.class,
tags = "Device Management",
extensions = {
@Extension(properties = {
@ExtensionProperty(name = Constants.SCOPE, value = "perm:devices:details")
})
}
)
@ApiResponses(value = {
@ApiResponse(
code = 200,
message = "OK.",
response = Response.class,
responseHeaders = {
@ResponseHeader(
name = "Content-Type",
description = "The content type of the body"),
@ResponseHeader(
name = "ETag",
description = "Entity Tag of the response resource.\n" +
"Used by caches, or in conditional requests."),
@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 getDeviceLocationInfo(
@ApiParam(
name = "device-type",
value = "The device type, such as ios, android, or windows.",
required = true)
@PathParam("deviceType")
@Size(max = 45)
String deviceType,
@ApiParam(
name = "deviceId",
value = "The device ID.",
required = true)
@PathParam("deviceId") String deviceId,
@ApiParam(
name = "from",
value = "Define the time to start getting the geo location history of the device in " +
"milliseconds.",
required = true)
@QueryParam("from") long from,
@ApiParam(
name = "to",
value = "Define the time to finish getting the geo location history of the device in " +
"milliseconds.",
required = true)
@QueryParam("to") long to);
@GET
@Path("/type/any/id/{id}")
@ApiOperation(

@ -42,13 +42,9 @@ import org.apache.commons.logging.LogFactory;
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.exceptions.DeviceManagementException;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceTypeNotFoundException;
import org.wso2.carbon.device.mgt.common.EnrolmentInfo;
import org.wso2.carbon.device.mgt.common.Feature;
import org.wso2.carbon.device.mgt.common.FeatureManager;
import org.wso2.carbon.device.mgt.common.exceptions.InvalidConfigurationException;
import org.wso2.carbon.device.mgt.common.exceptions.InvalidDeviceException;
import org.wso2.carbon.device.mgt.common.PaginationRequest;
import org.wso2.carbon.device.mgt.common.PaginationResult;
import org.wso2.carbon.device.mgt.common.app.mgt.Application;
@ -57,6 +53,11 @@ import org.wso2.carbon.device.mgt.common.authorization.DeviceAccessAuthorization
import org.wso2.carbon.device.mgt.common.authorization.DeviceAccessAuthorizationService;
import org.wso2.carbon.device.mgt.common.device.details.DeviceInfo;
import org.wso2.carbon.device.mgt.common.device.details.DeviceLocation;
import org.wso2.carbon.device.mgt.common.device.details.DeviceLocationHistory;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceTypeNotFoundException;
import org.wso2.carbon.device.mgt.common.exceptions.InvalidConfigurationException;
import org.wso2.carbon.device.mgt.common.exceptions.InvalidDeviceException;
import org.wso2.carbon.device.mgt.common.operation.mgt.Activity;
import org.wso2.carbon.device.mgt.common.operation.mgt.Operation;
import org.wso2.carbon.device.mgt.common.operation.mgt.OperationManagementException;
@ -80,6 +81,7 @@ import org.wso2.carbon.device.mgt.jaxrs.beans.ErrorResponse;
import org.wso2.carbon.device.mgt.jaxrs.beans.OperationList;
import org.wso2.carbon.device.mgt.jaxrs.beans.OperationRequest;
import org.wso2.carbon.device.mgt.jaxrs.service.api.DeviceManagementService;
import org.wso2.carbon.device.mgt.jaxrs.service.impl.util.InputValidationException;
import org.wso2.carbon.device.mgt.jaxrs.service.impl.util.RequestValidationUtil;
import org.wso2.carbon.device.mgt.jaxrs.util.DeviceMgtAPIUtils;
import org.wso2.carbon.policy.mgt.common.PolicyManagementException;
@ -145,6 +147,7 @@ public class DeviceManagementServiceImpl implements DeviceManagementService {
@QueryParam("role") String role,
@QueryParam("ownership") String ownership,
@QueryParam("status") String status,
@QueryParam("excludeStatus") String excludeStatus,
@QueryParam("groupId") int groupId,
@QueryParam("since") String since,
@HeaderParam("If-Modified-Since") String ifModifiedSince,
@ -186,6 +189,10 @@ public class DeviceManagementServiceImpl implements DeviceManagementService {
RequestValidationUtil.validateStatus(status);
request.setStatus(status);
}
if (excludeStatus != null && !excludeStatus.isEmpty()) {
RequestValidationUtil.validateStatus(excludeStatus);
request.setExcludeStatus(excludeStatus);
}
if (groupId != 0) {
request.setGroupId(groupId);
}
@ -466,6 +473,68 @@ public class DeviceManagementServiceImpl implements DeviceManagementService {
return Response.status(Response.Status.OK).entity(device).build();
}
@Path("/{deviceType}/{deviceId}/location-history")
@GET
@Consumes("application/json")
@Produces("application/json")
public Response getDeviceLocationInfo(@PathParam("deviceType") String deviceType,
@PathParam("deviceId") String deviceId,
@QueryParam("from") long from, @QueryParam("to") long to) {
List<DeviceLocationHistory> deviceLocationHistory;
String errorMessage;
try {
RequestValidationUtil.validateDeviceIdentifier(deviceType, deviceId);
DeviceManagementProviderService dms = DeviceMgtAPIUtils.getDeviceManagementService();
DeviceAccessAuthorizationService deviceAccessAuthorizationService =
DeviceMgtAPIUtils.getDeviceAccessAuthorizationService();
String authorizedUser = CarbonContext.getThreadLocalCarbonContext().getUsername();
DeviceIdentifier deviceIdentifier = new DeviceIdentifier(deviceId, deviceType);
deviceIdentifier.setId(deviceId);
deviceIdentifier.setType(deviceType);
if (deviceAccessAuthorizationService == null) {
errorMessage = "Device access authorization service is failed";
log.error(errorMessage);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(
new ErrorResponse.ErrorResponseBuilder().setCode(500l).setMessage(errorMessage).build()).build();
}
if (!deviceAccessAuthorizationService.isUserAuthorized(deviceIdentifier, authorizedUser)) {
String msg = "User '" + authorizedUser + "' is not authorized to retrieve the given device id '" +
deviceId + "'";
log.error(msg);
return Response.status(Response.Status.UNAUTHORIZED).entity(
new ErrorResponse.ErrorResponseBuilder().setCode(401l).setMessage(msg).build()).build();
}
if (from == 0 || to == 0) {
errorMessage = "Invalid values for from/to";
log.error(errorMessage);
return Response.status(Response.Status.BAD_REQUEST).entity(
new ErrorResponse.ErrorResponseBuilder().setCode(400l).setMessage(errorMessage)).build();
}
deviceLocationHistory = dms.getDeviceLocationInfo(deviceIdentifier, from, to);
} catch (DeviceManagementException e) {
errorMessage = "Error occurred while fetching the device information.";
log.error(errorMessage, e);
return Response.serverError().entity(
new ErrorResponse.ErrorResponseBuilder().setCode(500l).setMessage(errorMessage).build()).build();
} catch (DeviceAccessAuthorizationException e) {
errorMessage = "Error occurred while checking the device authorization.";
log.error(errorMessage, e);
return Response.serverError().entity(
new ErrorResponse.ErrorResponseBuilder().setCode(500l).setMessage(errorMessage).build()).build();
} catch (InputValidationException e){
errorMessage = "Invalid device Id or device type";
log.error(errorMessage, e);
return Response.status(Response.Status.BAD_REQUEST).entity(
new ErrorResponse.ErrorResponseBuilder().setCode(400l).setMessage(errorMessage)).build();
}
return Response.status(Response.Status.OK).entity(deviceLocationHistory).build();
}
@GET
@Path("/type/any/id/{id}")
@Override

@ -105,6 +105,7 @@ public class DeviceManagementServiceImplTest {
private static final String DEFAULT_ROLE = "admin";
private static final String DEFAULT_OWNERSHIP = "BYOD";
private static final String DEFAULT_STATUS = "ACTIVE";
private static final String DEFAULT_EXCLUDED_STATUS = "REMOVE";
private static final String DEFAULT_DATE_FORMAT = "EEE, d MMM yyyy HH:mm:ss Z";
private DeviceManagementService deviceManagementService;
private DeviceAccessAuthorizationService deviceAccessAuthorizationService;
@ -173,7 +174,7 @@ public class DeviceManagementServiceImplTest {
.toReturn(this.deviceAccessAuthorizationService);
Response response = this.deviceManagementService
.getDevices(TEST_DEVICE_NAME, TEST_DEVICE_TYPE, DEFAULT_USERNAME, null, DEFAULT_ROLE, DEFAULT_OWNERSHIP,
DEFAULT_STATUS, 1, null, null, false, 10, 5);
DEFAULT_EXCLUDED_STATUS, DEFAULT_STATUS, 1, null, null, false, 10, 5);
Assert.assertEquals(response.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
}
@ -190,19 +191,19 @@ public class DeviceManagementServiceImplTest {
Response response = this.deviceManagementService
.getDevices(null, TEST_DEVICE_TYPE, DEFAULT_USERNAME, null, DEFAULT_ROLE, DEFAULT_OWNERSHIP,
DEFAULT_STATUS, 1, null, null, false, 10, 5);
DEFAULT_EXCLUDED_STATUS, DEFAULT_STATUS, 1, null, null, false, 10, 5);
Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode());
response = this.deviceManagementService
.getDevices(TEST_DEVICE_NAME, TEST_DEVICE_TYPE, DEFAULT_USERNAME, null, null, DEFAULT_OWNERSHIP,
DEFAULT_STATUS, 1, null, null, false, 10, 5);
DEFAULT_EXCLUDED_STATUS, DEFAULT_STATUS, 1, null, null, false, 10, 5);
Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode());
response = this.deviceManagementService
.getDevices(TEST_DEVICE_NAME, TEST_DEVICE_TYPE, null, null, null, DEFAULT_OWNERSHIP,
DEFAULT_STATUS, 1, null, null, false, 10, 5);
DEFAULT_EXCLUDED_STATUS, DEFAULT_STATUS, 1, null, null, false, 10, 5);
Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode());
response = this.deviceManagementService
.getDevices(TEST_DEVICE_NAME, TEST_DEVICE_TYPE, null, null, null, DEFAULT_OWNERSHIP,
DEFAULT_STATUS, 1, null, null, true, 10, 5);
DEFAULT_EXCLUDED_STATUS, DEFAULT_STATUS, 1, null, null, true, 10, 5);
Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode());
}
@ -311,7 +312,7 @@ public class DeviceManagementServiceImplTest {
.toReturn(null);
Response response = this.deviceManagementService
.getDevices(null, TEST_DEVICE_TYPE, DEFAULT_USERNAME, null, DEFAULT_ROLE, DEFAULT_OWNERSHIP,
DEFAULT_STATUS, 1, null, null, false, 10, 5);
DEFAULT_EXCLUDED_STATUS, DEFAULT_STATUS, 1, null, null, false, 10, 5);
Assert.assertEquals(response.getStatus(), Response.Status.INTERNAL_SERVER_ERROR.getStatusCode());
}
@ -329,11 +330,11 @@ public class DeviceManagementServiceImplTest {
Response response = this.deviceManagementService
.getDevices(null, TEST_DEVICE_TYPE, DEFAULT_USERNAME, null, DEFAULT_ROLE, DEFAULT_OWNERSHIP,
DEFAULT_STATUS, 1, null, null, false, 10, 5);
DEFAULT_EXCLUDED_STATUS, DEFAULT_STATUS, 1, null, null, false, 10, 5);
Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode());
response = this.deviceManagementService
.getDevices(null, TEST_DEVICE_TYPE, null, DEFAULT_USERNAME, DEFAULT_ROLE, DEFAULT_OWNERSHIP,
DEFAULT_STATUS, 1, null, null, false, 10, 5);
DEFAULT_EXCLUDED_STATUS, DEFAULT_STATUS, 1, null, null, false, 10, 5);
Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode());
}
@ -352,8 +353,8 @@ public class DeviceManagementServiceImplTest {
Mockito.when(this.deviceAccessAuthorizationService.isDeviceAdminUser()).thenReturn(false);
Response response = this.deviceManagementService
.getDevices(null, TEST_DEVICE_TYPE, "newuser", null, DEFAULT_ROLE, DEFAULT_OWNERSHIP, DEFAULT_STATUS, 1,
null, null, false, 10, 5);
.getDevices(null, TEST_DEVICE_TYPE, "newuser", null, DEFAULT_ROLE, DEFAULT_OWNERSHIP, DEFAULT_EXCLUDED_STATUS,
DEFAULT_STATUS, 1, null, null, false, 10, 5);
Assert.assertEquals(response.getStatus(), Response.Status.UNAUTHORIZED.getStatusCode());
Mockito.reset(this.deviceAccessAuthorizationService);
}
@ -372,15 +373,15 @@ public class DeviceManagementServiceImplTest {
Response response = this.deviceManagementService
.getDevices(null, TEST_DEVICE_TYPE, DEFAULT_USERNAME, null, DEFAULT_ROLE, DEFAULT_OWNERSHIP,
DEFAULT_STATUS, 1, null, ifModifiedSince, false, 10, 5);
DEFAULT_EXCLUDED_STATUS, DEFAULT_STATUS, 1, null, ifModifiedSince, false, 10, 5);
Assert.assertEquals(response.getStatus(), Response.Status.NOT_MODIFIED.getStatusCode());
response = this.deviceManagementService
.getDevices(null, TEST_DEVICE_TYPE, DEFAULT_USERNAME, null, DEFAULT_ROLE, DEFAULT_OWNERSHIP,
DEFAULT_STATUS, 1, null, ifModifiedSince, true, 10, 5);
DEFAULT_EXCLUDED_STATUS, DEFAULT_STATUS, 1, null, ifModifiedSince, true, 10, 5);
Assert.assertEquals(response.getStatus(), Response.Status.NOT_MODIFIED.getStatusCode());
response = this.deviceManagementService
.getDevices(null, TEST_DEVICE_TYPE, DEFAULT_USERNAME, null, DEFAULT_ROLE, DEFAULT_OWNERSHIP,
DEFAULT_STATUS, 1, null, "ErrorModifiedSince", false, 10, 5);
DEFAULT_EXCLUDED_STATUS, DEFAULT_STATUS, 1, null, "ErrorModifiedSince", false, 10, 5);
Assert.assertEquals(response.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
}
@ -398,15 +399,15 @@ public class DeviceManagementServiceImplTest {
Response response = this.deviceManagementService
.getDevices(null, TEST_DEVICE_TYPE, DEFAULT_USERNAME, null, DEFAULT_ROLE, DEFAULT_OWNERSHIP,
DEFAULT_STATUS, 1, since, null, false, 10, 5);
DEFAULT_EXCLUDED_STATUS, DEFAULT_STATUS, 1, since, null, false, 10, 5);
Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode());
response = this.deviceManagementService
.getDevices(null, TEST_DEVICE_TYPE, DEFAULT_USERNAME, null, DEFAULT_ROLE, DEFAULT_OWNERSHIP,
DEFAULT_STATUS, 1, since, null, true, 10, 5);
DEFAULT_EXCLUDED_STATUS, DEFAULT_STATUS, 1, since, null, true, 10, 5);
Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode());
response = this.deviceManagementService
.getDevices(null, TEST_DEVICE_TYPE, DEFAULT_USERNAME, null, DEFAULT_ROLE, DEFAULT_OWNERSHIP,
DEFAULT_STATUS, 1, "ErrorSince", null, false, 10, 5);
DEFAULT_EXCLUDED_STATUS, DEFAULT_STATUS, 1, "ErrorSince", null, false, 10, 5);
Assert.assertEquals(response.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
}
@ -426,7 +427,7 @@ public class DeviceManagementServiceImplTest {
Response response = this.deviceManagementService
.getDevices(null, TEST_DEVICE_TYPE, DEFAULT_USERNAME, null, DEFAULT_ROLE, DEFAULT_OWNERSHIP,
DEFAULT_STATUS, 1, null, null, false, 10, 5);
DEFAULT_EXCLUDED_STATUS, DEFAULT_STATUS, 1, null, null, false, 10, 5);
Assert.assertEquals(response.getStatus(), Response.Status.INTERNAL_SERVER_ERROR.getStatusCode());
Mockito.reset(this.deviceManagementProviderService);
}
@ -446,7 +447,7 @@ public class DeviceManagementServiceImplTest {
Response response = this.deviceManagementService
.getDevices(null, TEST_DEVICE_TYPE, DEFAULT_USERNAME, null, DEFAULT_ROLE, DEFAULT_OWNERSHIP,
DEFAULT_STATUS, 1, null, null, false, 10, 5);
DEFAULT_EXCLUDED_STATUS, DEFAULT_STATUS, 1, null, null, false, 10, 5);
Assert.assertEquals(response.getStatus(), Response.Status.INTERNAL_SERVER_ERROR.getStatusCode());
Mockito.reset(this.deviceAccessAuthorizationService);
}

@ -33,6 +33,7 @@ public class PaginationRequest {
private String owner;
private String ownerPattern;
private String status;
private String excludeStatus;
private String deviceType;
private String deviceName;
private String ownership;
@ -85,6 +86,14 @@ public class PaginationRequest {
this.status = status;
}
public String getExcludeStatus() {
return excludeStatus;
}
public void setExcludeStatus(String excludeStatus) {
this.excludeStatus = excludeStatus;
}
public String getDeviceType() {
return deviceType;
}

@ -55,6 +55,46 @@ public class DeviceLocation implements Serializable {
private String country;
@ApiModelProperty(name = "updatedTime", value = "Update time of the device.", required = true)
private Date updatedTime;
@ApiModelProperty(name = "altitude", value = "Device altitude.", required = true)
private Double altitude;
@ApiModelProperty(name = "speed", value = "Device speed.", required = true)
private Float speed;
@ApiModelProperty(name = "bearing", value = "Device bearing.", required = true)
private Float bearing;
@ApiModelProperty(name = "distance", value = "Device distance.", required = true)
private Double distance;
public Double getDistance() {
return distance;
}
public void setDistance(Double distance) {
this.distance = distance;
}
public Double getAltitude() {
return altitude;
}
public Float getSpeed() {
return speed;
}
public void setSpeed(Float speed) {
this.speed = speed;
}
public Float getBearing() {
return bearing;
}
public void setBearing(Float bearing) {
this.bearing = bearing;
}
public void setAltitude(Double altitude) {
this.altitude = altitude;
}
public int getDeviceId() {
return deviceId;

@ -0,0 +1,164 @@
/*
* Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved.
*
* Entgra (pvt) Ltd. 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.common.device.details;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
@ApiModel(value = "DeviceLocationHistory", description = "This class carries all information related to the device location History" +
"details provided by a device.")
public class DeviceLocationHistory implements Serializable {
@ApiModelProperty(name = "deviceId", value = "Device id", required = true)
private int deviceId;
@ApiModelProperty(name = "geoHash", value = "Geo Hash", required = true)
private String geoHash;
@ApiModelProperty(name = "deviceType", value = "Device type", required = true)
private String deviceType;
@ApiModelProperty(name = "deviceIdentifier", value = "Device Id Name", required = true)
private String deviceIdentifier;
@ApiModelProperty(name = "latitude", value = "Device GPS latitude.", required = true)
private Double latitude;
@ApiModelProperty(name = "longitude", value = "Device GPS longitude.", required = true)
private Double longitude;
@ApiModelProperty(name = "tenantId", value = "Tenant Id.", required = true)
private int tenantId;
@ApiModelProperty(name = "altitude", value = "Device altitude.", required = true)
private Double altitude;
@ApiModelProperty(name = "speed", value = "Device speed.", required = true)
private Float speed;
@ApiModelProperty(name = "bearing", value = "Device bearing.", required = true)
private Float bearing;
@ApiModelProperty(name = "distance", value = "Device distance.", required = true)
private Double distance;
@ApiModelProperty(name = "timestamp", value = "Timestamp.", required = true)
private Long timestamp;
@ApiModelProperty(name = "owner", value = "Owner.", required = true)
private String owner;
public DeviceLocationHistory() {
}
public String getGeoHash() {
return geoHash;
}
public void setGeoHash(String geoHash) {
this.geoHash = geoHash;
}
public String getDeviceType() {
return deviceType;
}
public void setDeviceType(String deviceType) {
this.deviceType = deviceType;
}
public int getDeviceId() {
return deviceId;
}
public void setDeviceId(int deviceId) {
this.deviceId = deviceId;
}
public String getDeviceIdentifier() {
return deviceIdentifier;
}
public void setDeviceIdentifier(String deviceIdentifier) {
this.deviceIdentifier = deviceIdentifier;
}
public Double getLatitude() {
return latitude;
}
public void setLatitude(Double latitude) {
this.latitude = latitude;
}
public Double getLongitude() {
return longitude;
}
public void setLongitude(Double longitude) {
this.longitude = longitude;
}
public int getTenantId() {
return tenantId;
}
public void setTenantId(int tenantId) {
this.tenantId = tenantId;
}
public Double getAltitude() {
return altitude;
}
public void setAltitude(double altitude) {
this.altitude = altitude;
}
public Float getSpeed() {
return speed;
}
public void setSpeed(Float speed) {
this.speed = speed;
}
public Float getBearing() {
return bearing;
}
public void setBearing(Float bearing) {
this.bearing = bearing;
}
public Double getDistance() {
return distance;
}
public void setDistance(Double distance) {
this.distance = distance;
}
public Long getTimestamp() {
return timestamp;
}
public void setTimestamp(Long timestamp) {
this.timestamp = timestamp;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
}

@ -40,6 +40,7 @@ import org.wso2.carbon.device.mgt.common.DeviceIdentifier;
import org.wso2.carbon.device.mgt.common.EnrolmentInfo;
import org.wso2.carbon.device.mgt.common.EnrolmentInfo.Status;
import org.wso2.carbon.device.mgt.common.PaginationRequest;
import org.wso2.carbon.device.mgt.common.device.details.DeviceLocationHistory;
import org.wso2.carbon.device.mgt.common.configuration.mgt.DevicePropertyInfo;
import org.wso2.carbon.device.mgt.core.dto.DeviceType;
import org.wso2.carbon.device.mgt.core.geo.GeoCluster;
@ -550,4 +551,15 @@ public interface DeviceDAO {
int tenantId,
String fromDate,
String toDate) throws DeviceManagementDAOException;
/**
* Retrieve device location information
* @param deviceIdentifier Device Identifier object
* @param from Specified start timestamp
* @param to Specified end timestamp
* @return
* @throws DeviceManagementDAOException
*/
List<DeviceLocationHistory> getDeviceLocationInfo(DeviceIdentifier deviceIdentifier, long from, long to)
throws DeviceManagementDAOException;
}

@ -42,6 +42,7 @@ import org.wso2.carbon.device.mgt.common.DeviceIdentifier;
import org.wso2.carbon.device.mgt.common.EnrolmentInfo;
import org.wso2.carbon.device.mgt.common.EnrolmentInfo.Status;
import org.wso2.carbon.device.mgt.common.PaginationRequest;
import org.wso2.carbon.device.mgt.common.device.details.DeviceLocationHistory;
import org.wso2.carbon.device.mgt.common.configuration.mgt.DevicePropertyInfo;
import org.wso2.carbon.device.mgt.core.dao.DeviceDAO;
import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOException;
@ -939,6 +940,8 @@ public abstract class AbstractDeviceDAOImpl implements DeviceDAO {
boolean isOwnershipProvided = false;
String status = request.getStatus();
boolean isStatusProvided = false;
String excludeStatus = request.getExcludeStatus();
boolean isExcludeStatusProvided = false;
Date since = request.getSince();
boolean isSinceProvided = false;
try {
@ -987,6 +990,11 @@ public abstract class AbstractDeviceDAOImpl implements DeviceDAO {
isStatusProvided = true;
}
if (excludeStatus != null && !excludeStatus.isEmpty()) {
sql = sql + " AND e.STATUS != ?";
isExcludeStatusProvided = true;
}
stmt = conn.prepareStatement(sql);
stmt.setInt(1, tenantId);
int paramIdx = 2;
@ -1012,6 +1020,9 @@ public abstract class AbstractDeviceDAOImpl implements DeviceDAO {
if (isStatusProvided) {
stmt.setString(paramIdx++, request.getStatus());
}
if (isExcludeStatusProvided) {
stmt.setString(paramIdx++, excludeStatus);
}
rs = stmt.executeQuery();
if (rs.next()) {
deviceCount = rs.getInt("DEVICE_COUNT");
@ -1574,6 +1585,44 @@ public abstract class AbstractDeviceDAOImpl implements DeviceDAO {
}
@Override
public List<DeviceLocationHistory> getDeviceLocationInfo(DeviceIdentifier deviceIdentifier, long from, long to)
throws DeviceManagementDAOException {
Connection conn;
PreparedStatement stmt = null;
ResultSet rs = null;
List<DeviceLocationHistory> deviceLocationHistories = new ArrayList<>();
try {
conn = this.getConnection();
String sql =
"SELECT DEVICE_ID, TENANT_ID, DEVICE_ID_NAME, DEVICE_TYPE_NAME, LATITUDE, LONGITUDE, SPEED, " +
"HEADING, TIMESTAMP, GEO_HASH, DEVICE_OWNER, DEVICE_ALTITUDE, DISTANCE " +
"FROM DM_DEVICE_HISTORY_LAST_SEVEN_DAYS " +
"WHERE DEVICE_ID_NAME = ? " +
"AND DEVICE_TYPE_NAME = ? " +
"AND TIMESTAMP >= ? " +
"AND TIMESTAMP <= ?";
stmt = conn.prepareStatement(sql);
stmt.setString(1, deviceIdentifier.getId());
stmt.setString(2, deviceIdentifier.getType());
stmt.setLong(3, from);
stmt.setLong(4, to);
rs = stmt.executeQuery();
while (rs.next()) {
deviceLocationHistories.add(DeviceManagementDAOUtil.loadDeviceLocation(rs));
}
} catch (SQLException e) {
String errMessage = "Error occurred while obtaining the DB connection to get device location information";
log.error(errMessage, e);
throw new DeviceManagementDAOException(errMessage, e);
} finally {
DeviceManagementDAOUtil.cleanupResources(stmt, rs);
}
return deviceLocationHistories;
}
public void deleteDevices(List<String> deviceIdentifiers, List<Integer> deviceIds, List<Integer> enrollmentIds)
throws DeviceManagementDAOException {
Connection conn;

@ -61,6 +61,8 @@ public class GenericDeviceDAOImpl extends AbstractDeviceDAOImpl {
boolean isOwnershipProvided = false;
String status = request.getStatus();
boolean isStatusProvided = false;
String excludeStatus = request.getExcludeStatus();
boolean isExcludeStatusProvided = false;
Date since = request.getSince();
boolean isSinceProvided = false;
try {
@ -115,6 +117,11 @@ public class GenericDeviceDAOImpl extends AbstractDeviceDAOImpl {
sql = sql + " AND e.STATUS = ?";
isStatusProvided = true;
}
//Add the query for exclude status
if (excludeStatus != null && !excludeStatus.isEmpty()) {
sql = sql + " AND e.STATUS != ?";
isExcludeStatusProvided = true;
}
sql = sql + " LIMIT ?,?";
@ -143,6 +150,9 @@ public class GenericDeviceDAOImpl extends AbstractDeviceDAOImpl {
if (isStatusProvided) {
stmt.setString(paramIdx++, status);
}
if (isExcludeStatusProvided) {
stmt.setString(paramIdx++, excludeStatus);
}
stmt.setInt(paramIdx++, request.getStartIndex());
stmt.setInt(paramIdx, request.getRowCount());
rs = stmt.executeQuery();
@ -182,6 +192,8 @@ public class GenericDeviceDAOImpl extends AbstractDeviceDAOImpl {
boolean isOwnershipProvided = false;
String status = request.getStatus();
boolean isStatusProvided = false;
String excludeStatus = request.getExcludeStatus();
boolean isExcludeStatusProvided = false;
Date since = request.getSince();
boolean isSinceProvided = false;
@ -242,6 +254,11 @@ public class GenericDeviceDAOImpl extends AbstractDeviceDAOImpl {
sql = sql + " AND e.STATUS = ?";
isStatusProvided = true;
}
//Add the query for exclude status
if (excludeStatus != null && !excludeStatus.isEmpty()) {
sql = sql + " AND e.STATUS != ?";
isExcludeStatusProvided = true;
}
sql = sql + " LIMIT ?,?";
@ -273,6 +290,9 @@ public class GenericDeviceDAOImpl extends AbstractDeviceDAOImpl {
if (isStatusProvided) {
stmt.setString(paramIdx++, status);
}
if (isExcludeStatusProvided) {
stmt.setString(paramIdx++, excludeStatus);
}
stmt.setInt(paramIdx++, request.getStartIndex());
stmt.setInt(paramIdx, request.getRowCount());

@ -64,6 +64,8 @@ public class OracleDeviceDAOImpl extends AbstractDeviceDAOImpl {
boolean isOwnershipProvided = false;
String status = request.getStatus();
boolean isStatusProvided = false;
String excludeStatus = request.getExcludeStatus();
boolean isExcludeStatusProvided = false;
Date since = request.getSince();
boolean isSinceProvided = false;
try {
@ -118,6 +120,11 @@ public class OracleDeviceDAOImpl extends AbstractDeviceDAOImpl {
sql = sql + " AND e.STATUS = ?";
isStatusProvided = true;
}
//Add the query for exclude status
if (excludeStatus != null && !excludeStatus.isEmpty()) {
sql = sql + " AND e.STATUS != ?";
isExcludeStatusProvided = true;
}
sql = sql + " ORDER BY ENROLMENT_ID OFFSET ? ROWS FETCH NEXT ? ROWS ONLY";
@ -146,6 +153,9 @@ public class OracleDeviceDAOImpl extends AbstractDeviceDAOImpl {
if (isStatusProvided) {
stmt.setString(paramIdx++, status);
}
if (isExcludeStatusProvided) {
stmt.setString(paramIdx++, excludeStatus);
}
stmt.setInt(paramIdx++, request.getStartIndex());
stmt.setInt(paramIdx, request.getRowCount());
rs = stmt.executeQuery();
@ -184,6 +194,8 @@ public class OracleDeviceDAOImpl extends AbstractDeviceDAOImpl {
boolean isOwnershipProvided = false;
String status = request.getStatus();
boolean isStatusProvided = false;
String excludeStatus = request.getExcludeStatus();
boolean isExcludeStatusProvided = false;
Date since = request.getSince();
boolean isSinceProvided = false;
@ -244,6 +256,11 @@ public class OracleDeviceDAOImpl extends AbstractDeviceDAOImpl {
sql = sql + " AND e.STATUS = ?";
isStatusProvided = true;
}
//Add the query for exclude status
if (excludeStatus != null && !excludeStatus.isEmpty()) {
sql = sql + " AND e.STATUS != ?";
isExcludeStatusProvided = true;
}
sql = sql + " ORDER BY ENROLMENT_ID OFFSET ? ROWS FETCH NEXT ? ROWS ONLY";
@ -275,6 +292,9 @@ public class OracleDeviceDAOImpl extends AbstractDeviceDAOImpl {
if (isStatusProvided) {
stmt.setString(paramIdx++, status);
}
if (isExcludeStatusProvided) {
stmt.setString(paramIdx++, excludeStatus);
}
stmt.setInt(paramIdx++, request.getStartIndex());
stmt.setInt(paramIdx, request.getRowCount());

@ -61,6 +61,8 @@ public class PostgreSQLDeviceDAOImpl extends AbstractDeviceDAOImpl {
boolean isOwnershipProvided = false;
String status = request.getStatus();
boolean isStatusProvided = false;
String excludeStatus = request.getExcludeStatus();
boolean isExcludeStatusProvided = false;
Date since = request.getSince();
boolean isSinceProvided = false;
try {
@ -102,6 +104,11 @@ public class PostgreSQLDeviceDAOImpl extends AbstractDeviceDAOImpl {
sql = sql + " AND e.STATUS = ?";
isStatusProvided = true;
}
//Add the query for exclude status
if (excludeStatus != null && !excludeStatus.isEmpty()) {
sql = sql + " AND e.STATUS != ?";
isExcludeStatusProvided = true;
}
sql = sql + " LIMIT ? OFFSET ?";
@ -127,6 +134,9 @@ public class PostgreSQLDeviceDAOImpl extends AbstractDeviceDAOImpl {
if (isStatusProvided) {
stmt.setString(paramIdx++, status);
}
if (isExcludeStatusProvided) {
stmt.setString(paramIdx++, excludeStatus);
}
stmt.setInt(paramIdx++, request.getRowCount());
stmt.setInt(paramIdx, request.getStartIndex());
rs = stmt.executeQuery();
@ -165,6 +175,8 @@ public class PostgreSQLDeviceDAOImpl extends AbstractDeviceDAOImpl {
boolean isOwnershipProvided = false;
String status = request.getStatus();
boolean isStatusProvided = false;
String excludeStatus = request.getExcludeStatus();
boolean isExcludeStatusProvided = false;
Date since = request.getSince();
boolean isSinceProvided = false;
@ -225,6 +237,11 @@ public class PostgreSQLDeviceDAOImpl extends AbstractDeviceDAOImpl {
sql = sql + " AND e.STATUS = ?";
isStatusProvided = true;
}
//Add the query for exclude status
if (excludeStatus != null && !excludeStatus.isEmpty()) {
sql = sql + " AND e.STATUS != ?";
isExcludeStatusProvided = true;
}
sql = sql + " LIMIT ? OFFSET ?";
@ -256,6 +273,9 @@ public class PostgreSQLDeviceDAOImpl extends AbstractDeviceDAOImpl {
if (isStatusProvided) {
stmt.setString(paramIdx++, status);
}
if (isExcludeStatusProvided) {
stmt.setString(paramIdx++, excludeStatus);
}
stmt.setInt(paramIdx, request.getRowCount());
stmt.setInt(paramIdx++, request.getStartIndex());

@ -61,6 +61,8 @@ public class SQLServerDeviceDAOImpl extends AbstractDeviceDAOImpl {
boolean isOwnershipProvided = false;
String status = request.getStatus();
boolean isStatusProvided = false;
String excludeStatus = request.getExcludeStatus();
boolean isExcludeStatusProvided = false;
Date since = request.getSince();
boolean isSinceProvided = false;
try {
@ -115,6 +117,11 @@ public class SQLServerDeviceDAOImpl extends AbstractDeviceDAOImpl {
sql = sql + " AND e.STATUS = ?";
isStatusProvided = true;
}
//Add the query for exclude status
if (excludeStatus != null && !excludeStatus.isEmpty()) {
sql = sql + " AND e.STATUS != ?";
isExcludeStatusProvided = true;
}
sql = sql + " ORDER BY ENROLMENT_ID OFFSET ? ROWS FETCH NEXT ? ROWS ONLY";
@ -143,6 +150,9 @@ public class SQLServerDeviceDAOImpl extends AbstractDeviceDAOImpl {
if (isStatusProvided) {
stmt.setString(paramIdx++, status);
}
if (isExcludeStatusProvided) {
stmt.setString(paramIdx++, excludeStatus);
}
stmt.setInt(paramIdx++, request.getStartIndex());
stmt.setInt(paramIdx, request.getRowCount());
rs = stmt.executeQuery();
@ -181,6 +191,8 @@ public class SQLServerDeviceDAOImpl extends AbstractDeviceDAOImpl {
boolean isOwnershipProvided = false;
String status = request.getStatus();
boolean isStatusProvided = false;
String excludeStatus = request.getExcludeStatus();
boolean isExcludeStatusProvided = false;
Date since = request.getSince();
boolean isSinceProvided = false;
@ -241,6 +253,11 @@ public class SQLServerDeviceDAOImpl extends AbstractDeviceDAOImpl {
sql = sql + " AND e.STATUS = ?";
isStatusProvided = true;
}
//Add the query for exclude status
if (excludeStatus != null && !excludeStatus.isEmpty()) {
sql = sql + " AND e.STATUS != ?";
isExcludeStatusProvided = true;
}
sql = sql + " ORDER BY ENROLMENT_ID OFFSET ? ROWS FETCH NEXT ? ROWS ONLY";
@ -272,6 +289,9 @@ public class SQLServerDeviceDAOImpl extends AbstractDeviceDAOImpl {
if (isStatusProvided) {
stmt.setString(paramIdx++, status);
}
if (isExcludeStatusProvided) {
stmt.setString(paramIdx++, excludeStatus);
}
stmt.setInt(paramIdx++, request.getStartIndex());
stmt.setInt(paramIdx, request.getRowCount());

@ -23,6 +23,8 @@ import org.wso2.carbon.context.CarbonContext;
import org.wso2.carbon.device.mgt.common.Device;
import org.wso2.carbon.device.mgt.common.EnrolmentInfo;
import org.wso2.carbon.device.mgt.common.device.details.DeviceInfo;
import org.wso2.carbon.device.mgt.common.device.details.DeviceLocation;
import org.wso2.carbon.device.mgt.common.device.details.DeviceLocationHistory;
import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOException;
import org.wso2.carbon.device.mgt.core.dto.DeviceType;
import org.wso2.carbon.device.mgt.core.internal.DeviceManagementDataHolder;
@ -245,4 +247,23 @@ public final class DeviceManagementDAOUtil {
deviceInfo.setUpdatedTime(new java.util.Date(rs.getLong("UPDATE_TIMESTAMP")));
return deviceInfo;
}
public static DeviceLocationHistory loadDeviceLocation(ResultSet rs) throws SQLException {
DeviceLocationHistory deviceLocationHistory = new DeviceLocationHistory();
deviceLocationHistory.setDeviceId(rs.getInt("DEVICE_ID"));
deviceLocationHistory.setDeviceIdentifier(rs.getString("DEVICE_ID_NAME"));
deviceLocationHistory.setTenantId(rs.getInt("TENANT_ID"));
deviceLocationHistory.setDeviceType(rs.getString("DEVICE_TYPE_NAME"));
deviceLocationHistory.setLatitude(rs.getDouble("LATITUDE"));
deviceLocationHistory.setLongitude(rs.getDouble("LONGITUDE"));
deviceLocationHistory.setSpeed(rs.getFloat("SPEED"));
deviceLocationHistory.setBearing(rs.getFloat("HEADING"));
deviceLocationHistory.setAltitude(rs.getDouble("DEVICE_ALTITUDE"));
deviceLocationHistory.setDistance(rs.getDouble("DISTANCE"));
deviceLocationHistory.setOwner(rs.getString("DEVICE_OWNER"));
deviceLocationHistory.setTimestamp(rs.getLong("TIMESTAMP"));
deviceLocationHistory.setGeoHash(rs.getString("GEO_HASH"));
return deviceLocationHistory;
}
}

@ -19,9 +19,12 @@
package org.wso2.carbon.device.mgt.core.device.details.mgt;
import org.wso2.carbon.device.mgt.common.Device;
import org.wso2.carbon.device.mgt.common.DeviceIdentifier;
import org.wso2.carbon.device.mgt.common.EnrolmentInfo;
import org.wso2.carbon.device.mgt.common.device.details.DeviceInfo;
import org.wso2.carbon.device.mgt.common.device.details.DeviceLocation;
import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOException;
import java.util.List;

@ -0,0 +1,4 @@
package org.wso2.carbon.device.mgt.core.device.details.mgt;
public interface LocationCallback {
}

@ -19,6 +19,8 @@
package org.wso2.carbon.device.mgt.core.device.details.mgt.dao;
import org.wso2.carbon.device.mgt.common.Device;
import org.wso2.carbon.device.mgt.common.EnrolmentInfo;
import org.wso2.carbon.device.mgt.common.device.details.DeviceInfo;
import org.wso2.carbon.device.mgt.common.device.details.DeviceLocation;
@ -100,6 +102,16 @@ public interface DeviceDetailsDAO {
*/
void deleteDeviceLocation(int deviceId, int enrollmentId) throws DeviceDetailsMgtDAOException;
/**
* Add device location information to the database
* @param device Device object
* @param deviceLocation Device Location Object
* @param tenantId Tenant Id
* @throws DeviceDetailsMgtDAOException
*/
void addDeviceLocationInfo(Device device, DeviceLocation deviceLocation, int tenantId)
throws DeviceDetailsMgtDAOException;
// /**
// * This method will add device application to database.
// * @param deviceApplication - Device application

@ -21,6 +21,8 @@ package org.wso2.carbon.device.mgt.core.device.details.mgt.dao.impl;
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.EnrolmentInfo;
import org.wso2.carbon.device.mgt.common.device.details.DeviceInfo;
import org.wso2.carbon.device.mgt.common.device.details.DeviceLocation;
import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOFactory;
@ -238,8 +240,9 @@ public class DeviceDetailsDAOImpl implements DeviceDetailsDAO {
try {
conn = this.getConnection();
stmt = conn.prepareStatement("INSERT INTO DM_DEVICE_LOCATION (DEVICE_ID, LATITUDE, LONGITUDE, STREET1, " +
"STREET2, CITY, ZIP, STATE, COUNTRY, GEO_HASH, UPDATE_TIMESTAMP, ENROLMENT_ID) " +
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
"STREET2, CITY, ZIP, STATE, COUNTRY, GEO_HASH, UPDATE_TIMESTAMP, ENROLMENT_ID, ALTITUDE, SPEED, BEARING, " +
"DISTANCE) " +
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
stmt.setInt(1, deviceLocation.getDeviceId());
stmt.setDouble(2, deviceLocation.getLatitude());
stmt.setDouble(3, deviceLocation.getLongitude());
@ -252,6 +255,10 @@ public class DeviceDetailsDAOImpl implements DeviceDetailsDAO {
stmt.setString(10, GeoHashGenerator.encodeGeohash(deviceLocation));
stmt.setLong(11, System.currentTimeMillis());
stmt.setInt(12, enrollmentId);
stmt.setDouble(13, deviceLocation.getAltitude());
stmt.setFloat(14, deviceLocation.getSpeed());
stmt.setFloat(15, deviceLocation.getBearing());
stmt.setDouble(16, deviceLocation.getDistance());
stmt.execute();
} catch (SQLException e) {
throw new DeviceDetailsMgtDAOException("Error occurred while adding the device location to database.", e);
@ -317,6 +324,45 @@ public class DeviceDetailsDAOImpl implements DeviceDetailsDAO {
}
}
@Override
public void addDeviceLocationInfo(Device device, DeviceLocation deviceLocation, int tenantId)
throws DeviceDetailsMgtDAOException {
Connection conn;
PreparedStatement stmt = null;
String errMessage;
try {
conn = this.getConnection();
stmt = conn.prepareStatement(
"INSERT INTO " +
"DM_DEVICE_HISTORY_LAST_SEVEN_DAYS " +
"(DEVICE_ID, DEVICE_ID_NAME, TENANT_ID, DEVICE_TYPE_NAME, LATITUDE, LONGITUDE, SPEED, HEADING, " +
"TIMESTAMP, GEO_HASH, DEVICE_OWNER, DEVICE_ALTITUDE, DISTANCE) " +
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
stmt.setInt(1, device.getId());
stmt.setString(2, device.getDeviceIdentifier());
stmt.setInt(3, tenantId);
stmt.setString(4, device.getType());
stmt.setDouble(5, deviceLocation.getLatitude());
stmt.setDouble(6, deviceLocation.getLongitude());
stmt.setFloat(7, deviceLocation.getSpeed());
stmt.setFloat(8, deviceLocation.getBearing());
stmt.setLong(9, System.currentTimeMillis());
stmt.setString(10, GeoHashGenerator.encodeGeohash(deviceLocation));
stmt.setString(11, device.getEnrolmentInfo().getOwner());
stmt.setDouble(12, deviceLocation.getAltitude());
stmt.setDouble(13, deviceLocation.getDistance());
stmt.execute();
} catch (SQLException e) {
errMessage = "Error occurred while updating the device location information to database.";
log.error(errMessage);
throw new DeviceDetailsMgtDAOException(errMessage, e);
} finally {
DeviceManagementDAOUtil.cleanupResources(stmt, null);
}
}
private Connection getConnection() throws SQLException {
return DeviceManagementDAOFactory.getConnection();
}

@ -54,7 +54,6 @@ public class DeviceInformationManagerImpl implements DeviceInformationManager {
private static final String LOCATION_EVENT_STREAM_DEFINITION = "org.wso2.iot.LocationStream";
private static final String DEVICE_INFO_EVENT_STREAM_DEFINITION = "org.wso2.iot.DeviceInfoStream";
public DeviceInformationManagerImpl() {
this.deviceDAO = DeviceManagementDAOFactory.getDeviceDAO();
this.deviceDetailsDAO = DeviceManagementDAOFactory.getDeviceDetailsDAO();
@ -212,6 +211,8 @@ public class DeviceInformationManagerImpl implements DeviceInformationManager {
deviceLocation.setDeviceId(device.getId());
DeviceManagementDAOFactory.beginTransaction();
deviceDAO.updateDevice(device, CarbonContext.getThreadLocalCarbonContext().getTenantId());
deviceDetailsDAO.addDeviceLocationInfo(device, deviceLocation,
CarbonContext.getThreadLocalCarbonContext().getTenantId());
deviceDetailsDAO.deleteDeviceLocation(deviceLocation.getDeviceId(), device.getEnrolmentInfo().getId());
deviceDetailsDAO.addDeviceLocation(deviceLocation, device.getEnrolmentInfo().getId());
if (DeviceManagerUtil.isPublishLocationResponseEnabled()) {
@ -219,7 +220,11 @@ public class DeviceInformationManagerImpl implements DeviceInformationManager {
Object[] payload = new Object[]{
deviceLocation.getUpdatedTime().getTime(),
deviceLocation.getLatitude(),
deviceLocation.getLongitude()
deviceLocation.getLongitude(),
deviceLocation.getAltitude(),
deviceLocation.getSpeed(),
deviceLocation.getBearing(),
deviceLocation.getDistance()
};
DeviceManagerUtil.getEventPublisherService().publishEvent(
LOCATION_EVENT_STREAM_DEFINITION, "1.0.0", metaData, new Object[0], payload

@ -55,6 +55,7 @@ import org.wso2.carbon.device.mgt.common.configuration.mgt.AmbiguousConfiguratio
import org.wso2.carbon.device.mgt.common.configuration.mgt.ConfigurationManagementException;
import org.wso2.carbon.device.mgt.common.configuration.mgt.DeviceConfiguration;
import org.wso2.carbon.device.mgt.common.configuration.mgt.PlatformConfiguration;
import org.wso2.carbon.device.mgt.common.device.details.DeviceLocationHistory;
import org.wso2.carbon.device.mgt.common.license.mgt.License;
import org.wso2.carbon.device.mgt.common.operation.mgt.Activity;
import org.wso2.carbon.device.mgt.common.operation.mgt.Operation;
@ -715,6 +716,18 @@ public interface DeviceManagementProviderService {
*/
List<DeviceType> getDeviceTypes() throws DeviceManagementException;
/**
* This retrieves the device location histories
*
* @param deviceIdentifier Device Identifier object
* @param from Specified start timestamp
* @param to Specified end timestamp
* @throws DeviceManagementException
* @return list of device's location histories
*/
List<DeviceLocationHistory> getDeviceLocationInfo(DeviceIdentifier deviceIdentifier, long from, long to)
throws DeviceManagementException;
/**
* This retrieves the device pull notification payload and passes to device type pull notification subscriber.
* @throws PullNotificationExecutionFailedException

@ -79,6 +79,7 @@ import org.wso2.carbon.device.mgt.common.configuration.mgt.DevicePropertyInfo;
import org.wso2.carbon.device.mgt.common.configuration.mgt.PlatformConfiguration;
import org.wso2.carbon.device.mgt.common.device.details.DeviceInfo;
import org.wso2.carbon.device.mgt.common.device.details.DeviceLocation;
import org.wso2.carbon.device.mgt.common.device.details.DeviceLocationHistory;
import org.wso2.carbon.device.mgt.common.enrollment.notification.EnrollmentNotificationConfiguration;
import org.wso2.carbon.device.mgt.common.enrollment.notification.EnrollmentNotifier;
import org.wso2.carbon.device.mgt.common.enrollment.notification.EnrollmentNotifierException;
@ -2822,6 +2823,36 @@ public class DeviceManagementProviderServiceImpl implements DeviceManagementProv
}
}
@Override
public List<DeviceLocationHistory> getDeviceLocationInfo(DeviceIdentifier deviceIdentifier, long from, long to)
throws DeviceManagementException {
if (log.isDebugEnabled()) {
log.debug("Get device location information");
}
List<DeviceLocationHistory> deviceLocationHistory;
String errMessage;
try {
DeviceManagementDAOFactory.openConnection();
deviceLocationHistory = deviceDAO.getDeviceLocationInfo(deviceIdentifier, from, to);
} catch (DeviceManagementDAOException e) {
errMessage = "Error occurred in getDeviceLocationInfo";
log.error(errMessage, e);
throw new DeviceManagementException(errMessage, e);
} catch (SQLException e) {
errMessage = "Error occurred while opening a connection to the data source";
log.error(errMessage, e);
throw new DeviceManagementException(errMessage, e);
} finally {
DeviceManagementDAOFactory.closeConnection();
}
return deviceLocationHistory;
}
@Override
public void notifyPullNotificationSubscriber(DeviceIdentifier deviceIdentifier, Operation operation)
throws PullNotificationExecutionFailedException {
@ -3159,11 +3190,19 @@ public class DeviceManagementProviderServiceImpl implements DeviceManagementProv
}
}
/**
* Extracting device location properties
* @param device Device object
*/
private void extractDeviceLocationToUpdate(Device device) {
List<Device.Property> properties = device.getProperties();
if (properties != null) {
String latitude = null;
String longitude = null;
String altitude = null;
String speed = null;
String bearing = null;
String distance = null;
for (Device.Property p : properties) {
if (p.getName().equalsIgnoreCase("latitude")) {
latitude = p.getValue();
@ -3171,6 +3210,18 @@ public class DeviceManagementProviderServiceImpl implements DeviceManagementProv
if (p.getName().equalsIgnoreCase("longitude")) {
longitude = p.getValue();
}
if (p.getName().equalsIgnoreCase("altitude")) {
altitude = p.getValue();
}
if (p.getName().equalsIgnoreCase("speed")) {
speed = p.getValue();
}
if (p.getName().equalsIgnoreCase("bearing")) {
bearing = p.getValue();
}
if (p.getName().equalsIgnoreCase("distance")) {
distance = p.getValue();
}
}
if (latitude != null && longitude != null && !latitude.isEmpty() && !longitude.isEmpty()) {
DeviceLocation deviceLocation = new DeviceLocation();
@ -3178,8 +3229,12 @@ public class DeviceManagementProviderServiceImpl implements DeviceManagementProv
deviceLocation.setDeviceIdentifier(new DeviceIdentifier(device.getDeviceIdentifier(),
device.getType()));
try {
deviceLocation.setAltitude(Double.parseDouble(altitude));
deviceLocation.setLatitude(Double.parseDouble(latitude));
deviceLocation.setLongitude(Double.parseDouble(longitude));
deviceLocation.setDistance(Double.parseDouble(distance));
deviceLocation.setSpeed(Float.parseFloat(speed));
deviceLocation.setBearing(Float.parseFloat(bearing));
DeviceInformationManager deviceInformationManager = new DeviceInformationManagerImpl();
deviceInformationManager.addDeviceLocation(deviceLocation);
} catch (Exception e) {
@ -3187,7 +3242,8 @@ public class DeviceManagementProviderServiceImpl implements DeviceManagementProv
// a warning for reference.
log.warn("Error occurred while trying to add '" + device.getType() + "' device '" +
device.getDeviceIdentifier() + "' (id:'" + device.getId() + "') location (lat:" + latitude +
", lon:" + longitude + ") due to:" + e.getMessage());
", lon:" + longitude + ", altitude: " + altitude +
", speed: " + speed + ", bearing:" + bearing + ", distance: " + distance + ") due to:" + e.getMessage());
}
}
}

@ -458,6 +458,10 @@ CREATE TABLE IF NOT EXISTS DM_DEVICE_LOCATION (
COUNTRY VARCHAR(45) NULL,
GEO_HASH VARCHAR(45) NULL,
UPDATE_TIMESTAMP BIGINT(15) NOT NULL,
ALTITUDE DOUBLE NULL,
SPEED FLOAT NULL,
BEARING FLOAT NULL,
DISTANCE DOUBLE NULL,
PRIMARY KEY (ID),
CONSTRAINT DM_DEVICE_LOCATION_DEVICE
FOREIGN KEY (DEVICE_ID)
@ -505,6 +509,25 @@ CREATE TABLE IF NOT EXISTS DM_DEVICE_DETAIL (
ON UPDATE NO ACTION
);
CREATE TABLE IF NOT EXISTS DM_DEVICE_HISTORY_LAST_SEVEN_DAYS
(
ID INTEGER AUTO_INCREMENT NOT NULL,
DEVICE_ID INT NOT NULL,
DEVICE_ID_NAME VARCHAR(255) NOT NULL,
TENANT_ID INT NOT NULL,
DEVICE_TYPE_NAME VARCHAR(45) NOT NULL,
LATITUDE DOUBLE NULL,
LONGITUDE DOUBLE NULL,
SPEED FLOAT NULL,
HEADING FLOAT NULL,
TIMESTAMP BIGINT(15) NOT NULL,
GEO_HASH VARCHAR(45) NULL,
DEVICE_OWNER VARCHAR(45) NULL,
DEVICE_ALTITUDE DOUBLE NULL,
DISTANCE DOUBLE NULL,
PRIMARY KEY (ID)
);
-- POLICY AND DEVICE GROUP MAPPING --
CREATE TABLE IF NOT EXISTS DM_DEVICE_GROUP_POLICY (
ID INT NOT NULL AUTO_INCREMENT,

@ -507,6 +507,10 @@ CREATE TABLE DM_DEVICE_LOCATION (
COUNTRY VARCHAR(45) NULL,
GEO_HASH VARCHAR(45) NULL,
UPDATE_TIMESTAMP BIGINT NOT NULL,
ALTITUDE FLOAT NULL,
SPEED FLOAT NULL,
BEARING FLOAT NULL,
DISTANCE FLOAT NULL,
PRIMARY KEY (ID),
INDEX DM_DEVICE_LOCATION_DEVICE_idx (DEVICE_ID ASC),
INDEX DM_DEVICE_LOCATION_GEO_hashx (GEO_HASH ASC),
@ -523,6 +527,26 @@ CREATE TABLE DM_DEVICE_LOCATION (
ON UPDATE NO ACTION
);
IF NOT EXISTS (SELECT * FROM SYS.OBJECTS WHERE OBJECT_ID = OBJECT_ID(N'[DBO].[DM_DEVICE_HISTORY_LAST_SEVEN_DAYS]') AND TYPE IN (N'U'))
CREATE TABLE IF NOT EXISTS DM_DEVICE_HISTORY_LAST_SEVEN_DAYS
(
ID INTEGER IDENTITY (1,1) NOT NULL,
DEVICE_ID INTEGER NOT NULL,
DEVICE_ID_NAME VARCHAR(255) NOT NULL,
TENANT_ID INTEGER NOT NULL,
DEVICE_TYPE_NAME VARCHAR(45) NOT NULL,
LATITUDE FLOAT NULL,
LONGITUDE FLOAT NULL,
SPEED FLOAT NULL,
HEADING FLOAT NULL,
TIMESTAMP BIGINT(15) NOT NULL,
GEO_HASH VARCHAR(45) NULL,
DEVICE_OWNER VARCHAR(45) NULL,
DEVICE_ALTITUDE FLOAT NULL,
DISTANCE FLOAT NULL,
PRIMARY KEY (ID)
);
IF NOT EXISTS (SELECT * FROM SYS.OBJECTS WHERE OBJECT_ID = OBJECT_ID(N'[DBO].[DM_DEVICE_DETAIL]') AND TYPE IN (N'U'))
CREATE TABLE DM_DEVICE_DETAIL (
ID INTEGER IDENTITY(1,1) NOT NULL,

@ -520,6 +520,10 @@ CREATE TABLE IF NOT EXISTS DM_DEVICE_LOCATION (
COUNTRY VARCHAR(45) NULL,
GEO_HASH VARCHAR(45) NULL,
UPDATE_TIMESTAMP BIGINT(15) NOT NULL,
ALTITUDE DOUBLE NULL,
SPEED FLOAT NULL,
BEARING FLOAT NULL,
DISTANCE DOUBLE NULL,
PRIMARY KEY (ID),
INDEX DM_DEVICE_LOCATION_DEVICE_idx (DEVICE_ID ASC),
INDEX DM_DEVICE_LOCATION_GEO_hashx (GEO_HASH ASC),
@ -537,6 +541,26 @@ CREATE TABLE IF NOT EXISTS DM_DEVICE_LOCATION (
)
ENGINE = InnoDB;
CREATE TABLE IF NOT EXISTS DM_DEVICE_HISTORY_LAST_SEVEN_DAYS
(
ID INTEGER AUTO_INCREMENT NOT NULL,
DEVICE_ID INT NOT NULL,
DEVICE_ID_NAME VARCHAR(255) NOT NULL,
TENANT_ID INT NOT NULL,
DEVICE_TYPE_NAME VARCHAR(45) NOT NULL,
LATITUDE DOUBLE NULL,
LONGITUDE DOUBLE NULL,
SPEED FLOAT NULL,
HEADING FLOAT NULL,
TIMESTAMP BIGINT(15) NOT NULL,
GEO_HASH VARCHAR(45) NULL,
DEVICE_OWNER VARCHAR(45) NULL,
DEVICE_ALTITUDE DOUBLE NULL,
DISTANCE DOUBLE NULL,
PRIMARY KEY (ID)
)
ENGINE = InnoDB;
CREATE TABLE IF NOT EXISTS DM_DEVICE_DETAIL (
ID INT NOT NULL AUTO_INCREMENT,
DEVICE_ID INT NOT NULL,

@ -842,10 +842,11 @@ WHEN (NEW.ID IS NULL)
END;
/
CREATE TABLE DM_DEVICE_LOCATION (
CREATE TABLE DM_DEVICE_LOCATION
(
ID NUMBER(10) NOT NULL,
DEVICE_ID NUMBER(10) NOT NULL,
ENROLMENT_ID NUMBER(10) NOT NULL,
TENANT_ID NUMBER(10) NOT NULL,
LATITUDE BINARY_DOUBLE NULL,
LONGITUDE BINARY_DOUBLE NULL,
STREET1 VARCHAR2(255) NULL,
@ -856,6 +857,11 @@ CREATE TABLE DM_DEVICE_LOCATION (
COUNTRY VARCHAR2(45) NULL,
GEO_HASH VARCHAR(45) NULL,
UPDATE_TIMESTAMP NUMBER(19) NOT NULL,
ALTITUDE BINARY_DOUBLE NULL,
SPEED FLOAT NULL,
BEARING FLOAT NULL,
DISTANCE BINARY_DOUBLE NULL,
PRIMARY KEY (ID),
CONSTRAINT DM_DEVICE_LOCATION_DEVICE
FOREIGN KEY (DEVICE_ID)
@ -864,7 +870,6 @@ CREATE TABLE DM_DEVICE_LOCATION (
FOREIGN KEY (ENROLMENT_ID)
REFERENCES DM_ENROLMENT (ID)
)
/
CREATE INDEX DM_DEVICE_LOCATION_GEO_hashx ON DM_DEVICE_LOCATION(GEO_HASH ASC)
/
@ -880,6 +885,44 @@ WHEN (NEW.ID IS NULL)
END;
/
CREATE TABLE IF NOT EXISTS DM_DEVICE_HISTORY_LAST_SEVEN_DAYS
(
ID NUMBER(10) NOT NULL,
DEVICE_ID NUMBER(10) NOT NULL,
DEVICE_ID_NAME VARCHAR(255) NOT NULL,
ENROLMENT_ID NUMBER(10) NOT NULL,
DEVICE_TYPE_NAME VARCHAR(45) NOT NULL,
LATITUDE BINARY_DOUBLE NULL,
LONGITUDE BINARY_DOUBLE NULL,
SPEED FLOAT NULL,
HEADING FLOAT NULL,
TIMESTAMP BIGINT(15) NOT NULL,
GEO_HASH VARCHAR(45) NULL,
DEVICE_OWNER VARCHAR(45) NULL,
DEVICE_ALTITUDE BINARY_DOUBLE NULL,
DISTANCE BINARY_DOUBLE NULL,
PRIMARY KEY (ID)
)
/
CREATE INDEX DM_DEVICE_HISTORY_LAST_SEVEN_DAYS_GEO_hashx ON DM_DEVICE_HISTORY_LAST_SEVEN_DAYS (GEO_HASH ASC)
/
-- Generate ID using sequence and trigger
CREATE SEQUENCE DM_DEVICE_HISTORY_LAST_SEVEN_DAYS_seq START WITH 1 INCREMENT BY 1 NOCACHE
/
CREATE
OR
REPLACE
TRIGGER DM_DEVICE_HISTORY_LAST_SEVEN_DAYS_seq_tr
BEFORE INSERT
ON DM_DEVICE_HISTORY_LAST_SEVEN_DAYS FOR EACH ROW
WHEN (NEW.ID IS NULL)
BEGIN
SELECT DM_DEVICE_HISTORY_LAST_SEVEN_DAYS_seq.NEXTVAL INTO :NEW.ID
FROM DUAL;
END;
/
CREATE TABLE DM_DEVICE_DETAIL (
ID NUMBER(10) NOT NULL,
DEVICE_ID NUMBER(10) NOT NULL,

@ -461,6 +461,10 @@ CREATE TABLE IF NOT EXISTS DM_DEVICE_LOCATION (
COUNTRY VARCHAR(45) NULL,
GEO_HASH VARCHAR(45) NULL,
UPDATE_TIMESTAMP BIGINT NOT NULL,
ALTITUDE DOUBLE PRECISION NULL,
SPEED FLOAT NULL,
BEARING FLOAT NULL,
DISTANCE DOUBLE PRECISION NULL,
CONSTRAINT DM_DEVICE_LOCATION_DEVICE
FOREIGN KEY (DEVICE_ID)
REFERENCES DM_DEVICE (ID)
@ -473,6 +477,24 @@ CREATE TABLE IF NOT EXISTS DM_DEVICE_LOCATION (
ON UPDATE NO ACTION
);
CREATE TABLE IF NOT EXISTS DM_DEVICE_HISTORY_LAST_SEVEN_DAYS
(
ID BIGSERIAL NOT NULL PRIMARY KEY,
DEVICE_ID INT NOT NULL,
DEVICE_ID_NAME VARCHAR(255) NOT NULL,
TENANT_ID INT NOT NULL,
DEVICE_TYPE_NAME VARCHAR(45) NOT NULL,
LATITUDE DOUBLE PRECISION NULL,
LONGITUDE DOUBLE PRECISION NULL,
SPEED FLOAT NULL,
HEADING FLOAT NULL,
TIMESTAMP BIGINT(15) NOT NULL,
GEO_HASH VARCHAR(45) NULL,
DEVICE_OWNER VARCHAR(45) NULL,
DEVICE_ALTITUDE DOUBLE PRECISION NULL,
DISTANCE DOUBLE PRECISION NULL,
);
CREATE INDEX DM_DEVICE_LOCATION_GEO_hashx ON DM_DEVICE_LOCATION(GEO_HASH ASC);
CREATE TABLE IF NOT EXISTS DM_DEVICE_DETAIL (

Loading…
Cancel
Save