Merge branch 'geo-backend' into 'master'

Move device location history grouping logic to the backend

Closes product-iots#343

See merge request entgra/carbon-device-mgt!468
feature/appm-store/pbac
Charitha Goonetilleke 5 years ago
commit da5c7e307f

@ -150,7 +150,7 @@ class App extends React.Component {
<ConfigContext.Provider value={this.state.config}> <ConfigContext.Provider value={this.state.config}>
<div> <div>
<Switch> <Switch>
<Redirect exact from="/entgra" to="/entgra/reports" /> <Redirect exact from="/entgra" to="/entgra/devices" />
{this.props.routes.map(route => ( {this.props.routes.map(route => (
<RouteWithSubRoutes key={route.path} {...route} /> <RouteWithSubRoutes key={route.path} {...route} />
))} ))}

@ -65,7 +65,7 @@ const routes = [
exact: true, exact: true,
}, },
{ {
path: '/entgra/geo', path: '/entgra/geo/history/:deviceType/:deviceIdentifier',
component: Geo, component: Geo,
exact: true, exact: true,
}, },

@ -20,7 +20,6 @@ import React from 'react';
import { Layout, Menu, Icon } from 'antd'; import { Layout, Menu, Icon } from 'antd';
import { Switch, Link } from 'react-router-dom'; import { Switch, Link } from 'react-router-dom';
import RouteWithSubRoutes from '../../components/RouteWithSubRoutes'; import RouteWithSubRoutes from '../../components/RouteWithSubRoutes';
import { Redirect } from 'react-router';
import './styles.css'; import './styles.css';
import { withConfigContext } from '../../components/ConfigContext'; import { withConfigContext } from '../../components/ConfigContext';
import Logout from './components/Logout'; import Logout from './components/Logout';
@ -92,26 +91,6 @@ class Home extends React.Component {
</Link> </Link>
</Menu.Item> </Menu.Item>
</SubMenu> </SubMenu>
<SubMenu
key="geo"
title={
<span>
<Icon type="environment" />
<span>Geo</span>
</span>
}
>
<Menu.Item key="singleDevice">
<Link to="/entgra/geo">
<span>Single Device View</span>
</Link>
</Menu.Item>
<Menu.Item key="deviceGroup">
<Link to="#">
<span>Device Group View</span>
</Link>
</Menu.Item>
</SubMenu>
<Menu.Item key="reports"> <Menu.Item key="reports">
<Link to="/entgra/reports"> <Link to="/entgra/reports">
<Icon type="bar-chart" /> <Icon type="bar-chart" />
@ -198,7 +177,6 @@ class Home extends React.Component {
<Content> <Content>
<Switch> <Switch>
<Redirect exact from="/entgra/devices" to="/entgra/reports" />
{this.state.routes.map(route => ( {this.state.routes.map(route => (
<RouteWithSubRoutes key={route.path} {...route} /> <RouteWithSubRoutes key={route.path} {...route} />
))} ))}

@ -33,6 +33,7 @@ import TimeAgo from 'javascript-time-ago';
import en from 'javascript-time-ago/locale/en'; import en from 'javascript-time-ago/locale/en';
import { withConfigContext } from '../../../../../../components/ConfigContext'; import { withConfigContext } from '../../../../../../components/ConfigContext';
import BulkActionBar from './components/BulkActionBar'; import BulkActionBar from './components/BulkActionBar';
import { Link } from 'react-router-dom';
let config = null; let config = null;
@ -126,6 +127,20 @@ const columns = [
}, },
// todo add filtering options // todo add filtering options
}, },
{
title: '',
dataIndex: 'deviceIdentifier',
key: 'actions',
// eslint-disable-next-line react/display-name
render: (data, row) => {
const { type, deviceIdentifier } = row;
return (
<Link to={`/entgra/geo/history/${type}/${deviceIdentifier}`}>
<Icon type="environment" /> Location History
</Link>
);
},
},
]; ];
const getTimeAgo = time => { const getTimeAgo = time => {

@ -57,60 +57,42 @@ class CustomMap extends Component {
/** /**
* Polyline draw for historical locations * Polyline draw for historical locations
* @param locationData - location data object * @param locationHistorySnapshots - location data object
* @returns content * @returns content
*/ */
polylineMarker = locationData => { polylineMarker = locationHistorySnapshots => {
const locationPoints = [...locationData];
const polyLines = []; const polyLines = [];
locationHistorySnapshots.forEach(locationHistorySnapshots => {
polyLines.push(
<Polyline
key={polyLines.length}
color="#414042"
positions={locationHistorySnapshots.map(snapshot => {
return [snapshot.latitude, snapshot.longitude];
})}
smoothFactor={10}
weight={5}
/>,
);
});
while (locationPoints.length > 0) { for (let i = 1; i < locationHistorySnapshots.length; i++) {
// Array to store positions for next polyline const startPosition = locationHistorySnapshots[i][0];
const positions = []; const endingPosition =
// Make a copy of remaining location points locationHistorySnapshots[i - 1][
const cachedLocationPoints = [...locationPoints]; locationHistorySnapshots[i - 1].length - 1
// Iterate the remaining cached locations ];
for (let i = 0; i < cachedLocationPoints.length; i++) {
positions.push([
cachedLocationPoints[i].latitude,
cachedLocationPoints[i].longitude,
]);
const currentPoint = cachedLocationPoints[i];
// Remove the current location from the locationPoints
locationPoints.shift();
if (i < cachedLocationPoints.length - 1) {
const nextPoint = cachedLocationPoints[i + 1];
// Draw a dashed line for long for location points with long interval
if (
nextPoint.timestamp - currentPoint.timestamp >
this.props.context.geoMap.timeout * 1000
) {
// Create a dashed line
polyLines.push(
<Polyline
key={polyLines.length}
color="#414042"
positions={[
[currentPoint.latitude, currentPoint.longitude],
[nextPoint.latitude, nextPoint.longitude],
]}
smoothFactor={10}
weight={5}
dashArray="7"
/>,
);
break;
}
}
}
// Create a polyline from provided positions
polyLines.push( polyLines.push(
<Polyline <Polyline
key={polyLines.length} key={polyLines.length}
color="#414042" color="#414042"
positions={positions} positions={[
[startPosition.latitude, startPosition.longitude],
[endingPosition.latitude, endingPosition.longitude],
]}
smoothFactor={10} smoothFactor={10}
weight={5} weight={5}
dashArray="7"
/>, />,
); );
} }
@ -125,22 +107,23 @@ class CustomMap extends Component {
}; };
render() { render() {
const locationData = this.props.locationData; const locationHistorySnapshots = this.props.locationHistorySnapshots;
const config = this.props.context; const config = this.props.context;
const attribution = config.geoMap.attribution; const attribution = config.geoMap.attribution;
const url = config.geoMap.url; const url = config.geoMap.url;
const startingPoint = [locationData[0].latitude, locationData[0].longitude]; const firstSnapshot = locationHistorySnapshots[0][0];
const endPoint = [ const lastSnapshotList =
locationData[locationData.length - 1].latitude, locationHistorySnapshots[locationHistorySnapshots.length - 1];
locationData[locationData.length - 1].longitude, const lastSnapshot = lastSnapshotList[lastSnapshotList.length - 1];
]; const startingPoint = [firstSnapshot.latitude, firstSnapshot.longitude];
const endPoint = [lastSnapshot.latitude, lastSnapshot.longitude];
const zoom = config.geoMap.defaultZoomLevel; const zoom = config.geoMap.defaultZoomLevel;
return ( return (
<div style={{ backgroundColor: '#ffffff', borderRadius: 5, padding: 5 }}> <div style={{ backgroundColor: '#ffffff', borderRadius: 5, padding: 5 }}>
<Map center={startingPoint} zoom={zoom}> <Map center={startingPoint} zoom={zoom}>
<TileLayer url={url} attribution={attribution} /> <TileLayer url={url} attribution={attribution} />
<Fragment> <Fragment>
{this.polylineMarker(locationData)} {this.polylineMarker(locationHistorySnapshots)}
<Marker icon={pinStart} position={startingPoint}> <Marker icon={pinStart} position={startingPoint}>
<Popup keepInView={true}>Start</Popup> <Popup keepInView={true}>Start</Popup>
<Tooltip direction="top" permanent={true}> <Tooltip direction="top" permanent={true}>

@ -18,16 +18,7 @@
import React from 'react'; import React from 'react';
import moment from 'moment'; import moment from 'moment';
import { import { message, notification, Empty, DatePicker } from 'antd';
Button,
Select,
message,
notification,
Tag,
Tooltip,
Empty,
DatePicker,
} from 'antd';
import axios from 'axios'; import axios from 'axios';
import { withConfigContext } from '../../../../../../components/ConfigContext'; import { withConfigContext } from '../../../../../../components/ConfigContext';
import GeoCustomMap from '../CustomMap'; import GeoCustomMap from '../CustomMap';
@ -53,7 +44,7 @@ class GeoDashboard extends React.Component {
this.state = { this.state = {
deviceData: [], deviceData: [],
selectedDevice: '', selectedDevice: '',
locationData: [], locationHistorySnapshots: [],
loading: false, loading: false,
start: start, start: start,
end: end, end: end,
@ -62,119 +53,23 @@ class GeoDashboard extends React.Component {
} }
componentDidMount() { componentDidMount() {
this.fetchDevices(); this.fetchLocationHistory();
// this.fetchCurrentLocation();
} }
/** /**
* Call back on apply button in the date time picker * Call back on apply button in the date time picker
* @param startDate - start date * @param startDate - start date
* @param endDate - end date * @param endDate - end date
*/ */
applyCallback = (dates, dateStrings) => { applyCallback = (dates, dateStrings) => {
console.log('Apply Callback');
this.setState({ this.setState({
start: dateStrings[0], start: dateStrings[0],
end: dateStrings[1], end: dateStrings[1],
}); });
}; };
/** fetchLocationHistory = () => {
* Api call handle on fetch location date button const toInMills = moment(this.state.end);
*/ const fromInMills = moment(this.state.start);
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; const config = this.props.context;
this.setState({ loading: true }); this.setState({ loading: true });
@ -183,14 +78,21 @@ class GeoDashboard extends React.Component {
window.location.origin + window.location.origin +
config.serverConfig.invoker.uri + config.serverConfig.invoker.uri +
config.serverConfig.invoker.deviceMgt + config.serverConfig.invoker.deviceMgt +
'/devices?status=ACTIVE&status=INACTIVE&status=UNCLAIMED&status=UNREACHABLE&status=SUSPENDED&' + '/devices/' +
'status=DISENROLLMENT_REQUESTED&status=BLOCKED&status=CREATED', this.props.deviceType +
'/' +
this.props.deviceIdentifier +
'/location-history?' +
'from=' +
fromInMills +
'&to=' +
toInMills,
) )
.then(res => { .then(res => {
if (res.status === 200) { if (res.status === 200) {
this.setState({ this.setState({
loading: false, loading: false,
deviceData: res.data.data.devices, locationHistorySnapshots: res.data.data.locationHistorySnapshots,
}); });
} }
}) })
@ -202,11 +104,12 @@ class GeoDashboard extends React.Component {
notification.error({ notification.error({
message: 'There was a problem', message: 'There was a problem',
duration: 0, duration: 0,
description: 'Error occurred while trying to load devices.', description: 'Error occurred while trying to fetch locations......',
}); });
} }
this.setState({ loading: false }); this.setState({ loading: false });
console.log(error);
}); });
}; };
@ -235,76 +138,29 @@ class GeoDashboard extends React.Component {
'1 Month': [moment(start).subtract(1, 'months'), moment(end)], '1 Month': [moment(start).subtract(1, 'months'), moment(end)],
}; };
let { deviceData } = this.state;
return ( return (
<div className="controllerDiv"> <div className="controllerDiv">
<RangePicker <RangePicker
ranges={ranges} ranges={ranges}
style={{ marginRight: 20 }} style={{ marginRight: 20, width: 400 }}
showTime showTime
format="YYYY-MM-DD HH:mm:ss" format="YYYY-MM-DD HH:mm:ss"
defaultValue={[this.state.start, this.state.end]} defaultValue={[this.state.start, this.state.end]}
onChange={this.applyCallback} onChange={this.applyCallback}
onOk={this.fetchLocationHistory}
size="large"
/> />
<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> </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() { render() {
const locationData = [...this.state.locationData]; const { locationHistorySnapshots } = this.state;
return ( return (
<div className="container"> <div className="container">
{this.controllerBar()} {this.controllerBar()}
{locationData.length > 0 ? ( {locationHistorySnapshots.length > 0 ? (
<GeoCustomMap locationData={locationData} /> <GeoCustomMap locationHistorySnapshots={locationHistorySnapshots} />
) : ( ) : (
<Empty /> <Empty />
)} )}

@ -32,6 +32,7 @@ class Geo extends React.Component {
} }
render() { render() {
const { deviceIdentifier, deviceType } = this.props.match.params;
return ( return (
<div> <div>
<PageHeader style={{ paddingTop: 0 }}> <PageHeader style={{ paddingTop: 0 }}>
@ -41,11 +42,14 @@ class Geo extends React.Component {
<Icon type="home" /> Home <Icon type="home" /> Home
</Link> </Link>
</Breadcrumb.Item> </Breadcrumb.Item>
<Breadcrumb.Item>Geo</Breadcrumb.Item> <Breadcrumb.Item>
<Link to="/entgra">Devices</Link>
</Breadcrumb.Item>
<Breadcrumb.Item>{`Location History - ${deviceType} / ${deviceIdentifier}`}</Breadcrumb.Item>
</Breadcrumb> </Breadcrumb>
<div className="wrap"> <div className="wrap">
<h3>Geo</h3> <h3>Location History </h3>
<Paragraph>Geo Location Service</Paragraph> <Paragraph>{`${deviceType} / ${deviceIdentifier}`}</Paragraph>
</div> </div>
</PageHeader> </PageHeader>
<div <div
@ -56,7 +60,10 @@ class Geo extends React.Component {
alignItems: 'center', alignItems: 'center',
}} }}
> >
<GeoDashboard /> <GeoDashboard
deviceIdentifier={deviceIdentifier}
deviceType={deviceType}
/>
</div> </div>
</div> </div>
); );

@ -36,6 +36,8 @@
package org.wso2.carbon.device.mgt.jaxrs.service.impl; package org.wso2.carbon.device.mgt.jaxrs.service.impl;
import java.util.LinkedList;
import java.util.Queue;
import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
@ -46,6 +48,8 @@ import org.wso2.carbon.device.mgt.common.DeviceIdentifier;
import org.wso2.carbon.device.mgt.common.EnrolmentInfo; import org.wso2.carbon.device.mgt.common.EnrolmentInfo;
import org.wso2.carbon.device.mgt.common.Feature; import org.wso2.carbon.device.mgt.common.Feature;
import org.wso2.carbon.device.mgt.common.FeatureManager; import org.wso2.carbon.device.mgt.common.FeatureManager;
import org.wso2.carbon.device.mgt.common.MonitoringOperation;
import org.wso2.carbon.device.mgt.common.OperationMonitoringTaskConfig;
import org.wso2.carbon.device.mgt.common.PaginationRequest; import org.wso2.carbon.device.mgt.common.PaginationRequest;
import org.wso2.carbon.device.mgt.common.PaginationResult; import org.wso2.carbon.device.mgt.common.PaginationResult;
import org.wso2.carbon.device.mgt.common.app.mgt.Application; import org.wso2.carbon.device.mgt.common.app.mgt.Application;
@ -56,6 +60,7 @@ import org.wso2.carbon.device.mgt.common.device.details.DeviceData;
import org.wso2.carbon.device.mgt.common.device.details.DeviceInfo; 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.DeviceLocation;
import org.wso2.carbon.device.mgt.common.device.details.DeviceLocationHistory; import org.wso2.carbon.device.mgt.common.device.details.DeviceLocationHistory;
import org.wso2.carbon.device.mgt.common.device.details.DeviceLocationHistorySnapshot;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException; 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.DeviceTypeNotFoundException;
import org.wso2.carbon.device.mgt.common.exceptions.InvalidConfigurationException; import org.wso2.carbon.device.mgt.common.exceptions.InvalidConfigurationException;
@ -494,8 +499,8 @@ public class DeviceManagementServiceImpl implements DeviceManagementService {
@PathParam("deviceId") String deviceId, @PathParam("deviceId") String deviceId,
@QueryParam("from") long from, @QueryParam("to") long to) { @QueryParam("from") long from, @QueryParam("to") long to) {
List<DeviceLocationHistory> deviceLocationHistory;
String errorMessage; String errorMessage;
DeviceLocationHistory deviceLocationHistory = new DeviceLocationHistory();
try { try {
RequestValidationUtil.validateDeviceIdentifier(deviceType, deviceId); RequestValidationUtil.validateDeviceIdentifier(deviceType, deviceId);
@ -521,7 +526,46 @@ public class DeviceManagementServiceImpl implements DeviceManagementService {
new ErrorResponse.ErrorResponseBuilder().setCode(400l).setMessage(errorMessage)).build(); new ErrorResponse.ErrorResponseBuilder().setCode(400l).setMessage(errorMessage)).build();
} }
deviceLocationHistory = dms.getDeviceLocationInfo(deviceIdentifier, from, to); List<List<DeviceLocationHistorySnapshot>> locationHistorySnapshotList = new ArrayList<>();
// Get the location history snapshots for the given period
List<DeviceLocationHistorySnapshot> deviceLocationHistorySnapshots = dms.getDeviceLocationInfo(deviceIdentifier, from, to);
OperationMonitoringTaskConfig operationMonitoringTaskConfig = dms.getDeviceMonitoringConfig(deviceType);
int taskFrequency = operationMonitoringTaskConfig.getFrequency();
int operationRecurrentTimes = 0;
List<MonitoringOperation> monitoringOperations = operationMonitoringTaskConfig.getMonitoringOperation();
for (MonitoringOperation monitoringOperation :
monitoringOperations) {
if (monitoringOperation.getTaskName().equals("DEVICE_LOCATION")) {
operationRecurrentTimes = monitoringOperation.getRecurrentTimes();
break;
}
}
// Device Location operation frequency in milliseconds. Adding 100000 ms as an error
long operationFrequency = taskFrequency * operationRecurrentTimes + 100000;
Queue<DeviceLocationHistorySnapshot> deviceLocationHistorySnapshotsQueue = new LinkedList<>(deviceLocationHistorySnapshots);
while (deviceLocationHistorySnapshotsQueue.size() > 0) {
List<DeviceLocationHistorySnapshot> snapshots = new ArrayList<>();
// Make a copy of remaining snapshots
List<DeviceLocationHistorySnapshot> cachedSnapshots = new ArrayList<>(deviceLocationHistorySnapshotsQueue);
for (int i = 0; i < cachedSnapshots.size(); i++) {
DeviceLocationHistorySnapshot currentSnapshot = deviceLocationHistorySnapshotsQueue.poll();
snapshots.add(currentSnapshot);
if (deviceLocationHistorySnapshotsQueue.size() > 0) {
DeviceLocationHistorySnapshot nextSnapshot = deviceLocationHistorySnapshotsQueue.peek();
if (nextSnapshot.getUpdatedTime().getTime() - currentSnapshot.getUpdatedTime().getTime() > operationFrequency) {
break;
}
}
}
locationHistorySnapshotList.add(snapshots);
}
deviceLocationHistory.setLocationHistorySnapshots(locationHistorySnapshotList);
} catch (DeviceManagementException e) { } catch (DeviceManagementException e) {
errorMessage = "Error occurred while fetching the device information."; errorMessage = "Error occurred while fetching the device information.";

@ -18,147 +18,18 @@
package org.wso2.carbon.device.mgt.common.device.details; package org.wso2.carbon.device.mgt.common.device.details;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList;
@ApiModel(value = "DeviceLocationHistory", description = "This class carries all information related to the device location History" + import java.util.List;
"details provided by a device.")
public class DeviceLocationHistory implements Serializable { public class DeviceLocationHistory implements Serializable {
private List<List<DeviceLocationHistorySnapshot>> locationHistorySnapshots = new ArrayList<>();
@ApiModelProperty(name = "deviceId", value = "Device id", required = true) public List<List<DeviceLocationHistorySnapshot>> getLocationHistorySnapshots() {
private int deviceId; return locationHistorySnapshots;
@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) { public void setLocationHistorySnapshots(List<List<DeviceLocationHistorySnapshot>> locationHistorySnapshots) {
this.owner = owner; this.locationHistorySnapshots = locationHistorySnapshots;
} }
} }

@ -0,0 +1,63 @@
/*
* Copyright (c) 2020, 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 DeviceLocationHistorySnapshot extends DeviceLocation implements Serializable {
@ApiModelProperty(name = "geoHash", value = "Geo Hash", required = true)
private String geoHash;
@ApiModelProperty(name = "tenantId", value = "Tenant Id.", required = true)
private int tenantId;
@ApiModelProperty(name = "owner", value = "Owner.", required = true)
private String owner;
public DeviceLocationHistorySnapshot() {
}
public String getGeoHash() {
return geoHash;
}
public void setGeoHash(String geoHash) {
this.geoHash = geoHash;
}
public int getTenantId() {
return tenantId;
}
public void setTenantId(int tenantId) {
this.tenantId = tenantId;
}
public String getOwner() {
return owner;
}
public void setOwner(String owner) {
this.owner = owner;
}
}

@ -41,9 +41,9 @@ import org.wso2.carbon.device.mgt.common.EnrolmentInfo;
import org.wso2.carbon.device.mgt.common.EnrolmentInfo.Status; import org.wso2.carbon.device.mgt.common.EnrolmentInfo.Status;
import org.wso2.carbon.device.mgt.common.PaginationRequest; import org.wso2.carbon.device.mgt.common.PaginationRequest;
import org.wso2.carbon.device.mgt.common.Count; import org.wso2.carbon.device.mgt.common.Count;
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.common.configuration.mgt.DevicePropertyInfo;
import org.wso2.carbon.device.mgt.common.device.details.DeviceData; import org.wso2.carbon.device.mgt.common.device.details.DeviceData;
import org.wso2.carbon.device.mgt.common.device.details.DeviceLocationHistorySnapshot;
import org.wso2.carbon.device.mgt.core.dto.DeviceType; import org.wso2.carbon.device.mgt.core.dto.DeviceType;
import org.wso2.carbon.device.mgt.core.geo.GeoCluster; import org.wso2.carbon.device.mgt.core.geo.GeoCluster;
import org.wso2.carbon.device.mgt.core.geo.geoHash.GeoCoordinate; import org.wso2.carbon.device.mgt.core.geo.geoHash.GeoCoordinate;
@ -595,7 +595,7 @@ public interface DeviceDAO {
* @return * @return
* @throws DeviceManagementDAOException * @throws DeviceManagementDAOException
*/ */
List<DeviceLocationHistory> getDeviceLocationInfo(DeviceIdentifier deviceIdentifier, long from, long to) List<DeviceLocationHistorySnapshot> getDeviceLocationInfo(DeviceIdentifier deviceIdentifier, long from, long to)
throws DeviceManagementDAOException; throws DeviceManagementDAOException;
/** /**

@ -46,6 +46,7 @@ import org.wso2.carbon.device.mgt.common.PaginationRequest;
import org.wso2.carbon.device.mgt.common.configuration.mgt.DevicePropertyInfo; import org.wso2.carbon.device.mgt.common.configuration.mgt.DevicePropertyInfo;
import org.wso2.carbon.device.mgt.common.device.details.DeviceData; import org.wso2.carbon.device.mgt.common.device.details.DeviceData;
import org.wso2.carbon.device.mgt.common.device.details.DeviceLocationHistory; import org.wso2.carbon.device.mgt.common.device.details.DeviceLocationHistory;
import org.wso2.carbon.device.mgt.common.device.details.DeviceLocationHistorySnapshot;
import org.wso2.carbon.device.mgt.core.dao.DeviceDAO; import org.wso2.carbon.device.mgt.core.dao.DeviceDAO;
import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOException; import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOException;
import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOFactory; import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOFactory;
@ -1800,13 +1801,13 @@ public abstract class AbstractDeviceDAOImpl implements DeviceDAO {
} }
@Override @Override
public List<DeviceLocationHistory> getDeviceLocationInfo(DeviceIdentifier deviceIdentifier, long from, long to) public List<DeviceLocationHistorySnapshot> getDeviceLocationInfo(DeviceIdentifier deviceIdentifier, long from, long to)
throws DeviceManagementDAOException { throws DeviceManagementDAOException {
Connection conn; Connection conn;
PreparedStatement stmt = null; PreparedStatement stmt = null;
ResultSet rs = null; ResultSet rs = null;
List<DeviceLocationHistory> deviceLocationHistories = new ArrayList<>(); List<DeviceLocationHistorySnapshot> deviceLocationHistories = new ArrayList<>();
try { try {
conn = this.getConnection(); conn = this.getConnection();

@ -17,14 +17,15 @@
*/ */
package org.wso2.carbon.device.mgt.core.dao.util; package org.wso2.carbon.device.mgt.core.dao.util;
import java.util.Date;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.context.CarbonContext; import org.wso2.carbon.context.CarbonContext;
import org.wso2.carbon.device.mgt.common.Device; 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.EnrolmentInfo;
import org.wso2.carbon.device.mgt.common.device.details.DeviceInfo; 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.DeviceLocationHistorySnapshot;
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.dao.DeviceManagementDAOException;
import org.wso2.carbon.device.mgt.core.dto.DeviceType; import org.wso2.carbon.device.mgt.core.dto.DeviceType;
import org.wso2.carbon.device.mgt.core.internal.DeviceManagementDataHolder; import org.wso2.carbon.device.mgt.core.internal.DeviceManagementDataHolder;
@ -248,12 +249,13 @@ public final class DeviceManagementDAOUtil {
return deviceInfo; return deviceInfo;
} }
public static DeviceLocationHistory loadDeviceLocation(ResultSet rs) throws SQLException { public static DeviceLocationHistorySnapshot loadDeviceLocation(ResultSet rs) throws SQLException {
DeviceLocationHistory deviceLocationHistory = new DeviceLocationHistory(); DeviceLocationHistorySnapshot deviceLocationHistory = new DeviceLocationHistorySnapshot();
deviceLocationHistory.setDeviceId(rs.getInt("DEVICE_ID")); DeviceIdentifier deviceIdentifier = new DeviceIdentifier();
deviceLocationHistory.setDeviceIdentifier(rs.getString("DEVICE_ID_NAME")); deviceIdentifier.setId(rs.getString("DEVICE_ID_NAME"));
deviceIdentifier.setType(rs.getString("DEVICE_TYPE_NAME"));
deviceLocationHistory.setDeviceIdentifier(deviceIdentifier);
deviceLocationHistory.setTenantId(rs.getInt("TENANT_ID")); deviceLocationHistory.setTenantId(rs.getInt("TENANT_ID"));
deviceLocationHistory.setDeviceType(rs.getString("DEVICE_TYPE_NAME"));
deviceLocationHistory.setLatitude(rs.getDouble("LATITUDE")); deviceLocationHistory.setLatitude(rs.getDouble("LATITUDE"));
deviceLocationHistory.setLongitude(rs.getDouble("LONGITUDE")); deviceLocationHistory.setLongitude(rs.getDouble("LONGITUDE"));
deviceLocationHistory.setSpeed(rs.getFloat("SPEED")); deviceLocationHistory.setSpeed(rs.getFloat("SPEED"));
@ -261,7 +263,7 @@ public final class DeviceManagementDAOUtil {
deviceLocationHistory.setAltitude(rs.getDouble("DEVICE_ALTITUDE")); deviceLocationHistory.setAltitude(rs.getDouble("DEVICE_ALTITUDE"));
deviceLocationHistory.setDistance(rs.getDouble("DISTANCE")); deviceLocationHistory.setDistance(rs.getDouble("DISTANCE"));
deviceLocationHistory.setOwner(rs.getString("DEVICE_OWNER")); deviceLocationHistory.setOwner(rs.getString("DEVICE_OWNER"));
deviceLocationHistory.setTimestamp(rs.getLong("TIMESTAMP")); deviceLocationHistory.setUpdatedTime(new Date(rs.getLong("TIMESTAMP")));
deviceLocationHistory.setGeoHash(rs.getString("GEO_HASH")); deviceLocationHistory.setGeoHash(rs.getString("GEO_HASH"));
return deviceLocationHistory; return deviceLocationHistory;
} }

@ -268,6 +268,8 @@ public class DeviceInformationManagerImpl implements DeviceInformationManager {
} else { } else {
deviceDetailsDAO.updateDeviceLocation(deviceLocation, device.getEnrolmentInfo().getId()); deviceDetailsDAO.updateDeviceLocation(deviceLocation, device.getEnrolmentInfo().getId());
} }
deviceDetailsDAO.addDeviceLocationInfo(device, deviceLocation,
CarbonContext.getThreadLocalCarbonContext().getTenantId());
if (DeviceManagerUtil.isPublishLocationResponseEnabled()) { if (DeviceManagerUtil.isPublishLocationResponseEnabled()) {
Object[] metaData = {device.getDeviceIdentifier(), device.getEnrolmentInfo().getOwner(), device.getType()}; Object[] metaData = {device.getDeviceIdentifier(), device.getEnrolmentInfo().getOwner(), device.getType()};
Object[] payload = new Object[]{ Object[] payload = new Object[]{

@ -46,6 +46,7 @@ import org.wso2.carbon.device.mgt.common.MonitoringOperation;
import org.wso2.carbon.device.mgt.common.StartupOperationConfig; import org.wso2.carbon.device.mgt.common.StartupOperationConfig;
import org.wso2.carbon.device.mgt.common.OperationMonitoringTaskConfig; import org.wso2.carbon.device.mgt.common.OperationMonitoringTaskConfig;
import org.wso2.carbon.device.mgt.common.app.mgt.ApplicationManagementException; import org.wso2.carbon.device.mgt.common.app.mgt.ApplicationManagementException;
import org.wso2.carbon.device.mgt.common.device.details.DeviceLocationHistorySnapshot;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException; import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceNotFoundException; import org.wso2.carbon.device.mgt.common.exceptions.DeviceNotFoundException;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceTypeNotFoundException; import org.wso2.carbon.device.mgt.common.exceptions.DeviceTypeNotFoundException;
@ -56,7 +57,6 @@ 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.ConfigurationManagementException;
import org.wso2.carbon.device.mgt.common.configuration.mgt.DeviceConfiguration; 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.configuration.mgt.PlatformConfiguration;
import org.wso2.carbon.device.mgt.common.device.details.DeviceLocationHistory;
import org.wso2.carbon.device.mgt.common.device.details.DeviceData; import org.wso2.carbon.device.mgt.common.device.details.DeviceData;
import org.wso2.carbon.device.mgt.common.license.mgt.License; 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.Activity;
@ -765,7 +765,7 @@ public interface DeviceManagementProviderService {
* @throws DeviceManagementException * @throws DeviceManagementException
* @return list of device's location histories * @return list of device's location histories
*/ */
List<DeviceLocationHistory> getDeviceLocationInfo(DeviceIdentifier deviceIdentifier, long from, long to) List<DeviceLocationHistorySnapshot> getDeviceLocationInfo(DeviceIdentifier deviceIdentifier, long from, long to)
throws DeviceManagementException; throws DeviceManagementException;
/** /**

@ -64,6 +64,7 @@ import org.wso2.carbon.device.mgt.common.DevicePropertyNotification;
import org.wso2.carbon.device.mgt.common.DeviceEnrollmentInfoNotification; import org.wso2.carbon.device.mgt.common.DeviceEnrollmentInfoNotification;
import org.wso2.carbon.device.mgt.common.DeviceNotification; import org.wso2.carbon.device.mgt.common.DeviceNotification;
import org.wso2.carbon.device.mgt.common.app.mgt.ApplicationManagementException; import org.wso2.carbon.device.mgt.common.app.mgt.ApplicationManagementException;
import org.wso2.carbon.device.mgt.common.device.details.DeviceLocationHistorySnapshot;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException; import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceNotFoundException; import org.wso2.carbon.device.mgt.common.exceptions.DeviceNotFoundException;
import org.wso2.carbon.device.mgt.common.exceptions.DeviceTypeNotFoundException; import org.wso2.carbon.device.mgt.common.exceptions.DeviceTypeNotFoundException;
@ -81,7 +82,6 @@ import org.wso2.carbon.device.mgt.common.configuration.mgt.PlatformConfiguration
import org.wso2.carbon.device.mgt.common.device.details.DeviceData; import org.wso2.carbon.device.mgt.common.device.details.DeviceData;
import org.wso2.carbon.device.mgt.common.device.details.DeviceInfo; 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.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.EnrollmentNotificationConfiguration;
import org.wso2.carbon.device.mgt.common.enrollment.notification.EnrollmentNotifier; import org.wso2.carbon.device.mgt.common.enrollment.notification.EnrollmentNotifier;
import org.wso2.carbon.device.mgt.common.enrollment.notification.EnrollmentNotifierException; import org.wso2.carbon.device.mgt.common.enrollment.notification.EnrollmentNotifierException;
@ -3013,14 +3013,14 @@ public class DeviceManagementProviderServiceImpl implements DeviceManagementProv
} }
@Override @Override
public List<DeviceLocationHistory> getDeviceLocationInfo(DeviceIdentifier deviceIdentifier, long from, long to) public List<DeviceLocationHistorySnapshot> getDeviceLocationInfo(DeviceIdentifier deviceIdentifier, long from, long to)
throws DeviceManagementException { throws DeviceManagementException {
if (log.isDebugEnabled()) { if (log.isDebugEnabled()) {
log.debug("Get device location information"); log.debug("Get device location information");
} }
List<DeviceLocationHistory> deviceLocationHistory; List<DeviceLocationHistorySnapshot> deviceLocationHistory;
String errMessage; String errMessage;
try { try {

Loading…
Cancel
Save