adding new device type: drone analyzer
@ -0,0 +1,129 @@
|
||||
/*
|
||||
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.homeautomation.droneanalyzer.api;
|
||||
|
||||
import org.homeautomation.droneanalyzer.api.dto.DeviceJSON;
|
||||
|
||||
import org.wso2.carbon.apimgt.annotations.api.API;
|
||||
import org.wso2.carbon.apimgt.annotations.api.Permission;
|
||||
import org.wso2.carbon.device.mgt.extensions.feature.mgt.annotations.DeviceType;
|
||||
import org.wso2.carbon.device.mgt.extensions.feature.mgt.annotations.Feature;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.PUT;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
|
||||
/**
|
||||
* This is the API which is used to control and manage device type functionality
|
||||
*/
|
||||
@SuppressWarnings("NonJaxWsWebServices")
|
||||
@API(name = "drone", version = "1.0.0", context = "/drone", tags = "drone")
|
||||
@DeviceType(value = "drone")
|
||||
public interface DroneAnalyzerService {
|
||||
|
||||
/**
|
||||
* @param agentInfo device owner,id
|
||||
* @return true if device instance is added to map
|
||||
*/
|
||||
@Path("device/register")
|
||||
@POST
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Permission(scope = "drone_user", permissions = {"/permission/admin/device-mgt/user/register"})
|
||||
Response registerDevice(final DeviceJSON agentInfo);
|
||||
|
||||
/**
|
||||
* Retrieve Sensor data for the given time period
|
||||
* @param deviceId unique identifier for given device type instance
|
||||
* @param from starting time
|
||||
* @param to ending time
|
||||
* @return response with List<SensorRecord> object which includes sensor data which is requested
|
||||
*/
|
||||
@Path("device/stats/{deviceId}")
|
||||
@GET
|
||||
@Consumes("application/json")
|
||||
@Produces("application/json")
|
||||
@Permission(scope = "drone_user", permissions = {"/permission/admin/device-mgt/stats"})
|
||||
Response getSensorStats(@PathParam("deviceId") String deviceId, @QueryParam("from") long from,
|
||||
@QueryParam("to") long to);
|
||||
|
||||
/**
|
||||
* Remove device type instance using device id
|
||||
* @param deviceId unique identifier for given device type instance
|
||||
*/
|
||||
@Path("/device/{device_id}")
|
||||
@DELETE
|
||||
@Permission(scope = "drone_user", permissions = {"/permission/admin/device-mgt/removeDevice"})
|
||||
Response removeDevice(@PathParam("device_id") String deviceId);
|
||||
|
||||
/**
|
||||
* Update device instance name
|
||||
* @param deviceId unique identifier for given device type instance
|
||||
* @param name new name for the device type instance
|
||||
*/
|
||||
@Path("/device/{device_id}")
|
||||
@PUT
|
||||
@Permission(scope = "drone_user", permissions = {"/permission/admin/device-mgt/updateDevice"})
|
||||
Response updateDevice(@PathParam("device_id") String deviceId, @QueryParam("name") String name);
|
||||
|
||||
/**
|
||||
* To get device information
|
||||
* @param deviceId unique identifier for given device type instance
|
||||
* @return
|
||||
*/
|
||||
@Path("/device/{device_id}")
|
||||
@GET
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Permission(scope = "drone_user", permissions = {"/permission/admin/device-mgt/updateDevice"})
|
||||
Response getDevice(@PathParam("device_id") String deviceId);
|
||||
|
||||
/**
|
||||
* Get all device type instance which belongs to user
|
||||
* @return Array of devices which includes device's information
|
||||
*/
|
||||
@Path("/devices")
|
||||
@GET
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Permission(scope = "drone_user", permissions = {"/permission/admin/device-mgt/devices"})
|
||||
Response getAllDevices();
|
||||
|
||||
/**
|
||||
* To download device type agent source code as zip file
|
||||
* @param deviceName name for the device type instance
|
||||
* @param sketchType folder name where device type agent was installed into server
|
||||
* @return Agent source code as zip file
|
||||
*/
|
||||
@Path("/device/download")
|
||||
@GET
|
||||
@Produces("application/zip")
|
||||
@Permission(scope = "drone_user", permissions = {"/permission/admin/device-mgt/download"})
|
||||
Response downloadSketch(@QueryParam("deviceName") String deviceName, @QueryParam("sketchType") String sketchType);
|
||||
}
|
||||
@ -0,0 +1,382 @@
|
||||
/*
|
||||
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.homeautomation.droneanalyzer.api;
|
||||
|
||||
import org.homeautomation.droneanalyzer.api.dto.DeviceJSON;
|
||||
import org.homeautomation.droneanalyzer.api.dto.SensorRecord;
|
||||
import org.homeautomation.droneanalyzer.api.util.APIUtil;
|
||||
import org.homeautomation.droneanalyzer.api.util.ZipUtil;
|
||||
import org.homeautomation.droneanalyzer.plugin.constants.DroneAnalyzerConstants;
|
||||
import org.homeautomation.droneanalyzer.api.DroneAnalyzerService;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.wso2.carbon.analytics.dataservice.commons.SORT;
|
||||
import org.wso2.carbon.analytics.dataservice.commons.SortByField;
|
||||
import org.wso2.carbon.analytics.datasource.commons.exception.AnalyticsException;
|
||||
import org.wso2.carbon.apimgt.annotations.api.API;
|
||||
import org.wso2.carbon.apimgt.application.extension.APIManagementProviderService;
|
||||
import org.wso2.carbon.apimgt.application.extension.dto.ApiApplicationKey;
|
||||
import org.wso2.carbon.apimgt.application.extension.exception.APIManagerException;
|
||||
import org.wso2.carbon.context.PrivilegedCarbonContext;
|
||||
import org.wso2.carbon.device.mgt.common.Device;
|
||||
import org.wso2.carbon.device.mgt.common.DeviceIdentifier;
|
||||
import org.wso2.carbon.device.mgt.common.DeviceManagementException;
|
||||
import org.wso2.carbon.device.mgt.common.EnrolmentInfo;
|
||||
import org.wso2.carbon.device.mgt.common.authorization.DeviceAccessAuthorizationException;
|
||||
import org.wso2.carbon.device.mgt.extensions.feature.mgt.annotations.DeviceType;
|
||||
import org.wso2.carbon.device.mgt.extensions.feature.mgt.annotations.Feature;
|
||||
import org.wso2.carbon.device.mgt.iot.util.ZipArchive;
|
||||
import org.wso2.carbon.identity.jwt.client.extension.JWTClient;
|
||||
import org.wso2.carbon.identity.jwt.client.extension.dto.AccessTokenInfo;
|
||||
import org.wso2.carbon.identity.jwt.client.extension.exception.JWTClientException;
|
||||
import org.wso2.carbon.user.api.UserStoreException;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.PUT;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
import java.util.HashMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
|
||||
/**
|
||||
* This is the API which is used to control and manage device type functionality
|
||||
*/
|
||||
@SuppressWarnings("NonJaxWsWebServices")
|
||||
@API(name = "drone", version = "1.0.0", context = "/drone", tags = "drone")
|
||||
@DeviceType(value = "drone")
|
||||
public class DroneAnalyzerServiceImpl implements DroneAnalyzerService {
|
||||
|
||||
private static final String KEY_TYPE = "PRODUCTION";
|
||||
private static Log log = LogFactory.getLog(DroneAnalyzerService.class);
|
||||
private static ApiApplicationKey apiApplicationKey;
|
||||
private ConcurrentHashMap<String, DeviceJSON> deviceToIpMap = new ConcurrentHashMap<>();
|
||||
|
||||
private static String shortUUID() {
|
||||
UUID uuid = UUID.randomUUID();
|
||||
long l = ByteBuffer.wrap(uuid.toString().getBytes(StandardCharsets.UTF_8)).getLong();
|
||||
return Long.toString(l, Character.MAX_RADIX);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param agentInfo device owner,id
|
||||
* @return true if device instance is added to map
|
||||
*/
|
||||
@Path("device/register")
|
||||
@POST
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
public Response registerDevice(final DeviceJSON agentInfo) {
|
||||
String deviceId = agentInfo.deviceId;
|
||||
if ((agentInfo.deviceId != null) && (agentInfo.owner != null)) {
|
||||
deviceToIpMap.put(deviceId, agentInfo);
|
||||
return Response.status(Response.Status.OK).build();
|
||||
}
|
||||
return Response.status(Response.Status.NOT_ACCEPTABLE).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve Sensor data for the given time period
|
||||
* @param deviceId unique identifier for given device type instance
|
||||
* @param from starting time
|
||||
* @param to ending time
|
||||
* @return response with List<SensorRecord> object which includes sensor data which is requested
|
||||
*/
|
||||
@Path("device/stats/{deviceId}")
|
||||
@GET
|
||||
@Consumes("application/json")
|
||||
@Produces("application/json")
|
||||
public Response getSensorStats(@PathParam("deviceId") String deviceId, @QueryParam("from") long from,
|
||||
@QueryParam("to") long to) {
|
||||
String fromDate = String.valueOf(from);
|
||||
String toDate = String.valueOf(to);
|
||||
String query = "meta_deviceId:" + deviceId + " AND meta_deviceType:" +
|
||||
DroneAnalyzerConstants.DEVICE_TYPE + " AND meta_time : [" + fromDate + " TO " + toDate + "]";
|
||||
String sensorTableName = DroneAnalyzerConstants.SENSOR_EVENT_TABLE;
|
||||
try {
|
||||
if (!APIUtil.getDeviceAccessAuthorizationService().isUserAuthorized(new DeviceIdentifier(deviceId,
|
||||
DroneAnalyzerConstants.DEVICE_TYPE))) {
|
||||
return Response.status(Response.Status.UNAUTHORIZED.getStatusCode()).build();
|
||||
}
|
||||
if (sensorTableName != null) {
|
||||
List<SortByField> sortByFields = new ArrayList<>();
|
||||
SortByField sortByField = new SortByField("meta_time", SORT.ASC, false);
|
||||
sortByFields.add(sortByField);
|
||||
List<SensorRecord> sensorRecords = APIUtil.getAllEventsForDevice(sensorTableName, query, sortByFields);
|
||||
return Response.status(Response.Status.OK.getStatusCode()).entity(sensorRecords).build();
|
||||
}
|
||||
} catch (AnalyticsException e) {
|
||||
String errorMsg = "Error on retrieving stats on table " + sensorTableName + " with query " + query;
|
||||
log.error(errorMsg);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()).entity(errorMsg).build();
|
||||
} catch (DeviceAccessAuthorizationException e) {
|
||||
log.error(e.getErrorMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
|
||||
}
|
||||
return Response.status(Response.Status.BAD_REQUEST).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove device type instance using device id
|
||||
* @param deviceId unique identifier for given device type instance
|
||||
*/
|
||||
@Path("/device/{device_id}")
|
||||
@DELETE
|
||||
public Response removeDevice(@PathParam("device_id") String deviceId) {
|
||||
try {
|
||||
DeviceIdentifier deviceIdentifier = new DeviceIdentifier();
|
||||
deviceIdentifier.setId(deviceId);
|
||||
deviceIdentifier.setType(DroneAnalyzerConstants.DEVICE_TYPE);
|
||||
if (!APIUtil.getDeviceAccessAuthorizationService().isUserAuthorized(deviceIdentifier)) {
|
||||
return Response.status(Response.Status.UNAUTHORIZED.getStatusCode()).build();
|
||||
}
|
||||
boolean removed = APIUtil.getDeviceManagementService().disenrollDevice(
|
||||
deviceIdentifier);
|
||||
if (removed) {
|
||||
return Response.ok().build();
|
||||
} else {
|
||||
return Response.status(Response.Status.NOT_ACCEPTABLE.getStatusCode()).build();
|
||||
}
|
||||
} catch (DeviceManagementException e) {
|
||||
log.error(e.getErrorMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()).build();
|
||||
} catch (DeviceAccessAuthorizationException e) {
|
||||
log.error(e.getErrorMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()).build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update device instance name
|
||||
* @param deviceId unique identifier for given device type instance
|
||||
* @param name new name for the device type instance
|
||||
*/
|
||||
@Path("/device/{device_id}")
|
||||
@PUT
|
||||
public Response updateDevice(@PathParam("device_id") String deviceId, @QueryParam("name") String name) {
|
||||
try {
|
||||
DeviceIdentifier deviceIdentifier = new DeviceIdentifier();
|
||||
deviceIdentifier.setId(deviceId);
|
||||
deviceIdentifier.setType(DroneAnalyzerConstants.DEVICE_TYPE);
|
||||
if (!APIUtil.getDeviceAccessAuthorizationService().isUserAuthorized(deviceIdentifier)) {
|
||||
return Response.status(Response.Status.UNAUTHORIZED.getStatusCode()).build();
|
||||
}
|
||||
Device device = APIUtil.getDeviceManagementService().getDevice(deviceIdentifier);
|
||||
device.setDeviceIdentifier(deviceId);
|
||||
device.getEnrolmentInfo().setDateOfLastUpdate(new Date().getTime());
|
||||
device.setName(name);
|
||||
device.setType(DroneAnalyzerConstants.DEVICE_TYPE);
|
||||
boolean updated = APIUtil.getDeviceManagementService().modifyEnrollment(device);
|
||||
if (updated) {
|
||||
return Response.ok().build();
|
||||
} else {
|
||||
return Response.status(Response.Status.NOT_ACCEPTABLE.getStatusCode()).build();
|
||||
}
|
||||
} catch (DeviceManagementException e) {
|
||||
log.error(e.getErrorMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()).build();
|
||||
} catch (DeviceAccessAuthorizationException e) {
|
||||
log.error(e.getErrorMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()).build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* To get device information
|
||||
* @param deviceId unique identifier for given device type instance
|
||||
* @return
|
||||
*/
|
||||
@Path("/device/{device_id}")
|
||||
@GET
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response getDevice(@PathParam("device_id") String deviceId) {
|
||||
try {
|
||||
DeviceIdentifier deviceIdentifier = new DeviceIdentifier();
|
||||
deviceIdentifier.setId(deviceId);
|
||||
deviceIdentifier.setType(DroneAnalyzerConstants.DEVICE_TYPE);
|
||||
if (!APIUtil.getDeviceAccessAuthorizationService().isUserAuthorized(deviceIdentifier)) {
|
||||
return Response.status(Response.Status.UNAUTHORIZED.getStatusCode()).build();
|
||||
}
|
||||
Device device = APIUtil.getDeviceManagementService().getDevice(deviceIdentifier);
|
||||
return Response.ok().entity(device).build();
|
||||
} catch (DeviceManagementException e) {
|
||||
log.error(e.getErrorMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()).build();
|
||||
} catch (DeviceAccessAuthorizationException e) {
|
||||
log.error(e.getErrorMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()).build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all device type instance which belongs to user
|
||||
* @return Array of devices which includes device's information
|
||||
*/
|
||||
@Path("/devices")
|
||||
@GET
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response getAllDevices() {
|
||||
try {
|
||||
List<Device> userDevices =
|
||||
APIUtil.getDeviceManagementService().getDevicesOfUser(APIUtil.getAuthenticatedUser());
|
||||
ArrayList<Device> userDevicesforFirealarm = new ArrayList<>();
|
||||
for (Device device : userDevices) {
|
||||
if (device.getType().equals(DroneAnalyzerConstants.DEVICE_TYPE) &&
|
||||
device.getEnrolmentInfo().getStatus().equals(EnrolmentInfo.Status.ACTIVE)) {
|
||||
userDevicesforFirealarm.add(device);
|
||||
}
|
||||
}
|
||||
Device[] devices = userDevicesforFirealarm.toArray(new Device[]{});
|
||||
return Response.ok().entity(devices).build();
|
||||
} catch (DeviceManagementException e) {
|
||||
log.error(e.getErrorMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()).build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* To download device type agent source code as zip file
|
||||
* @param deviceName name for the device type instance
|
||||
* @param sketchType folder name where device type agent was installed into server
|
||||
* @return Agent source code as zip file
|
||||
*/
|
||||
@Path("/device/download")
|
||||
@GET
|
||||
@Produces("application/zip")
|
||||
public Response downloadSketch(@QueryParam("deviceName") String deviceName,
|
||||
@QueryParam("sketchType") String sketchType) {
|
||||
try {
|
||||
ZipArchive zipFile = createDownloadFile(APIUtil.getAuthenticatedUser(), deviceName, sketchType);
|
||||
Response.ResponseBuilder response = Response.ok(FileUtils.readFileToByteArray(zipFile.getZipFile()));
|
||||
response.status(Response.Status.OK);
|
||||
response.type("application/zip");
|
||||
response.header("Content-Disposition", "attachment; filename=\"" + zipFile.getFileName() + "\"");
|
||||
Response resp = response.build();
|
||||
zipFile.getZipFile().delete();
|
||||
return resp;
|
||||
} catch (IllegalArgumentException ex) {
|
||||
return Response.status(400).entity(ex.getMessage()).build();//bad request
|
||||
} catch (DeviceManagementException ex) {
|
||||
log.error(ex.getMessage(), ex);
|
||||
return Response.status(500).entity(ex.getMessage()).build();
|
||||
} catch (JWTClientException ex) {
|
||||
log.error(ex.getMessage(), ex);
|
||||
return Response.status(500).entity(ex.getMessage()).build();
|
||||
} catch (APIManagerException ex) {
|
||||
log.error(ex.getMessage(), ex);
|
||||
return Response.status(500).entity(ex.getMessage()).build();
|
||||
} catch (IOException ex) {
|
||||
log.error(ex.getMessage(), ex);
|
||||
return Response.status(500).entity(ex.getMessage()).build();
|
||||
} catch (UserStoreException ex) {
|
||||
log.error(ex.getMessage(), ex);
|
||||
return Response.status(500).entity(ex.getMessage()).build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register device into device management service
|
||||
* @param deviceId unique identifier for given device type instance
|
||||
* @param name name for the device type instance
|
||||
* @return check whether device is installed into cdmf
|
||||
*/
|
||||
private boolean register(String deviceId, String name) {
|
||||
try {
|
||||
DeviceIdentifier deviceIdentifier = new DeviceIdentifier();
|
||||
deviceIdentifier.setId(deviceId);
|
||||
deviceIdentifier.setType(DroneAnalyzerConstants.DEVICE_TYPE);
|
||||
if (APIUtil.getDeviceManagementService().isEnrolled(deviceIdentifier)) {
|
||||
return false;
|
||||
}
|
||||
Device device = new Device();
|
||||
device.setDeviceIdentifier(deviceId);
|
||||
EnrolmentInfo enrolmentInfo = new EnrolmentInfo();
|
||||
enrolmentInfo.setDateOfEnrolment(new Date().getTime());
|
||||
enrolmentInfo.setDateOfLastUpdate(new Date().getTime());
|
||||
enrolmentInfo.setStatus(EnrolmentInfo.Status.ACTIVE);
|
||||
enrolmentInfo.setOwnership(EnrolmentInfo.OwnerShip.BYOD);
|
||||
device.setName(name);
|
||||
device.setType(DroneAnalyzerConstants.DEVICE_TYPE);
|
||||
enrolmentInfo.setOwner(APIUtil.getAuthenticatedUser());
|
||||
device.setEnrolmentInfo(enrolmentInfo);
|
||||
boolean added = APIUtil.getDeviceManagementService().enrollDevice(device);
|
||||
if (added) {
|
||||
APIUtil.registerApiAccessRoles(APIUtil.getAuthenticatedUser());
|
||||
}
|
||||
return added;
|
||||
} catch (DeviceManagementException e) {
|
||||
log.error(e.getMessage(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private ZipArchive createDownloadFile(String owner, String deviceName, String sketchType)
|
||||
throws DeviceManagementException, JWTClientException, APIManagerException,
|
||||
UserStoreException {
|
||||
//create new device id
|
||||
String deviceId = shortUUID();
|
||||
if (apiApplicationKey == null) {
|
||||
String applicationUsername = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUserRealm()
|
||||
.getRealmConfiguration().getAdminUserName();
|
||||
applicationUsername = applicationUsername + "@" + APIUtil.getAuthenticatedUserTenantDomain();
|
||||
APIManagementProviderService apiManagementProviderService = APIUtil.getAPIManagementProviderService();
|
||||
String[] tags = {DroneAnalyzerConstants.DEVICE_TYPE};
|
||||
apiApplicationKey = apiManagementProviderService.generateAndRetrieveApplicationKeys(
|
||||
DroneAnalyzerConstants.DEVICE_TYPE, tags, KEY_TYPE, applicationUsername, true);
|
||||
}
|
||||
JWTClient jwtClient = APIUtil.getJWTClientManagerService().getJWTClient();
|
||||
String scopes = "device_type_" + DroneAnalyzerConstants.DEVICE_TYPE + " device_" + deviceId;
|
||||
AccessTokenInfo accessTokenInfo = jwtClient.getAccessToken(apiApplicationKey.getConsumerKey(),
|
||||
apiApplicationKey.getConsumerSecret(), owner + "@" + APIUtil.getAuthenticatedUserTenantDomain(), scopes);
|
||||
|
||||
//create token
|
||||
String accessToken = accessTokenInfo.getAccessToken();
|
||||
String refreshToken = accessTokenInfo.getRefreshToken();
|
||||
boolean status = register(deviceId, deviceName);
|
||||
if (!status) {
|
||||
String msg = "Error occurred while registering the device with " + "id: " + deviceId + " owner:" + owner;
|
||||
throw new DeviceManagementException(msg);
|
||||
}
|
||||
ZipUtil ziputil = new ZipUtil();
|
||||
ZipArchive zipFile = ziputil.createZipFile(owner, APIUtil.getTenantDomainOftheUser(), sketchType,
|
||||
deviceId, deviceName, accessToken, refreshToken);
|
||||
return zipFile;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.homeautomation.droneanalyzer.api.dto;
|
||||
|
||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
/**
|
||||
* These information are sent by agent in each request to server
|
||||
*/
|
||||
@XmlRootElement
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class DeviceJSON {
|
||||
@XmlElement(required = true)
|
||||
public String owner;
|
||||
@XmlElement(required = true)
|
||||
public String deviceId;
|
||||
@XmlElement(required = true)
|
||||
public Float sensorValue;
|
||||
}
|
||||
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.homeautomation.droneanalyzer.api.dto;
|
||||
|
||||
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
|
||||
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
import javax.xml.bind.annotation.XmlElementWrapper;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This stores sensor event data for drone.
|
||||
*/
|
||||
@XmlRootElement
|
||||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class SensorRecord {
|
||||
|
||||
@XmlElementWrapper(required = true, name = "values")
|
||||
private Map<String, Object> values;
|
||||
|
||||
/**
|
||||
* The id.
|
||||
*/
|
||||
@XmlElement(required = false, name = "id")
|
||||
private String id;
|
||||
|
||||
/**
|
||||
* Gets the values.
|
||||
* @return the values
|
||||
*/
|
||||
public Map<String, Object> getValues() {
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the values.
|
||||
* @param values the values
|
||||
*/
|
||||
public void setValues(Map<String, Object> values) {
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the id.
|
||||
* @return the id
|
||||
*/
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the id.
|
||||
* @param id the new id
|
||||
*/
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
List<String> valueList = new ArrayList<String>();
|
||||
for (Map.Entry<String, Object> entry : values.entrySet()) {
|
||||
valueList.add(entry.getKey() + ":" + entry.getValue());
|
||||
}
|
||||
return valueList.toString();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.homeautomation.droneanalyzer.api.exception;
|
||||
|
||||
public class DroneAnalyzerException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 2736466230451105441L;
|
||||
|
||||
private String errorMessage;
|
||||
|
||||
public DroneAnalyzerException(String msg, DroneAnalyzerException nestedEx) {
|
||||
super(msg, nestedEx);
|
||||
setErrorMessage(msg);
|
||||
}
|
||||
|
||||
public DroneAnalyzerException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
setErrorMessage(message);
|
||||
}
|
||||
|
||||
public DroneAnalyzerException(String msg) {
|
||||
super(msg);
|
||||
setErrorMessage(msg);
|
||||
}
|
||||
|
||||
public DroneAnalyzerException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public DroneAnalyzerException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public String getErrorMessage() {
|
||||
return errorMessage;
|
||||
}
|
||||
|
||||
public void setErrorMessage(String errorMessage) {
|
||||
this.errorMessage = errorMessage;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,221 @@
|
||||
package org.homeautomation.droneanalyzer.api.util;
|
||||
|
||||
import org.homeautomation.droneanalyzer.api.dto.SensorRecord;
|
||||
|
||||
import org.wso2.carbon.event.output.adapter.core.OutputEventAdapterService;
|
||||
import org.wso2.carbon.analytics.api.AnalyticsDataAPI;
|
||||
import org.wso2.carbon.analytics.dataservice.core.AnalyticsDataServiceUtils;
|
||||
import org.wso2.carbon.analytics.dataservice.commons.AnalyticsDataResponse;
|
||||
import org.wso2.carbon.analytics.dataservice.commons.SearchResultEntry;
|
||||
import org.wso2.carbon.analytics.dataservice.commons.SortByField;
|
||||
import org.wso2.carbon.analytics.datasource.commons.Record;
|
||||
import org.wso2.carbon.analytics.datasource.commons.exception.AnalyticsException;
|
||||
import org.wso2.carbon.apimgt.application.extension.APIManagementProviderService;
|
||||
import org.wso2.carbon.context.CarbonContext;
|
||||
import org.wso2.carbon.context.PrivilegedCarbonContext;
|
||||
import org.wso2.carbon.device.mgt.common.authorization.DeviceAccessAuthorizationService;
|
||||
import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService;
|
||||
import org.wso2.carbon.identity.jwt.client.extension.service.JWTClientManagerService;
|
||||
import org.wso2.carbon.user.api.UserStoreException;
|
||||
import org.wso2.carbon.user.api.UserStoreManager;
|
||||
import org.wso2.carbon.user.core.service.RealmService;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This class provides utility functions used by REST-API.
|
||||
*/
|
||||
public class APIUtil {
|
||||
|
||||
private static Log log = LogFactory.getLog(APIUtil.class);
|
||||
|
||||
public static String getAuthenticatedUser() {
|
||||
PrivilegedCarbonContext threadLocalCarbonContext = PrivilegedCarbonContext.getThreadLocalCarbonContext();
|
||||
String username = threadLocalCarbonContext.getUsername();
|
||||
String tenantDomain = threadLocalCarbonContext.getTenantDomain();
|
||||
if (username.endsWith(tenantDomain)) {
|
||||
return username.substring(0, username.lastIndexOf("@"));
|
||||
}
|
||||
return username;
|
||||
}
|
||||
|
||||
public static DeviceManagementProviderService getDeviceManagementService() {
|
||||
PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext();
|
||||
DeviceManagementProviderService deviceManagementProviderService =
|
||||
(DeviceManagementProviderService) ctx.getOSGiService(DeviceManagementProviderService.class, null);
|
||||
if (deviceManagementProviderService == null) {
|
||||
String msg = "Device Management service has not initialized.";
|
||||
log.error(msg);
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
return deviceManagementProviderService;
|
||||
}
|
||||
|
||||
public static APIManagementProviderService getAPIManagementProviderService() {
|
||||
PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext();
|
||||
APIManagementProviderService apiManagementProviderService =
|
||||
(APIManagementProviderService) ctx.getOSGiService(APIManagementProviderService.class, null);
|
||||
if (apiManagementProviderService == null) {
|
||||
String msg = "API management provider service has not initialized.";
|
||||
log.error(msg);
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
return apiManagementProviderService;
|
||||
}
|
||||
|
||||
public static JWTClientManagerService getJWTClientManagerService() {
|
||||
PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext();
|
||||
JWTClientManagerService jwtClientManagerService =
|
||||
(JWTClientManagerService) ctx.getOSGiService(JWTClientManagerService.class, null);
|
||||
if (jwtClientManagerService == null) {
|
||||
String msg = "JWT Client manager service has not initialized.";
|
||||
log.error(msg);
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
return jwtClientManagerService;
|
||||
}
|
||||
|
||||
public static String getTenantDomainOftheUser() {
|
||||
PrivilegedCarbonContext threadLocalCarbonContext = PrivilegedCarbonContext.getThreadLocalCarbonContext();
|
||||
return threadLocalCarbonContext.getTenantDomain();
|
||||
}
|
||||
|
||||
public static UserStoreManager getUserStoreManager() {
|
||||
RealmService realmService;
|
||||
UserStoreManager userStoreManager;
|
||||
try {
|
||||
PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext();
|
||||
realmService = (RealmService) ctx.getOSGiService(RealmService.class, null);
|
||||
if (realmService == null) {
|
||||
String msg = "Realm service has not initialized.";
|
||||
log.error(msg);
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
int tenantId = ctx.getTenantId();
|
||||
userStoreManager = realmService.getTenantUserRealm(tenantId).getUserStoreManager();
|
||||
} catch (UserStoreException e) {
|
||||
String msg = "Error occurred while retrieving current user store manager";
|
||||
log.error(msg, e);
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
return userStoreManager;
|
||||
}
|
||||
|
||||
public static void registerApiAccessRoles(String user) {
|
||||
UserStoreManager userStoreManager = null;
|
||||
try {
|
||||
userStoreManager = getUserStoreManager();
|
||||
String[] userList = new String[]{user};
|
||||
if (userStoreManager != null) {
|
||||
String rolesOfUser[] = userStoreManager.getRoleListOfUser(user);
|
||||
if (!userStoreManager.isExistingRole(Constants.DEFAULT_ROLE_NAME)) {
|
||||
userStoreManager.addRole(Constants.DEFAULT_ROLE_NAME, userList, Constants.DEFAULT_PERMISSION);
|
||||
} else if (rolesOfUser != null && Arrays.asList(rolesOfUser).contains(Constants.DEFAULT_ROLE_NAME)) {
|
||||
return;
|
||||
} else {
|
||||
userStoreManager.updateUserListOfRole(Constants.DEFAULT_ROLE_NAME, new String[0], userList);
|
||||
}
|
||||
}
|
||||
} catch (UserStoreException e) {
|
||||
log.error("Error while creating a role and adding a user for virtual_firealarm.", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static DeviceAccessAuthorizationService getDeviceAccessAuthorizationService() {
|
||||
PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext();
|
||||
DeviceAccessAuthorizationService deviceAccessAuthorizationService =
|
||||
(DeviceAccessAuthorizationService) ctx.getOSGiService(DeviceAccessAuthorizationService.class, null);
|
||||
if (deviceAccessAuthorizationService == null) {
|
||||
String msg = "Device Authorization service has not initialized.";
|
||||
log.error(msg);
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
return deviceAccessAuthorizationService;
|
||||
}
|
||||
|
||||
public static List<SensorRecord> getAllEventsForDevice(String tableName, String query,
|
||||
List<SortByField> sortByFields) throws AnalyticsException {
|
||||
int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
|
||||
AnalyticsDataAPI analyticsDataAPI = getAnalyticsDataAPI();
|
||||
int eventCount = analyticsDataAPI.searchCount(tenantId, tableName, query);
|
||||
if (eventCount == 0) {
|
||||
return null;
|
||||
}
|
||||
List<SearchResultEntry> resultEntries = analyticsDataAPI.search(tenantId, tableName, query, 0, eventCount,
|
||||
sortByFields);
|
||||
List<String> recordIds = getRecordIds(resultEntries);
|
||||
AnalyticsDataResponse response = analyticsDataAPI.get(tenantId, tableName, 1, null, recordIds);
|
||||
Map<String, SensorRecord> sensorDatas = createSensorData(AnalyticsDataServiceUtils.listRecords(
|
||||
analyticsDataAPI, response));
|
||||
List<SensorRecord> sortedSensorData = getSortedSensorData(sensorDatas, resultEntries);
|
||||
return sortedSensorData;
|
||||
}
|
||||
|
||||
public static List<SensorRecord> getSortedSensorData(Map<String, SensorRecord> sensorDatas,
|
||||
List<SearchResultEntry> searchResults) {
|
||||
List<SensorRecord> sortedRecords = new ArrayList<>();
|
||||
for (SearchResultEntry searchResultEntry : searchResults) {
|
||||
sortedRecords.add(sensorDatas.get(searchResultEntry.getId()));
|
||||
}
|
||||
return sortedRecords;
|
||||
}
|
||||
|
||||
private static List<String> getRecordIds(List<SearchResultEntry> searchResults) {
|
||||
List<String> ids = new ArrayList<>();
|
||||
for (SearchResultEntry searchResult : searchResults) {
|
||||
ids.add(searchResult.getId());
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
public static Map<String, SensorRecord> createSensorData(List<Record> records) {
|
||||
Map<String, SensorRecord> sensorDatas = new HashMap<>();
|
||||
for (Record record : records) {
|
||||
SensorRecord sensorData = createSensorData(record);
|
||||
sensorDatas.put(sensorData.getId(), sensorData);
|
||||
}
|
||||
return sensorDatas;
|
||||
}
|
||||
|
||||
public static SensorRecord createSensorData(Record record) {
|
||||
SensorRecord recordBean = new SensorRecord();
|
||||
recordBean.setId(record.getId());
|
||||
recordBean.setValues(record.getValues());
|
||||
return recordBean;
|
||||
}
|
||||
|
||||
public static AnalyticsDataAPI getAnalyticsDataAPI() {
|
||||
PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext();
|
||||
AnalyticsDataAPI analyticsDataAPI =
|
||||
(AnalyticsDataAPI) ctx.getOSGiService(AnalyticsDataAPI.class, null);
|
||||
if (analyticsDataAPI == null) {
|
||||
String msg = "Analytics api service has not initialized.";
|
||||
log.error(msg);
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
return analyticsDataAPI;
|
||||
}
|
||||
|
||||
public static String getAuthenticatedUserTenantDomain() {
|
||||
PrivilegedCarbonContext threadLocalCarbonContext = PrivilegedCarbonContext.getThreadLocalCarbonContext();
|
||||
return threadLocalCarbonContext.getTenantDomain();
|
||||
}
|
||||
|
||||
public static OutputEventAdapterService getOutputEventAdapterService() {
|
||||
PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext();
|
||||
OutputEventAdapterService outputEventAdapterService =
|
||||
(OutputEventAdapterService) ctx.getOSGiService(OutputEventAdapterService.class, null);
|
||||
if (outputEventAdapterService == null) {
|
||||
String msg = "Device Authorization service has not initialized.";
|
||||
log.error(msg);
|
||||
throw new IllegalStateException(msg);
|
||||
}
|
||||
return outputEventAdapterService;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.homeautomation.droneanalyzer.api.util;
|
||||
|
||||
import org.wso2.carbon.user.core.Permission;
|
||||
|
||||
/**
|
||||
* This hold the constants related to the device type.
|
||||
*/
|
||||
public class Constants {
|
||||
|
||||
public static final String DEFAULT_PERMISSION_RESOURCE = "/permission/admin/device-mgt/drone/user";
|
||||
public static final String DEFAULT_ROLE_NAME = "drone_user";
|
||||
public static final Permission DEFAULT_PERMISSION[]
|
||||
= new Permission[]{new Permission(Constants.DEFAULT_PERMISSION_RESOURCE, "ui.execute")};
|
||||
}
|
||||
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.homeautomation.droneanalyzer.api.util;
|
||||
|
||||
import javax.xml.bind.annotation.XmlElement;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
|
||||
@XmlRootElement
|
||||
public class ResponsePayload {
|
||||
|
||||
private int statusCode;
|
||||
private String messageFromServer;
|
||||
private Object responseContent;
|
||||
|
||||
public static ResponsePayloadBuilder statusCode(int statusCode) {
|
||||
ResponsePayload message = new ResponsePayload();
|
||||
return message.getBuilder().statusCode(statusCode);
|
||||
}
|
||||
|
||||
public static ResponsePayloadBuilder messageFromServer(
|
||||
String messageFromServer) {
|
||||
ResponsePayload message = new ResponsePayload();
|
||||
return message.getBuilder().messageFromServer(messageFromServer);
|
||||
}
|
||||
|
||||
public static ResponsePayloadBuilder responseContent(String responseContent) {
|
||||
ResponsePayload message = new ResponsePayload();
|
||||
return message.getBuilder().responseContent(responseContent);
|
||||
}
|
||||
|
||||
@XmlElement
|
||||
public int getStatusCode() {
|
||||
return statusCode;
|
||||
}
|
||||
|
||||
public void setStatusCode(int statusCode) {
|
||||
this.statusCode = statusCode;
|
||||
}
|
||||
|
||||
@XmlElement
|
||||
public String getMessageFromServer() {
|
||||
return messageFromServer;
|
||||
}
|
||||
|
||||
public void setMessageFromServer(String messageFromServer) {
|
||||
this.messageFromServer = messageFromServer;
|
||||
}
|
||||
|
||||
@XmlElement
|
||||
public Object getResponseContent() {
|
||||
return responseContent;
|
||||
}
|
||||
|
||||
public void setResponseContent(Object responseContent) {
|
||||
this.responseContent = responseContent;
|
||||
}
|
||||
|
||||
private ResponsePayloadBuilder getBuilder() {
|
||||
return new ResponsePayloadBuilder();
|
||||
}
|
||||
|
||||
public class ResponsePayloadBuilder {
|
||||
|
||||
private int statusCode;
|
||||
private String messageFromServer;
|
||||
private Object responseContent;
|
||||
|
||||
public ResponsePayloadBuilder statusCode(int statusCode) {
|
||||
this.statusCode = statusCode;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ResponsePayloadBuilder messageFromServer(String messageFromServer) {
|
||||
this.messageFromServer = messageFromServer;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ResponsePayloadBuilder responseContent(String responseContent) {
|
||||
this.responseContent = responseContent;
|
||||
return this;
|
||||
}
|
||||
|
||||
public ResponsePayload build() {
|
||||
ResponsePayload payload = new ResponsePayload();
|
||||
payload.setStatusCode(statusCode);
|
||||
payload.setMessageFromServer(messageFromServer);
|
||||
payload.setResponseContent(responseContent);
|
||||
return payload;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.homeautomation.droneanalyzer.api.util;
|
||||
|
||||
import org.homeautomation.droneanalyzer.plugin.constants.DroneAnalyzerConstants;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.wso2.carbon.device.mgt.analytics.data.publisher.exception.DataPublisherConfigurationException;
|
||||
import org.wso2.carbon.device.mgt.analytics.data.publisher.service.EventsPublisherService;
|
||||
import org.wso2.carbon.context.PrivilegedCarbonContext;
|
||||
|
||||
/**
|
||||
* This can be used to publish data to DAS for given stream definition
|
||||
*/
|
||||
public class ServiceUtils {
|
||||
private static final Log log = LogFactory.getLog(ServiceUtils.class);
|
||||
|
||||
/**
|
||||
* Sensor data are published to DAS
|
||||
*
|
||||
* @param deviceId unique identifier of the device
|
||||
* @param sensorValue current value of sensor which is set at agent side
|
||||
* @return
|
||||
*/
|
||||
public static boolean publishToDAS(String deviceId, float sensorValue) {
|
||||
PrivilegedCarbonContext ctx = PrivilegedCarbonContext.getThreadLocalCarbonContext();
|
||||
EventsPublisherService deviceAnalyticsService = (EventsPublisherService) ctx.getOSGiService(
|
||||
EventsPublisherService.class, null);
|
||||
String owner = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUsername();
|
||||
Object metdaData[] = {owner, DroneAnalyzerConstants.DEVICE_TYPE, deviceId, System.currentTimeMillis()};
|
||||
Object payloadData[] = {sensorValue};
|
||||
try {
|
||||
deviceAnalyticsService.publishEvent(DroneAnalyzerConstants.SENSOR_STREAM_DEFINITION,
|
||||
DroneAnalyzerConstants.SENSOR_STREAM_DEFINITION_VERSION, metdaData, new Object[0], payloadData);
|
||||
} catch (DataPublisherConfigurationException e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
package org.homeautomation.droneanalyzer.api.util;
|
||||
|
||||
import org.homeautomation.droneanalyzer.plugin.mqtt.MqttConfig;
|
||||
|
||||
import org.wso2.carbon.device.mgt.common.DeviceManagementException;
|
||||
import org.wso2.carbon.device.mgt.iot.util.Utils;
|
||||
import org.wso2.carbon.device.mgt.iot.util.ZipArchive;
|
||||
import org.wso2.carbon.utils.CarbonUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This is used to create a zip file that includes the necessary configuration required for the agent.
|
||||
*/
|
||||
public class ZipUtil {
|
||||
|
||||
private static final String HTTPS_PORT_PROPERTY = "httpsPort";
|
||||
private static final String HTTP_PORT_PROPERTY = "httpPort";
|
||||
|
||||
private static final String LOCALHOST = "localhost";
|
||||
private static final String HTTPS_PROTOCOL_APPENDER = "https://";
|
||||
private static final String HTTP_PROTOCOL_APPENDER = "http://";
|
||||
|
||||
public ZipArchive createZipFile(String owner, String tenantDomain, String deviceType,
|
||||
String deviceId, String deviceName, String token,
|
||||
String refreshToken) throws DeviceManagementException {
|
||||
|
||||
String sketchFolder = "repository" + File.separator + "resources" + File.separator + "sketches";
|
||||
String archivesPath = CarbonUtils.getCarbonHome() + File.separator + sketchFolder + File.separator + "archives" +
|
||||
File.separator + deviceId;
|
||||
String templateSketchPath = sketchFolder + File.separator + deviceType;
|
||||
String iotServerIP;
|
||||
|
||||
try {
|
||||
iotServerIP = Utils.getServerUrl();
|
||||
String httpsServerPort = System.getProperty(HTTPS_PORT_PROPERTY);
|
||||
String httpServerPort = System.getProperty(HTTP_PORT_PROPERTY);
|
||||
String httpsServerEP = HTTPS_PROTOCOL_APPENDER + iotServerIP + ":" + httpsServerPort;
|
||||
String httpServerEP = HTTP_PROTOCOL_APPENDER + iotServerIP + ":" + httpServerPort;
|
||||
String apimEndpoint = httpsServerEP;
|
||||
String mqttEndpoint = MqttConfig.getInstance().getBrokerEndpoint();
|
||||
if (mqttEndpoint.contains(LOCALHOST)) {
|
||||
mqttEndpoint = mqttEndpoint.replace(LOCALHOST, iotServerIP);
|
||||
}
|
||||
|
||||
Map<String, String> contextParams = new HashMap<>();
|
||||
contextParams.put("SERVER_NAME", APIUtil.getTenantDomainOftheUser());
|
||||
contextParams.put("DEVICE_OWNER", owner);
|
||||
contextParams.put("DEVICE_ID", deviceId);
|
||||
contextParams.put("DEVICE_NAME", deviceName);
|
||||
contextParams.put("HTTPS_EP", httpsServerEP);
|
||||
contextParams.put("HTTP_EP", httpServerEP);
|
||||
contextParams.put("APIM_EP", apimEndpoint);
|
||||
contextParams.put("MQTT_EP", mqttEndpoint);
|
||||
contextParams.put("DEVICE_TOKEN", token);
|
||||
contextParams.put("DEVICE_REFRESH_TOKEN", refreshToken);
|
||||
|
||||
ZipArchive zipFile;
|
||||
zipFile = Utils.getSketchArchive(archivesPath, templateSketchPath, contextParams, deviceName);
|
||||
return zipFile;
|
||||
} catch (IOException e) {
|
||||
throw new DeviceManagementException("Zip File Creation Failed", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,87 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!--
|
||||
~ Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
~
|
||||
~ WSO2 Inc. licenses this file to you under the Apache License,
|
||||
~ Version 2.0 (the "License"); you may not use this file except
|
||||
~ in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing,
|
||||
~ software distributed under the License is distributed on an
|
||||
~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
~ KIND, either express or implied. See the License for the
|
||||
~ specific language governing permissions and limitations
|
||||
~ under the License.
|
||||
-->
|
||||
|
||||
<!-- This file contains the list of permissions that are associated with URL end points
|
||||
of the web app. Each permission should contain the name, permission path ,API path
|
||||
(URL) , HTTP method and OAUTH2 authorization scope (not-required).
|
||||
When defining dynamic paths for APIs, path variables are denoted by '*' notation.
|
||||
NOTE: All the endpoints of the web app should be available in this file. Otherwise
|
||||
it will result 403 error at the runtime.
|
||||
-->
|
||||
<PermissionConfiguration>
|
||||
<APIVersion></APIVersion>
|
||||
<!-- Device related APIs -->
|
||||
<Permission>
|
||||
<name>Get device</name>
|
||||
<path>/device-mgt/drone/user</path>
|
||||
<url>/device/*</url>
|
||||
<method>GET</method>
|
||||
<scope>drone_user</scope>
|
||||
</Permission>
|
||||
<Permission>
|
||||
<name>Remove device</name>
|
||||
<path>/device-mgt/drone/user</path>
|
||||
<url>/device/*</url>
|
||||
<method>DELETE</method>
|
||||
<scope>drone_user</scope>
|
||||
</Permission>
|
||||
<Permission>
|
||||
<name>Download device</name>
|
||||
<path>/device-mgt/drone/user</path>
|
||||
<url>/device/download</url>
|
||||
<method>GET</method>
|
||||
<scope>drone_user</scope>
|
||||
</Permission>
|
||||
<Permission>
|
||||
<name>Update device</name>
|
||||
<path>/device-mgt/drone/user</path>
|
||||
<url>/device/*</url>
|
||||
<method>POST</method>
|
||||
<scope>drone_user</scope>
|
||||
</Permission>
|
||||
<Permission>
|
||||
<name>Get Devices</name>
|
||||
<path>/device-mgt/drone/user</path>
|
||||
<url>/device</url>
|
||||
<method>GET</method>
|
||||
<scope>drone_user</scope>
|
||||
</Permission>
|
||||
<Permission>
|
||||
<name>Register Device</name>
|
||||
<path>/device-mgt/drone/user</path>
|
||||
<url>/device/register</url>
|
||||
<method>POST</method>
|
||||
<scope>drone_device</scope>
|
||||
</Permission>
|
||||
<Permission>
|
||||
<name>Control Sensor</name>
|
||||
<path>/device-mgt/drone/user</path>
|
||||
<url>/device/*/change-status</url>
|
||||
<method>POST</method>
|
||||
<scope>drone_user</scope>
|
||||
</Permission>
|
||||
<Permission>
|
||||
<name>Get Stats</name>
|
||||
<path>/device-mgt/drone/user</path>
|
||||
<url>/device/stats/*</url>
|
||||
<method>GET</method>
|
||||
<scope>drone_device</scope>
|
||||
</Permission>
|
||||
</PermissionConfiguration>
|
||||
@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
|
||||
<!--
|
||||
~ Copyright 2005-2013 WSO2, Inc. (http://wso2.com)
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<!--
|
||||
This file defines class loading policy of the whole container. But this behaviour can be overridden by individual webapps by putting this file into the META-INF/ directory.
|
||||
-->
|
||||
<Classloading xmlns="http://wso2.org/projects/as/classloading">
|
||||
|
||||
<!-- Parent-first or child-first. Default behaviour is child-first.-->
|
||||
<ParentFirst>false</ParentFirst>
|
||||
|
||||
<!--
|
||||
Default environments that contains provides to all the webapps. This can be overridden by individual webapps by specifing required environments
|
||||
Tomcat environment is the default and every webapps gets it even if they didn't specify it.
|
||||
e.g. If a webapps requires CXF, they will get both Tomcat and CXF.
|
||||
-->
|
||||
<Environments>CXF,Carbon</Environments>
|
||||
</Classloading>
|
||||
@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
~
|
||||
~ WSO2 Inc. licenses this file to you under the Apache License,
|
||||
~ Version 2.0 (the "License"); you may not use this file except
|
||||
~ in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing,
|
||||
~ software distributed under the License is distributed on an
|
||||
~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
~ KIND, either express or implied. See the License for the
|
||||
~ specific language governing permissions and limitations
|
||||
~ under the License.
|
||||
-->
|
||||
|
||||
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:jaxrs="http://cxf.apache.org/jaxrs"
|
||||
xmlns="http://www.springframework.org/schema/beans"
|
||||
xsi:schemaLocation="
|
||||
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
|
||||
http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd">
|
||||
<jaxrs:server id="drone" address="/">
|
||||
<jaxrs:serviceBeans>
|
||||
<bean id="DroneAnalyzerService"
|
||||
class="org.homeautomation.droneanalyzer.api.DroneAnalyzerServiceImpl">
|
||||
</bean>
|
||||
</jaxrs:serviceBeans>
|
||||
<jaxrs:providers>
|
||||
<bean class="org.codehaus.jackson.jaxrs.JacksonJsonProvider" />
|
||||
</jaxrs:providers>
|
||||
</jaxrs:server>
|
||||
</beans>
|
||||
@ -0,0 +1,46 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<web-app version="2.5"
|
||||
xmlns="http://java.sun.com/xml/ns/javaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
|
||||
metadata-complete="true">
|
||||
<display-name>WSO2 IoT Server</display-name>
|
||||
<description>WSO2 IoT Server</description>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>CXFServlet</servlet-name>
|
||||
<servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
|
||||
<load-on-startup>1</load-on-startup>
|
||||
</servlet>
|
||||
<servlet-mapping>
|
||||
<servlet-name>CXFServlet</servlet-name>
|
||||
<url-pattern>/*</url-pattern>
|
||||
</servlet-mapping>
|
||||
<context-param>
|
||||
<param-name>isAdminService</param-name>
|
||||
<param-value>false</param-value>
|
||||
</context-param>
|
||||
<context-param>
|
||||
<param-name>doAuthentication</param-name>
|
||||
<param-value>true</param-value>
|
||||
</context-param>
|
||||
<context-param>
|
||||
<param-name>isSharedWithAllTenants</param-name>
|
||||
<param-value>false</param-value>
|
||||
</context-param>
|
||||
<context-param>
|
||||
<param-name>providerTenantDomain</param-name>
|
||||
<param-value>carbon.super</param-value>
|
||||
</context-param>
|
||||
|
||||
<!--publish to apim-->
|
||||
<context-param>
|
||||
<param-name>managed-api-enabled</param-name>
|
||||
<param-value>true</param-value>
|
||||
</context-param>
|
||||
<context-param>
|
||||
<param-name>managed-api-owner</param-name>
|
||||
<param-value>admin</param-value>
|
||||
</context-param>
|
||||
|
||||
</web-app>
|
||||
56
modules/samples/droneanalyzer/component/ui/pom.xml
Normal file
@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
~
|
||||
~ WSO2 Inc. licenses this file to you under the Apache License,
|
||||
~ Version 2.0 (the "License"); you may not use this file except
|
||||
~ in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing,
|
||||
~ software distributed under the License is distributed on an
|
||||
~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
~ KIND, either express or implied. See the License for the
|
||||
~ specific language governing permissions and limitations
|
||||
~ under the License.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<parent>
|
||||
<groupId>org.homeautomation</groupId>
|
||||
<artifactId>droneanalyzer-component</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>${project-base-package}.ui</artifactId>
|
||||
<name>${project-base-package}.ui</name>
|
||||
<packaging>pom</packaging>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<version>${maven-assembly-plugin.version}</version>
|
||||
<configuration>
|
||||
<finalName>${project.artifactId}-1.0.0</finalName>
|
||||
<appendAssemblyId>false</appendAssemblyId>
|
||||
<descriptors>
|
||||
<descriptor>src/assembly/src.xml</descriptor>
|
||||
</descriptors>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>create-archive</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
~
|
||||
~ WSO2 Inc. licenses this file to you under the Apache License,
|
||||
~ Version 2.0 (the "License"); you may not use this file except
|
||||
~ in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing,
|
||||
~ software distributed under the License is distributed on an
|
||||
~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
~ KIND, either express or implied. See the License for the
|
||||
~ specific language governing permissions and limitations
|
||||
~ under the License.
|
||||
-->
|
||||
|
||||
<assembly
|
||||
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
|
||||
<id>src</id>
|
||||
<formats>
|
||||
<format>zip</format>
|
||||
</formats>
|
||||
<includeBaseDirectory>false</includeBaseDirectory>
|
||||
<baseDirectory>${basedir}/src</baseDirectory>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>${basedir}/src/main/resources/jaggeryapps/devicemgt</directory>
|
||||
<outputDirectory>/</outputDirectory>
|
||||
<useDefaultExcludes>true</useDefaultExcludes>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
</assembly>
|
||||
@ -0,0 +1,55 @@
|
||||
<span id="drone-details" data-devices="{{devices}}" data-devicename="{{device.name}}"
|
||||
data-deviceid="{{device.deviceIdentifier}}"
|
||||
data-appcontext="{{@app.context}}"></span>
|
||||
<div id="drone-div-chart" data-backend-api-url= {{backendApiUrl}}>
|
||||
<div class="chartWrapper" id="chartWrapper-battery">
|
||||
<span id="span-title">Battery</span>
|
||||
<div id="y_axis-battery" class="custom_y_axis"></div>
|
||||
<div class="legend_container">
|
||||
<div id="smoother-battery" title="Smoothing"></div>
|
||||
<div id="legend-battery"></div>
|
||||
</div>
|
||||
<div id="chart-battery" class="custom_rickshaw_graph" ></div>
|
||||
<div id="x_axis-battery" class="custom_x_axis"></div>
|
||||
<div id="slider-battery" class="custom_slider"></div>
|
||||
</div>
|
||||
|
||||
<div class="chartWrapper" id="chartWrapper-heading">
|
||||
<span id="span-title">Heading</span>
|
||||
<div id="y_axis-heading" class="custom_y_axis"></div>
|
||||
<div class="legend_container">
|
||||
<div id="smoother-heading" title="Smoothing"></div>
|
||||
<div id="legend-heading"></div>
|
||||
</div>
|
||||
<div id="chart-heading" class="custom_rickshaw_graph" ></div>
|
||||
<div id="x_axis-heading" class="custom_x_axis"></div>
|
||||
<div id="slider-heading" class="custom_slider"></div>
|
||||
</div>
|
||||
|
||||
<div class="chartWrapper" id="chartWrapper-bank">
|
||||
<span id="span-title">Bank</span>
|
||||
<div id="y_axis-bank" class="custom_y_axis"></div>
|
||||
<div class="legend_container">
|
||||
<div id="smoother-bank" title="Smoothing"></div>
|
||||
<div id="legend-bank"></div>
|
||||
</div>
|
||||
<div id="chart-bank" class="custom_rickshaw_graph" ></div>
|
||||
<div id="x_axis-bank" class="custom_x_axis"></div>
|
||||
<div id="slider-bank" class="custom_slider"></div>
|
||||
</div>
|
||||
<div class="chartWrapper" id="chartWrapper-attitude">
|
||||
<span id="span-title">Attitude</span>
|
||||
<div id="y_axis-attitude" class="custom_y_axis"></div>
|
||||
<div class="legend_container">
|
||||
<div id="smoother-attitude" title="Smoothing"></div>
|
||||
<div id="legend-attitude"></div>
|
||||
</div>
|
||||
<div id="chart-attitude" class="custom_rickshaw_graph" ></div>
|
||||
<div id="x_axis-attitude" class="custom_x_axis"></div>
|
||||
<div id="slider-attitude" class="custom_slider"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#zone "bottomJs"}}
|
||||
{{js "js/drone.js"}}
|
||||
{{/zone}}
|
||||
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
function onRequest(context) {
|
||||
var devices = context.unit.params.devices;
|
||||
var deviceType = context.uriParams.deviceType;
|
||||
var deviceId = request.getParameter("deviceId");
|
||||
|
||||
if (devices) {
|
||||
return {
|
||||
"devices": stringify(devices),
|
||||
"backendApiUrl": devicemgtProps["httpsURL"] + "/"+deviceType+"/device/stats/"
|
||||
};
|
||||
} else if (deviceType != null && deviceType != undefined && deviceId != null && deviceId != undefined) {
|
||||
var deviceModule = require("/app/modules/device.js").deviceModule;
|
||||
var device = deviceModule.viewDevice(deviceType, deviceId);
|
||||
if (device && device.status != "error") {
|
||||
return {
|
||||
"device": device,
|
||||
"backendApiUrl": devicemgtProps["httpsURL"] + "/"+deviceType+"/device/stats/" + deviceId
|
||||
};
|
||||
} else {
|
||||
response.sendError(404, "Device Id " + deviceId + " of type " + deviceType + " cannot be found!");
|
||||
exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
{
|
||||
"version": "1.0.0"
|
||||
}
|
||||
@ -0,0 +1,470 @@
|
||||
/*
|
||||
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
/* graph */
|
||||
|
||||
.rickshaw_graph {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.rickshaw_graph svg {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* ticks */
|
||||
|
||||
.rickshaw_graph .x_tick {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 0;
|
||||
border-left: 1px dotted rgba(0, 0, 0, 0.2);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.rickshaw_graph .x_tick .title {
|
||||
position: absolute;
|
||||
font-size: 12px;
|
||||
font-family: Arial, sans-serif;
|
||||
opacity: 0.5;
|
||||
white-space: nowrap;
|
||||
margin-left: 3px;
|
||||
bottom: -20px;
|
||||
height: auto;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
/* annotations */
|
||||
|
||||
.rickshaw_annotation_timeline {
|
||||
height: 1px;
|
||||
border-top: 1px solid #e0e0e0;
|
||||
margin-top: 10px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.rickshaw_annotation_timeline .annotation {
|
||||
position: absolute;
|
||||
height: 6px;
|
||||
width: 6px;
|
||||
margin-left: -2px;
|
||||
top: -3px;
|
||||
border-radius: 5px;
|
||||
background-color: rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
.rickshaw_graph .annotation_line {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: -6px;
|
||||
width: 0;
|
||||
border-left: 2px solid rgba(0, 0, 0, 0.3);
|
||||
display: none;
|
||||
}
|
||||
|
||||
.rickshaw_graph .annotation_line.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.rickshaw_graph .annotation_range {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: -6px;
|
||||
}
|
||||
|
||||
.rickshaw_graph .annotation_range.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.rickshaw_graph .annotation_range.active.offscreen {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.rickshaw_annotation_timeline .annotation .content {
|
||||
background: white;
|
||||
color: black;
|
||||
opacity: 0.9;
|
||||
box-shadow: 0 0 2px rgba(0, 0, 0, 0.8);
|
||||
border-radius: 3px;
|
||||
position: relative;
|
||||
z-index: 20;
|
||||
font-size: 12px;
|
||||
padding: 6px 8px 8px;
|
||||
top: 18px;
|
||||
left: -11px;
|
||||
width: 160px;
|
||||
display: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.rickshaw_annotation_timeline .annotation .content:before {
|
||||
content: "\25b2";
|
||||
position: absolute;
|
||||
top: -11px;
|
||||
color: white;
|
||||
text-shadow: 0 -1px 1px rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
.rickshaw_annotation_timeline .annotation.active,
|
||||
.rickshaw_annotation_timeline .annotation:hover {
|
||||
background-color: rgba(0, 0, 0, 0.8);
|
||||
cursor: none;
|
||||
}
|
||||
|
||||
.rickshaw_annotation_timeline .annotation .content:hover {
|
||||
z-index: 50;
|
||||
}
|
||||
|
||||
.rickshaw_annotation_timeline .annotation.active .content {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.rickshaw_annotation_timeline .annotation:hover .content {
|
||||
display: block;
|
||||
z-index: 50;
|
||||
}
|
||||
|
||||
.rickshaw_graph .y_axis,
|
||||
.rickshaw_graph .x_axis_d3 {
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.rickshaw_graph .y_ticks .tick line,
|
||||
.rickshaw_graph .x_ticks_d3 .tick {
|
||||
stroke: rgba(0, 0, 0, 0.16);
|
||||
stroke-width: 2px;
|
||||
shape-rendering: crisp-edges;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.rickshaw_graph .y_grid .tick,
|
||||
.rickshaw_graph .x_grid_d3 .tick {
|
||||
z-index: -1;
|
||||
stroke: rgba(0, 0, 0, 0.20);
|
||||
stroke-width: 1px;
|
||||
stroke-dasharray: 1 1;
|
||||
}
|
||||
|
||||
.rickshaw_graph .y_grid .tick[data-y-value="0"] {
|
||||
stroke-dasharray: 1 0;
|
||||
}
|
||||
|
||||
.rickshaw_graph .y_grid path,
|
||||
.rickshaw_graph .x_grid_d3 path {
|
||||
fill: none;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
.rickshaw_graph .y_ticks path,
|
||||
.rickshaw_graph .x_ticks_d3 path {
|
||||
fill: none;
|
||||
stroke: #808080;
|
||||
}
|
||||
|
||||
.rickshaw_graph .y_ticks text,
|
||||
.rickshaw_graph .x_ticks_d3 text {
|
||||
opacity: 0.5;
|
||||
font-size: 12px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.rickshaw_graph .x_tick.glow .title,
|
||||
.rickshaw_graph .y_ticks.glow text {
|
||||
fill: black;
|
||||
color: black;
|
||||
text-shadow: -1px 1px 0 rgba(255, 255, 255, 0.1),
|
||||
1px -1px 0 rgba(255, 255, 255, 0.1),
|
||||
1px 1px 0 rgba(255, 255, 255, 0.1),
|
||||
0 1px 0 rgba(255, 255, 255, 0.1),
|
||||
0 -1px 0 rgba(255, 255, 255, 0.1),
|
||||
1px 0 0 rgba(255, 255, 255, 0.1),
|
||||
-1px 0 0 rgba(255, 255, 255, 0.1),
|
||||
-1px -1px 0 rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.rickshaw_graph .x_tick.inverse .title,
|
||||
.rickshaw_graph .y_ticks.inverse text {
|
||||
fill: white;
|
||||
color: white;
|
||||
text-shadow: -1px 1px 0 rgba(0, 0, 0, 0.8),
|
||||
1px -1px 0 rgba(0, 0, 0, 0.8),
|
||||
1px 1px 0 rgba(0, 0, 0, 0.8),
|
||||
0 1px 0 rgba(0, 0, 0, 0.8),
|
||||
0 -1px 0 rgba(0, 0, 0, 0.8),
|
||||
1px 0 0 rgba(0, 0, 0, 0.8),
|
||||
-1px 0 0 rgba(0, 0, 0, 0.8),
|
||||
-1px -1px 0 rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
.custom_rickshaw_graph {
|
||||
position: relative;
|
||||
left: 40px;
|
||||
}
|
||||
|
||||
.custom_y_axis {
|
||||
position: absolute;
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
.custom_slider {
|
||||
left: 40px;
|
||||
}
|
||||
|
||||
.custom_x_axis {
|
||||
position: relative;
|
||||
left: 40px;
|
||||
height: 30px;
|
||||
width: 97%;
|
||||
top: 20px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.chartWrapper {
|
||||
padding-top: 50px;
|
||||
}
|
||||
|
||||
/*detail*/
|
||||
|
||||
.rickshaw_graph .detail {
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
z-index: 2;
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
bottom: 0;
|
||||
width: 1px;
|
||||
transition: opacity 0.25s linear;
|
||||
-moz-transition: opacity 0.25s linear;
|
||||
-o-transition: opacity 0.25s linear;
|
||||
-webkit-transition: opacity 0.25s linear;
|
||||
}
|
||||
|
||||
.rickshaw_graph .detail.inactive {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.rickshaw_graph .detail .item.active {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.rickshaw_graph .detail .x_label {
|
||||
font-family: Arial, sans-serif;
|
||||
border-radius: 3px;
|
||||
padding: 6px;
|
||||
opacity: 0.5;
|
||||
border: 1px solid #e0e0e0;
|
||||
font-size: 12px;
|
||||
position: absolute;
|
||||
background: white;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.rickshaw_graph .detail .x_label.left {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.rickshaw_graph .detail .x_label.right {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.rickshaw_graph .detail .item {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
border-radius: 3px;
|
||||
padding: 0.25em;
|
||||
font-size: 12px;
|
||||
font-family: Arial, sans-serif;
|
||||
opacity: 0;
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
color: white;
|
||||
border: 1px solid rgba(0, 0, 0, 0.4);
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
margin-top: -1em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.rickshaw_graph .detail .item.left {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.rickshaw_graph .detail .item.right {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.rickshaw_graph .detail .item.active {
|
||||
opacity: 1;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
.rickshaw_graph .detail .item:after {
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
|
||||
content: "";
|
||||
|
||||
border: 5px solid transparent;
|
||||
}
|
||||
|
||||
.rickshaw_graph .detail .item.left:after {
|
||||
top: 1em;
|
||||
left: -5px;
|
||||
margin-top: -5px;
|
||||
border-right-color: rgba(0, 0, 0, 0.8);
|
||||
border-left-width: 0;
|
||||
}
|
||||
|
||||
.rickshaw_graph .detail .item.right:after {
|
||||
top: 1em;
|
||||
right: -5px;
|
||||
margin-top: -5px;
|
||||
border-left-color: rgba(0, 0, 0, 0.8);
|
||||
border-right-width: 0;
|
||||
}
|
||||
|
||||
.rickshaw_graph .detail .dot {
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
margin-left: -3px;
|
||||
margin-top: -3.5px;
|
||||
border-radius: 5px;
|
||||
position: absolute;
|
||||
box-shadow: 0 0 2px rgba(0, 0, 0, 0.6);
|
||||
box-sizing: content-box;
|
||||
-moz-box-sizing: content-box;
|
||||
background: white;
|
||||
border-width: 2px;
|
||||
border-style: solid;
|
||||
display: none;
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
.rickshaw_graph .detail .dot.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/*legend*/
|
||||
.rickshaw_legend {
|
||||
font-family: Arial;
|
||||
font-size: 12px;
|
||||
color: white;
|
||||
background: #404040;
|
||||
display: inline-block;
|
||||
padding: 12px 5px;
|
||||
border-radius: 2px;
|
||||
position: relative;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.rickshaw_legend:hover {
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.rickshaw_legend .swatch {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.rickshaw_legend .line {
|
||||
clear: both;
|
||||
line-height: 140%;
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
.rickshaw_legend .line .swatch {
|
||||
display: inline-block;
|
||||
margin-right: 3px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.rickshaw_legend .label {
|
||||
margin: 0;
|
||||
white-space: nowrap;
|
||||
display: inline;
|
||||
font-size: inherit;
|
||||
background-color: transparent;
|
||||
color: inherit;
|
||||
font-weight: normal;
|
||||
line-height: normal;
|
||||
padding: 0;
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
.rickshaw_legend .action:hover {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.rickshaw_legend .action {
|
||||
margin-right: 0.2em;
|
||||
opacity: 0.2;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.rickshaw_legend .line.disabled {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.rickshaw_legend ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 2px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.rickshaw_legend li {
|
||||
padding: 0 0 0 2px;
|
||||
min-width: 80px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.rickshaw_legend li:hover {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.rickshaw_legend li:active {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.legend {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
left: 8px;
|
||||
}
|
||||
|
||||
.legend_container {
|
||||
float: right;
|
||||
padding-right: 10px;
|
||||
width: 0;
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.spaced {
|
||||
margin-top: 20px !important;
|
||||
}
|
||||
|
||||
@ -0,0 +1,265 @@
|
||||
/*
|
||||
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
var palette = new Rickshaw.Color.Palette({scheme: "classic9"});
|
||||
var graphMap = {};
|
||||
|
||||
function drawGraph_drone(from, to) {
|
||||
var devices = $("#drone-details").data("devices");
|
||||
var tzOffset = new Date().getTimezoneOffset() * 60;
|
||||
|
||||
var streamIndex = 0;
|
||||
var streams = ["battery", "heading", "bank", "attitude"];
|
||||
|
||||
populateGraph();
|
||||
|
||||
function populateGraph() {
|
||||
if(streamIndex<4){
|
||||
retrieveDataAndDrawLineGraph(streams[streamIndex], from, to);
|
||||
}
|
||||
streamIndex++;
|
||||
}
|
||||
|
||||
function clearContent(type) {
|
||||
$("#y_axis-" + type).html("");
|
||||
$("#smoother-" + type).html("");
|
||||
$("#legend-" + type).html("");
|
||||
$("#chart-" + type).html("");
|
||||
$("#x_axis-" + type).html("");
|
||||
$("#slider-" + type).html("");
|
||||
}
|
||||
|
||||
function initGraph(type, isMultilined) {
|
||||
if (graphMap[type]) {
|
||||
return graphMap[type];
|
||||
}
|
||||
|
||||
var chartWrapperElmId = "#drone-div-chart";
|
||||
var graphWidth = $(chartWrapperElmId).width() - 50;
|
||||
|
||||
console.log("document.getElementById("+ document.getElementById("chart-" + type)+ " ---"+ type);
|
||||
var graphConfig = {
|
||||
element: document.getElementById("chart-" + type),
|
||||
width: graphWidth,
|
||||
height: 400,
|
||||
strokeWidth: 2,
|
||||
renderer: 'line',
|
||||
interpolation: "linear",
|
||||
unstack: true,
|
||||
stack: false,
|
||||
xScale: d3.time.scale(),
|
||||
padding: {top: 0.2, left: 0.02, right: 0.02, bottom: 0.2},
|
||||
series: []
|
||||
};
|
||||
|
||||
if (devices) {
|
||||
for (var i = 0; i < devices.length; i++) {
|
||||
graphConfig['series'].push(
|
||||
{
|
||||
'color': palette.color(),
|
||||
'data': [{
|
||||
x: parseInt(new Date().getTime() / 1000),
|
||||
y: 0
|
||||
}],
|
||||
'name': devices[i].name
|
||||
});
|
||||
}
|
||||
} else {
|
||||
if (isMultilined) {
|
||||
graphConfig['series'].push(
|
||||
{
|
||||
'color': palette.color(),
|
||||
'data': [{
|
||||
x: parseInt(new Date().getTime() / 1000),
|
||||
y: 0
|
||||
}],
|
||||
'name': "x"
|
||||
},
|
||||
{
|
||||
'color': palette.color(),
|
||||
'data': [{
|
||||
x: parseInt(new Date().getTime() / 1000),
|
||||
y: 0
|
||||
}],
|
||||
'name': "y"
|
||||
},
|
||||
{
|
||||
'color': palette.color(),
|
||||
'data': [{
|
||||
x: parseInt(new Date().getTime() / 1000),
|
||||
y: 0
|
||||
}],
|
||||
'name': "z"
|
||||
}
|
||||
);
|
||||
} else {
|
||||
graphConfig['series'].push(
|
||||
{
|
||||
'color': palette.color(),
|
||||
'data': [{
|
||||
x: parseInt(new Date().getTime() / 1000),
|
||||
y: 0
|
||||
}],
|
||||
'name': $("#drone-details").data("devicename")
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var graph = new Rickshaw.Graph(graphConfig);
|
||||
graph.render();
|
||||
|
||||
var xAxis = new Rickshaw.Graph.Axis.Time({
|
||||
graph: graph
|
||||
});
|
||||
xAxis.render();
|
||||
|
||||
var yAxis = new Rickshaw.Graph.Axis.Y({
|
||||
graph: graph,
|
||||
orientation: 'left',
|
||||
element: document.getElementById("y_axis-" + type),
|
||||
width: 40,
|
||||
height: 410
|
||||
});
|
||||
yAxis.render();
|
||||
|
||||
var slider = new Rickshaw.Graph.RangeSlider.Preview({
|
||||
graph: graph,
|
||||
element: document.getElementById("slider-" + type)
|
||||
});
|
||||
|
||||
var legend = new Rickshaw.Graph.Legend({
|
||||
graph: graph,
|
||||
element: document.getElementById("legend-" + type)
|
||||
});
|
||||
|
||||
var hoverDetail = new Rickshaw.Graph.HoverDetail({
|
||||
graph: graph,
|
||||
formatter: function (series, x, y) {
|
||||
var date = '<span class="date">' +
|
||||
moment((x + tzOffset) * 1000).format('Do MMM YYYY h:mm:ss a') + '</span>';
|
||||
var swatch = '<span class="detail_swatch" style="background-color: ' +
|
||||
series.color + '"></span>';
|
||||
return swatch + series.name + ": " + parseInt(y) + '<br>' + date;
|
||||
}
|
||||
});
|
||||
|
||||
var shelving = new Rickshaw.Graph.Behavior.Series.Toggle({
|
||||
graph: graph,
|
||||
legend: legend
|
||||
});
|
||||
|
||||
var order = new Rickshaw.Graph.Behavior.Series.Order({
|
||||
graph: graph,
|
||||
legend: legend
|
||||
});
|
||||
|
||||
var highlighter = new Rickshaw.Graph.Behavior.Series.Highlight({
|
||||
graph: graph,
|
||||
legend: legend
|
||||
});
|
||||
|
||||
graphMap[type] = {};
|
||||
graphMap[type].graph = graph;
|
||||
graphMap[type].config = graphConfig;
|
||||
return graphMap[type];
|
||||
}
|
||||
|
||||
function retrieveDataAndDrawLineGraph(type, from, to) {
|
||||
var graphObj = initGraph(type, false);
|
||||
var graph = graphObj.graph;
|
||||
var graphConfig = graphObj.config;
|
||||
|
||||
var deviceIndex = 0;
|
||||
|
||||
if (devices) {
|
||||
getData();
|
||||
} else {
|
||||
var backendApiUrl = $("#drone-div-chart").data("backend-api-url") + "?from=" + from + "&to=" + to;
|
||||
var successCallback = function (data) {
|
||||
if (data) {
|
||||
drawLineGraph(JSON.parse(data));
|
||||
}
|
||||
populateGraph();
|
||||
};
|
||||
invokerUtil.get(backendApiUrl, successCallback, function (message) {
|
||||
console.log(message);
|
||||
populateGraph();
|
||||
});
|
||||
}
|
||||
|
||||
function getData() {
|
||||
if (deviceIndex >= devices.length) {
|
||||
return;
|
||||
}
|
||||
var backendApiUrl = $("#drone-div-chart").data("backend-api-url") + "?from=" + from + "&to=" + to;
|
||||
var successCallback = function (data) {
|
||||
if (data) {
|
||||
drawLineGraph(JSON.parse(data));
|
||||
}
|
||||
deviceIndex++;
|
||||
getData();
|
||||
};
|
||||
invokerUtil.get(backendApiUrl, successCallback, function (message) {
|
||||
console.log(message);
|
||||
deviceIndex++;
|
||||
getData();
|
||||
});
|
||||
}
|
||||
|
||||
function drawLineGraph(data) {
|
||||
if (data.length === 0 || data.length === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
var chartData = [];
|
||||
for (var i = 0; i < data.length; i++) {
|
||||
|
||||
chartData.push(
|
||||
{
|
||||
x: parseInt(data[i].values.meta_time) - tzOffset,
|
||||
y: parseInt(getFieldData(data[i], type))
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
graphConfig.series[deviceIndex].data = chartData;
|
||||
graph.update();
|
||||
}
|
||||
}
|
||||
|
||||
function getFieldData(data, type) {
|
||||
var columnData;
|
||||
switch (type) {
|
||||
case "battery" :
|
||||
columnData = data.values.battery_level;
|
||||
break;
|
||||
case "heading" :
|
||||
columnData = data.values.yaw;
|
||||
break;
|
||||
case "bank" :
|
||||
columnData = data.values.roll;
|
||||
break;
|
||||
case "attitude" :
|
||||
columnData = data.values.pitch;
|
||||
break;
|
||||
}
|
||||
|
||||
return columnData;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
{{#zone "topCss"}}
|
||||
<style>
|
||||
.thumbnail.icon:before {
|
||||
padding-top: 0;
|
||||
}
|
||||
</style>
|
||||
{{/zone}}
|
||||
|
||||
{{#zone "device-thumbnail"}}
|
||||
<img src="{{@unit.publicUri}}/images/drone-icon.png"/>
|
||||
{{/zone}}
|
||||
|
||||
{{#zone "device-opetations"}}
|
||||
<div style="background: #11375B; color: #fff; padding: 10px; margin-bottom: 5px">
|
||||
Operations
|
||||
</div>
|
||||
<div class="add-margin-top-4x">
|
||||
{{unit "iot.unit.device.operation-bar" device=device backendApiUri=backendApiUri autoCompleteParams=autoCompleteParams}}
|
||||
</div>
|
||||
{{/zone}}
|
||||
|
||||
{{#zone "device-detail-properties"}}
|
||||
<div class="media">
|
||||
<div class="media-left col-xs-12 col-sm-2 col-md-2 col-lg-2">
|
||||
<ul class="list-group" role="tablist">
|
||||
<li class="active"><a class="list-group-item" href="#device_statistics" role="tab"
|
||||
data-toggle="tab" aria-controls="device_statistics">Device
|
||||
Statistics</a>
|
||||
</li>
|
||||
<li><a class="list-group-item" href="#event_log" role="tab" data-toggle="tab"
|
||||
aria-controls="event_log">Operations Log</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="media-body add-padding-left-5x remove-padding-xs tab-content">
|
||||
<div class="panel-group tab-content">
|
||||
|
||||
<div class="panel panel-default tab-pane active"
|
||||
id="device_statistics" role="tabpanel" aria-labelledby="device_statistics">
|
||||
<div class="panel-heading">Device Statistics</div>
|
||||
{{unit "cdmf.unit.device.type.drone.realtime.analytics-view" device=device}}
|
||||
</div>
|
||||
<div class="panel panel-default tab-pane" id="event_log" role="tabpanel"
|
||||
aria-labelledby="event_log">
|
||||
<div class="panel-heading">Operations Log <span><a href="#"
|
||||
id="refresh-operations"><i
|
||||
class="fw fw-refresh"></i></a></span></div>
|
||||
<div class="panel-body">
|
||||
<div id="operations-spinner" class="wr-advance-operations-init hidden">
|
||||
<br>
|
||||
|
||||
<i class="fw fw-settings fw-spin fw-2x"></i>
|
||||
|
||||
Loading Operations Log . . .
|
||||
<br>
|
||||
<br>
|
||||
</div>
|
||||
<div id="operations-log-container">
|
||||
<div class="panel-body">
|
||||
Not available yet
|
||||
</div>
|
||||
<br class="c-both"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{/zone}}
|
||||
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
function onRequest(context) {
|
||||
var log = new Log("device-view.js");
|
||||
var deviceType = context.uriParams.deviceType;
|
||||
var deviceId = request.getParameter("id");
|
||||
var autoCompleteParams = [
|
||||
{"name" : "deviceId", "value" : deviceId}
|
||||
];
|
||||
|
||||
if (deviceType != null && deviceType != undefined && deviceId != null && deviceId != undefined) {
|
||||
var deviceModule = require("/app/modules/device.js").deviceModule;
|
||||
var device = deviceModule.viewDevice(deviceType, deviceId);
|
||||
if (device && device.status != "error") {
|
||||
return {"device": device, "backendApiUri" : devicemgtProps["httpsURL"] + "/"+deviceType+"/", "autoCompleteParams" : autoCompleteParams};
|
||||
} else {
|
||||
response.sendError(404, "Device Id " + deviceId + " of type " + deviceType + " cannot be found!");
|
||||
exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
{
|
||||
"version": "1.0.0"
|
||||
}
|
||||
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 46 KiB |
@ -0,0 +1,210 @@
|
||||
{{#zone "topCss"}}
|
||||
{{css "css/graph.css"}}
|
||||
{{css "css/main-app.css" }}
|
||||
{{/zone}}
|
||||
<div id="div-chart" data-websocketurl="{{websocketEndpoint}}">
|
||||
<div class="chartWrapper" id="chartWrapper">
|
||||
<div class=" row">
|
||||
<div class="box col-md-12">
|
||||
<div>
|
||||
<div class="col-md-0.5 col-sm-1 col-xs-2">
|
||||
<p>Battery Level </p>
|
||||
</div>
|
||||
<div class="col-md-4 col-sm-2 col-xs-2">
|
||||
<div class="progress">
|
||||
<div class="progress-bar" role="progressbar" aria-valuenow="60" aria-valuemin="0"
|
||||
aria-valuemax="100" style="width: 0%;" id="batteryLevelPlaceholder">
|
||||
<p id="batteryLevel">0%</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-1 col-sm-1 col-xs-2">
|
||||
<p>Battery Voltage </p>
|
||||
</div>
|
||||
<div class="col-md-4 col-sm-2 col-xs-2">
|
||||
<div class="progress">
|
||||
<div class="progress-bar" role="progressbar" aria-valuenow="60" aria-valuemin="0"
|
||||
aria-valuemax="100" style="width: 0%;" id="batteryVoltagePlaceholder">
|
||||
<p id="batteryVoltage">0%</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="box col-md-12">
|
||||
<div class="box col-md-4">
|
||||
<div class="box-inner">
|
||||
<div class="box-header well" data-original-title="">
|
||||
<h2><i class="glyphicon glyphicon-list"></i> Angle of Rotation</h2>
|
||||
|
||||
<div class="box-icon" id="Rotation">
|
||||
<a href="#" class="btn btn-minimize btn-round btn-default"><i
|
||||
class="glyphicon glyphicon-chevron-up"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content" style="position: relative;" id="objectHolder">
|
||||
<div class="grayscale" border-radius="2" id="virtualDrone">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box col-md-2">
|
||||
<div class="box-inner" style="max-height: 280px;">
|
||||
<div class="box-header well" data-original-title="">
|
||||
|
||||
<div class="box-icon" id="AngleOfRotation">
|
||||
<a href="#" class="btn btn-minimize btn-round btn-default"><i
|
||||
class="glyphicon glyphicon-chevron-up"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div style="position: relative;">
|
||||
|
||||
<img class="grayscale" id="imageTop"
|
||||
src="{{@unit.publicUri}}/images/drone_position_controller/direction_drone.png"
|
||||
style="width: 100%;"/>
|
||||
<img class="grayscale" id="imageBack"
|
||||
src="{{@unit.publicUri}}/images/drone_position_controller/background_drone.png"
|
||||
style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box col-md-2">
|
||||
<div class="box-inner" style="max-height: 280px;">
|
||||
<div class="box-header well" data-original-title="">
|
||||
|
||||
<div class="box-icon">
|
||||
<a href="#" class="btn btn-minimize btn-round btn-default"><i
|
||||
class="glyphicon glyphicon-chevron-up"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div style="position: relative;">
|
||||
|
||||
<img id="imageBackSecond"
|
||||
src="{{@unit.publicUri}}/images/drone_position_controller/pitch_drone.png"
|
||||
style="width: 100%;"/>
|
||||
<img id="imageTopSecond"
|
||||
src="{{@unit.publicUri}}/images/drone_position_controller/background_drone.png"
|
||||
style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="box col-md-4">
|
||||
<div class="box-inner">
|
||||
<div class="box-header well" data-original-title="">
|
||||
<h2><i class="glyphicon glyphicon-list"></i>Live Video Stream</h2>
|
||||
|
||||
<div class="box-icon" id="LiveVideoStream">
|
||||
<a href="#" class="btn btn-minimize btn-round btn-default"><i
|
||||
class="glyphicon glyphicon-chevron-up"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<img class="grayscale" src="{{@unit.publicUri}}/images/no_video_preview.gif"
|
||||
alt="video stream" style="display: block; height:225px; margin: auto;">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="box col-md-12">
|
||||
<div class="box col-md-4">
|
||||
<div class="box-inner">
|
||||
<div class="box-header well" data-original-title="">
|
||||
<h2><i class="glyphicon glyphicon-list"></i>Sensor Readings</h2>
|
||||
<div class="box-icon" id="SensorReadings">
|
||||
<a href="#" class="btn btn-minimize btn-round btn-default"><i
|
||||
class="glyphicon glyphicon-chevron-up"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content" id="basicSensoReading">
|
||||
<div>
|
||||
<p>Location</br> latitude: <label id="locationLat"></label></br>
|
||||
longitude: <label id="locationLog"></label></br>
|
||||
altitudes: <label id="locationAlt"></label></p>
|
||||
<p> Velocity:</br> x :<label id="velocityx"></label></br>
|
||||
y : <label id="velocityy"></label></br>
|
||||
z : <label id="velocityz"></label></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box col-md-8">
|
||||
<div class="box-inner">
|
||||
<div class="box-header well" data-original-title="">
|
||||
<h2><i class="glyphicon glyphicon-list-alt"></i>Realtime Plotting</h2>
|
||||
|
||||
<div class="box-icon" id="RealtimePlotting">
|
||||
<a href="#" class="btn btn-minimize btn-round btn-default"><i
|
||||
class="glyphicon glyphicon-chevron-up"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div class="row">
|
||||
<div class="box col-md-4" id="plottingMetadata" style="margin-left: 5%">
|
||||
<div>
|
||||
Window size : <label id="windowSizeCurrentValue">30</label>
|
||||
<div id="windowSize" style="width: 100%;"></div>
|
||||
<br>
|
||||
Update period : <label id="windowUpdateValue">300</label>
|
||||
</div>
|
||||
<div id="windowUpdate" style="width: 100%;"></div>
|
||||
</br>
|
||||
<div style="width:100%;">Y-axis: <label>Min: <input id="rangeMin" type="text"
|
||||
value="-4" size="3"></label>
|
||||
<label>Max: <input id="rangeMax" value="4" type="text" size="3"></label></div>
|
||||
<div style="width:100%;"><select ng-model='discussionsSelect' class='form-control'
|
||||
id="plottingAttribute">
|
||||
<option value='heading' selected>Heading</option>
|
||||
<option value='bank'>Bank</option>
|
||||
<option value='attitude'>Attitude</option>
|
||||
</select></div>
|
||||
</br>
|
||||
<div>
|
||||
<button class="btn btn-primary" id="replotting">Replot</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box col-md-8" id="realtimeChart" style="width:20%; height: 250px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<a class="padding-left"
|
||||
href="{{@app.context}}/device/{{device.type}}/analytics?deviceId={{device.deviceIdentifier}}&deviceName={{device.name}}">
|
||||
<span class="fw-stack">
|
||||
<i class="fw fw-ring fw-stack-2x"></i>
|
||||
<i class="fw fw-statistics fw-stack-1x"></i>
|
||||
</span> View Device Analytics
|
||||
</a>
|
||||
<!-- /statistics -->
|
||||
{{#zone "bottomJs"}}
|
||||
{{js "js/libs/d3.min.js"}}
|
||||
{{js "js/libs/rickshaw.min.js"}}
|
||||
{{js "js/libs/moment.min.js"}}
|
||||
{{js "js/libs/socket.io.min.js"}}
|
||||
{{js "/js/libs/three.min.js" }}
|
||||
{{js "/js/libs/Coordinates.js" }}
|
||||
{{js "/js/libs/OrbitAndPanControls.js" }}
|
||||
{{js "/js/3DDroneController/controler.js" }}
|
||||
{{js "/js/libs/jQueryRotate.js" }}
|
||||
{{js "/js/config/config.js" }}
|
||||
{{js "/js/common/ajaxHandler.js" }}
|
||||
{{js "/js/modules/realtimePlotting.js" }}
|
||||
{{js "/js/init.js" }}
|
||||
{{js "/js/modules/controller.js" }}
|
||||
{{js "/js/modules/flightDynamics.js" }}
|
||||
{{js "/js/mainHandler.js" }}
|
||||
{{/zone}}
|
||||
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
function onRequest(context) {
|
||||
var log = new Log("stats.js");
|
||||
var device = context.unit.params.device;
|
||||
var devicemgtProps = require('/app/conf/devicemgt-props.js').config();
|
||||
var constants = require("/app/modules/constants.js");
|
||||
var websocketEndpoint = devicemgtProps["httpsURL"].replace("https", "wss");
|
||||
var tokenPair = session.get(constants.ACCESS_TOKEN_PAIR_IDENTIFIER);
|
||||
var token = "";
|
||||
if (tokenPair) {
|
||||
token = tokenPair.accessToken;
|
||||
}
|
||||
websocketEndpoint = websocketEndpoint + "/secured-outputui/org.wso2.iot.devices.droneStats/1.0.0?" +
|
||||
"token="+ token +"&deviceId=" + device.deviceIdentifier + "&deviceType=" + device.type;
|
||||
return {"device": device, "websocketEndpoint" : websocketEndpoint};
|
||||
}
|
||||
@ -0,0 +1,3 @@
|
||||
{
|
||||
"version": "1.0.0"
|
||||
}
|
||||
@ -0,0 +1,471 @@
|
||||
/*
|
||||
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
/* graph */
|
||||
|
||||
.rickshaw_graph {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.rickshaw_graph svg {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* ticks */
|
||||
|
||||
.rickshaw_graph .x_tick {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 0;
|
||||
border-left: 1px dotted rgba(0, 0, 0, 0.2);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.rickshaw_graph .x_tick .title {
|
||||
position: absolute;
|
||||
font-size: 12px;
|
||||
font-family: Arial, sans-serif;
|
||||
opacity: 0.5;
|
||||
white-space: nowrap;
|
||||
margin-left: 3px;
|
||||
bottom: -20px;
|
||||
height: auto;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
/* annotations */
|
||||
|
||||
.rickshaw_annotation_timeline {
|
||||
height: 1px;
|
||||
border-top: 1px solid #e0e0e0;
|
||||
margin-top: 10px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.rickshaw_annotation_timeline .annotation {
|
||||
position: absolute;
|
||||
height: 6px;
|
||||
width: 6px;
|
||||
margin-left: -2px;
|
||||
top: -3px;
|
||||
border-radius: 5px;
|
||||
background-color: rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
.rickshaw_graph .annotation_line {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: -6px;
|
||||
width: 0;
|
||||
border-left: 2px solid rgba(0, 0, 0, 0.3);
|
||||
display: none;
|
||||
}
|
||||
|
||||
.rickshaw_graph .annotation_line.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.rickshaw_graph .annotation_range {
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: -6px;
|
||||
}
|
||||
|
||||
.rickshaw_graph .annotation_range.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.rickshaw_graph .annotation_range.active.offscreen {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.rickshaw_annotation_timeline .annotation .content {
|
||||
background: white;
|
||||
color: black;
|
||||
opacity: 0.9;
|
||||
box-shadow: 0 0 2px rgba(0, 0, 0, 0.8);
|
||||
border-radius: 3px;
|
||||
position: relative;
|
||||
z-index: 20;
|
||||
font-size: 12px;
|
||||
padding: 6px 8px 8px;
|
||||
top: 18px;
|
||||
left: -11px;
|
||||
width: 160px;
|
||||
display: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.rickshaw_annotation_timeline .annotation .content:before {
|
||||
content: "\25b2";
|
||||
position: absolute;
|
||||
top: -11px;
|
||||
color: white;
|
||||
text-shadow: 0 -1px 1px rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
.rickshaw_annotation_timeline .annotation.active,
|
||||
.rickshaw_annotation_timeline .annotation:hover {
|
||||
background-color: rgba(0, 0, 0, 0.8);
|
||||
cursor: none;
|
||||
}
|
||||
|
||||
.rickshaw_annotation_timeline .annotation .content:hover {
|
||||
z-index: 50;
|
||||
}
|
||||
|
||||
.rickshaw_annotation_timeline .annotation.active .content {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.rickshaw_annotation_timeline .annotation:hover .content {
|
||||
display: block;
|
||||
z-index: 50;
|
||||
}
|
||||
|
||||
.rickshaw_graph .y_axis,
|
||||
.rickshaw_graph .x_axis_d3 {
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.rickshaw_graph .y_ticks .tick line,
|
||||
.rickshaw_graph .x_ticks_d3 .tick {
|
||||
stroke: rgba(0, 0, 0, 0.16);
|
||||
stroke-width: 2px;
|
||||
shape-rendering: crisp-edges;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.rickshaw_graph .y_grid .tick,
|
||||
.rickshaw_graph .x_grid_d3 .tick {
|
||||
z-index: -1;
|
||||
stroke: rgba(0, 0, 0, 0.20);
|
||||
stroke-width: 1px;
|
||||
stroke-dasharray: 1 1;
|
||||
}
|
||||
|
||||
.rickshaw_graph .y_grid .tick[data-y-value="0"] {
|
||||
stroke-dasharray: 1 0;
|
||||
}
|
||||
|
||||
.rickshaw_graph .y_grid path,
|
||||
.rickshaw_graph .x_grid_d3 path {
|
||||
fill: none;
|
||||
stroke: none;
|
||||
}
|
||||
|
||||
.rickshaw_graph .y_ticks path,
|
||||
.rickshaw_graph .x_ticks_d3 path {
|
||||
fill: none;
|
||||
stroke: #808080;
|
||||
}
|
||||
|
||||
.rickshaw_graph .y_ticks text,
|
||||
.rickshaw_graph .x_ticks_d3 text {
|
||||
opacity: 0.5;
|
||||
font-size: 12px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.rickshaw_graph .x_tick.glow .title,
|
||||
.rickshaw_graph .y_ticks.glow text {
|
||||
fill: black;
|
||||
color: black;
|
||||
text-shadow: -1px 1px 0 rgba(255, 255, 255, 0.1),
|
||||
1px -1px 0 rgba(255, 255, 255, 0.1),
|
||||
1px 1px 0 rgba(255, 255, 255, 0.1),
|
||||
0 1px 0 rgba(255, 255, 255, 0.1),
|
||||
0 -1px 0 rgba(255, 255, 255, 0.1),
|
||||
1px 0 0 rgba(255, 255, 255, 0.1),
|
||||
-1px 0 0 rgba(255, 255, 255, 0.1),
|
||||
-1px -1px 0 rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.rickshaw_graph .x_tick.inverse .title,
|
||||
.rickshaw_graph .y_ticks.inverse text {
|
||||
fill: white;
|
||||
color: white;
|
||||
text-shadow: -1px 1px 0 rgba(0, 0, 0, 0.8),
|
||||
1px -1px 0 rgba(0, 0, 0, 0.8),
|
||||
1px 1px 0 rgba(0, 0, 0, 0.8),
|
||||
0 1px 0 rgba(0, 0, 0, 0.8),
|
||||
0 -1px 0 rgba(0, 0, 0, 0.8),
|
||||
1px 0 0 rgba(0, 0, 0, 0.8),
|
||||
-1px 0 0 rgba(0, 0, 0, 0.8),
|
||||
-1px -1px 0 rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
.custom_rickshaw_graph {
|
||||
position: relative;
|
||||
left: 40px;
|
||||
}
|
||||
|
||||
.custom_y_axis {
|
||||
position: absolute;
|
||||
width: 40px;
|
||||
margin-top: -20px;
|
||||
}
|
||||
|
||||
.custom_slider {
|
||||
left: 40px;
|
||||
}
|
||||
|
||||
.custom_x_axis {
|
||||
position: relative;
|
||||
left: 40px;
|
||||
height: 30px;
|
||||
width: 97%;
|
||||
top: 20px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.chartWrapper {
|
||||
padding-top: 50px;
|
||||
}
|
||||
|
||||
/*detail*/
|
||||
|
||||
.rickshaw_graph .detail {
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
z-index: 2;
|
||||
background: rgba(0, 0, 0, 0.1);
|
||||
bottom: 0;
|
||||
width: 1px;
|
||||
transition: opacity 0.25s linear;
|
||||
-moz-transition: opacity 0.25s linear;
|
||||
-o-transition: opacity 0.25s linear;
|
||||
-webkit-transition: opacity 0.25s linear;
|
||||
}
|
||||
|
||||
.rickshaw_graph .detail.inactive {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.rickshaw_graph .detail .item.active {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.rickshaw_graph .detail .x_label {
|
||||
font-family: Arial, sans-serif;
|
||||
border-radius: 3px;
|
||||
padding: 6px;
|
||||
opacity: 0.5;
|
||||
border: 1px solid #e0e0e0;
|
||||
font-size: 12px;
|
||||
position: absolute;
|
||||
background: white;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.rickshaw_graph .detail .x_label.left {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.rickshaw_graph .detail .x_label.right {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.rickshaw_graph .detail .item {
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
border-radius: 3px;
|
||||
padding: 0.25em;
|
||||
font-size: 12px;
|
||||
font-family: Arial, sans-serif;
|
||||
opacity: 0;
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
color: white;
|
||||
border: 1px solid rgba(0, 0, 0, 0.4);
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
margin-top: -1em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.rickshaw_graph .detail .item.left {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.rickshaw_graph .detail .item.right {
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.rickshaw_graph .detail .item.active {
|
||||
opacity: 1;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
.rickshaw_graph .detail .item:after {
|
||||
position: absolute;
|
||||
display: block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
|
||||
content: "";
|
||||
|
||||
border: 5px solid transparent;
|
||||
}
|
||||
|
||||
.rickshaw_graph .detail .item.left:after {
|
||||
top: 1em;
|
||||
left: -5px;
|
||||
margin-top: -5px;
|
||||
border-right-color: rgba(0, 0, 0, 0.8);
|
||||
border-left-width: 0;
|
||||
}
|
||||
|
||||
.rickshaw_graph .detail .item.right:after {
|
||||
top: 1em;
|
||||
right: -5px;
|
||||
margin-top: -5px;
|
||||
border-left-color: rgba(0, 0, 0, 0.8);
|
||||
border-right-width: 0;
|
||||
}
|
||||
|
||||
.rickshaw_graph .detail .dot {
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
margin-left: -3px;
|
||||
margin-top: -3.5px;
|
||||
border-radius: 5px;
|
||||
position: absolute;
|
||||
box-shadow: 0 0 2px rgba(0, 0, 0, 0.6);
|
||||
box-sizing: content-box;
|
||||
-moz-box-sizing: content-box;
|
||||
background: white;
|
||||
border-width: 2px;
|
||||
border-style: solid;
|
||||
display: none;
|
||||
background-clip: padding-box;
|
||||
}
|
||||
|
||||
.rickshaw_graph .detail .dot.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/*legend*/
|
||||
.rickshaw_legend {
|
||||
font-family: Arial;
|
||||
font-size: 12px;
|
||||
color: white;
|
||||
background: #404040;
|
||||
display: inline-block;
|
||||
padding: 12px 5px;
|
||||
border-radius: 2px;
|
||||
position: relative;
|
||||
float: right;
|
||||
}
|
||||
|
||||
.rickshaw_legend:hover {
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.rickshaw_legend .swatch {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.rickshaw_legend .line {
|
||||
clear: both;
|
||||
line-height: 140%;
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
.rickshaw_legend .line .swatch {
|
||||
display: inline-block;
|
||||
margin-right: 3px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.rickshaw_legend .label {
|
||||
margin: 0;
|
||||
white-space: nowrap;
|
||||
display: inline;
|
||||
font-size: inherit;
|
||||
background-color: transparent;
|
||||
color: inherit;
|
||||
font-weight: normal;
|
||||
line-height: normal;
|
||||
padding: 0;
|
||||
text-shadow: none;
|
||||
}
|
||||
|
||||
.rickshaw_legend .action:hover {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.rickshaw_legend .action {
|
||||
margin-right: 0.2em;
|
||||
opacity: 0.2;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.rickshaw_legend .line.disabled {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.rickshaw_legend ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 2px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.rickshaw_legend li {
|
||||
padding: 0 0 0 2px;
|
||||
min-width: 80px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.rickshaw_legend li:hover {
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.rickshaw_legend li:active {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.legend {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
left: 8px;
|
||||
}
|
||||
|
||||
.legend_container {
|
||||
float: right;
|
||||
padding-right: 10px;
|
||||
width: 0;
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.spaced {
|
||||
margin-top: 20px !important;
|
||||
}
|
||||
|
||||
@ -0,0 +1,220 @@
|
||||
/*
|
||||
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
.box {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.box-inner {
|
||||
border: 1px solid #DEDEDE;
|
||||
border-radius: 3px;
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
box-shadow: 0 0 10px rgba(189, 189, 189, 0.4);
|
||||
-webkit-box-shadow: 0 0 10px rgba(189, 189, 189, 0.4);
|
||||
-moz-box-shadow: 0 0 10px rgba(189, 189, 189, 0.4);
|
||||
}
|
||||
|
||||
.box-header {
|
||||
border: none;
|
||||
padding-top: 5px;
|
||||
border-bottom: 1px solid #DEDEDE;
|
||||
border-radius: 3px 3px 0 0;
|
||||
-webkit-border-radius: 3px 3px 0 0;
|
||||
-moz-border-radius: 3px 3px 0 0;
|
||||
height: 35px;
|
||||
min-height: 35px !important;
|
||||
margin-bottom: 0;
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
background: -moz-linear-gradient(top, rgba(255, 255, 255, 0) 0%, rgba(0, 0, 0, 0.1) 100%);
|
||||
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, rgba(255, 255, 255, 0)), color-stop(100%, rgba(0, 0, 0, 0.1)));
|
||||
background: -webkit-linear-gradient(top, rgba(255, 255, 255, 0) 0%, rgba(0, 0, 0, 0.1) 100%);
|
||||
background: -o-linear-gradient(top, rgba(255, 255, 255, 0) 0%, rgba(0, 0, 0, 0.1) 100%);
|
||||
background: -ms-linear-gradient(top, rgba(255, 255, 255, 0) 0%, rgba(0, 0, 0, 0.1) 100%);
|
||||
background: linear-gradient(to bottom, rgba(255, 255, 255, 0) 0%, rgba(0, 0, 0, 0.1) 100%);
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00ffffff', endColorstr='#1a000000', GradientType=0);
|
||||
|
||||
}
|
||||
|
||||
.box-header h2 {
|
||||
font-size: 15px;
|
||||
width: auto;
|
||||
clear: none;
|
||||
float: left;
|
||||
line-height: 25px;
|
||||
white-space: nowrap;
|
||||
font-weight: bold;
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.box-header h3 {
|
||||
font-size: 13px;
|
||||
width: auto;
|
||||
clear: none;
|
||||
float: left;
|
||||
line-height: 25px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.box-header h2 > i {
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.box-icon {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.box-icon a {
|
||||
clear: none;
|
||||
float: left;
|
||||
margin: 0 2px;
|
||||
height: 20px;
|
||||
width: 5px;
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.box-icon a i {
|
||||
margin-left: -6px;
|
||||
top: -1px;
|
||||
}
|
||||
|
||||
.box-content {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.btn-round {
|
||||
border-radius: 40px;
|
||||
-webkit-border-radius: 40px;
|
||||
-moz-border-radius: 40px;
|
||||
font-size: 12px;
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
font-family: 'Shojumaru', cursive, Arial, serif;
|
||||
letter-spacing: 2px;
|
||||
text-shadow: 1px 1px 5px rgba(0, 0, 0, 0.5);
|
||||
width: 183px;
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
.navbar-brand img {
|
||||
float: left;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.navbar-brand span {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.navbar-search {
|
||||
margin-left: 10px;
|
||||
margin-top: 7px;
|
||||
}
|
||||
|
||||
.navbar-inner {
|
||||
padding-top: 5px;
|
||||
padding-bottom: 5px;
|
||||
line-height: 30px;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
.navbar-inner .btn-group {
|
||||
margin: 7px 5px 0 5px;
|
||||
}
|
||||
|
||||
.bs-icons li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.box-content .nav-tabs {
|
||||
margin-right: -10px;
|
||||
margin-left: -10px;
|
||||
}
|
||||
|
||||
.box-content.buttons {
|
||||
min-height: 297px;
|
||||
}
|
||||
|
||||
.sidebar-nav .nav-header {
|
||||
display: block;
|
||||
padding: 3px 15px;
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
line-height: 18px;
|
||||
color: #999999;
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
border-radius: 0;
|
||||
}
|
||||
.circle {
|
||||
background: none repeat scroll 0 0 #191919;
|
||||
border-radius: 50px;
|
||||
height: 50px;
|
||||
padding: 10px;
|
||||
width: 50px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.padding-top-double {
|
||||
padding-top: 20px;
|
||||
}
|
||||
.padding-double {
|
||||
padding: 20px;
|
||||
}
|
||||
.grey {
|
||||
color: #333;
|
||||
}
|
||||
hr {
|
||||
display: block;
|
||||
height: 1px;
|
||||
border: 0;
|
||||
border-top: 1px solid #7f7f7f;
|
||||
margin: 1em 0;
|
||||
padding: 0;
|
||||
opacity: 0.2;
|
||||
}
|
||||
.light-grey {
|
||||
color: #7c7c7c;
|
||||
}
|
||||
.uppercase {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.grey-bg {
|
||||
background-color: #f6f4f4;
|
||||
}
|
||||
|
||||
path {
|
||||
stroke: black;
|
||||
stroke-width: 2;
|
||||
fill: none;
|
||||
}
|
||||
|
||||
.axis path, .axis line {
|
||||
fill: none;
|
||||
stroke: #ccc;
|
||||
stroke-width: 2;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
After Width: | Height: | Size: 26 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 2.1 KiB |
@ -0,0 +1,186 @@
|
||||
/*
|
||||
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
var camera, scene, renderer;
|
||||
var cameraControls;
|
||||
var clock = new THREE.Clock();
|
||||
|
||||
var ground = true;
|
||||
var circle1, plate1;
|
||||
|
||||
var droneRender = function () {
|
||||
var api = this;
|
||||
api.fillScene = function () {
|
||||
scene = new THREE.Scene();
|
||||
scene.fog = new THREE.Fog(0x808080, 2000, 4000);
|
||||
var ambientLight = new THREE.AmbientLight(0x222222);
|
||||
var light = new THREE.DirectionalLight(0xFFFFFF, 1.0);
|
||||
light.position.set(200, 400, 500);
|
||||
var light2 = new THREE.DirectionalLight(0xFFFFFF, 1.0);
|
||||
light2.position.set(-500, 250, -200);
|
||||
scene.add(ambientLight);
|
||||
scene.add(light);
|
||||
scene.add(light2);
|
||||
if (ground) {
|
||||
Coordinates.drawGround({size: 10000});
|
||||
}
|
||||
var robotBaseMaterial = new THREE.MeshPhongMaterial({color: 0x6E23BB, specular: 0x6E23BB, shininess: 20});
|
||||
var robotForearmMaterial = new THREE.MeshPhongMaterial({color: 0xF4C154, specular: 0xF4C154, shininess: 100});
|
||||
circle1 = new THREE.Object3D();
|
||||
var circleLength = 40;
|
||||
api.addCircles(circle1, circleLength, robotBaseMaterial);
|
||||
circle1.position.y = circleLength * 2;
|
||||
scene.add(circle1);
|
||||
plate1 = new THREE.Object3D();
|
||||
var plateLength = 40;
|
||||
api.addPlates(plate1, plateLength, robotForearmMaterial);
|
||||
plate1.position.y = circleLength / 8;
|
||||
circle1.add(plate1);
|
||||
},
|
||||
|
||||
api.addPlates = function (part, plateLength, material) {
|
||||
var cylinder = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(5, 5, 40, 32), material);
|
||||
cylinder.rotation.x = 90 * Math.PI / 180;
|
||||
cylinder.position.x = plateLength + 15;
|
||||
part.add(cylinder);
|
||||
cylinder = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(5, 5, 40, 32), material);
|
||||
cylinder.rotation.x = 90 * Math.PI / 180;
|
||||
cylinder.rotation.z = 90 * Math.PI / 180;
|
||||
cylinder.position.x = plateLength + 15;
|
||||
part.add(cylinder);
|
||||
cylinder = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(5, 5, 40, 32), material);
|
||||
cylinder.rotation.x = 90 * Math.PI / 180;
|
||||
cylinder.position.x = -plateLength - 15;
|
||||
part.add(cylinder);
|
||||
cylinder = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(5, 5, 40, 32), material);
|
||||
cylinder.rotation.x = 90 * Math.PI / 180;
|
||||
cylinder.rotation.z = 90 * Math.PI / 180;
|
||||
cylinder.position.x = -plateLength - 15;
|
||||
part.add(cylinder);
|
||||
cylinder = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(5, 5, 40, 32), material);
|
||||
cylinder.rotation.x = 90 * Math.PI / 180;
|
||||
cylinder.position.z = -plateLength - 15;
|
||||
part.add(cylinder);
|
||||
cylinder = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(5, 5, 40, 32), material);
|
||||
cylinder.rotation.x = 90 * Math.PI / 180;
|
||||
cylinder.rotation.z = 90 * Math.PI / 180;
|
||||
cylinder.position.z = -plateLength - 15;
|
||||
part.add(cylinder);
|
||||
cylinder = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(5, 5, 40, 32), material);
|
||||
cylinder.rotation.x = 90 * Math.PI / 180;
|
||||
cylinder.position.z = plateLength + 15;
|
||||
part.add(cylinder);
|
||||
cylinder = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(5, 5, 40, 32), material);
|
||||
cylinder.rotation.x = 90 * Math.PI / 180;
|
||||
cylinder.rotation.z = 90 * Math.PI / 180;
|
||||
cylinder.position.z = plateLength + 15;
|
||||
part.add(cylinder);
|
||||
},
|
||||
api.addCircles = function (part, circleLength, material) {
|
||||
var circle = new THREE.Mesh(
|
||||
new THREE.TorusGeometry(40, 10, 40, 20, 6.3), material);
|
||||
circle.position.x = circleLength + 10;
|
||||
circle.rotation.x = 90 * Math.PI / 180;
|
||||
part.add(circle);
|
||||
circle = new THREE.Mesh(
|
||||
new THREE.TorusGeometry(40, 10, 40, 20, 6.3), material);
|
||||
circle.position.x = -circleLength - 10;
|
||||
circle.rotation.x = 90 * Math.PI / 180;
|
||||
part.add(circle);
|
||||
circle = new THREE.Mesh(
|
||||
new THREE.TorusGeometry(40, 10, 40, 20, 6.3), material);
|
||||
circle.position.z = -circleLength - 10;
|
||||
circle.rotation.x = 90 * Math.PI / 180;
|
||||
part.add(circle);
|
||||
circle = new THREE.Mesh(
|
||||
new THREE.TorusGeometry(40, 10, 40, 20, 6.3), material);
|
||||
circle.position.z = circleLength + 10;
|
||||
circle.rotation.x = 90 * Math.PI / 180;
|
||||
part.add(circle);
|
||||
},
|
||||
|
||||
api.init = function (holder, object_width, object_height) {
|
||||
var canvasRatio = 1;
|
||||
renderer = new THREE.WebGLRenderer({antialias: true});
|
||||
renderer.gammaInput = true;
|
||||
renderer.gammaOutput = true;
|
||||
renderer.setSize(object_width, object_height);
|
||||
renderer.setClearColorHex(0xAAAAAA, 1.0);
|
||||
$(holder).append(renderer.domElement);
|
||||
camera = new THREE.PerspectiveCamera(30, canvasRatio, 1, 10000);
|
||||
camera.position.set(-510, 240, 100);
|
||||
cameraControls = new THREE.OrbitAndPanControls(camera, renderer.domElement);
|
||||
cameraControls.target.set(0, 100, 0);
|
||||
api.fillScene();
|
||||
|
||||
},
|
||||
api.animate = function () {
|
||||
window.requestAnimationFrame(api.animate);
|
||||
api.render();
|
||||
},
|
||||
api.render = function () {
|
||||
var delta = clock.getDelta();
|
||||
cameraControls.update(delta);
|
||||
circle1.rotation.z = config.effectController.uz;
|
||||
circle1.rotation.y = config.effectController.uy; // yaw
|
||||
circle1.rotation.x = config.effectController.ux; // roll
|
||||
circle1.position.z = config.effectController.fz;
|
||||
circle1.position.x = config.effectController.fx;
|
||||
renderer.render(scene, camera);
|
||||
},
|
||||
api.getHeadingAttitudeAndBank = function (data) {
|
||||
if (data.length < 4) {
|
||||
return {"heading": data[0], "attitude": data[1], "bank": data[2]};
|
||||
} else {
|
||||
var heading = Math.atan2(2 * data[1] * data[3] - 2 * data[0] * data[1], 1 - 2 * data[1] * data[1]
|
||||
- 2 * data[2] * data[2]);
|
||||
var bank = Math.atan2(2 * data[0] * data[3] - 2 * data[1] * data[2], 1 - 2 * data[0] * data[0]
|
||||
- 2 * data[2] * data[2]);
|
||||
var attitude = Math.asin(2 * data[0] * data[1] + 2 * data[2] * data[3]);
|
||||
return {
|
||||
"heading": isNaN(heading) ? 0 : heading,
|
||||
"bank": isNaN(bank) ? 0 : bank,
|
||||
"attitude": isNaN(attitude) ? 0 : attitude
|
||||
};
|
||||
}
|
||||
},
|
||||
api.setHeadingAttitudeAndBank = function (data) {
|
||||
config.effectController.uy = data.heading;
|
||||
config.effectController.uz = data.attitude;
|
||||
config.effectController.ux = data.bank;
|
||||
},
|
||||
api.setHeading = function (holder, heading) {
|
||||
var r = (180 / Math.PI) * parseFloat(heading);
|
||||
$(holder).rotate(r);
|
||||
|
||||
},
|
||||
api.setBank = function (holder, bank) {
|
||||
var r = (180 / Math.PI) * parseFloat(bank);
|
||||
$(holder).rotate(r);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -0,0 +1,206 @@
|
||||
/*
|
||||
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
var camera, scene, renderer;
|
||||
var cameraControls;
|
||||
var clock = new THREE.Clock();
|
||||
|
||||
var ground = true;
|
||||
var circle1, plate1;
|
||||
|
||||
var object_maker = function () {
|
||||
var make_object = this;
|
||||
make_object.fillScene = function () {
|
||||
scene = new THREE.Scene();
|
||||
scene.fog = new THREE.Fog(0x808080, 2000, 4000);
|
||||
|
||||
|
||||
var ambientLight = new THREE.AmbientLight(0x222222);
|
||||
var light = new THREE.DirectionalLight(0xFFFFFF, 1.0);
|
||||
light.position.set(200, 400, 500);
|
||||
|
||||
var light2 = new THREE.DirectionalLight(0xFFFFFF, 1.0);
|
||||
light2.position.set(-500, 250, -200);
|
||||
|
||||
scene.add(ambientLight);
|
||||
scene.add(light);
|
||||
scene.add(light2);
|
||||
|
||||
if (ground) {
|
||||
Coordinates.drawGround({size: 10000});
|
||||
}
|
||||
|
||||
|
||||
var robotBaseMaterial = new THREE.MeshPhongMaterial({color: 0x6E23BB, specular: 0x6E23BB, shininess: 20});
|
||||
var robotForearmMaterial = new THREE.MeshPhongMaterial({color: 0xF4C154, specular: 0xF4C154, shininess: 100});
|
||||
|
||||
circle1 = new THREE.Object3D();
|
||||
var circleLength = 40;
|
||||
make_object.addCircles(circle1, circleLength, robotBaseMaterial);
|
||||
circle1.position.y = circleLength * 2;
|
||||
scene.add(circle1);
|
||||
|
||||
plate1 = new THREE.Object3D();
|
||||
var plateLength = 40;
|
||||
make_object.addPlates(plate1, plateLength, robotForearmMaterial);
|
||||
plate1.position.y = circleLength / 8;
|
||||
circle1.add(plate1);
|
||||
|
||||
},
|
||||
|
||||
make_object.addPlates = function (part, plateLength, material) {
|
||||
|
||||
var cylinder = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(5, 5, 40, 32), material);
|
||||
cylinder.rotation.x = 90 * Math.PI / 180;
|
||||
cylinder.position.x = plateLength + 15;
|
||||
part.add(cylinder);
|
||||
cylinder = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(5, 5, 40, 32), material);
|
||||
cylinder.rotation.x = 90 * Math.PI / 180;
|
||||
cylinder.rotation.z = 90 * Math.PI / 180;
|
||||
cylinder.position.x = plateLength + 15;
|
||||
part.add(cylinder);
|
||||
|
||||
cylinder = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(5, 5, 40, 32), material);
|
||||
cylinder.rotation.x = 90 * Math.PI / 180;
|
||||
cylinder.position.x = -plateLength - 15;
|
||||
part.add(cylinder);
|
||||
cylinder = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(5, 5, 40, 32), material);
|
||||
cylinder.rotation.x = 90 * Math.PI / 180;
|
||||
cylinder.rotation.z = 90 * Math.PI / 180;
|
||||
cylinder.position.x = -plateLength - 15;
|
||||
part.add(cylinder);
|
||||
|
||||
cylinder = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(5, 5, 40, 32), material);
|
||||
cylinder.rotation.x = 90 * Math.PI / 180;
|
||||
cylinder.position.z = -plateLength - 15;
|
||||
part.add(cylinder);
|
||||
cylinder = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(5, 5, 40, 32), material);
|
||||
cylinder.rotation.x = 90 * Math.PI / 180;
|
||||
cylinder.rotation.z = 90 * Math.PI / 180;
|
||||
cylinder.position.z = -plateLength - 15;
|
||||
part.add(cylinder);
|
||||
//
|
||||
cylinder = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(5, 5, 40, 32), material);
|
||||
cylinder.rotation.x = 90 * Math.PI / 180;
|
||||
cylinder.position.z = plateLength + 15;
|
||||
part.add(cylinder);
|
||||
cylinder = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(5, 5, 40, 32), material);
|
||||
cylinder.rotation.x = 90 * Math.PI / 180;
|
||||
cylinder.rotation.z = 90 * Math.PI / 180;
|
||||
cylinder.position.z = plateLength + 15;
|
||||
part.add(cylinder);
|
||||
|
||||
},
|
||||
|
||||
make_object.addCircles = function (part, circleLength, material) {
|
||||
var circle = new THREE.Mesh(
|
||||
new THREE.TorusGeometry(40, 10, 40, 20, 6.3), material);
|
||||
circle.position.x = circleLength + 10;
|
||||
circle.rotation.x = 90 * Math.PI / 180;
|
||||
part.add(circle);
|
||||
circle = new THREE.Mesh(
|
||||
new THREE.TorusGeometry(40, 10, 40, 20, 6.3), material);
|
||||
circle.position.x = -circleLength - 10;
|
||||
circle.rotation.x = 90 * Math.PI / 180;
|
||||
part.add(circle);
|
||||
circle = new THREE.Mesh(
|
||||
new THREE.TorusGeometry(40, 10, 40, 20, 6.3), material);
|
||||
circle.position.z = -circleLength - 10;
|
||||
circle.rotation.x = 90 * Math.PI / 180;
|
||||
part.add(circle);
|
||||
circle = new THREE.Mesh(
|
||||
new THREE.TorusGeometry(40, 10, 40, 20, 6.3), material);
|
||||
circle.position.z = circleLength + 10;
|
||||
circle.rotation.x = 90 * Math.PI / 180;
|
||||
part.add(circle);
|
||||
},
|
||||
|
||||
make_object.init = function (holder, object_width, object_height) {
|
||||
var canvasRatio = 1;
|
||||
renderer = new THREE.WebGLRenderer({antialias: true});
|
||||
renderer.gammaInput = true;
|
||||
renderer.gammaOutput = true;
|
||||
renderer.setSize(object_width, object_height);
|
||||
renderer.setClearColorHex(0xAAAAAA, 1.0);
|
||||
|
||||
$(holder).append(renderer.domElement);
|
||||
camera = new THREE.PerspectiveCamera(30, canvasRatio, 1, 10000);
|
||||
camera.position.set(-510, 240, 100);
|
||||
cameraControls = new THREE.OrbitAndPanControls(camera, renderer.domElement);
|
||||
cameraControls.target.set(0, 100, 0);
|
||||
make_object.fillScene();
|
||||
|
||||
},
|
||||
make_object.animate = function () {
|
||||
window.requestAnimationFrame(make_object.animate);
|
||||
make_object.render();
|
||||
},
|
||||
make_object.render = function () {
|
||||
var delta = clock.getDelta();
|
||||
cameraControls.update(delta);
|
||||
circle1.rotation.z = config_api.effectController.uz;
|
||||
circle1.rotation.y = config_api.effectController.uy; // yaw
|
||||
circle1.rotation.x = config_api.effectController.ux; // roll
|
||||
circle1.position.z = config_api.effectController.fz;
|
||||
circle1.position.x = config_api.effectController.fx;
|
||||
renderer.render(scene, camera);
|
||||
},
|
||||
make_object.get_heading_attitude_bank = function (data) {
|
||||
if (data.length < 4) {
|
||||
return {"heading": data[0], "attitude": data[1], "bank": data[2]};
|
||||
} else {
|
||||
|
||||
var heading = Math.atan2(2 * data[1] * data[3] - 2 * data[0] * data[1], 1 - 2 * data[1] * data[1]
|
||||
- 2 * data[2] * data[2]);
|
||||
var bank = Math.atan2(2 * data[0] * data[3] - 2 * data[1] * data[2], 1 - 2 * data[0] * data[0]
|
||||
- 2 * data[2] * data[2]);
|
||||
var attitude = Math.asin(2 * data[0] * data[1] + 2 * data[2] * data[3]);
|
||||
|
||||
return {
|
||||
"heading": isNaN(heading) ? 0 : heading,
|
||||
"bank": isNaN(bank) ? 0 : bank,
|
||||
"attitude": isNaN(attitude) ? 0 : attitude
|
||||
};
|
||||
}
|
||||
},
|
||||
make_object.set_heading_attitude_bank = function (data) {
|
||||
config_api.effectController.uy = data.heading;
|
||||
config_api.effectController.uz = data.attitude;
|
||||
config_api.effectController.ux = data.bank;
|
||||
},
|
||||
make_object.set_heading = function (holder, heading) {
|
||||
var r = (180 / Math.PI) * parseFloat(heading);
|
||||
$(holder).rotate(r);
|
||||
|
||||
},
|
||||
make_object.set_bank = function (holder, bank) {
|
||||
var r = (180 / Math.PI) * parseFloat(bank);
|
||||
$(holder).rotate(r);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -0,0 +1,166 @@
|
||||
/*
|
||||
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
/*global THREE, scene*/
|
||||
var Coordinates = {
|
||||
drawGrid:function(params) {
|
||||
params = params || {};
|
||||
var size = params.size !== undefined ? params.size:100;
|
||||
var scale = params.scale !== undefined ? params.scale:0.1;
|
||||
var orientation = params.orientation !== undefined ? params.orientation:"x";
|
||||
var grid = new THREE.Mesh(
|
||||
new THREE.PlaneGeometry(size, size, size * scale, size * scale),
|
||||
new THREE.MeshBasicMaterial({ color: 0x555555, wireframe: true })
|
||||
);
|
||||
// Yes, these are poorly labeled! It would be a mess to fix.
|
||||
// What's really going on here:
|
||||
// "x" means "rotate 90 degrees around x", etc.
|
||||
// So "x" really means "show a grid with a normal of Y"
|
||||
// "y" means "show a grid with a normal of X"
|
||||
// "z" means (logically enough) "show a grid with a normal of Z"
|
||||
if (orientation === "x") {
|
||||
grid.rotation.x = - Math.PI / 2;
|
||||
} else if (orientation === "y") {
|
||||
grid.rotation.y = - Math.PI / 2;
|
||||
} else if (orientation === "z") {
|
||||
grid.rotation.z = - Math.PI / 2;
|
||||
}
|
||||
|
||||
scene.add(grid);
|
||||
},
|
||||
drawGround:function(params) {
|
||||
params = params || {};
|
||||
var size = params.size !== undefined ? params.size:100;
|
||||
var color = params.color !== undefined ? params.color:0xFFFFFF;
|
||||
var ground = new THREE.Mesh(
|
||||
new THREE.PlaneGeometry(size, size),
|
||||
// When we use a ground plane we use directional lights, so illuminating
|
||||
// just the corners is sufficient.
|
||||
// Use MeshPhongMaterial if you want to capture per-pixel lighting:
|
||||
// new THREE.MeshPhongMaterial({ color: color, specular: 0x000000,
|
||||
new THREE.MeshLambertMaterial({ color: color,
|
||||
// polygonOffset moves the plane back from the eye a bit, so that the lines on top of
|
||||
// the grid do not have z-fighting with the grid:
|
||||
// Factor == 1 moves it back relative to the slope (more on-edge means move back farther)
|
||||
// Units == 4 is a fixed amount to move back, and 4 is usually a good value
|
||||
polygonOffset: true, polygonOffsetFactor: 1.0, polygonOffsetUnits: 4.0
|
||||
}));
|
||||
ground.rotation.x = - Math.PI / 2;
|
||||
scene.add(ground);
|
||||
},
|
||||
drawAxes:function(params) {
|
||||
// x = red, y = green, z = blue (RGB = xyz)
|
||||
params = params || {};
|
||||
var axisRadius = params.axisRadius !== undefined ? params.axisRadius:0.04;
|
||||
var axisLength = params.axisLength !== undefined ? params.axisLength:11;
|
||||
var axisTess = params.axisTess !== undefined ? params.axisTess:48;
|
||||
var axisOrientation = params.axisOrientation !== undefined ? params.axisOrientation:"x";
|
||||
|
||||
var axisMaterial = new THREE.MeshLambertMaterial({ color: 0x000000, side: THREE.DoubleSide });
|
||||
var axis = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(axisRadius, axisRadius, axisLength, axisTess, 1, true),
|
||||
axisMaterial
|
||||
);
|
||||
if (axisOrientation === "x") {
|
||||
axis.rotation.z = - Math.PI / 2;
|
||||
axis.position.x = axisLength/2-1;
|
||||
} else if (axisOrientation === "y") {
|
||||
axis.position.y = axisLength/2-1;
|
||||
}
|
||||
|
||||
scene.add( axis );
|
||||
|
||||
var arrow = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(0, 4*axisRadius, 8*axisRadius, axisTess, 1, true),
|
||||
axisMaterial
|
||||
);
|
||||
if (axisOrientation === "x") {
|
||||
arrow.rotation.z = - Math.PI / 2;
|
||||
arrow.position.x = axisLength - 1 + axisRadius*4/2;
|
||||
} else if (axisOrientation === "y") {
|
||||
arrow.position.y = axisLength - 1 + axisRadius*4/2;
|
||||
}
|
||||
|
||||
scene.add( arrow );
|
||||
|
||||
},
|
||||
drawAllAxes:function(params) {
|
||||
params = params || {};
|
||||
var axisRadius = params.axisRadius !== undefined ? params.axisRadius:0.04;
|
||||
var axisLength = params.axisLength !== undefined ? params.axisLength:11;
|
||||
var axisTess = params.axisTess !== undefined ? params.axisTess:48;
|
||||
|
||||
var axisXMaterial = new THREE.MeshLambertMaterial({ color: 0xFF0000 });
|
||||
var axisYMaterial = new THREE.MeshLambertMaterial({ color: 0x00FF00 });
|
||||
var axisZMaterial = new THREE.MeshLambertMaterial({ color: 0x0000FF });
|
||||
axisXMaterial.side = THREE.DoubleSide;
|
||||
axisYMaterial.side = THREE.DoubleSide;
|
||||
axisZMaterial.side = THREE.DoubleSide;
|
||||
var axisX = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(axisRadius, axisRadius, axisLength, axisTess, 1, true),
|
||||
axisXMaterial
|
||||
);
|
||||
var axisY = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(axisRadius, axisRadius, axisLength, axisTess, 1, true),
|
||||
axisYMaterial
|
||||
);
|
||||
var axisZ = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(axisRadius, axisRadius, axisLength, axisTess, 1, true),
|
||||
axisZMaterial
|
||||
);
|
||||
axisX.rotation.z = - Math.PI / 2;
|
||||
axisX.position.x = axisLength/2-1;
|
||||
|
||||
axisY.position.y = axisLength/2-1;
|
||||
|
||||
axisZ.rotation.y = - Math.PI / 2;
|
||||
axisZ.rotation.z = - Math.PI / 2;
|
||||
axisZ.position.z = axisLength/2-1;
|
||||
|
||||
scene.add( axisX );
|
||||
scene.add( axisY );
|
||||
scene.add( axisZ );
|
||||
|
||||
var arrowX = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(0, 4*axisRadius, 4*axisRadius, axisTess, 1, true),
|
||||
axisXMaterial
|
||||
);
|
||||
var arrowY = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(0, 4*axisRadius, 4*axisRadius, axisTess, 1, true),
|
||||
axisYMaterial
|
||||
);
|
||||
var arrowZ = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(0, 4*axisRadius, 4*axisRadius, axisTess, 1, true),
|
||||
axisZMaterial
|
||||
);
|
||||
arrowX.rotation.z = - Math.PI / 2;
|
||||
arrowX.position.x = axisLength - 1 + axisRadius*4/2;
|
||||
|
||||
arrowY.position.y = axisLength - 1 + axisRadius*4/2;
|
||||
|
||||
arrowZ.rotation.z = - Math.PI / 2;
|
||||
arrowZ.rotation.y = - Math.PI / 2;
|
||||
arrowZ.position.z = axisLength - 1 + axisRadius*4/2;
|
||||
|
||||
scene.add( arrowX );
|
||||
scene.add( arrowY );
|
||||
scene.add( arrowZ );
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
@ -0,0 +1,549 @@
|
||||
/*
|
||||
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @author qiao / https://github.com/qiao
|
||||
* @author mrdoob / http://mrdoob.com
|
||||
* @author alteredq / http://alteredqualia.com/
|
||||
* @author WestLangley / http://github.com/WestLangley
|
||||
* @author erich666 / http://erichaines.com
|
||||
*/
|
||||
/*global THREE, console */
|
||||
|
||||
THREE.OrbitAndPanControls = function ( object, domElement ) {
|
||||
|
||||
THREE.EventDispatcher.call( this );
|
||||
|
||||
this.enabled = true;
|
||||
|
||||
this.object = object;
|
||||
this.domElement = ( domElement !== undefined ) ? domElement : document;
|
||||
|
||||
// API
|
||||
|
||||
this.enabled = true;
|
||||
|
||||
this.target = new THREE.Vector3();
|
||||
// center is old, deprecated; use "target" instead
|
||||
this.center = this.target;
|
||||
|
||||
// This option actually enables dollying in and out
|
||||
this.noZoom = false;
|
||||
this.zoomSpeed = 1.0;
|
||||
|
||||
this.noRotate = false;
|
||||
this.rotateSpeed = 1.0;
|
||||
|
||||
this.noPan = false;
|
||||
|
||||
this.autoRotate = false;
|
||||
this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
|
||||
|
||||
this.minPolarAngle = 0; // radians
|
||||
this.maxPolarAngle = Math.PI; // radians
|
||||
|
||||
this.minDistance = 0;
|
||||
this.maxDistance = Infinity;
|
||||
|
||||
this.noKeys = false;
|
||||
this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };
|
||||
|
||||
// internals
|
||||
|
||||
var scope = this;
|
||||
|
||||
var EPS = 0.000001;
|
||||
|
||||
var rotateStart = new THREE.Vector2();
|
||||
var rotateEnd = new THREE.Vector2();
|
||||
var rotateDelta = new THREE.Vector2();
|
||||
|
||||
var panStart = new THREE.Vector2();
|
||||
var panEnd = new THREE.Vector2();
|
||||
var panDelta = new THREE.Vector2();
|
||||
|
||||
var dollyStart = new THREE.Vector2();
|
||||
var dollyEnd = new THREE.Vector2();
|
||||
var dollyDelta = new THREE.Vector2();
|
||||
|
||||
var phiDelta = 0;
|
||||
var thetaDelta = 0;
|
||||
var scale = 1;
|
||||
var pan = new THREE.Vector3();
|
||||
|
||||
var lastPosition = new THREE.Vector3();
|
||||
|
||||
var STATE = { NONE : -1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 };
|
||||
var state = STATE.NONE;
|
||||
|
||||
// events
|
||||
|
||||
var changeEvent = { type: 'change' };
|
||||
|
||||
|
||||
this.rotateLeft = function ( angle ) {
|
||||
|
||||
if ( angle === undefined ) {
|
||||
|
||||
angle = getAutoRotationAngle();
|
||||
|
||||
}
|
||||
|
||||
thetaDelta -= angle;
|
||||
|
||||
};
|
||||
|
||||
this.rotateUp = function ( angle ) {
|
||||
|
||||
if ( angle === undefined ) {
|
||||
|
||||
angle = getAutoRotationAngle();
|
||||
|
||||
}
|
||||
|
||||
phiDelta -= angle;
|
||||
|
||||
};
|
||||
|
||||
// pass in distance in world space to move left
|
||||
this.panLeft = function ( distance ) {
|
||||
|
||||
var panOffset = new THREE.Vector3();
|
||||
var te = this.object.matrix.elements;
|
||||
// get X column of matrix
|
||||
panOffset.set( te[0], te[1], te[2] );
|
||||
panOffset.multiplyScalar(-distance);
|
||||
|
||||
pan.add( panOffset );
|
||||
|
||||
};
|
||||
|
||||
// pass in distance in world space to move up
|
||||
this.panUp = function ( distance ) {
|
||||
|
||||
var panOffset = new THREE.Vector3();
|
||||
var te = this.object.matrix.elements;
|
||||
// get Y column of matrix
|
||||
panOffset.set( te[4], te[5], te[6] );
|
||||
panOffset.multiplyScalar(distance);
|
||||
|
||||
pan.add( panOffset );
|
||||
};
|
||||
|
||||
// main entry point; pass in Vector2 of change desired in pixel space,
|
||||
// right and down are positive
|
||||
this.pan = function ( delta ) {
|
||||
|
||||
if ( scope.object.fov !== undefined )
|
||||
{
|
||||
// perspective
|
||||
var position = scope.object.position;
|
||||
var offset = position.clone().sub( scope.target );
|
||||
var targetDistance = offset.length();
|
||||
|
||||
// half of the fov is center to top of screen
|
||||
targetDistance *= Math.tan( (scope.object.fov/2) * Math.PI / 180.0 );
|
||||
// we actually don't use screenWidth, since perspective camera is fixed to screen height
|
||||
scope.panLeft( 2 * delta.x * targetDistance / scope.domElement.height );
|
||||
scope.panUp( 2 * delta.y * targetDistance / scope.domElement.height );
|
||||
}
|
||||
else if ( scope.object.top !== undefined )
|
||||
{
|
||||
// orthographic
|
||||
scope.panLeft( delta.x * (scope.object.right - scope.object.left) / scope.domElement.width );
|
||||
scope.panUp( delta.y * (scope.object.top - scope.object.bottom) / scope.domElement.height );
|
||||
}
|
||||
else
|
||||
{
|
||||
// camera neither orthographic or perspective - warn user
|
||||
console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' );
|
||||
}
|
||||
};
|
||||
|
||||
this.dollyIn = function ( dollyScale ) {
|
||||
|
||||
if ( dollyScale === undefined ) {
|
||||
|
||||
dollyScale = getZoomScale();
|
||||
|
||||
}
|
||||
|
||||
scale /= dollyScale;
|
||||
|
||||
};
|
||||
|
||||
this.dollyOut = function ( dollyScale ) {
|
||||
|
||||
if ( dollyScale === undefined ) {
|
||||
|
||||
dollyScale = getZoomScale();
|
||||
|
||||
}
|
||||
|
||||
scale *= dollyScale;
|
||||
|
||||
};
|
||||
|
||||
this.update = function () {
|
||||
|
||||
var position = this.object.position;
|
||||
var offset = position.clone().sub( this.target );
|
||||
|
||||
// angle from z-axis around y-axis
|
||||
|
||||
var theta = Math.atan2( offset.x, offset.z );
|
||||
|
||||
// angle from y-axis
|
||||
|
||||
var phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y );
|
||||
|
||||
if ( this.autoRotate ) {
|
||||
|
||||
this.rotateLeft( getAutoRotationAngle() );
|
||||
|
||||
}
|
||||
|
||||
theta += thetaDelta;
|
||||
phi += phiDelta;
|
||||
|
||||
// restrict phi to be between desired limits
|
||||
phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) );
|
||||
|
||||
// restrict phi to be betwee EPS and PI-EPS
|
||||
phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) );
|
||||
|
||||
var radius = offset.length() * scale;
|
||||
|
||||
// restrict radius to be between desired limits
|
||||
radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) );
|
||||
|
||||
// move target to panned location
|
||||
this.target.add( pan );
|
||||
|
||||
offset.x = radius * Math.sin( phi ) * Math.sin( theta );
|
||||
offset.y = radius * Math.cos( phi );
|
||||
offset.z = radius * Math.sin( phi ) * Math.cos( theta );
|
||||
|
||||
position.copy( this.target ).add( offset );
|
||||
|
||||
this.object.lookAt( this.target );
|
||||
|
||||
thetaDelta = 0;
|
||||
phiDelta = 0;
|
||||
scale = 1;
|
||||
pan.set(0,0,0);
|
||||
|
||||
if ( lastPosition.distanceTo( this.object.position ) > 0 ) {
|
||||
|
||||
this.dispatchEvent( changeEvent );
|
||||
|
||||
lastPosition.copy( this.object.position );
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
function getAutoRotationAngle() {
|
||||
|
||||
return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
|
||||
|
||||
}
|
||||
|
||||
function getZoomScale() {
|
||||
|
||||
return Math.pow( 0.95, scope.zoomSpeed );
|
||||
|
||||
}
|
||||
|
||||
function onMouseDown( event ) {
|
||||
|
||||
if ( scope.enabled === false ) { return; }
|
||||
event.preventDefault();
|
||||
|
||||
if ( event.button === 0 ) {
|
||||
if ( scope.noRotate === true ) { return; }
|
||||
|
||||
state = STATE.ROTATE;
|
||||
|
||||
rotateStart.set( event.clientX, event.clientY );
|
||||
|
||||
} else if ( event.button === 1 ) {
|
||||
if ( scope.noZoom === true ) { return; }
|
||||
|
||||
state = STATE.DOLLY;
|
||||
|
||||
dollyStart.set( event.clientX, event.clientY );
|
||||
|
||||
} else if ( event.button === 2 ) {
|
||||
if ( scope.noPan === true ) { return; }
|
||||
|
||||
state = STATE.PAN;
|
||||
|
||||
panStart.set( event.clientX, event.clientY );
|
||||
|
||||
}
|
||||
|
||||
document.addEventListener( 'mousemove', onMouseMove, false );
|
||||
document.addEventListener( 'mouseup', onMouseUp, false );
|
||||
|
||||
}
|
||||
|
||||
function onMouseMove( event ) {
|
||||
|
||||
if ( scope.enabled === false ) { return; }
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
if ( state === STATE.ROTATE ) {
|
||||
if ( scope.noRotate === true ) { return; }
|
||||
|
||||
rotateEnd.set( event.clientX, event.clientY );
|
||||
rotateDelta.subVectors( rotateEnd, rotateStart );
|
||||
|
||||
// rotating across whole screen goes 360 degrees around
|
||||
scope.rotateLeft( 2 * Math.PI * rotateDelta.x / scope.domElement.width * scope.rotateSpeed );
|
||||
// rotating up and down along whole screen attempts to go 360, but limited to 180
|
||||
scope.rotateUp( 2 * Math.PI * rotateDelta.y / scope.domElement.height * scope.rotateSpeed );
|
||||
|
||||
rotateStart.copy( rotateEnd );
|
||||
|
||||
} else if ( state === STATE.DOLLY ) {
|
||||
if ( scope.noZoom === true ) { return; }
|
||||
|
||||
dollyEnd.set( event.clientX, event.clientY );
|
||||
dollyDelta.subVectors( dollyEnd, dollyStart );
|
||||
|
||||
if ( dollyDelta.y > 0 ) {
|
||||
|
||||
scope.dollyIn();
|
||||
|
||||
} else {
|
||||
|
||||
scope.dollyOut();
|
||||
|
||||
}
|
||||
|
||||
dollyStart.copy( dollyEnd );
|
||||
|
||||
} else if ( state === STATE.PAN ) {
|
||||
if ( scope.noPan === true ) { return; }
|
||||
|
||||
panEnd.set( event.clientX, event.clientY );
|
||||
panDelta.subVectors( panEnd, panStart );
|
||||
|
||||
scope.pan( panDelta );
|
||||
|
||||
panStart.copy( panEnd );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function onMouseUp( /* event */ ) {
|
||||
|
||||
if ( scope.enabled === false ) { return; }
|
||||
|
||||
document.removeEventListener( 'mousemove', onMouseMove, false );
|
||||
document.removeEventListener( 'mouseup', onMouseUp, false );
|
||||
|
||||
state = STATE.NONE;
|
||||
|
||||
}
|
||||
|
||||
function onMouseWheel( event ) {
|
||||
// this is needed when the program is inside an iframe
|
||||
// to prevent scrolling the whole page
|
||||
event.preventDefault();
|
||||
if ( scope.enabled === false ) { return; }
|
||||
if ( scope.noZoom === true ) { return; }
|
||||
|
||||
var delta = 0;
|
||||
|
||||
if ( event.wheelDelta ) { // WebKit / Opera / Explorer 9
|
||||
|
||||
delta = event.wheelDelta;
|
||||
|
||||
} else if ( event.detail ) { // Firefox
|
||||
|
||||
delta = - event.detail;
|
||||
|
||||
}
|
||||
|
||||
if ( delta > 0 ) {
|
||||
|
||||
scope.dollyOut();
|
||||
|
||||
} else {
|
||||
|
||||
scope.dollyIn();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function onKeyDown( event ) {
|
||||
|
||||
if ( scope.enabled === false ) { return; }
|
||||
if ( scope.noKeys === true ) { return; }
|
||||
if ( scope.noPan === true ) { return; }
|
||||
|
||||
// pan a pixel - I guess for precise positioning?
|
||||
switch ( event.keyCode ) {
|
||||
|
||||
case scope.keys.UP:
|
||||
scope.pan( new THREE.Vector2( 0, 1 ) );
|
||||
break;
|
||||
case scope.keys.BOTTOM:
|
||||
scope.pan( new THREE.Vector2( 0, -1 ) );
|
||||
break;
|
||||
case scope.keys.LEFT:
|
||||
scope.pan( new THREE.Vector2( 1, 0 ) );
|
||||
break;
|
||||
case scope.keys.RIGHT:
|
||||
scope.pan( new THREE.Vector2( -1, 0 ) );
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function touchstart( event ) {
|
||||
|
||||
if ( scope.enabled === false ) { return; }
|
||||
|
||||
switch ( event.touches.length ) {
|
||||
|
||||
case 1: // one-fingered touch: rotate
|
||||
if ( scope.noRotate === true ) { return; }
|
||||
|
||||
state = STATE.TOUCH_ROTATE;
|
||||
|
||||
rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
|
||||
break;
|
||||
|
||||
case 2: // two-fingered touch: dolly
|
||||
if ( scope.noZoom === true ) { return; }
|
||||
|
||||
state = STATE.TOUCH_DOLLY;
|
||||
|
||||
var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
|
||||
var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
|
||||
var distance = Math.sqrt( dx * dx + dy * dy );
|
||||
dollyStart.set( 0, distance );
|
||||
break;
|
||||
|
||||
case 3: // three-fingered touch: pan
|
||||
if ( scope.noPan === true ) { return; }
|
||||
|
||||
state = STATE.TOUCH_PAN;
|
||||
|
||||
panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
|
||||
break;
|
||||
|
||||
default:
|
||||
state = STATE.NONE;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function touchmove( event ) {
|
||||
|
||||
if ( scope.enabled === false ) { return; }
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
switch ( event.touches.length ) {
|
||||
|
||||
case 1: // one-fingered touch: rotate
|
||||
if ( scope.noRotate === true ) { return; }
|
||||
if ( state !== STATE.TOUCH_ROTATE ) { return; }
|
||||
|
||||
rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
|
||||
rotateDelta.subVectors( rotateEnd, rotateStart );
|
||||
|
||||
// rotating across whole screen goes 360 degrees around
|
||||
scope.rotateLeft( 2 * Math.PI * rotateDelta.x / scope.domElement.width * scope.rotateSpeed );
|
||||
// rotating up and down along whole screen attempts to go 360, but limited to 180
|
||||
scope.rotateUp( 2 * Math.PI * rotateDelta.y / scope.domElement.height * scope.rotateSpeed );
|
||||
|
||||
rotateStart.copy( rotateEnd );
|
||||
break;
|
||||
|
||||
case 2: // two-fingered touch: dolly
|
||||
if ( scope.noZoom === true ) { return; }
|
||||
if ( state !== STATE.TOUCH_DOLLY ) { return; }
|
||||
|
||||
var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
|
||||
var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
|
||||
var distance = Math.sqrt( dx * dx + dy * dy );
|
||||
|
||||
dollyEnd.set( 0, distance );
|
||||
dollyDelta.subVectors( dollyEnd, dollyStart );
|
||||
|
||||
if ( dollyDelta.y > 0 ) {
|
||||
|
||||
scope.dollyOut();
|
||||
|
||||
} else {
|
||||
|
||||
scope.dollyIn();
|
||||
|
||||
}
|
||||
|
||||
dollyStart.copy( dollyEnd );
|
||||
break;
|
||||
|
||||
case 3: // three-fingered touch: pan
|
||||
if ( scope.noPan === true ) { return; }
|
||||
if ( state !== STATE.TOUCH_PAN ) { return; }
|
||||
|
||||
panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
|
||||
panDelta.subVectors( panEnd, panStart );
|
||||
|
||||
scope.pan( panDelta );
|
||||
|
||||
panStart.copy( panEnd );
|
||||
break;
|
||||
|
||||
default:
|
||||
state = STATE.NONE;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function touchend( /* event */ ) {
|
||||
|
||||
if ( scope.enabled === false ) { return; }
|
||||
|
||||
state = STATE.NONE;
|
||||
}
|
||||
|
||||
this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false );
|
||||
this.domElement.addEventListener( 'mousedown', onMouseDown, false );
|
||||
this.domElement.addEventListener( 'mousewheel', onMouseWheel, false );
|
||||
this.domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox
|
||||
|
||||
this.domElement.addEventListener( 'keydown', onKeyDown, false );
|
||||
|
||||
this.domElement.addEventListener( 'touchstart', touchstart, false );
|
||||
this.domElement.addEventListener( 'touchend', touchend, false );
|
||||
this.domElement.addEventListener( 'touchmove', touchmove, false );
|
||||
|
||||
};
|
||||
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
var ajax_handler = function () {
|
||||
var api = this;
|
||||
api.response = "v";
|
||||
api.ajaxRequest = function (url, type, data, dataType, callback) {
|
||||
var response;
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: type,
|
||||
dataType: dataType,
|
||||
success: function (data, success) {
|
||||
api.response = data;
|
||||
callback(data, success);
|
||||
},
|
||||
error: function (jqxhr, textStatus, error) {
|
||||
var err = textStatus + ', ' + error;
|
||||
callback(data, error);
|
||||
api.response = data;
|
||||
},
|
||||
data: data
|
||||
});
|
||||
return api.response;
|
||||
};
|
||||
api.makeJSONObject = function () {
|
||||
var object = {};
|
||||
for (var i = 0; i < arguments.length - 1; i = i + 2) {
|
||||
object[arguments[i]] = arguments[i + 1];
|
||||
}
|
||||
return object;
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
var ajax_handler = function () {
|
||||
var api = this;
|
||||
api.response = "v";
|
||||
api.ajaxRequest = function (url, type, data, dataType, callback) {
|
||||
var response;
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: type,
|
||||
dataType: dataType,
|
||||
success: function (data, success) {
|
||||
api.response = data;
|
||||
console.log(" success " + JSON.stringify(success));
|
||||
console.log(" data " + JSON.stringify(data));
|
||||
callback(data, success);
|
||||
},
|
||||
error: function (jqxhr, textStatus, error) {
|
||||
var err = textStatus + ', ' + error;
|
||||
console.log("Request Failed: " + err);
|
||||
callback(data, error);
|
||||
api.response = data;
|
||||
},
|
||||
data: data
|
||||
});
|
||||
return api.response;
|
||||
};
|
||||
api.makeJSONObject = function () {
|
||||
var object = {};
|
||||
for (var i = 0; i < arguments.length - 1; i = i + 2) {
|
||||
object[arguments[i]] = arguments[i + 1];
|
||||
}
|
||||
return object;
|
||||
}
|
||||
};
|
||||
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
$('.btn-minimize').click(function (e) {
|
||||
e.preventDefault();
|
||||
var $target = $(this).parent().parent().next('.box-content');
|
||||
if ($target.is(':visible')) {
|
||||
$('i', $(this)).removeClass('glyphicon-chevron-up').addClass('glyphicon-chevron-down');
|
||||
checkAndDisable($(this).parent().attr('id'));
|
||||
|
||||
}
|
||||
else {
|
||||
$('i', $(this)).removeClass('glyphicon-chevron-down').addClass('glyphicon-chevron-up');
|
||||
checkAndEnable($(this).parent().attr('id'));
|
||||
}
|
||||
$target.slideToggle();
|
||||
});
|
||||
|
||||
function checkAndEnable(id) {
|
||||
|
||||
//console.log("enable: " + id);
|
||||
if (id === "RealtimePlotting") {
|
||||
config_api.modules_status.realtimePlotting = true;
|
||||
}
|
||||
else if (id === "SensorReadings") {
|
||||
config_api.modules_status.sensorReadings = true;
|
||||
} else if (id === "AngleOfRotation_2") {
|
||||
config_api.modules_status.angleOfRotation_2 = true;
|
||||
} else if (id === "AngleOfRotation_1") {
|
||||
config_api.modules_status.angleOfRotation_1 = true;
|
||||
}
|
||||
}
|
||||
|
||||
function checkAndDisable(id) {
|
||||
//console.log("disable: " + id);
|
||||
if (id === "RealtimePlotting") {
|
||||
config_api.modules_status.realtimePlotting = false;
|
||||
}
|
||||
else if (id === "SensorReadings") {
|
||||
config_api.modules_status.sensorReadings = false;
|
||||
} else if (id === "AngleOfRotation_2") {
|
||||
config_api.modules_status.angleOfRotation_2 = false;
|
||||
} else if (id === "AngleOfRotation_1") {
|
||||
config_api.modules_status.angleOfRotation_1 = false;
|
||||
}
|
||||
}
|
||||
|
||||
function isJSON(data) {
|
||||
try {
|
||||
return JSON.parse(data);
|
||||
}
|
||||
catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function Queue() {
|
||||
var a = [], b = '';
|
||||
this.enqueue = function (b) {
|
||||
a.push([this.getLength() - 1 <= 0 ? 0 : this.getLength() - 1, b]);
|
||||
};
|
||||
this.dequeue = function () {
|
||||
if (0 != a.length) {
|
||||
var c = a[b];
|
||||
2 * ++b >= a.length && (a = a.slice(b), b = 0);
|
||||
return c
|
||||
}
|
||||
};
|
||||
this.getLength = function () {
|
||||
return a.length - b;
|
||||
};
|
||||
this.isEmpty = function () {
|
||||
return 0 == a.length;
|
||||
};
|
||||
|
||||
this.peek = function () {
|
||||
return 0 < a.length ? a[b] : void 0
|
||||
};
|
||||
this.getData = function () {
|
||||
return a;
|
||||
};
|
||||
this.make_fixed_size = function (start, end) {
|
||||
a = a.slice(start, end);
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
var config = function () {
|
||||
var api = this;
|
||||
api.configDronePlaceholder = "#virtualDrone";
|
||||
api.realtimePlottingUpdateInterval = 30;
|
||||
api.realtimePlottingTotalPoints = 30;
|
||||
api.realtimePlottingDataWindow = {};
|
||||
api.effectController = {uy: 70.0, uz: 15.0, ux: 10.0, fx: 2.0, fz: 15.0, Tmax: 1};
|
||||
api.controlType = "POST";
|
||||
api.dataType = "json";
|
||||
api.modulesStatus = {
|
||||
"realtimePlotting": false,
|
||||
"sensorReadings": false,
|
||||
"angleOfRotationv1": false,
|
||||
"angleOfRotationv2": false
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
var ws;
|
||||
var graph;
|
||||
var chartData = [];
|
||||
var palette = new Rickshaw.Color.Palette({scheme: "classic9"});
|
||||
|
||||
$(window).load(function () {
|
||||
var tNow = new Date().getTime() / 1000;
|
||||
for (var i = 0; i < 30; i++) {
|
||||
chartData.push({
|
||||
x: tNow - (30 - i) * 15,
|
||||
y: parseFloat(0)
|
||||
});
|
||||
}
|
||||
|
||||
graph = new Rickshaw.Graph({
|
||||
element: document.getElementById("chart"),
|
||||
width: $("#div-chart").width() - 50,
|
||||
height: 300,
|
||||
renderer: "line",
|
||||
padding: {top: 0.2, left: 0.0, right: 0.0, bottom: 0.2},
|
||||
xScale: d3.time.scale(),
|
||||
series: [{
|
||||
'color': palette.color(),
|
||||
'data': chartData,
|
||||
'name': "Temperature"
|
||||
}]
|
||||
});
|
||||
|
||||
graph.render();
|
||||
|
||||
var xAxis = new Rickshaw.Graph.Axis.Time({
|
||||
graph: graph
|
||||
});
|
||||
|
||||
xAxis.render();
|
||||
|
||||
new Rickshaw.Graph.Axis.Y({
|
||||
graph: graph,
|
||||
orientation: 'left',
|
||||
height: 300,
|
||||
tickFormat: Rickshaw.Fixtures.Number.formatKMBT,
|
||||
element: document.getElementById('y_axis')
|
||||
});
|
||||
|
||||
new Rickshaw.Graph.HoverDetail({
|
||||
graph: graph,
|
||||
formatter: function (series, x, y) {
|
||||
var date = '<span class="date">' + moment.unix(x*1000).format('Do MMM YYYY h:mm:ss a') + '</span>';
|
||||
var swatch = '<span class="detail_swatch" style="background-color: ' + series.color + '"></span>';
|
||||
return swatch + series.name + ": " + parseInt(y) + '<br>' + date;
|
||||
}
|
||||
});
|
||||
|
||||
var websocketUrl = $("#div-chart").data("websocketurl");
|
||||
connect(websocketUrl)
|
||||
});
|
||||
|
||||
$(window).unload(function () {
|
||||
disconnect();
|
||||
});
|
||||
|
||||
//websocket connection
|
||||
function connect(target) {
|
||||
if ('WebSocket' in window) {
|
||||
ws = new WebSocket(target);
|
||||
} else if ('MozWebSocket' in window) {
|
||||
ws = new MozWebSocket(target);
|
||||
} else {
|
||||
console.log('WebSocket is not supported by this browser.');
|
||||
}
|
||||
if (ws) {
|
||||
ws.onmessage = function (event) {
|
||||
var dataPoint = JSON.parse(event.data);
|
||||
|
||||
/*var dataPoint = JSON.parse(event.data);
|
||||
chartData.push({
|
||||
x: parseInt(dataPoint[4]) / 1000,
|
||||
y: parseFloat(dataPoint[5])
|
||||
});
|
||||
chartData.shift();
|
||||
graph.update();*/
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function disconnect() {
|
||||
if (ws != null) {
|
||||
ws.close();
|
||||
ws = null;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
var modalPopup = ".wr-modalpopup";
|
||||
var modalPopupContainer = modalPopup + " .modalpopup-container";
|
||||
var modalPopupContent = modalPopup + " .modalpopup-content";
|
||||
var body = "body";
|
||||
|
||||
/*
|
||||
* set popup maximum height function.
|
||||
*/
|
||||
function setPopupMaxHeight() {
|
||||
$(modalPopupContent).css('max-height', ($(body).height() - ($(body).height() / 100 * 30)));
|
||||
$(modalPopupContainer).css('margin-top', (-($(modalPopupContainer).height() / 2)));
|
||||
}
|
||||
|
||||
/*
|
||||
* show popup function.
|
||||
*/
|
||||
function showPopup() {
|
||||
$(modalPopup).show();
|
||||
setPopupMaxHeight();
|
||||
$('#downloadForm').validate({
|
||||
rules: {
|
||||
deviceName: {
|
||||
minlength: 4,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
highlight: function (element) {
|
||||
$(element).closest('.control-group').removeClass('success').addClass('error');
|
||||
},
|
||||
success: function (element) {
|
||||
$(element).closest('.control-group').removeClass('error').addClass('success');
|
||||
$('label[for=deviceName]').remove();
|
||||
}
|
||||
});
|
||||
var deviceType = "";
|
||||
$('.deviceType').each(function () {
|
||||
if (this.value != "") {
|
||||
deviceType = this.value;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* hide popup function.
|
||||
*/
|
||||
function hidePopup() {
|
||||
$('label[for=deviceName]').remove();
|
||||
$('.control-group').removeClass('success').removeClass('error');
|
||||
$(modalPopupContent).html('');
|
||||
$(modalPopup).hide();
|
||||
}
|
||||
|
||||
/*
|
||||
* DOM ready functions.
|
||||
*/
|
||||
$(document).ready(function () {
|
||||
attachEvents();
|
||||
});
|
||||
|
||||
function attachEvents() {
|
||||
/**
|
||||
* Following click function would execute
|
||||
* when a user clicks on "Download" link
|
||||
* on Device Management page in WSO2 DC Console.
|
||||
*/
|
||||
$("a.download-link").click(function () {
|
||||
var sketchType = $(this).data("sketchtype");
|
||||
var deviceType = $(this).data("devicetype");
|
||||
var downloadDeviceAPI = "/devicemgt/api/devices/sketch/generate_link";
|
||||
var payload = {"sketchType": sketchType, "deviceType": deviceType};
|
||||
$(modalPopupContent).html($('#download-device-modal-content').html());
|
||||
showPopup();
|
||||
var deviceName;
|
||||
$("a#download-device-download-link").click(function () {
|
||||
$('.new-device-name').each(function () {
|
||||
if (this.value != "") {
|
||||
deviceName = this.value;
|
||||
}
|
||||
});
|
||||
$('label[for=deviceName]').remove();
|
||||
if (deviceName && deviceName.length >= 4) {
|
||||
payload.deviceName = deviceName;
|
||||
invokerUtil.post(
|
||||
downloadDeviceAPI,
|
||||
payload,
|
||||
function (data, textStatus, jqxhr) {
|
||||
doAction(data);
|
||||
},
|
||||
function (data) {
|
||||
doAction(data);
|
||||
}
|
||||
);
|
||||
} else if (deviceName) {
|
||||
$('.controls').append('<label for="deviceName" generated="true" class="error" ' +
|
||||
'style="display: inline-block;">Please enter at least 4 characters.</label>');
|
||||
$('.control-group').removeClass('success').addClass('error');
|
||||
} else {
|
||||
$('.controls').append('<label for="deviceName" generated="true" class="error" ' +
|
||||
'style="display: inline-block;">This field is required.</label>');
|
||||
$('.control-group').removeClass('success').addClass('error');
|
||||
}
|
||||
});
|
||||
|
||||
$("a#download-device-cancel-link").click(function () {
|
||||
hidePopup();
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function downloadAgent() {
|
||||
$('#downloadForm').submit();
|
||||
|
||||
var deviceName;
|
||||
$('.new-device-name').each(function () {
|
||||
if (this.value != "") {
|
||||
deviceName = this.value;
|
||||
}
|
||||
});
|
||||
if (deviceName && deviceName.length >= 4) {
|
||||
setTimeout(function () {
|
||||
hidePopup();
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
function doAction(data) {
|
||||
//if it is saml redirection response
|
||||
if (data.status == null) {
|
||||
document.write(data);
|
||||
}
|
||||
|
||||
if (data.status == "200") {
|
||||
$(modalPopupContent).html($('#download-device-modal-content-links').html());
|
||||
$("input#download-device-url").val(data.responseText);
|
||||
$("input#download-device-url").focus(function () {
|
||||
$(this).select();
|
||||
});
|
||||
showPopup();
|
||||
} else if (data.status == "401") {
|
||||
$(modalPopupContent).html($('#device-401-content').html());
|
||||
$("#device-401-link").click(function () {
|
||||
window.location = "/devicemgt/login";
|
||||
});
|
||||
showPopup();
|
||||
} else if (data == "403") {
|
||||
$(modalPopupContent).html($('#device-403-content').html());
|
||||
$("#device-403-link").click(function () {
|
||||
window.location = "/devicemgt/login";
|
||||
});
|
||||
showPopup();
|
||||
} else {
|
||||
$(modalPopupContent).html($('#device-unexpected-error-content').html());
|
||||
$("a#device-unexpected-error-link").click(function () {
|
||||
hidePopup();
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
var ajax_handler = new ajax_handler();
|
||||
var config = new config();
|
||||
var plotting = new plotting();
|
||||
var droneRender = new droneRender();
|
||||
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
var ajax_handler = new ajax_handler();
|
||||
var config_api = new config_api();
|
||||
var plotting = new plotting();
|
||||
var object_maker = new object_maker();
|
||||
@ -0,0 +1,357 @@
|
||||
/*
|
||||
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
// VERSION: 2.3 LAST UPDATE: 11.07.2013
|
||||
/*
|
||||
* Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* Made by Wilq32, wilq32@gmail.com, Wroclaw, Poland, 01.2009
|
||||
* Website: http://jqueryrotate.com
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
var supportedCSS,supportedCSSOrigin, styles=document.getElementsByTagName("head")[0].style,toCheck="transformProperty WebkitTransform OTransform msTransform MozTransform".split(" ");
|
||||
for (var a = 0; a < toCheck.length; a++) if (styles[toCheck[a]] !== undefined) { supportedCSS = toCheck[a]; }
|
||||
if (supportedCSS) {
|
||||
supportedCSSOrigin = supportedCSS.replace(/[tT]ransform/,"TransformOrigin");
|
||||
if (supportedCSSOrigin[0] == "T") supportedCSSOrigin[0] = "t";
|
||||
}
|
||||
|
||||
// Bad eval to preven google closure to remove it from code o_O
|
||||
eval('IE = "v"=="\v"');
|
||||
|
||||
jQuery.fn.extend({
|
||||
rotate:function(parameters)
|
||||
{
|
||||
if (this.length===0||typeof parameters=="undefined") return;
|
||||
if (typeof parameters=="number") parameters={angle:parameters};
|
||||
var returned=[];
|
||||
for (var i=0,i0=this.length;i<i0;i++)
|
||||
{
|
||||
var element=this.get(i);
|
||||
if (!element.Wilq32 || !element.Wilq32.PhotoEffect) {
|
||||
|
||||
var paramClone = $.extend(true, {}, parameters);
|
||||
var newRotObject = new Wilq32.PhotoEffect(element,paramClone)._rootObj;
|
||||
|
||||
returned.push($(newRotObject));
|
||||
}
|
||||
else {
|
||||
element.Wilq32.PhotoEffect._handleRotation(parameters);
|
||||
}
|
||||
}
|
||||
return returned;
|
||||
},
|
||||
getRotateAngle: function(){
|
||||
var ret = [];
|
||||
for (var i=0,i0=this.length;i<i0;i++)
|
||||
{
|
||||
var element=this.get(i);
|
||||
if (element.Wilq32 && element.Wilq32.PhotoEffect) {
|
||||
ret[i] = element.Wilq32.PhotoEffect._angle;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
},
|
||||
stopRotate: function(){
|
||||
for (var i=0,i0=this.length;i<i0;i++)
|
||||
{
|
||||
var element=this.get(i);
|
||||
if (element.Wilq32 && element.Wilq32.PhotoEffect) {
|
||||
clearTimeout(element.Wilq32.PhotoEffect._timer);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Library agnostic interface
|
||||
|
||||
Wilq32=window.Wilq32||{};
|
||||
Wilq32.PhotoEffect=(function(){
|
||||
|
||||
if (supportedCSS) {
|
||||
return function(img,parameters){
|
||||
img.Wilq32 = {
|
||||
PhotoEffect: this
|
||||
};
|
||||
|
||||
this._img = this._rootObj = this._eventObj = img;
|
||||
this._handleRotation(parameters);
|
||||
}
|
||||
} else {
|
||||
return function(img,parameters) {
|
||||
this._img = img;
|
||||
this._onLoadDelegate = [parameters];
|
||||
|
||||
this._rootObj=document.createElement('span');
|
||||
this._rootObj.style.display="inline-block";
|
||||
this._rootObj.Wilq32 =
|
||||
{
|
||||
PhotoEffect: this
|
||||
};
|
||||
img.parentNode.insertBefore(this._rootObj,img);
|
||||
|
||||
if (img.complete) {
|
||||
this._Loader();
|
||||
} else {
|
||||
var self=this;
|
||||
// TODO: Remove jQuery dependency
|
||||
jQuery(this._img).bind("load", function(){ self._Loader(); });
|
||||
}
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
Wilq32.PhotoEffect.prototype = {
|
||||
_setupParameters : function (parameters){
|
||||
this._parameters = this._parameters || {};
|
||||
if (typeof this._angle !== "number") { this._angle = 0 ; }
|
||||
if (typeof parameters.angle==="number") { this._angle = parameters.angle; }
|
||||
this._parameters.animateTo = (typeof parameters.animateTo === "number") ? (parameters.animateTo) : (this._angle);
|
||||
|
||||
this._parameters.step = parameters.step || this._parameters.step || null;
|
||||
this._parameters.easing = parameters.easing || this._parameters.easing || this._defaultEasing;
|
||||
this._parameters.duration = 'duration' in parameters ? parameters.duration : parameters.duration || this._parameters.duration || 1000;
|
||||
this._parameters.callback = parameters.callback || this._parameters.callback || this._emptyFunction;
|
||||
this._parameters.center = parameters.center || this._parameters.center || ["50%","50%"];
|
||||
if (typeof this._parameters.center[0] == "string") {
|
||||
this._rotationCenterX = (parseInt(this._parameters.center[0],10) / 100) * this._imgWidth * this._aspectW;
|
||||
} else {
|
||||
this._rotationCenterX = this._parameters.center[0];
|
||||
}
|
||||
if (typeof this._parameters.center[1] == "string") {
|
||||
this._rotationCenterY = (parseInt(this._parameters.center[1],10) / 100) * this._imgHeight * this._aspectH;
|
||||
} else {
|
||||
this._rotationCenterY = this._parameters.center[1];
|
||||
}
|
||||
|
||||
if (parameters.bind && parameters.bind != this._parameters.bind) { this._BindEvents(parameters.bind); }
|
||||
},
|
||||
_emptyFunction: function(){},
|
||||
_defaultEasing: function (x, t, b, c, d) { return -c * ((t=t/d-1)*t*t*t - 1) + b },
|
||||
_handleRotation : function(parameters, dontcheck){
|
||||
if (!supportedCSS && !this._img.complete && !dontcheck) {
|
||||
this._onLoadDelegate.push(parameters);
|
||||
return;
|
||||
}
|
||||
this._setupParameters(parameters);
|
||||
if (this._angle==this._parameters.animateTo) {
|
||||
this._rotate(this._angle);
|
||||
}
|
||||
else {
|
||||
this._animateStart();
|
||||
}
|
||||
},
|
||||
|
||||
_BindEvents:function(events){
|
||||
if (events && this._eventObj)
|
||||
{
|
||||
// Unbinding previous Events
|
||||
if (this._parameters.bind){
|
||||
var oldEvents = this._parameters.bind;
|
||||
for (var a in oldEvents) if (oldEvents.hasOwnProperty(a))
|
||||
// TODO: Remove jQuery dependency
|
||||
jQuery(this._eventObj).unbind(a,oldEvents[a]);
|
||||
}
|
||||
|
||||
this._parameters.bind = events;
|
||||
for (var a in events) if (events.hasOwnProperty(a))
|
||||
// TODO: Remove jQuery dependency
|
||||
jQuery(this._eventObj).bind(a,events[a]);
|
||||
}
|
||||
},
|
||||
|
||||
_Loader:(function()
|
||||
{
|
||||
if (IE)
|
||||
return function() {
|
||||
var width=this._img.width;
|
||||
var height=this._img.height;
|
||||
this._imgWidth = width;
|
||||
this._imgHeight = height;
|
||||
this._img.parentNode.removeChild(this._img);
|
||||
|
||||
this._vimage = this.createVMLNode('image');
|
||||
this._vimage.src=this._img.src;
|
||||
this._vimage.style.height=height+"px";
|
||||
this._vimage.style.width=width+"px";
|
||||
this._vimage.style.position="absolute"; // FIXES IE PROBLEM - its only rendered if its on absolute position!
|
||||
this._vimage.style.top = "0px";
|
||||
this._vimage.style.left = "0px";
|
||||
this._aspectW = this._aspectH = 1;
|
||||
|
||||
/* Group minifying a small 1px precision problem when rotating object */
|
||||
this._container = this.createVMLNode('group');
|
||||
this._container.style.width=width;
|
||||
this._container.style.height=height;
|
||||
this._container.style.position="absolute";
|
||||
this._container.style.top="0px";
|
||||
this._container.style.left="0px";
|
||||
this._container.setAttribute('coordsize',width-1+','+(height-1)); // This -1, -1 trying to fix ugly problem with small displacement on IE
|
||||
this._container.appendChild(this._vimage);
|
||||
|
||||
this._rootObj.appendChild(this._container);
|
||||
this._rootObj.style.position="relative"; // FIXES IE PROBLEM
|
||||
this._rootObj.style.width=width+"px";
|
||||
this._rootObj.style.height=height+"px";
|
||||
this._rootObj.setAttribute('id',this._img.getAttribute('id'));
|
||||
this._rootObj.className=this._img.className;
|
||||
this._eventObj = this._rootObj;
|
||||
var parameters;
|
||||
while (parameters = this._onLoadDelegate.shift()) {
|
||||
this._handleRotation(parameters, true);
|
||||
}
|
||||
}
|
||||
else return function () {
|
||||
this._rootObj.setAttribute('id',this._img.getAttribute('id'));
|
||||
this._rootObj.className=this._img.className;
|
||||
|
||||
this._imgWidth=this._img.naturalWidth;
|
||||
this._imgHeight=this._img.naturalHeight;
|
||||
var _widthMax=Math.sqrt((this._imgHeight)*(this._imgHeight) + (this._imgWidth) * (this._imgWidth));
|
||||
this._width = _widthMax * 3;
|
||||
this._height = _widthMax * 3;
|
||||
|
||||
this._aspectW = this._img.offsetWidth/this._img.naturalWidth;
|
||||
this._aspectH = this._img.offsetHeight/this._img.naturalHeight;
|
||||
|
||||
this._img.parentNode.removeChild(this._img);
|
||||
|
||||
|
||||
this._canvas=document.createElement('canvas');
|
||||
this._canvas.setAttribute('width',this._width);
|
||||
this._canvas.style.position="relative";
|
||||
this._canvas.style.left = -this._img.height * this._aspectW + "px";
|
||||
this._canvas.style.top = -this._img.width * this._aspectH + "px";
|
||||
this._canvas.Wilq32 = this._rootObj.Wilq32;
|
||||
|
||||
this._rootObj.appendChild(this._canvas);
|
||||
this._rootObj.style.width=this._img.width*this._aspectW+"px";
|
||||
this._rootObj.style.height=this._img.height*this._aspectH+"px";
|
||||
this._eventObj = this._canvas;
|
||||
|
||||
this._cnv=this._canvas.getContext('2d');
|
||||
var parameters;
|
||||
while (parameters = this._onLoadDelegate.shift()) {
|
||||
this._handleRotation(parameters, true);
|
||||
}
|
||||
}
|
||||
})(),
|
||||
|
||||
_animateStart:function()
|
||||
{
|
||||
if (this._timer) {
|
||||
clearTimeout(this._timer);
|
||||
}
|
||||
this._animateStartTime = +new Date;
|
||||
this._animateStartAngle = this._angle;
|
||||
this._animate();
|
||||
},
|
||||
_animate:function()
|
||||
{
|
||||
var actualTime = +new Date;
|
||||
var checkEnd = actualTime - this._animateStartTime > this._parameters.duration;
|
||||
|
||||
// TODO: Bug for animatedGif for static rotation ? (to test)
|
||||
if (checkEnd && !this._parameters.animatedGif)
|
||||
{
|
||||
clearTimeout(this._timer);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this._canvas||this._vimage||this._img) {
|
||||
var angle = this._parameters.easing(0, actualTime - this._animateStartTime, this._animateStartAngle, this._parameters.animateTo - this._animateStartAngle, this._parameters.duration);
|
||||
this._rotate((~~(angle*10))/10);
|
||||
}
|
||||
if (this._parameters.step) {
|
||||
this._parameters.step(this._angle);
|
||||
}
|
||||
var self = this;
|
||||
this._timer = setTimeout(function()
|
||||
{
|
||||
self._animate.call(self);
|
||||
}, 10);
|
||||
}
|
||||
|
||||
// To fix Bug that prevents using recursive function in callback I moved this function to back
|
||||
if (this._parameters.callback && checkEnd){
|
||||
this._angle = this._parameters.animateTo;
|
||||
this._rotate(this._angle);
|
||||
this._parameters.callback.call(this._rootObj);
|
||||
}
|
||||
},
|
||||
|
||||
_rotate : (function()
|
||||
{
|
||||
var rad = Math.PI/180;
|
||||
if (IE)
|
||||
return function(angle)
|
||||
{
|
||||
this._angle = angle;
|
||||
this._container.style.rotation=(angle%360)+"deg";
|
||||
this._vimage.style.top = -(this._rotationCenterY - this._imgHeight/2) + "px";
|
||||
this._vimage.style.left = -(this._rotationCenterX - this._imgWidth/2) + "px";
|
||||
this._container.style.top = this._rotationCenterY - this._imgHeight/2 + "px";
|
||||
this._container.style.left = this._rotationCenterX - this._imgWidth/2 + "px";
|
||||
|
||||
}
|
||||
else if (supportedCSS)
|
||||
return function(angle){
|
||||
this._angle = angle;
|
||||
this._img.style[supportedCSS]="rotate("+(angle%360)+"deg)";
|
||||
this._img.style[supportedCSSOrigin]=this._parameters.center.join(" ");
|
||||
}
|
||||
else
|
||||
return function(angle)
|
||||
{
|
||||
this._angle = angle;
|
||||
angle=(angle%360)* rad;
|
||||
// clear canvas
|
||||
this._canvas.width = this._width;//+this._widthAdd;
|
||||
this._canvas.height = this._height;//+this._heightAdd;
|
||||
|
||||
// REMEMBER: all drawings are read from backwards.. so first function is translate, then rotate, then translate, translate..
|
||||
this._cnv.translate(this._imgWidth*this._aspectW,this._imgHeight*this._aspectH); // at least center image on screen
|
||||
this._cnv.translate(this._rotationCenterX,this._rotationCenterY); // we move image back to its orginal
|
||||
this._cnv.rotate(angle); // rotate image
|
||||
this._cnv.translate(-this._rotationCenterX,-this._rotationCenterY); // move image to its center, so we can rotate around its center
|
||||
this._cnv.scale(this._aspectW,this._aspectH); // SCALE - if needed ;)
|
||||
this._cnv.drawImage(this._img, 0, 0); // First - we draw image
|
||||
}
|
||||
|
||||
})()
|
||||
}
|
||||
|
||||
if (IE)
|
||||
{
|
||||
Wilq32.PhotoEffect.prototype.createVMLNode=(function(){
|
||||
document.createStyleSheet().addRule(".rvml", "behavior:url(#default#VML)");
|
||||
try {
|
||||
!document.namespaces.rvml && document.namespaces.add("rvml", "urn:schemas-microsoft-com:vml");
|
||||
return function (tagName) {
|
||||
return document.createElement('<rvml:' + tagName + ' class="rvml">');
|
||||
};
|
||||
} catch (e) {
|
||||
return function (tagName) {
|
||||
return document.createElement('<' + tagName + ' xmlns="urn:schemas-microsoft.com:vml" class="rvml">');
|
||||
};
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
})(jQuery);
|
||||
@ -0,0 +1,132 @@
|
||||
/*global THREE, scene*/
|
||||
|
||||
var Coordinates = {
|
||||
drawGrid:function(params) {
|
||||
params = params || {};
|
||||
var size = params.size !== undefined ? params.size:100;
|
||||
var scale = params.scale !== undefined ? params.scale:0.1;
|
||||
var orientation = params.orientation !== undefined ? params.orientation:"x";
|
||||
var grid = new THREE.Mesh(
|
||||
new THREE.PlaneGeometry(size, size, size * scale, size * scale),
|
||||
new THREE.MeshBasicMaterial({ color: 0x555555, wireframe: true })
|
||||
);
|
||||
if (orientation === "x") {
|
||||
grid.rotation.x = - Math.PI / 2;
|
||||
} else if (orientation === "y") {
|
||||
grid.rotation.y = - Math.PI / 2;
|
||||
} else if (orientation === "z") {
|
||||
grid.rotation.z = - Math.PI / 2;
|
||||
}
|
||||
|
||||
scene.add(grid);
|
||||
},
|
||||
drawGround:function(params) {
|
||||
params = params || {};
|
||||
var size = params.size !== undefined ? params.size:100;
|
||||
var color = params.color !== undefined ? params.color:0xFFFFFF;
|
||||
var ground = new THREE.Mesh(
|
||||
new THREE.PlaneGeometry(size, size),
|
||||
new THREE.MeshLambertMaterial({ color: color,
|
||||
polygonOffset: true, polygonOffsetFactor: 1.0, polygonOffsetUnits: 4.0
|
||||
}));
|
||||
ground.rotation.x = - Math.PI / 2;
|
||||
scene.add(ground);
|
||||
},
|
||||
drawAxes:function(params) {
|
||||
// x = red, y = green, z = blue (RGB = xyz)
|
||||
params = params || {};
|
||||
var axisRadius = params.axisRadius !== undefined ? params.axisRadius:0.04;
|
||||
var axisLength = params.axisLength !== undefined ? params.axisLength:11;
|
||||
var axisTess = params.axisTess !== undefined ? params.axisTess:48;
|
||||
var axisOrientation = params.axisOrientation !== undefined ? params.axisOrientation:"x";
|
||||
|
||||
var axisMaterial = new THREE.MeshLambertMaterial({ color: 0x000000, side: THREE.DoubleSide });
|
||||
var axis = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(axisRadius, axisRadius, axisLength, axisTess, 1, true),
|
||||
axisMaterial
|
||||
);
|
||||
if (axisOrientation === "x") {
|
||||
axis.rotation.z = - Math.PI / 2;
|
||||
axis.position.x = axisLength/2-1;
|
||||
} else if (axisOrientation === "y") {
|
||||
axis.position.y = axisLength/2-1;
|
||||
}
|
||||
|
||||
scene.add( axis );
|
||||
|
||||
var arrow = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(0, 4*axisRadius, 8*axisRadius, axisTess, 1, true),
|
||||
axisMaterial
|
||||
);
|
||||
if (axisOrientation === "x") {
|
||||
arrow.rotation.z = - Math.PI / 2;
|
||||
arrow.position.x = axisLength - 1 + axisRadius*4/2;
|
||||
} else if (axisOrientation === "y") {
|
||||
arrow.position.y = axisLength - 1 + axisRadius*4/2;
|
||||
}
|
||||
scene.add( arrow );
|
||||
|
||||
},
|
||||
drawAllAxes:function(params) {
|
||||
params = params || {};
|
||||
var axisRadius = params.axisRadius !== undefined ? params.axisRadius:0.04;
|
||||
var axisLength = params.axisLength !== undefined ? params.axisLength:11;
|
||||
var axisTess = params.axisTess !== undefined ? params.axisTess:48;
|
||||
|
||||
var axisXMaterial = new THREE.MeshLambertMaterial({ color: 0xFF0000 });
|
||||
var axisYMaterial = new THREE.MeshLambertMaterial({ color: 0x00FF00 });
|
||||
var axisZMaterial = new THREE.MeshLambertMaterial({ color: 0x0000FF });
|
||||
axisXMaterial.side = THREE.DoubleSide;
|
||||
axisYMaterial.side = THREE.DoubleSide;
|
||||
axisZMaterial.side = THREE.DoubleSide;
|
||||
var axisX = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(axisRadius, axisRadius, axisLength, axisTess, 1, true),
|
||||
axisXMaterial
|
||||
);
|
||||
var axisY = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(axisRadius, axisRadius, axisLength, axisTess, 1, true),
|
||||
axisYMaterial
|
||||
);
|
||||
var axisZ = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(axisRadius, axisRadius, axisLength, axisTess, 1, true),
|
||||
axisZMaterial
|
||||
);
|
||||
axisX.rotation.z = - Math.PI / 2;
|
||||
axisX.position.x = axisLength/2-1;
|
||||
|
||||
axisY.position.y = axisLength/2-1;
|
||||
|
||||
axisZ.rotation.y = - Math.PI / 2;
|
||||
axisZ.rotation.z = - Math.PI / 2;
|
||||
axisZ.position.z = axisLength/2-1;
|
||||
|
||||
scene.add( axisX );
|
||||
scene.add( axisY );
|
||||
scene.add( axisZ );
|
||||
|
||||
var arrowX = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(0, 4*axisRadius, 4*axisRadius, axisTess, 1, true),
|
||||
axisXMaterial
|
||||
);
|
||||
var arrowY = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(0, 4*axisRadius, 4*axisRadius, axisTess, 1, true),
|
||||
axisYMaterial
|
||||
);
|
||||
var arrowZ = new THREE.Mesh(
|
||||
new THREE.CylinderGeometry(0, 4*axisRadius, 4*axisRadius, axisTess, 1, true),
|
||||
axisZMaterial
|
||||
);
|
||||
arrowX.rotation.z = - Math.PI / 2;
|
||||
arrowX.position.x = axisLength - 1 + axisRadius*4/2;
|
||||
arrowY.position.y = axisLength - 1 + axisRadius*4/2;
|
||||
|
||||
arrowZ.rotation.z = - Math.PI / 2;
|
||||
arrowZ.rotation.y = - Math.PI / 2;
|
||||
arrowZ.position.z = axisLength - 1 + axisRadius*4/2;
|
||||
|
||||
scene.add( arrowX );
|
||||
scene.add( arrowY );
|
||||
scene.add( arrowZ );
|
||||
}
|
||||
|
||||
};
|
||||
@ -0,0 +1,531 @@
|
||||
/**
|
||||
* @author qiao / https://github.com/qiao
|
||||
* @author mrdoob / http://mrdoob.com
|
||||
* @author alteredq / http://alteredqualia.com/
|
||||
* @author WestLangley / http://github.com/WestLangley
|
||||
* @author erich666 / http://erichaines.com
|
||||
*/
|
||||
/*global THREE, console */
|
||||
|
||||
THREE.OrbitAndPanControls = function ( object, domElement ) {
|
||||
|
||||
THREE.EventDispatcher.call( this );
|
||||
|
||||
this.enabled = true;
|
||||
|
||||
this.object = object;
|
||||
this.domElement = ( domElement !== undefined ) ? domElement : document;
|
||||
|
||||
// API
|
||||
|
||||
this.enabled = true;
|
||||
|
||||
this.target = new THREE.Vector3();
|
||||
// center is old, deprecated; use "target" instead
|
||||
this.center = this.target;
|
||||
|
||||
// This option actually enables dollying in and out
|
||||
this.noZoom = false;
|
||||
this.zoomSpeed = 1.0;
|
||||
|
||||
this.noRotate = false;
|
||||
this.rotateSpeed = 1.0;
|
||||
|
||||
this.noPan = false;
|
||||
|
||||
this.autoRotate = false;
|
||||
this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
|
||||
|
||||
this.minPolarAngle = 0; // radians
|
||||
this.maxPolarAngle = Math.PI; // radians
|
||||
|
||||
this.minDistance = 0;
|
||||
this.maxDistance = Infinity;
|
||||
|
||||
this.noKeys = false;
|
||||
this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };
|
||||
|
||||
// internals
|
||||
|
||||
var scope = this;
|
||||
|
||||
var EPS = 0.000001;
|
||||
|
||||
var rotateStart = new THREE.Vector2();
|
||||
var rotateEnd = new THREE.Vector2();
|
||||
var rotateDelta = new THREE.Vector2();
|
||||
|
||||
var panStart = new THREE.Vector2();
|
||||
var panEnd = new THREE.Vector2();
|
||||
var panDelta = new THREE.Vector2();
|
||||
|
||||
var dollyStart = new THREE.Vector2();
|
||||
var dollyEnd = new THREE.Vector2();
|
||||
var dollyDelta = new THREE.Vector2();
|
||||
|
||||
var phiDelta = 0;
|
||||
var thetaDelta = 0;
|
||||
var scale = 1;
|
||||
var pan = new THREE.Vector3();
|
||||
|
||||
var lastPosition = new THREE.Vector3();
|
||||
|
||||
var STATE = { NONE : -1, ROTATE : 0, DOLLY : 1, PAN : 2, TOUCH_ROTATE : 3, TOUCH_DOLLY : 4, TOUCH_PAN : 5 };
|
||||
var state = STATE.NONE;
|
||||
|
||||
// events
|
||||
|
||||
var changeEvent = { type: 'change' };
|
||||
|
||||
|
||||
this.rotateLeft = function ( angle ) {
|
||||
|
||||
if ( angle === undefined ) {
|
||||
|
||||
angle = getAutoRotationAngle();
|
||||
|
||||
}
|
||||
|
||||
thetaDelta -= angle;
|
||||
|
||||
};
|
||||
|
||||
this.rotateUp = function ( angle ) {
|
||||
|
||||
if ( angle === undefined ) {
|
||||
|
||||
angle = getAutoRotationAngle();
|
||||
|
||||
}
|
||||
|
||||
phiDelta -= angle;
|
||||
|
||||
};
|
||||
|
||||
// pass in distance in world space to move left
|
||||
this.panLeft = function ( distance ) {
|
||||
|
||||
var panOffset = new THREE.Vector3();
|
||||
var te = this.object.matrix.elements;
|
||||
// get X column of matrix
|
||||
panOffset.set( te[0], te[1], te[2] );
|
||||
panOffset.multiplyScalar(-distance);
|
||||
|
||||
pan.add( panOffset );
|
||||
|
||||
};
|
||||
|
||||
// pass in distance in world space to move up
|
||||
this.panUp = function ( distance ) {
|
||||
|
||||
var panOffset = new THREE.Vector3();
|
||||
var te = this.object.matrix.elements;
|
||||
// get Y column of matrix
|
||||
panOffset.set( te[4], te[5], te[6] );
|
||||
panOffset.multiplyScalar(distance);
|
||||
|
||||
pan.add( panOffset );
|
||||
};
|
||||
|
||||
// main entry point; pass in Vector2 of change desired in pixel space,
|
||||
// right and down are positive
|
||||
this.pan = function ( delta ) {
|
||||
|
||||
if ( scope.object.fov !== undefined )
|
||||
{
|
||||
// perspective
|
||||
var position = scope.object.position;
|
||||
var offset = position.clone().sub( scope.target );
|
||||
var targetDistance = offset.length();
|
||||
|
||||
// half of the fov is center to top of screen
|
||||
targetDistance *= Math.tan( (scope.object.fov/2) * Math.PI / 180.0 );
|
||||
// we actually don't use screenWidth, since perspective camera is fixed to screen height
|
||||
scope.panLeft( 2 * delta.x * targetDistance / scope.domElement.height );
|
||||
scope.panUp( 2 * delta.y * targetDistance / scope.domElement.height );
|
||||
}
|
||||
else if ( scope.object.top !== undefined )
|
||||
{
|
||||
// orthographic
|
||||
scope.panLeft( delta.x * (scope.object.right - scope.object.left) / scope.domElement.width );
|
||||
scope.panUp( delta.y * (scope.object.top - scope.object.bottom) / scope.domElement.height );
|
||||
}
|
||||
else
|
||||
{
|
||||
// camera neither orthographic or perspective - warn user
|
||||
console.warn( 'WARNING: OrbitControls.js encountered an unknown camera type - pan disabled.' );
|
||||
}
|
||||
};
|
||||
|
||||
this.dollyIn = function ( dollyScale ) {
|
||||
|
||||
if ( dollyScale === undefined ) {
|
||||
|
||||
dollyScale = getZoomScale();
|
||||
|
||||
}
|
||||
|
||||
scale /= dollyScale;
|
||||
|
||||
};
|
||||
|
||||
this.dollyOut = function ( dollyScale ) {
|
||||
|
||||
if ( dollyScale === undefined ) {
|
||||
|
||||
dollyScale = getZoomScale();
|
||||
|
||||
}
|
||||
|
||||
scale *= dollyScale;
|
||||
|
||||
};
|
||||
|
||||
this.update = function () {
|
||||
|
||||
var position = this.object.position;
|
||||
var offset = position.clone().sub( this.target );
|
||||
|
||||
// angle from z-axis around y-axis
|
||||
|
||||
var theta = Math.atan2( offset.x, offset.z );
|
||||
|
||||
// angle from y-axis
|
||||
|
||||
var phi = Math.atan2( Math.sqrt( offset.x * offset.x + offset.z * offset.z ), offset.y );
|
||||
|
||||
if ( this.autoRotate ) {
|
||||
|
||||
this.rotateLeft( getAutoRotationAngle() );
|
||||
|
||||
}
|
||||
|
||||
theta += thetaDelta;
|
||||
phi += phiDelta;
|
||||
|
||||
// restrict phi to be between desired limits
|
||||
phi = Math.max( this.minPolarAngle, Math.min( this.maxPolarAngle, phi ) );
|
||||
|
||||
// restrict phi to be betwee EPS and PI-EPS
|
||||
phi = Math.max( EPS, Math.min( Math.PI - EPS, phi ) );
|
||||
|
||||
var radius = offset.length() * scale;
|
||||
|
||||
// restrict radius to be between desired limits
|
||||
radius = Math.max( this.minDistance, Math.min( this.maxDistance, radius ) );
|
||||
|
||||
// move target to panned location
|
||||
this.target.add( pan );
|
||||
|
||||
offset.x = radius * Math.sin( phi ) * Math.sin( theta );
|
||||
offset.y = radius * Math.cos( phi );
|
||||
offset.z = radius * Math.sin( phi ) * Math.cos( theta );
|
||||
|
||||
position.copy( this.target ).add( offset );
|
||||
|
||||
this.object.lookAt( this.target );
|
||||
|
||||
thetaDelta = 0;
|
||||
phiDelta = 0;
|
||||
scale = 1;
|
||||
pan.set(0,0,0);
|
||||
|
||||
if ( lastPosition.distanceTo( this.object.position ) > 0 ) {
|
||||
|
||||
this.dispatchEvent( changeEvent );
|
||||
|
||||
lastPosition.copy( this.object.position );
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
function getAutoRotationAngle() {
|
||||
|
||||
return 2 * Math.PI / 60 / 60 * scope.autoRotateSpeed;
|
||||
|
||||
}
|
||||
|
||||
function getZoomScale() {
|
||||
|
||||
return Math.pow( 0.95, scope.zoomSpeed );
|
||||
|
||||
}
|
||||
|
||||
function onMouseDown( event ) {
|
||||
|
||||
if ( scope.enabled === false ) { return; }
|
||||
event.preventDefault();
|
||||
|
||||
if ( event.button === 0 ) {
|
||||
if ( scope.noRotate === true ) { return; }
|
||||
|
||||
state = STATE.ROTATE;
|
||||
|
||||
rotateStart.set( event.clientX, event.clientY );
|
||||
|
||||
} else if ( event.button === 1 ) {
|
||||
if ( scope.noZoom === true ) { return; }
|
||||
|
||||
state = STATE.DOLLY;
|
||||
|
||||
dollyStart.set( event.clientX, event.clientY );
|
||||
|
||||
} else if ( event.button === 2 ) {
|
||||
if ( scope.noPan === true ) { return; }
|
||||
|
||||
state = STATE.PAN;
|
||||
|
||||
panStart.set( event.clientX, event.clientY );
|
||||
|
||||
}
|
||||
|
||||
document.addEventListener( 'mousemove', onMouseMove, false );
|
||||
document.addEventListener( 'mouseup', onMouseUp, false );
|
||||
|
||||
}
|
||||
|
||||
function onMouseMove( event ) {
|
||||
|
||||
if ( scope.enabled === false ) { return; }
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
if ( state === STATE.ROTATE ) {
|
||||
if ( scope.noRotate === true ) { return; }
|
||||
|
||||
rotateEnd.set( event.clientX, event.clientY );
|
||||
rotateDelta.subVectors( rotateEnd, rotateStart );
|
||||
|
||||
// rotating across whole screen goes 360 degrees around
|
||||
scope.rotateLeft( 2 * Math.PI * rotateDelta.x / scope.domElement.width * scope.rotateSpeed );
|
||||
// rotating up and down along whole screen attempts to go 360, but limited to 180
|
||||
scope.rotateUp( 2 * Math.PI * rotateDelta.y / scope.domElement.height * scope.rotateSpeed );
|
||||
|
||||
rotateStart.copy( rotateEnd );
|
||||
|
||||
} else if ( state === STATE.DOLLY ) {
|
||||
if ( scope.noZoom === true ) { return; }
|
||||
|
||||
dollyEnd.set( event.clientX, event.clientY );
|
||||
dollyDelta.subVectors( dollyEnd, dollyStart );
|
||||
|
||||
if ( dollyDelta.y > 0 ) {
|
||||
|
||||
scope.dollyIn();
|
||||
|
||||
} else {
|
||||
|
||||
scope.dollyOut();
|
||||
|
||||
}
|
||||
|
||||
dollyStart.copy( dollyEnd );
|
||||
|
||||
} else if ( state === STATE.PAN ) {
|
||||
if ( scope.noPan === true ) { return; }
|
||||
|
||||
panEnd.set( event.clientX, event.clientY );
|
||||
panDelta.subVectors( panEnd, panStart );
|
||||
|
||||
scope.pan( panDelta );
|
||||
|
||||
panStart.copy( panEnd );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function onMouseUp( /* event */ ) {
|
||||
|
||||
if ( scope.enabled === false ) { return; }
|
||||
|
||||
document.removeEventListener( 'mousemove', onMouseMove, false );
|
||||
document.removeEventListener( 'mouseup', onMouseUp, false );
|
||||
|
||||
state = STATE.NONE;
|
||||
|
||||
}
|
||||
|
||||
function onMouseWheel( event ) {
|
||||
// this is needed when the program is inside an iframe
|
||||
// to prevent scrolling the whole page
|
||||
event.preventDefault();
|
||||
if ( scope.enabled === false ) { return; }
|
||||
if ( scope.noZoom === true ) { return; }
|
||||
|
||||
var delta = 0;
|
||||
|
||||
if ( event.wheelDelta ) { // WebKit / Opera / Explorer 9
|
||||
|
||||
delta = event.wheelDelta;
|
||||
|
||||
} else if ( event.detail ) { // Firefox
|
||||
|
||||
delta = - event.detail;
|
||||
|
||||
}
|
||||
|
||||
if ( delta > 0 ) {
|
||||
|
||||
scope.dollyOut();
|
||||
|
||||
} else {
|
||||
|
||||
scope.dollyIn();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function onKeyDown( event ) {
|
||||
|
||||
if ( scope.enabled === false ) { return; }
|
||||
if ( scope.noKeys === true ) { return; }
|
||||
if ( scope.noPan === true ) { return; }
|
||||
|
||||
// pan a pixel - I guess for precise positioning?
|
||||
switch ( event.keyCode ) {
|
||||
|
||||
case scope.keys.UP:
|
||||
scope.pan( new THREE.Vector2( 0, 1 ) );
|
||||
break;
|
||||
case scope.keys.BOTTOM:
|
||||
scope.pan( new THREE.Vector2( 0, -1 ) );
|
||||
break;
|
||||
case scope.keys.LEFT:
|
||||
scope.pan( new THREE.Vector2( 1, 0 ) );
|
||||
break;
|
||||
case scope.keys.RIGHT:
|
||||
scope.pan( new THREE.Vector2( -1, 0 ) );
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function touchstart( event ) {
|
||||
|
||||
if ( scope.enabled === false ) { return; }
|
||||
|
||||
switch ( event.touches.length ) {
|
||||
|
||||
case 1: // one-fingered touch: rotate
|
||||
if ( scope.noRotate === true ) { return; }
|
||||
|
||||
state = STATE.TOUCH_ROTATE;
|
||||
|
||||
rotateStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
|
||||
break;
|
||||
|
||||
case 2: // two-fingered touch: dolly
|
||||
if ( scope.noZoom === true ) { return; }
|
||||
|
||||
state = STATE.TOUCH_DOLLY;
|
||||
|
||||
var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
|
||||
var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
|
||||
var distance = Math.sqrt( dx * dx + dy * dy );
|
||||
dollyStart.set( 0, distance );
|
||||
break;
|
||||
|
||||
case 3: // three-fingered touch: pan
|
||||
if ( scope.noPan === true ) { return; }
|
||||
|
||||
state = STATE.TOUCH_PAN;
|
||||
|
||||
panStart.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
|
||||
break;
|
||||
|
||||
default:
|
||||
state = STATE.NONE;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function touchmove( event ) {
|
||||
|
||||
if ( scope.enabled === false ) { return; }
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
switch ( event.touches.length ) {
|
||||
|
||||
case 1: // one-fingered touch: rotate
|
||||
if ( scope.noRotate === true ) { return; }
|
||||
if ( state !== STATE.TOUCH_ROTATE ) { return; }
|
||||
|
||||
rotateEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
|
||||
rotateDelta.subVectors( rotateEnd, rotateStart );
|
||||
|
||||
// rotating across whole screen goes 360 degrees around
|
||||
scope.rotateLeft( 2 * Math.PI * rotateDelta.x / scope.domElement.width * scope.rotateSpeed );
|
||||
// rotating up and down along whole screen attempts to go 360, but limited to 180
|
||||
scope.rotateUp( 2 * Math.PI * rotateDelta.y / scope.domElement.height * scope.rotateSpeed );
|
||||
|
||||
rotateStart.copy( rotateEnd );
|
||||
break;
|
||||
|
||||
case 2: // two-fingered touch: dolly
|
||||
if ( scope.noZoom === true ) { return; }
|
||||
if ( state !== STATE.TOUCH_DOLLY ) { return; }
|
||||
|
||||
var dx = event.touches[ 0 ].pageX - event.touches[ 1 ].pageX;
|
||||
var dy = event.touches[ 0 ].pageY - event.touches[ 1 ].pageY;
|
||||
var distance = Math.sqrt( dx * dx + dy * dy );
|
||||
|
||||
dollyEnd.set( 0, distance );
|
||||
dollyDelta.subVectors( dollyEnd, dollyStart );
|
||||
|
||||
if ( dollyDelta.y > 0 ) {
|
||||
|
||||
scope.dollyOut();
|
||||
|
||||
} else {
|
||||
|
||||
scope.dollyIn();
|
||||
|
||||
}
|
||||
|
||||
dollyStart.copy( dollyEnd );
|
||||
break;
|
||||
|
||||
case 3: // three-fingered touch: pan
|
||||
if ( scope.noPan === true ) { return; }
|
||||
if ( state !== STATE.TOUCH_PAN ) { return; }
|
||||
|
||||
panEnd.set( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
|
||||
panDelta.subVectors( panEnd, panStart );
|
||||
|
||||
scope.pan( panDelta );
|
||||
|
||||
panStart.copy( panEnd );
|
||||
break;
|
||||
|
||||
default:
|
||||
state = STATE.NONE;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function touchend( /* event */ ) {
|
||||
|
||||
if ( scope.enabled === false ) { return; }
|
||||
|
||||
state = STATE.NONE;
|
||||
}
|
||||
|
||||
this.domElement.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false );
|
||||
this.domElement.addEventListener( 'mousedown', onMouseDown, false );
|
||||
this.domElement.addEventListener( 'mousewheel', onMouseWheel, false );
|
||||
this.domElement.addEventListener( 'DOMMouseScroll', onMouseWheel, false ); // firefox
|
||||
|
||||
this.domElement.addEventListener( 'keydown', onKeyDown, false );
|
||||
|
||||
this.domElement.addEventListener( 'touchstart', touchstart, false );
|
||||
this.domElement.addEventListener( 'touchend', touchend, false );
|
||||
this.domElement.addEventListener( 'touchmove', touchmove, false );
|
||||
|
||||
};
|
||||
@ -0,0 +1,357 @@
|
||||
/*
|
||||
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
// VERSION: 2.3 LAST UPDATE: 11.07.2013
|
||||
/*
|
||||
* Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* Made by Wilq32, wilq32@gmail.com, Wroclaw, Poland, 01.2009
|
||||
* Website: http://jqueryrotate.com
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
var supportedCSS,supportedCSSOrigin, styles=document.getElementsByTagName("head")[0].style,toCheck="transformProperty WebkitTransform OTransform msTransform MozTransform".split(" ");
|
||||
for (var a = 0; a < toCheck.length; a++) if (styles[toCheck[a]] !== undefined) { supportedCSS = toCheck[a]; }
|
||||
if (supportedCSS) {
|
||||
supportedCSSOrigin = supportedCSS.replace(/[tT]ransform/,"TransformOrigin");
|
||||
if (supportedCSSOrigin[0] == "T") supportedCSSOrigin[0] = "t";
|
||||
}
|
||||
|
||||
// Bad eval to preven google closure to remove it from code o_O
|
||||
eval('IE = "v"=="\v"');
|
||||
|
||||
jQuery.fn.extend({
|
||||
rotate:function(parameters)
|
||||
{
|
||||
if (this.length===0||typeof parameters=="undefined") return;
|
||||
if (typeof parameters=="number") parameters={angle:parameters};
|
||||
var returned=[];
|
||||
for (var i=0,i0=this.length;i<i0;i++)
|
||||
{
|
||||
var element=this.get(i);
|
||||
if (!element.Wilq32 || !element.Wilq32.PhotoEffect) {
|
||||
|
||||
var paramClone = $.extend(true, {}, parameters);
|
||||
var newRotObject = new Wilq32.PhotoEffect(element,paramClone)._rootObj;
|
||||
|
||||
returned.push($(newRotObject));
|
||||
}
|
||||
else {
|
||||
element.Wilq32.PhotoEffect._handleRotation(parameters);
|
||||
}
|
||||
}
|
||||
return returned;
|
||||
},
|
||||
getRotateAngle: function(){
|
||||
var ret = [];
|
||||
for (var i=0,i0=this.length;i<i0;i++)
|
||||
{
|
||||
var element=this.get(i);
|
||||
if (element.Wilq32 && element.Wilq32.PhotoEffect) {
|
||||
ret[i] = element.Wilq32.PhotoEffect._angle;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
},
|
||||
stopRotate: function(){
|
||||
for (var i=0,i0=this.length;i<i0;i++)
|
||||
{
|
||||
var element=this.get(i);
|
||||
if (element.Wilq32 && element.Wilq32.PhotoEffect) {
|
||||
clearTimeout(element.Wilq32.PhotoEffect._timer);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Library agnostic interface
|
||||
|
||||
Wilq32=window.Wilq32||{};
|
||||
Wilq32.PhotoEffect=(function(){
|
||||
|
||||
if (supportedCSS) {
|
||||
return function(img,parameters){
|
||||
img.Wilq32 = {
|
||||
PhotoEffect: this
|
||||
};
|
||||
|
||||
this._img = this._rootObj = this._eventObj = img;
|
||||
this._handleRotation(parameters);
|
||||
}
|
||||
} else {
|
||||
return function(img,parameters) {
|
||||
this._img = img;
|
||||
this._onLoadDelegate = [parameters];
|
||||
|
||||
this._rootObj=document.createElement('span');
|
||||
this._rootObj.style.display="inline-block";
|
||||
this._rootObj.Wilq32 =
|
||||
{
|
||||
PhotoEffect: this
|
||||
};
|
||||
img.parentNode.insertBefore(this._rootObj,img);
|
||||
|
||||
if (img.complete) {
|
||||
this._Loader();
|
||||
} else {
|
||||
var self=this;
|
||||
// TODO: Remove jQuery dependency
|
||||
jQuery(this._img).bind("load", function(){ self._Loader(); });
|
||||
}
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
Wilq32.PhotoEffect.prototype = {
|
||||
_setupParameters : function (parameters){
|
||||
this._parameters = this._parameters || {};
|
||||
if (typeof this._angle !== "number") { this._angle = 0 ; }
|
||||
if (typeof parameters.angle==="number") { this._angle = parameters.angle; }
|
||||
this._parameters.animateTo = (typeof parameters.animateTo === "number") ? (parameters.animateTo) : (this._angle);
|
||||
|
||||
this._parameters.step = parameters.step || this._parameters.step || null;
|
||||
this._parameters.easing = parameters.easing || this._parameters.easing || this._defaultEasing;
|
||||
this._parameters.duration = 'duration' in parameters ? parameters.duration : parameters.duration || this._parameters.duration || 1000;
|
||||
this._parameters.callback = parameters.callback || this._parameters.callback || this._emptyFunction;
|
||||
this._parameters.center = parameters.center || this._parameters.center || ["50%","50%"];
|
||||
if (typeof this._parameters.center[0] == "string") {
|
||||
this._rotationCenterX = (parseInt(this._parameters.center[0],10) / 100) * this._imgWidth * this._aspectW;
|
||||
} else {
|
||||
this._rotationCenterX = this._parameters.center[0];
|
||||
}
|
||||
if (typeof this._parameters.center[1] == "string") {
|
||||
this._rotationCenterY = (parseInt(this._parameters.center[1],10) / 100) * this._imgHeight * this._aspectH;
|
||||
} else {
|
||||
this._rotationCenterY = this._parameters.center[1];
|
||||
}
|
||||
|
||||
if (parameters.bind && parameters.bind != this._parameters.bind) { this._BindEvents(parameters.bind); }
|
||||
},
|
||||
_emptyFunction: function(){},
|
||||
_defaultEasing: function (x, t, b, c, d) { return -c * ((t=t/d-1)*t*t*t - 1) + b },
|
||||
_handleRotation : function(parameters, dontcheck){
|
||||
if (!supportedCSS && !this._img.complete && !dontcheck) {
|
||||
this._onLoadDelegate.push(parameters);
|
||||
return;
|
||||
}
|
||||
this._setupParameters(parameters);
|
||||
if (this._angle==this._parameters.animateTo) {
|
||||
this._rotate(this._angle);
|
||||
}
|
||||
else {
|
||||
this._animateStart();
|
||||
}
|
||||
},
|
||||
|
||||
_BindEvents:function(events){
|
||||
if (events && this._eventObj)
|
||||
{
|
||||
// Unbinding previous Events
|
||||
if (this._parameters.bind){
|
||||
var oldEvents = this._parameters.bind;
|
||||
for (var a in oldEvents) if (oldEvents.hasOwnProperty(a))
|
||||
// TODO: Remove jQuery dependency
|
||||
jQuery(this._eventObj).unbind(a,oldEvents[a]);
|
||||
}
|
||||
|
||||
this._parameters.bind = events;
|
||||
for (var a in events) if (events.hasOwnProperty(a))
|
||||
// TODO: Remove jQuery dependency
|
||||
jQuery(this._eventObj).bind(a,events[a]);
|
||||
}
|
||||
},
|
||||
|
||||
_Loader:(function()
|
||||
{
|
||||
if (IE)
|
||||
return function() {
|
||||
var width=this._img.width;
|
||||
var height=this._img.height;
|
||||
this._imgWidth = width;
|
||||
this._imgHeight = height;
|
||||
this._img.parentNode.removeChild(this._img);
|
||||
|
||||
this._vimage = this.createVMLNode('image');
|
||||
this._vimage.src=this._img.src;
|
||||
this._vimage.style.height=height+"px";
|
||||
this._vimage.style.width=width+"px";
|
||||
this._vimage.style.position="absolute"; // FIXES IE PROBLEM - its only rendered if its on absolute position!
|
||||
this._vimage.style.top = "0px";
|
||||
this._vimage.style.left = "0px";
|
||||
this._aspectW = this._aspectH = 1;
|
||||
|
||||
/* Group minifying a small 1px precision problem when rotating object */
|
||||
this._container = this.createVMLNode('group');
|
||||
this._container.style.width=width;
|
||||
this._container.style.height=height;
|
||||
this._container.style.position="absolute";
|
||||
this._container.style.top="0px";
|
||||
this._container.style.left="0px";
|
||||
this._container.setAttribute('coordsize',width-1+','+(height-1)); // This -1, -1 trying to fix ugly problem with small displacement on IE
|
||||
this._container.appendChild(this._vimage);
|
||||
|
||||
this._rootObj.appendChild(this._container);
|
||||
this._rootObj.style.position="relative"; // FIXES IE PROBLEM
|
||||
this._rootObj.style.width=width+"px";
|
||||
this._rootObj.style.height=height+"px";
|
||||
this._rootObj.setAttribute('id',this._img.getAttribute('id'));
|
||||
this._rootObj.className=this._img.className;
|
||||
this._eventObj = this._rootObj;
|
||||
var parameters;
|
||||
while (parameters = this._onLoadDelegate.shift()) {
|
||||
this._handleRotation(parameters, true);
|
||||
}
|
||||
}
|
||||
else return function () {
|
||||
this._rootObj.setAttribute('id',this._img.getAttribute('id'));
|
||||
this._rootObj.className=this._img.className;
|
||||
|
||||
this._imgWidth=this._img.naturalWidth;
|
||||
this._imgHeight=this._img.naturalHeight;
|
||||
var _widthMax=Math.sqrt((this._imgHeight)*(this._imgHeight) + (this._imgWidth) * (this._imgWidth));
|
||||
this._width = _widthMax * 3;
|
||||
this._height = _widthMax * 3;
|
||||
|
||||
this._aspectW = this._img.offsetWidth/this._img.naturalWidth;
|
||||
this._aspectH = this._img.offsetHeight/this._img.naturalHeight;
|
||||
|
||||
this._img.parentNode.removeChild(this._img);
|
||||
|
||||
|
||||
this._canvas=document.createElement('canvas');
|
||||
this._canvas.setAttribute('width',this._width);
|
||||
this._canvas.style.position="relative";
|
||||
this._canvas.style.left = -this._img.height * this._aspectW + "px";
|
||||
this._canvas.style.top = -this._img.width * this._aspectH + "px";
|
||||
this._canvas.Wilq32 = this._rootObj.Wilq32;
|
||||
|
||||
this._rootObj.appendChild(this._canvas);
|
||||
this._rootObj.style.width=this._img.width*this._aspectW+"px";
|
||||
this._rootObj.style.height=this._img.height*this._aspectH+"px";
|
||||
this._eventObj = this._canvas;
|
||||
|
||||
this._cnv=this._canvas.getContext('2d');
|
||||
var parameters;
|
||||
while (parameters = this._onLoadDelegate.shift()) {
|
||||
this._handleRotation(parameters, true);
|
||||
}
|
||||
}
|
||||
})(),
|
||||
|
||||
_animateStart:function()
|
||||
{
|
||||
if (this._timer) {
|
||||
clearTimeout(this._timer);
|
||||
}
|
||||
this._animateStartTime = +new Date;
|
||||
this._animateStartAngle = this._angle;
|
||||
this._animate();
|
||||
},
|
||||
_animate:function()
|
||||
{
|
||||
var actualTime = +new Date;
|
||||
var checkEnd = actualTime - this._animateStartTime > this._parameters.duration;
|
||||
|
||||
// TODO: Bug for animatedGif for static rotation ? (to test)
|
||||
if (checkEnd && !this._parameters.animatedGif)
|
||||
{
|
||||
clearTimeout(this._timer);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (this._canvas||this._vimage||this._img) {
|
||||
var angle = this._parameters.easing(0, actualTime - this._animateStartTime, this._animateStartAngle, this._parameters.animateTo - this._animateStartAngle, this._parameters.duration);
|
||||
this._rotate((~~(angle*10))/10);
|
||||
}
|
||||
if (this._parameters.step) {
|
||||
this._parameters.step(this._angle);
|
||||
}
|
||||
var self = this;
|
||||
this._timer = setTimeout(function()
|
||||
{
|
||||
self._animate.call(self);
|
||||
}, 10);
|
||||
}
|
||||
|
||||
// To fix Bug that prevents using recursive function in callback I moved this function to back
|
||||
if (this._parameters.callback && checkEnd){
|
||||
this._angle = this._parameters.animateTo;
|
||||
this._rotate(this._angle);
|
||||
this._parameters.callback.call(this._rootObj);
|
||||
}
|
||||
},
|
||||
|
||||
_rotate : (function()
|
||||
{
|
||||
var rad = Math.PI/180;
|
||||
if (IE)
|
||||
return function(angle)
|
||||
{
|
||||
this._angle = angle;
|
||||
this._container.style.rotation=(angle%360)+"deg";
|
||||
this._vimage.style.top = -(this._rotationCenterY - this._imgHeight/2) + "px";
|
||||
this._vimage.style.left = -(this._rotationCenterX - this._imgWidth/2) + "px";
|
||||
this._container.style.top = this._rotationCenterY - this._imgHeight/2 + "px";
|
||||
this._container.style.left = this._rotationCenterX - this._imgWidth/2 + "px";
|
||||
|
||||
}
|
||||
else if (supportedCSS)
|
||||
return function(angle){
|
||||
this._angle = angle;
|
||||
this._img.style[supportedCSS]="rotate("+(angle%360)+"deg)";
|
||||
this._img.style[supportedCSSOrigin]=this._parameters.center.join(" ");
|
||||
}
|
||||
else
|
||||
return function(angle)
|
||||
{
|
||||
this._angle = angle;
|
||||
angle=(angle%360)* rad;
|
||||
// clear canvas
|
||||
this._canvas.width = this._width;//+this._widthAdd;
|
||||
this._canvas.height = this._height;//+this._heightAdd;
|
||||
|
||||
// REMEMBER: all drawings are read from backwards.. so first function is translate, then rotate, then translate, translate..
|
||||
this._cnv.translate(this._imgWidth*this._aspectW,this._imgHeight*this._aspectH); // at least center image on screen
|
||||
this._cnv.translate(this._rotationCenterX,this._rotationCenterY); // we move image back to its orginal
|
||||
this._cnv.rotate(angle); // rotate image
|
||||
this._cnv.translate(-this._rotationCenterX,-this._rotationCenterY); // move image to its center, so we can rotate around its center
|
||||
this._cnv.scale(this._aspectW,this._aspectH); // SCALE - if needed ;)
|
||||
this._cnv.drawImage(this._img, 0, 0); // First - we draw image
|
||||
}
|
||||
|
||||
})()
|
||||
}
|
||||
|
||||
if (IE)
|
||||
{
|
||||
Wilq32.PhotoEffect.prototype.createVMLNode=(function(){
|
||||
document.createStyleSheet().addRule(".rvml", "behavior:url(#default#VML)");
|
||||
try {
|
||||
!document.namespaces.rvml && document.namespaces.add("rvml", "urn:schemas-microsoft-com:vml");
|
||||
return function (tagName) {
|
||||
return document.createElement('<rvml:' + tagName + ' class="rvml">');
|
||||
};
|
||||
} catch (e) {
|
||||
return function (tagName) {
|
||||
return document.createElement('<' + tagName + ' xmlns="urn:schemas-microsoft.com:vml" class="rvml">');
|
||||
};
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
})(jQuery);
|
||||
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
object_maker.init(config_api.config_3dobject_holder, $("#objectHolder").width(), $("#objectHolder").width()/1.5);
|
||||
object_maker.animate();
|
||||
var ws;
|
||||
var graph;
|
||||
var chartData = [];
|
||||
var flight_dynamics = new flight_dynamics();
|
||||
|
||||
$("#window_size").slider({
|
||||
range: "min",
|
||||
value: 37,
|
||||
min: 10,
|
||||
max: 300,
|
||||
slide: function (event, ui) {
|
||||
$("#window_size_current_value").html($("#window_size").slider("value"));
|
||||
|
||||
}
|
||||
});
|
||||
$("#window_update").slider({
|
||||
range: "min",
|
||||
value: 234,
|
||||
min: 100,
|
||||
max: 1000,
|
||||
slide: function (event, ui) {
|
||||
$("#window_update_value").html($("#window_update").slider("value"));
|
||||
}
|
||||
});
|
||||
$("#replotting").click(function () {
|
||||
plotting.finishPlotting(function (status) {
|
||||
if (status) {
|
||||
plotting.initPlotting(function (status) {
|
||||
d3.select("#realtimechart").select("svg").remove();
|
||||
plotting.realtime_plotting("#realtimechart", "#range_min", "#range_max", "#window_update_value",
|
||||
600, $("#realtimechart").height(), "#window_size_current_value",
|
||||
'#plotting_attribute');
|
||||
});
|
||||
} else {
|
||||
$("#realtimechart").html("There is no data to plot");
|
||||
}
|
||||
});
|
||||
});
|
||||
$('.btn-minimize').click(function (e) {
|
||||
e.preventDefault();
|
||||
var $target = $(this).parent().parent().next('.box-content');
|
||||
if ($target.is(':visible')) {
|
||||
if ($(this).parent().attr('id') === "RealtimePlotting") {
|
||||
plotting.forceToRedraw(function (status) {
|
||||
d3.select("#realtimechart").select("svg").remove();
|
||||
plotting.realtime_plotting("#realtimechart", "#range_min", "#range_max", "#window_update_value",
|
||||
600, $("#realtimechart").height(), "#window_size_current_value",
|
||||
'#plotting_attribute');
|
||||
});
|
||||
}
|
||||
} else {
|
||||
}
|
||||
});
|
||||
$('.btn-minimize').parent().parent().next('.box-content').hide();
|
||||
|
||||
var webSocket;
|
||||
config_api.realtime_plotting_data_window["attitude"] = new Queue();
|
||||
var current_status = {};
|
||||
$(window).load(function () {
|
||||
var websocketUrl = $("#div-chart").data("websocketurl");
|
||||
connect(websocketUrl);
|
||||
});
|
||||
|
||||
$(window).unload(function () {
|
||||
disconnect();
|
||||
});
|
||||
|
||||
//websocket connection
|
||||
function connect(target) {
|
||||
if ('WebSocket' in window) {
|
||||
ws = new WebSocket(target);
|
||||
} else if ('MozWebSocket' in window) {
|
||||
ws = new MozWebSocket(target);
|
||||
} else {
|
||||
console.log('WebSocket is not supported by this browser.');
|
||||
}
|
||||
if (ws) {
|
||||
ws.onmessage = function (event) {
|
||||
var sender_message = event.data;
|
||||
sender_message = isJSON(sender_message);
|
||||
if (sender_message != null) {
|
||||
var droneStats = mapDroneStats(sender_message);
|
||||
flight_dynamics.processingMessage(droneStats);
|
||||
} else {
|
||||
console.log("Message has been corrupted.");
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function disconnect() {
|
||||
if (ws != null) {
|
||||
ws.close();
|
||||
ws = null;
|
||||
}
|
||||
}
|
||||
|
||||
function mapDroneStats(sender_message){
|
||||
var responseMessage = {
|
||||
"quatanium_val":[sender_message[5],sender_message[6],sender_message[7],sender_message[8]],
|
||||
"basicParam":{
|
||||
"velocity":[sender_message[9],sender_message[10],sender_message[11]],
|
||||
"global_location":[sender_message[12],sender_message[13],sender_message[14]],
|
||||
"battery_level":sender_message[15],
|
||||
"device_type":"drone",
|
||||
"battery_voltage": sender_message[16]
|
||||
}
|
||||
};
|
||||
return responseMessage;
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,131 @@
|
||||
/*
|
||||
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
droneRender.init(config.configDronePlaceholder, $("#objectHolder").width(), $("#objectHolder").width() / 1.5);
|
||||
droneRender.animate();
|
||||
var ws;
|
||||
var graph;
|
||||
var chartData = [];
|
||||
var flightDynamics = new flightDynamics();
|
||||
|
||||
$("#windowSize").slider({
|
||||
range: "min",
|
||||
value: 37,
|
||||
min: 10,
|
||||
max: 300,
|
||||
slide: function (event, ui) {
|
||||
$("#windowSizeCurrentValue").html($("#windowSize").slider("value"));
|
||||
|
||||
}
|
||||
});
|
||||
$("#windowUpdate").slider({
|
||||
range: "min",
|
||||
value: 234,
|
||||
min: 100,
|
||||
max: 1000,
|
||||
slide: function (event, ui) {
|
||||
$("#windowUpdateValue").html($("#windowUpdate").slider("value"));
|
||||
}
|
||||
});
|
||||
$("#replotting").click(function () {
|
||||
plotting.finishPlotting(function (status) {
|
||||
if (status) {
|
||||
plotting.initPlotting(function (status) {
|
||||
d3.select("#realtimeChart").select("svg").remove();
|
||||
plotting.realtimePlotting("#realtimeChart", "#rangeMin", "#rangeMax", "#windowUpdateValue",
|
||||
600, $("#realtimeChart").height(), "#windowSizeCurrentValue",
|
||||
'#plottingAttribute');
|
||||
});
|
||||
} else {
|
||||
$("#realtimeChart").html("There is no data to plot");
|
||||
}
|
||||
});
|
||||
});
|
||||
$('.btn-minimize').click(function (e) {
|
||||
e.preventDefault();
|
||||
var $target = $(this).parent().parent().next('.box-content');
|
||||
if ($target.is(':visible')) {
|
||||
if ($(this).parent().attr('id') === "RealtimePlotting") {
|
||||
plotting.forceToRedraw(function (status) {
|
||||
d3.select("#realtimeChart").select("svg").remove();
|
||||
plotting.realtimePlotting("#realtimeChart", "#rangeMin", "#rangeMax", "#windowUpdateValue",
|
||||
600, $("#realtimeChart").height(), "#windowSizeCurrentValue",
|
||||
'#plottingAttribute');
|
||||
});
|
||||
}
|
||||
} else {
|
||||
}
|
||||
});
|
||||
$('.btn-minimize').parent().parent().next('.box-content').hide();
|
||||
|
||||
config.realtimePlottingDataWindow["attitude"] = new Queue();
|
||||
var currentStatus = {};
|
||||
$(window).load(function () {
|
||||
var websocketUrl = $("#div-chart").data("websocketurl");
|
||||
connect(websocketUrl);
|
||||
});
|
||||
|
||||
$(window).unload(function () {
|
||||
disconnect();
|
||||
});
|
||||
|
||||
//websocket connection
|
||||
function connect(target) {
|
||||
if ('WebSocket' in window) {
|
||||
ws = new WebSocket(target);
|
||||
} else if ('MozWebSocket' in window) {
|
||||
ws = new MozWebSocket(target);
|
||||
} else {
|
||||
console.log('WebSocket is not supported by this browser.');
|
||||
}
|
||||
if (ws) {
|
||||
ws.onmessage = function (event) {
|
||||
var sender_message = event.data;
|
||||
sender_message = isJSON(sender_message);
|
||||
if (sender_message != null) {
|
||||
var droneStats = mapDroneStats(sender_message);
|
||||
flightDynamics.processingMessage(droneStats);
|
||||
} else {
|
||||
console.log("Message has been corrupted.");
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function disconnect() {
|
||||
if (ws != null) {
|
||||
ws.close();
|
||||
ws = null;
|
||||
}
|
||||
}
|
||||
|
||||
function mapDroneStats(sender_message) {
|
||||
var responseMessage = {
|
||||
"quatanium_val": [sender_message[5], sender_message[6], sender_message[7], sender_message[8]],
|
||||
"basicParam": {
|
||||
"velocity": [sender_message[9], sender_message[10], sender_message[11]],
|
||||
"global_location": [sender_message[12], sender_message[13], sender_message[14]],
|
||||
"battery_level": sender_message[15],
|
||||
"device_type": "drone",
|
||||
"battery_voltage": sender_message[16]
|
||||
}
|
||||
};
|
||||
return responseMessage;
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
$('.btn-minimize').click(function (e) {
|
||||
e.preventDefault();
|
||||
var $target = $(this).parent().parent().next('.box-content');
|
||||
if ($target.is(':visible')) {
|
||||
$('i', $(this)).removeClass('glyphicon-chevron-up').addClass('glyphicon-chevron-down');
|
||||
checkAndDisable($(this).parent().attr('id'));
|
||||
}
|
||||
else {
|
||||
$('i', $(this)).removeClass('glyphicon-chevron-down').addClass('glyphicon-chevron-up');
|
||||
checkAndEnable($(this).parent().attr('id'));
|
||||
}
|
||||
$target.slideToggle();
|
||||
});
|
||||
|
||||
function checkAndEnable(id) {
|
||||
if (id === "RealtimePlotting") {
|
||||
config.modulesStatus.realtimePlotting = true;
|
||||
}
|
||||
else if (id === "SensorReadings") {
|
||||
config.modulesStatus.sensorReadings = true;
|
||||
} else if (id === "Rotation") {
|
||||
config.modulesStatus.angleOfRotationv1 = true;
|
||||
} else if (id === "AngleOfRotation") {
|
||||
config.modulesStatus.angleOfRotationv2 = true;
|
||||
}
|
||||
}
|
||||
|
||||
function checkAndDisable(id) {
|
||||
if (id === "RealtimePlotting") {
|
||||
config.modulesStatus.realtimePlotting = false;
|
||||
}
|
||||
else if (id === "SensorReadings") {
|
||||
config.modulesStatus.sensorReadings = false;
|
||||
} else if (id === "Rotation") {
|
||||
config.modulesStatus.angleOfRotationv1 = false;
|
||||
} else if (id === "AngleOfRotation") {
|
||||
config.modulesStatus.angleOfRotationv2 = false;
|
||||
}
|
||||
}
|
||||
|
||||
function isJSON(data) {
|
||||
try {
|
||||
return JSON.parse(data);
|
||||
}
|
||||
catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function Queue() {
|
||||
var a = [], b = '';
|
||||
this.enqueue = function (b) {
|
||||
a.push([this.getLength() - 1 <= 0 ? 0 : this.getLength() - 1, b]);
|
||||
};
|
||||
this.dequeue = function () {
|
||||
if (0 != a.length) {
|
||||
var c = a[b];
|
||||
2 * ++b >= a.length && (a = a.slice(b), b = 0);
|
||||
return c
|
||||
}
|
||||
};
|
||||
this.getLength = function () {
|
||||
return a.length - b;
|
||||
};
|
||||
this.isEmpty = function () {
|
||||
return 0 == a.length;
|
||||
};
|
||||
this.peek = function () {
|
||||
return 0 < a.length ? a[b] : void 0
|
||||
};
|
||||
this.getData = function () {
|
||||
return a;
|
||||
};
|
||||
this.makeFixedSize = function (start, end) {
|
||||
a = a.slice(start, end);
|
||||
}
|
||||
}
|
||||
|
||||
$("#module_control button").click(function (index) {
|
||||
var url = config.drone_control;
|
||||
ajax_handler.ajaxRequest(url, config.controlType, {action: $(this).attr('id'), speed: 7, duration: 7},
|
||||
config.dataType, function (data, status) {
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
var flightDynamics = function () {
|
||||
var api = this;
|
||||
api.processingMessage = function (droneStats) {
|
||||
if (droneStats.basicParam.battery_level != undefined) {
|
||||
$("#batteryLevelPlaceholder").width(parseInt(droneStats.basicParam.battery_level) + "%");
|
||||
$("#batteryLevel").html(droneStats.basicParam.battery_level + "%");
|
||||
}
|
||||
if (droneStats.basicParam.battery_voltage != undefined) {
|
||||
$("#batteryVoltagePlaceholder").width(parseInt(droneStats.basicParam.battery_voltage) + "%");
|
||||
$("#batteryVoltage").html(droneStats.basicParam.battery_voltage + "%");
|
||||
}
|
||||
if (droneStats.quatanium_val != undefined) {
|
||||
currentStatus = droneRender.getHeadingAttitudeAndBank(droneStats.quatanium_val);
|
||||
droneRender.setHeadingAttitudeAndBank(currentStatus);
|
||||
$("#imageTop").animate({rotate: '' + (180 / Math.PI) * 2.890456 + 'deg'}, 2);
|
||||
}
|
||||
if (config.modulesStatus.angleOfRotationv1 || config.modulesStatus.angleOfRotationv2) {
|
||||
droneRender.setBank("#imageTop", currentStatus.bank);
|
||||
droneRender.setHeading("#imageBackSecond", currentStatus.heading);
|
||||
}
|
||||
if (config.modulesStatus.realtimePlotting) {
|
||||
if (currentStatus[$('#plottingAttribute').val()] != undefined) {
|
||||
plotting.pushData(currentStatus[$('#plottingAttribute').val()]);
|
||||
}
|
||||
}
|
||||
if (droneStats.basicParam != undefined) {
|
||||
if (droneStats.basicParam.velocity != undefined) {
|
||||
var velocity = droneStats.basicParam.velocity;
|
||||
if (velocity.length == 3) {
|
||||
$("#velocityx").html(velocity[0]);
|
||||
$("#velocityy").html(velocity[1]);
|
||||
$("#velocityz").html(velocity[2]);
|
||||
}
|
||||
} else {
|
||||
$("#velocityx").html(NaN);
|
||||
$("#velocityy").html(NaN);
|
||||
$("#velocityz").html(NaN);
|
||||
}
|
||||
if (droneStats.basicParam.global_location != undefined) {
|
||||
var location = droneStats.basicParam.global_location;
|
||||
if (location.length == 3) {
|
||||
$("#locationLog").html(location[0]);
|
||||
$("#locationAlt").html(location[1]);
|
||||
$("#locationLat").html(location[2]);
|
||||
}
|
||||
} else {
|
||||
$("#locationLog").html(NaN);
|
||||
$("#locationAlt").html(NaN);
|
||||
$("#locationLat").html(NaN);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
var flight_dynamics = function () {
|
||||
var api = this;
|
||||
api.processingMessage = function (sender_message) {
|
||||
if(sender_message.basicParam.battery_level!= undefined){
|
||||
|
||||
$("#battery_level_holder").width( parseInt(sender_message.basicParam.battery_level)+"%" );
|
||||
$("#battery_level").html(sender_message.basicParam.battery_level+"%");
|
||||
}
|
||||
if(sender_message.basicParam.battery_voltage!= undefined){
|
||||
|
||||
$("#battery_voltage_holder").width( parseInt(sender_message.basicParam.battery_voltage)+"%" );
|
||||
console.log(sender_message.basicParam.battery_voltage);
|
||||
$("#battery_voltage").html(sender_message.basicParam.battery_voltage+"%");
|
||||
}
|
||||
if (sender_message.quatanium_val != undefined) {
|
||||
current_status = object_maker.get_heading_attitude_bank(sender_message.quatanium_val);
|
||||
object_maker.set_heading_attitude_bank(current_status);
|
||||
$("#imageTop").animate({rotate: '' + (180 / Math.PI) * 2.890456 + 'deg'}, 2);
|
||||
}
|
||||
if (config_api.modules_status.angleOfRotation_2 || config_api.modules_status.angleOfRotation_1) {
|
||||
object_maker.set_bank("#imageTop", current_status.bank);
|
||||
object_maker.set_heading("#imageBackSecond", current_status.heading);
|
||||
|
||||
}
|
||||
if (config_api.modules_status.realtimePlotting) {
|
||||
if (current_status[$('#plotting_attribute').val()] != undefined) {
|
||||
plotting.pushData(current_status[$('#plotting_attribute').val()]);
|
||||
}
|
||||
}
|
||||
if (sender_message.basicParam != undefined) {
|
||||
if (sender_message.basicParam.velocity != undefined) {
|
||||
var velocity = sender_message.basicParam.velocity;
|
||||
if (velocity.length == 3) {
|
||||
$("#velocityx").html(velocity[0]);
|
||||
$("#velocityy").html(velocity[1]);
|
||||
$("#velocityz").html(velocity[2]);
|
||||
}
|
||||
} else {
|
||||
$("#velocityx").html(NaN);
|
||||
$("#velocityy").html(NaN);
|
||||
$("#velocityz").html(NaN);
|
||||
}
|
||||
if (sender_message.basicParam.global_location != undefined) {
|
||||
var location = sender_message.basicParam.global_location;
|
||||
if (location.length == 3) {
|
||||
$("#locationLog").html(location[0]);
|
||||
$("#locationAlt").html(location[1]);
|
||||
$("#locationLat").html(location[2]);
|
||||
}
|
||||
} else {
|
||||
$("#locationLog").html(NaN);
|
||||
$("#locationAlt").html(NaN);
|
||||
$("#locationLat").html(NaN);
|
||||
}
|
||||
}
|
||||
if (sender_message.battery_voltage != undefined) {
|
||||
$("#battery_voltage").html(sender_message.battery_voltage);
|
||||
} else {
|
||||
$("#battery_voltage").html(NaN);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
@ -0,0 +1,138 @@
|
||||
/*
|
||||
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
var plotting = function () {
|
||||
var api = this;
|
||||
api.isDone = false;
|
||||
api.currentValue = 0;
|
||||
api.finishPlotting = function (callBack) {
|
||||
api.isDone = true;
|
||||
callBack(true);
|
||||
},
|
||||
api.initPlotting = function (callback) {
|
||||
api.isDone = false;
|
||||
callback(true);
|
||||
},
|
||||
api.forceToRedraw = function (callback) {
|
||||
api.isDone == true;
|
||||
callback(true);
|
||||
},
|
||||
api.pushData = function (new_value) {
|
||||
api.currentValue = new_value;
|
||||
},
|
||||
api.realtimePlotting = function (placeholder, placeholderYMin, placeholderYMax, updateInterval, placeholderWidth,
|
||||
placeholderHeight, placeholderWindowSize, title) {
|
||||
$(placeholder).html();
|
||||
var initWindow = function () {
|
||||
return 0;
|
||||
}
|
||||
api.data = d3.range(parseInt($(placeholderWindowSize).html())).map(initWindow);
|
||||
var margin = {top: 20, right: 20, bottom: 20, left: 40},
|
||||
width = placeholderWidth - margin.left - margin.right,
|
||||
height = placeholderHeight - margin.top - margin.bottom;
|
||||
var x = d3.scale.linear()
|
||||
.domain([1, parseInt($(placeholderWindowSize).html()) - 2])
|
||||
.range([0, width]);
|
||||
|
||||
var y = d3.scale.linear()
|
||||
.domain([parseInt($(placeholderYMin).val()), parseInt($(placeholderYMax).val())])
|
||||
.range([height, 0]);
|
||||
var line = d3.svg.line()
|
||||
.interpolate("basis")
|
||||
.x(function (d, i) {
|
||||
return x(i);
|
||||
})
|
||||
.y(function (d, i) {
|
||||
return y(d);
|
||||
});
|
||||
|
||||
var svg = d3.select(placeholder).append("svg")
|
||||
.attr("width", width + margin.left + margin.right)
|
||||
.attr("height", height + margin.top + margin.bottom)
|
||||
.append("g")
|
||||
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
|
||||
svg.append("defs").append("clipPath")
|
||||
.attr("id", "clip")
|
||||
.append("rect")
|
||||
.attr("width", width)
|
||||
.attr("height", height);
|
||||
var axis_x = svg.append("g")
|
||||
.attr("class", "x_axis")
|
||||
.attr("transform", "translate(0," + y(0) + ")")
|
||||
.call(d3.svg.axis().scale(x).orient("bottom"));
|
||||
var axis_y = svg.append("g")
|
||||
.attr("class", "y_axis")
|
||||
.call(d3.svg.axis().scale(y).orient("left"));
|
||||
var path = svg.append("g")
|
||||
.attr("clip-path", "url(#clip)")
|
||||
.append("path")
|
||||
.datum(api.data)
|
||||
.attr("class", "line")
|
||||
.attr("d", line);
|
||||
svg.append("text")
|
||||
.attr("class", "yaxis_label")
|
||||
.attr("transform", "rotate(-90)")
|
||||
.attr("y", 0 - margin.left - 4)
|
||||
.attr("x", (0 - (height / 2)))
|
||||
.attr("dy", "1em")
|
||||
.style("text-anchor", "middle")
|
||||
.text($(title).val());
|
||||
svg.append("text")
|
||||
.attr("class", "xaixs_label")
|
||||
.attr("transform",
|
||||
"translate(" + (width / 2) + " ," +
|
||||
(height + margin.bottom) + ")")
|
||||
.style("text-anchor", "middle")
|
||||
.text("Window Size");
|
||||
svg.append("text")
|
||||
.attr("class", "title_label")
|
||||
.attr("x", (width / 2))
|
||||
.attr("y", 0 - (margin.top / 4))
|
||||
.attr("text-anchor", "middle")
|
||||
.style("font-size", "16px")
|
||||
.style("text-decoration", "underline")
|
||||
.text($(title).val() + " variation within last " + $(placeholderWindowSize).html() + " frames");
|
||||
|
||||
updateAgain();
|
||||
|
||||
function updateAgain() {
|
||||
if (api.isDone)return;
|
||||
api.data.push(api.currentValue);
|
||||
path.attr("d", line)
|
||||
.attr("transform", null)
|
||||
.transition()
|
||||
.duration($(updateInterval).html())
|
||||
.ease("linear")
|
||||
.attr("transform", "translate(" + x(0) + ",0)")
|
||||
.each("end", updateAgain);
|
||||
api.data.shift();
|
||||
}
|
||||
|
||||
function rescale() {
|
||||
y.domain([parseInt($(placeholderYMin).val()), parseInt($(placeholderYMax).val())])
|
||||
svg.select(".title_label")
|
||||
.text($(title).val() + " variation within last " + $(placeholderWindowSize).html() + " frames");
|
||||
svg.select(".yaxis_label")
|
||||
.text($(title).val());
|
||||
}
|
||||
|
||||
$("#plottingAttribute").change(function () {
|
||||
rescale();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
var plotting = function () {
|
||||
var api = this;
|
||||
api.isDone = false;
|
||||
api.current_value = 0;
|
||||
api.finishPlotting = function (callBack) {
|
||||
api.isDone = true;
|
||||
callBack(true);
|
||||
},
|
||||
api.initPlotting = function (callback) {
|
||||
api.isDone = false;
|
||||
callback(true);
|
||||
},
|
||||
api.forceToRedraw = function (callback) {
|
||||
api.isDone == true;
|
||||
callback(true);
|
||||
},
|
||||
api.pushData = function (new_value) {
|
||||
api.current_value = new_value;
|
||||
},
|
||||
api.realtime_plotting = function (holder, y_min_hollder, y_max_holder, update_interval_holder, holder_width,
|
||||
holder_height, window_size_holder, title) {
|
||||
$(holder).html();
|
||||
var init_window = function () {
|
||||
return 0;
|
||||
}
|
||||
api.data = d3.range(parseInt($(window_size_holder).html())).map(init_window);
|
||||
var margin = {top: 20, right: 20, bottom: 20, left: 40},
|
||||
width = holder_width - margin.left - margin.right,
|
||||
height = holder_height - margin.top - margin.bottom;
|
||||
var x = d3.scale.linear()
|
||||
.domain([1, parseInt($(window_size_holder).html()) - 2])
|
||||
.range([0, width]);
|
||||
|
||||
var y = d3.scale.linear()
|
||||
.domain([parseInt($(y_min_hollder).val()), parseInt($(y_max_holder).val())])
|
||||
.range([height, 0]);
|
||||
var line = d3.svg.line()
|
||||
.interpolate("basis")
|
||||
.x(function (d, i) {
|
||||
return x(i);
|
||||
})
|
||||
.y(function (d, i) {
|
||||
return y(d);
|
||||
});
|
||||
|
||||
var svg = d3.select(holder).append("svg")
|
||||
.attr("width", width + margin.left + margin.right)
|
||||
.attr("height", height + margin.top + margin.bottom)
|
||||
.append("g")
|
||||
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
|
||||
svg.append("defs").append("clipPath")
|
||||
.attr("id", "clip")
|
||||
.append("rect")
|
||||
.attr("width", width)
|
||||
.attr("height", height);
|
||||
var axis_x = svg.append("g")
|
||||
.attr("class", "x_axis")
|
||||
.attr("transform", "translate(0," + y(0) + ")")
|
||||
.call(d3.svg.axis().scale(x).orient("bottom"));
|
||||
var axis_y = svg.append("g")
|
||||
.attr("class", "y_axis")
|
||||
.call(d3.svg.axis().scale(y).orient("left"));
|
||||
var path = svg.append("g")
|
||||
.attr("clip-path", "url(#clip)")
|
||||
.append("path")
|
||||
.datum(api.data)
|
||||
.attr("class", "line")
|
||||
.attr("d", line);
|
||||
svg.append("text")
|
||||
.attr("class", "yaxis_label")
|
||||
.attr("transform", "rotate(-90)")
|
||||
.attr("y", 0 - margin.left - 4)
|
||||
.attr("x", (0 - (height / 2)))
|
||||
.attr("dy", "1em")
|
||||
.style("text-anchor", "middle")
|
||||
.text($(title).val());
|
||||
svg.append("text")
|
||||
.attr("class", "xaixs_label")
|
||||
.attr("transform",
|
||||
"translate(" + (width / 2) + " ," +
|
||||
(height + margin.bottom) + ")")
|
||||
.style("text-anchor", "middle")
|
||||
.text("Window Size");
|
||||
svg.append("text")
|
||||
.attr("class", "title_label")
|
||||
.attr("x", (width / 2))
|
||||
.attr("y", 0 - (margin.top / 4))
|
||||
.attr("text-anchor", "middle")
|
||||
.style("font-size", "16px")
|
||||
.style("text-decoration", "underline")
|
||||
.text($(title).val() + " variation within last " + $(window_size_holder).html() + " frames");
|
||||
|
||||
updateAgain();
|
||||
|
||||
function updateAgain() {
|
||||
if (api.isDone)return;
|
||||
api.data.push(api.current_value);
|
||||
path
|
||||
.attr("d", line)
|
||||
.attr("transform", null)
|
||||
.transition()
|
||||
.duration($(update_interval_holder).html())
|
||||
.ease("linear")
|
||||
.attr("transform", "translate(" + x(0) + ",0)")
|
||||
.each("end", updateAgain);
|
||||
api.data.shift();
|
||||
}
|
||||
|
||||
function rescale() {
|
||||
y.domain([parseInt($(y_min_hollder).val()), parseInt($(y_max_holder).val())])
|
||||
|
||||
svg.select(".title_label")
|
||||
.text($(title).val() + " variation within last " + $(window_size_holder).html() + " frames");
|
||||
svg.select(".yaxis_label")
|
||||
.text($(title).val());
|
||||
|
||||
}
|
||||
|
||||
function rescale_x() {
|
||||
x.domain([1, parseInt($(window_size_holder).html()) - 2]).range([0, width])
|
||||
svg.select(".x_axis").transition().call(axis_x);
|
||||
}
|
||||
|
||||
$("#plotting_attribute").change(function () {
|
||||
rescale();
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
|
||||
{
|
||||
"deviceType": {
|
||||
"label": "drone",
|
||||
"category": "virtual",
|
||||
"downloadAgentUri": "drone/device/download"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 112 KiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 148 KiB |
|
After Width: | Height: | Size: 48 KiB |
@ -0,0 +1,183 @@
|
||||
/*
|
||||
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
|
||||
var modalPopup = ".wr-modalpopup";
|
||||
var modalPopupContainer = modalPopup + " .modalpopup-container";
|
||||
var modalPopupContent = modalPopup + " .modalpopup-content";
|
||||
var body = "body";
|
||||
|
||||
/*
|
||||
* set popup maximum height function.
|
||||
*/
|
||||
function setPopupMaxHeight() {
|
||||
$(modalPopupContent).css('max-height', ($(body).height() - ($(body).height() / 100 * 30)));
|
||||
$(modalPopupContainer).css('margin-top', (-($(modalPopupContainer).height() / 2)));
|
||||
}
|
||||
|
||||
/*
|
||||
* show popup function.
|
||||
*/
|
||||
function showPopup() {
|
||||
$(modalPopup).show();
|
||||
setPopupMaxHeight();
|
||||
$('#downloadForm').validate({
|
||||
rules: {
|
||||
deviceName: {
|
||||
minlength: 4,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
highlight: function (element) {
|
||||
$(element).closest('.control-group').removeClass('success').addClass('error');
|
||||
},
|
||||
success: function (element) {
|
||||
$(element).closest('.control-group').removeClass('error').addClass('success');
|
||||
$('label[for=deviceName]').remove();
|
||||
}
|
||||
});
|
||||
var deviceType = "";
|
||||
$('.deviceType').each(function () {
|
||||
if (this.value != "") {
|
||||
deviceType = this.value;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* hide popup function.
|
||||
*/
|
||||
function hidePopup() {
|
||||
$('label[for=deviceName]').remove();
|
||||
$('.control-group').removeClass('success').removeClass('error');
|
||||
$(modalPopupContent).html('');
|
||||
$(modalPopup).hide();
|
||||
}
|
||||
|
||||
/*
|
||||
* DOM ready functions.
|
||||
*/
|
||||
$(document).ready(function () {
|
||||
attachEvents();
|
||||
});
|
||||
|
||||
function attachEvents() {
|
||||
/**
|
||||
* Following click function would execute
|
||||
* when a user clicks on "Download" link
|
||||
* on Device Management page in WSO2 DC Console.
|
||||
*/
|
||||
$("a.download-link").click(function () {
|
||||
var sketchType = $(this).data("sketchtype");
|
||||
var deviceType = $(this).data("devicetype");
|
||||
var downloadDeviceAPI = "/devicemgt/api/devices/sketch/generate_link";
|
||||
var payload = {"sketchType": sketchType, "deviceType": deviceType};
|
||||
$(modalPopupContent).html($('#download-device-modal-content').html());
|
||||
showPopup();
|
||||
var deviceName;
|
||||
$("a#download-device-download-link").click(function () {
|
||||
$('.new-device-name').each(function () {
|
||||
if (this.value != "") {
|
||||
deviceName = this.value;
|
||||
}
|
||||
});
|
||||
$('label[for=deviceName]').remove();
|
||||
if (deviceName && deviceName.length >= 4) {
|
||||
payload.deviceName = deviceName;
|
||||
invokerUtil.post(
|
||||
downloadDeviceAPI,
|
||||
payload,
|
||||
function (data, textStatus, jqxhr) {
|
||||
doAction(data);
|
||||
},
|
||||
function (data) {
|
||||
doAction(data);
|
||||
}
|
||||
);
|
||||
}else if(deviceName){
|
||||
$('.controls').append('<label for="deviceName" generated="true" class="error" ' +
|
||||
'style="display: inline-block;">Please enter at least 4 ' +
|
||||
'characters.</label>');
|
||||
$('.control-group').removeClass('success').addClass('error');
|
||||
} else {
|
||||
$('.controls').append('<label for="deviceName" generated="true" class="error" ' +
|
||||
'style="display: inline-block;">This field is required.' +
|
||||
'</label>');
|
||||
$('.control-group').removeClass('success').addClass('error');
|
||||
}
|
||||
});
|
||||
|
||||
$("a#download-device-cancel-link").click(function () {
|
||||
hidePopup();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function downloadAgent() {
|
||||
var deviceName;
|
||||
$('.new-device-name').each(function () {
|
||||
if (this.value != "") {
|
||||
deviceName = this.value;
|
||||
}
|
||||
});
|
||||
var deviceNameFormat = /^[^~?!#$:;%^*`+={}\[\]\\()|<>,'"]{1,30}$/;
|
||||
if (deviceName && deviceNameFormat.test(deviceName)) {
|
||||
$('#downloadForm').submit();
|
||||
hidePopup();
|
||||
$(modalPopupContent).html($('#device-agent-downloading-content').html());
|
||||
showPopup();
|
||||
setTimeout(function () {
|
||||
hidePopup();
|
||||
}, 1000);
|
||||
}else {
|
||||
$("#invalid-username-error-msg span").text("Invalid device name");
|
||||
$("#invalid-username-error-msg").removeClass("hidden");
|
||||
}
|
||||
}
|
||||
|
||||
function doAction(data) {
|
||||
//if it is saml redirection response
|
||||
if (data.status == null) {
|
||||
document.write(data);
|
||||
}
|
||||
|
||||
if (data.status == "200") {
|
||||
$(modalPopupContent).html($('#download-device-modal-content-links').html());
|
||||
$("input#download-device-url").val(data.responseText);
|
||||
$("input#download-device-url").focus(function () {
|
||||
$(this).select();
|
||||
});
|
||||
showPopup();
|
||||
} else if (data.status == "401") {
|
||||
$(modalPopupContent).html($('#device-401-content').html());
|
||||
$("#device-401-link").click(function () {
|
||||
window.location = "/devicemgt/login";
|
||||
});
|
||||
showPopup();
|
||||
} else if (data == "403") {
|
||||
$(modalPopupContent).html($('#device-403-content').html());
|
||||
$("#device-403-link").click(function () {
|
||||
window.location = "/devicemgt/login";
|
||||
});
|
||||
showPopup();
|
||||
} else {
|
||||
$(modalPopupContent).html($('#device-unexpected-error-content').html());
|
||||
$("a#device-unexpected-error-link").click(function () {
|
||||
hidePopup();
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,329 @@
|
||||
<div class="col-lg-12 margin-top-double">
|
||||
<h1 class="grey ">Drone Analyzer</h1>
|
||||
<hr>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-12 col-sm-4 col-md-4 col-lg-4 padding-top">
|
||||
<img src="{{@unit.publicUri}}/images/drone-icon.png" class="img-responsive">
|
||||
</div>
|
||||
|
||||
<div class="col-xs-12 col-sm-8 col-md-8 col-lg-8 padding-top">
|
||||
<h4 class="doc-link">Click <a href="https://docs.wso2.com/pages/viewpage.action?pageId=48289181"
|
||||
target="_blank">[ here ]</a> for latest instructions and
|
||||
troubleshooting.</h4>
|
||||
</div>
|
||||
|
||||
<div class="col-xs-12 col-sm-8 col-md-8 col-lg-8 padding-top">
|
||||
<h3 class="uppercase">What it Does</h3>
|
||||
<hr>
|
||||
<p class="grey margin-top">Connect an
|
||||
<a href="https://store.3drobotics.com/products/iris" target="_blank">[IRIS+]</a> Drone to
|
||||
WSO2 IoT Server and visualize statistics.
|
||||
</p>
|
||||
<br>
|
||||
|
||||
<h3 class="uppercase">What You Need</h3>
|
||||
<hr>
|
||||
<ul class="list-unstyled">
|
||||
<li class="padding-top-double">
|
||||
<span class="circle">ITEM 01</span>
|
||||
IRIS+ Drone.
|
||||
</li>
|
||||
<li class="padding-top-double">
|
||||
<span class="circle">ITEM 02</span>
|
||||
USB to Micro USB cable or Telemetry Radio receiver.
|
||||
</li>
|
||||
<li class="padding-top-double">
|
||||
<span class="circle">STEP 03</span>
|
||||
Proceed to [Prepare] section.
|
||||
</ul>
|
||||
<br>
|
||||
<a href="/api-store/apis/info?name={{@uriParams.deviceType}}&version=1.0.0&provider=admin"
|
||||
class="btn-operations"
|
||||
target="_blank"><i class="fw fw-api"></i> View API</i>
|
||||
</a>
|
||||
<a href="#" class="download-link btn-operations">
|
||||
<i class="fw fw-download"></i>Download Agent
|
||||
</a>
|
||||
<div id="download-device-modal-content" class="hide">
|
||||
<div class="modal-content">
|
||||
<div class="row">
|
||||
<div class="col-md-7 center-container">
|
||||
<h3>Name your device and download the agent from following link.</h3>
|
||||
<br/>
|
||||
<form id="downloadForm" method="GET"
|
||||
action="{{@app.context}}/api/devices/sketch/download">
|
||||
<div id="invalid-username-error-msg" class="alert alert-danger hidden"
|
||||
role="alert">
|
||||
<i class="icon fw fw-error"></i><span></span>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<div class="controls">
|
||||
<input class="new-device-name" style="color:#3f3f3f;padding:5px"
|
||||
type="text"
|
||||
placeholder="Ex. drone"
|
||||
name="deviceName" size="60" required>
|
||||
<br/>
|
||||
<input type="hidden" class="deviceType" name="deviceType"
|
||||
value="drone"/>
|
||||
<input type="hidden" class="sketchType" name="sketchType"
|
||||
value="drone"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons" style="padding-bottom: 0px">
|
||||
<a class="btn btn-operations" onclick="downloadAgent()">Download Now</a>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="device-agent-downloading-content" class="hide">
|
||||
<div class="modal-content">
|
||||
<div class="row">
|
||||
<div class="col-md-7 col-centered center-container">
|
||||
<h3>Device Agent will downloading shortly.</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="device-400-content" class="hide">
|
||||
<div class="modal-content">
|
||||
<div class="row">
|
||||
<div class="col-md-7 col-centered center-container">
|
||||
<h3>Exception at backend. Try Later.</h3>
|
||||
<div class="buttons">
|
||||
<a href="#" id="device-400-link" class="btn-operations">
|
||||
OK
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="device-401-content" class="hide">
|
||||
<div class="modal-content">
|
||||
<div class="row">
|
||||
<div class="col-md-7 col-centered center-container">
|
||||
<h3>You have to log in first.</h3><br/>
|
||||
<div class="buttons">
|
||||
<a href="#" id="device-401-link" class="blue-button">
|
||||
Goto Login Page
|
||||
</a>
|
||||
<a href="#" onclick="hidePopup();" class="btn-operations">
|
||||
Cancel
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="device-403-content" class="hide">
|
||||
<div class="modal-content">
|
||||
<div class="row">
|
||||
<div class="col-md-7 col-centered center-container">
|
||||
<h3>Action not permitted.</h3><br/>
|
||||
<div class="buttons">
|
||||
<a href="#" id="device-403-link" class="btn-operations">
|
||||
OK
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="device-409-content" class="hide">
|
||||
<div class="modal-content">
|
||||
<div class="row">
|
||||
<div class="col-md-7 col-centered center-container">
|
||||
<h3>Device Sketch does not exist.</h3><br/>
|
||||
<div class="buttons">
|
||||
<a href="#" id="device-409-link" class="btn-operations">
|
||||
OK
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="device-unexpected-error-content" class="hide">
|
||||
<div class="modal-content">
|
||||
<div class="row">
|
||||
<div class="col-md-7 col-centered center-container">
|
||||
<h3>Unexpected error.</h3><br/>
|
||||
<div class="buttons">
|
||||
<a href="#" id="device-unexpected-error-link" class="btn-operations">
|
||||
OK
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br/><br/>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 padding-double grey-bg">
|
||||
<h3 class="uppercase">Prepare</h3>
|
||||
<hr>
|
||||
<ul class="list-unstyled">
|
||||
<li class="padding-top-double">
|
||||
<span class="circle">01</span>
|
||||
Connect your IRIS+ Drone to your computer using either USB to Micro
|
||||
USB cable or Telemetry Radio receiver.
|
||||
</li>
|
||||
<li class="padding-top-double">
|
||||
<span class="circle">02</span>
|
||||
Click on [Download Agent] button above to get IRIS+ Drone agent.
|
||||
</li>
|
||||
<li class="padding-top-double">
|
||||
<span class="circle">03</span>
|
||||
Once you have downloaded the agent please run
|
||||
<i>"[startService.sh]"</i> script with root privileges.
|
||||
</li>
|
||||
<li class="padding-top-double">
|
||||
<span class="circle">04</span>
|
||||
Then you will be prompted to enter time interval (in seconds) between
|
||||
successive Data-Pushes to the XMPP server, connection target and communication speed. So
|
||||
below table will help you to find what is the correct connection target and
|
||||
communication speed.
|
||||
</li>
|
||||
<div class="padding-top-double">
|
||||
<table class="table table-bordered ">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<center><i>CONNECTION-TYPE</i></center>
|
||||
</th>
|
||||
<th>
|
||||
<center><i>CONNECTION-STRING</i></center>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Linux computer connected to the vehicle via USB</td>
|
||||
<td> e.g. /dev/ttyUSB0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Linux computer connected to the drone via Serial port</td>
|
||||
<td> e.g. /dev/ttyAMA0 <i>(also set baud=57600)</i></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>OSX computer connected to the drone via USB</td>
|
||||
<td>e.g. dev/cu.usbmodem1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Windows computer connected to the drone via USB (in this case on COM14)</td>
|
||||
<td>e.g. com14</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Windows computer connected to the drone using a 3DR Telemetry Radio on
|
||||
COM14
|
||||
</td>
|
||||
<td>e.g. com14 <i>(also set baud=57600)</i></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</ul>
|
||||
<br>
|
||||
<br>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-6 col-md-6 col-lg-6 padding-double">
|
||||
<h3 class="uppercase">IRIS+ Drone Connected to a computer</h3>
|
||||
<hr>
|
||||
<p class="grey margin-top">Click on the image to zoom</p>
|
||||
<center>
|
||||
<a href="{{@unit.publicUri}}/images/schematicsGuide.png" target="_blank">
|
||||
<img src="{{@unit.publicUri}}/images/schematicsGuide.png" class="img-responsive">
|
||||
</a>
|
||||
</center>
|
||||
<br/>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-6 col-md-6 col-lg-6 padding-double">
|
||||
<h3 class="uppercase">Try Out</h3>
|
||||
<hr>
|
||||
<ul class="list-unstyled">
|
||||
<li class="padding-top-double">
|
||||
<span class="circle">01</span>
|
||||
You can view all your connected devices at
|
||||
<a href="{{@app.context}}/devices">[Device Management]</a> page.
|
||||
</li>
|
||||
<li class="padding-top-double">
|
||||
<span class="circle">02</span>
|
||||
Select one of connected devices and view stats which are published by
|
||||
the device.
|
||||
</li>
|
||||
</ul>
|
||||
<br/>
|
||||
<p class="grey margin-top">Click on the image to zoom</p>
|
||||
<center>
|
||||
<a href="{{@unit.publicUri}}/images/devices_analytics.png" target="_blank">
|
||||
<img src="{{@unit.publicUri}}/images/devices_analytics.png" class="img-responsive">
|
||||
</a>
|
||||
</center>
|
||||
</div>
|
||||
|
||||
{{#zone "topCss"}}
|
||||
<style type="text/css">
|
||||
.circle {
|
||||
background: none repeat scroll 0 0 #191919;
|
||||
border-radius: 50px;
|
||||
height: 50px;
|
||||
padding: 10px;
|
||||
width: 50px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.padding-top-double {
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.padding-double {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.grey {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
hr {
|
||||
display: block;
|
||||
height: 1px;
|
||||
border: 0;
|
||||
border-top: 1px solid #7f7f7f;
|
||||
margin: 1em 0;
|
||||
padding: 0;
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.light-grey {
|
||||
color: #7c7c7c;
|
||||
}
|
||||
|
||||
.uppercase {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.grey-bg {
|
||||
background-color: #f6f4f4;
|
||||
}
|
||||
|
||||
.doc-link {
|
||||
background: #11375B;
|
||||
padding: 20px;
|
||||
color: white;
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.doc-link a {
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
{{/zone}}
|
||||
|
||||
{{#zone "bottomJs"}}
|
||||
{{js "/js/download.js"}}
|
||||
{{js "/js/jquery.validate.js"}}
|
||||
{{/zone}}
|
||||
@ -0,0 +1,3 @@
|
||||
{
|
||||
"version": "1.0.0"
|
||||
}
|
||||
@ -0,0 +1,315 @@
|
||||
<div class="col-lg-12 margin-top-double">
|
||||
<h1 class="grey ">Drone Analyzer</h1>
|
||||
<hr>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-4 col-md-4 col-lg-4 padding-top">
|
||||
<img src="{{@unit.publicUri}}/images/drone-icon.png" class="img-responsive">
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-8 col-md-8 col-lg-8 padding-top">
|
||||
<h4 class="doc-link">Click <a href="https://docs.wso2.com/pages/viewpage.action?pageId=48289181"
|
||||
target="_blank">[ here ]</a> for latest instructions and
|
||||
troubleshooting.</h4>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-8 col-md-8 col-lg-8 padding-top">
|
||||
<h3 class="uppercase">What it Does</h3>
|
||||
<hr>
|
||||
<p class="grey margin-top">Connect an
|
||||
<a href="https://store.3drobotics.com/products/iris" target="_blank">[IRIS+]</a> Drone to
|
||||
WSO2 IoT Server and visualize statistics.
|
||||
</p>
|
||||
<br>
|
||||
|
||||
<h3 class="uppercase">What You Need</h3>
|
||||
<hr>
|
||||
<ul class="list-unstyled">
|
||||
<li class="padding-top-double">
|
||||
<span class="circle">ITEM 01</span>
|
||||
IRIS+ Drone.
|
||||
</li>
|
||||
<li class="padding-top-double">
|
||||
<span class="circle">ITEM 02</span>
|
||||
USB to Micro USB cable or Telemetry Radio receiver.
|
||||
</li>
|
||||
<li class="padding-top-double">
|
||||
<span class="circle">STEP 03</span>
|
||||
Proceed to [Prepare] section.
|
||||
</ul>
|
||||
<br>
|
||||
<a href="/api-store/apis/info?name={{@uriParams.deviceType}}&version=1.0.0&provider=admin"
|
||||
class="btn-operations"
|
||||
target="_blank"><i class="fw fw-api"></i> View API</i> </a>
|
||||
<a href="#" class="download-link btn-operations">
|
||||
<i class="fw fw-download"></i>Download Agent
|
||||
</a>
|
||||
<div id="download-device-modal-content" class="hide">
|
||||
<div class="modal-content">
|
||||
<div class="row">
|
||||
<div class="col-md-7 center-container">
|
||||
<h3>Name your device and download the agent from following link.</h3>
|
||||
<br/>
|
||||
<form id="downloadForm" method="GET"
|
||||
action="{{@app.context}}/api/devices/sketch/download">
|
||||
<div id="invalid-username-error-msg" class="alert alert-danger hidden"
|
||||
role="alert">
|
||||
<i class="icon fw fw-error"></i><span></span>
|
||||
</div>
|
||||
<div class="control-group">
|
||||
<div class="controls">
|
||||
<input class="new-device-name" style="color:#3f3f3f;padding:5px"
|
||||
type="text"
|
||||
placeholder="Ex. drone"
|
||||
name="deviceName" size="60" required>
|
||||
<br/>
|
||||
<input type="hidden" class="deviceType" name="deviceType"
|
||||
value="drone_analyzer"/>
|
||||
<input type="hidden" class="sketchType" name="sketchType"
|
||||
value="drone_analyzer"/>
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
<div class="buttons">
|
||||
<a class="btn-operations" onclick="downloadAgent()">Download Now</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="device-agent-downloading-content" class="hide">
|
||||
<div class="modal-content">
|
||||
<div class="row">
|
||||
<div class="col-md-7 col-centered center-container">
|
||||
<h3>Device Agent will downloading shortly.</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="device-400-content" class="hide">
|
||||
<div class="modal-content">
|
||||
<div class="row">
|
||||
<div class="col-md-7 col-centered center-container">
|
||||
<h3>Exception at backend. Try Later.</h3>
|
||||
<div class="buttons">
|
||||
<a href="#" id="device-400-link" class="btn-operations">
|
||||
OK
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="device-401-content" class="hide">
|
||||
<div class="modal-content">
|
||||
<div class="row">
|
||||
<div class="col-md-7 col-centered center-container">
|
||||
<h3>You have to log in first.</h3><br/>
|
||||
<div class="buttons">
|
||||
<a href="#" id="device-401-link" class="blue-button">
|
||||
Goto Login Page
|
||||
</a>
|
||||
<a href="#" onclick="hidePopup();" class="btn-operations">
|
||||
Cancel
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="device-403-content" class="hide">
|
||||
<div class="modal-content">
|
||||
<div class="row">
|
||||
<div class="col-md-7 col-centered center-container">
|
||||
<h3>Action not permitted.</h3><br/>
|
||||
<div class="buttons">
|
||||
<a href="#" id="device-403-link" class="btn-operations">
|
||||
OK
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="device-409-content" class="hide">
|
||||
<div class="modal-content">
|
||||
<div class="row">
|
||||
<div class="col-md-7 col-centered center-container">
|
||||
<h3>Device Sketch does not exist.</h3><br/>
|
||||
<div class="buttons">
|
||||
<a href="#" id="device-409-link" class="btn-operations">
|
||||
OK
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="device-unexpected-error-content" class="hide">
|
||||
<div class="modal-content">
|
||||
<div class="row">
|
||||
<div class="col-md-7 col-centered center-container">
|
||||
<h3>Unexpected error.</h3><br/>
|
||||
<div class="buttons">
|
||||
<a href="#" id="device-unexpected-error-link" class="btn-operations">
|
||||
OK
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br/><br/>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12 padding-double grey-bg">
|
||||
<h3 class="uppercase">Prepare</h3>
|
||||
<hr>
|
||||
<ul class="list-unstyled">
|
||||
<li class="padding-top-double">
|
||||
<span class="circle">01</span>
|
||||
Connect your IRIS+ Drone to your computer using either USB to Micro
|
||||
USB cable or Telemetry Radio receiver.
|
||||
</li>
|
||||
<li class="padding-top-double">
|
||||
<span class="circle">02</span>
|
||||
Click on [Download Agent] button above to get IRIS+ Drone agent.
|
||||
</li>
|
||||
<li class="padding-top-double">
|
||||
<span class="circle">03</span>
|
||||
Once you have downloaded the agent please run
|
||||
<i>"[startService.sh]"</i> script with root privileges.
|
||||
</li>
|
||||
<li class="padding-top-double">
|
||||
<span class="circle">04</span>
|
||||
Then you will be prompted to enter time interval (in seconds) between
|
||||
successive Data-Pushes to the XMPP server, connection target and communication speed. So
|
||||
below table will help you to find what is the correct connection target and
|
||||
communication speed.
|
||||
</li>
|
||||
<div class="padding-top-double">
|
||||
<table class="table table-bordered ">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<center><i>CONNECTION-TYPE</i></center>
|
||||
</th>
|
||||
<th>
|
||||
<center><i>CONNECTION-STRING</i></center>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Linux computer connected to the vehicle via USB</td>
|
||||
<td> e.g. /dev/ttyUSB0</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Linux computer connected to the drone via Serial port</td>
|
||||
<td> e.g. /dev/ttyAMA0 <i>(also set baud=57600)</i></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>OSX computer connected to the drone via USB</td>
|
||||
<td>e.g. dev/cu.usbmodem1</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Windows computer connected to the drone via USB (in this case on COM14)</td>
|
||||
<td>e.g. com14</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Windows computer connected to the drone using a 3DR Telemetry Radio on
|
||||
COM14
|
||||
</td>
|
||||
<td>e.g. com14 <i>(also set baud=57600)</i></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</ul>
|
||||
<br>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-6 col-md-6 col-lg-6 padding-double">
|
||||
<h3 class="uppercase">IRIS+ Drone Connected to a computer</h3>
|
||||
<hr>
|
||||
<p class="grey margin-top">Click on the image to zoom</p>
|
||||
<center>
|
||||
<a href="{{@unit.publicUri}}/images/schematicsGuide.png" target="_blank">
|
||||
<img src="{{@unit.publicUri}}/images/schematicsGuide.png" class="img-responsive">
|
||||
</a>
|
||||
</center>
|
||||
<br/>
|
||||
</div>
|
||||
<div class="col-xs-12 col-sm-6 col-md-6 col-lg-6 padding-double">
|
||||
<h3 class="uppercase">Try Out</h3>
|
||||
<hr>
|
||||
<ul class="list-unstyled">
|
||||
<li class="padding-top-double">
|
||||
<span class="circle">01</span>
|
||||
You can view all your connected devices at
|
||||
<a href="{{@app.context}}/devices">[Device Management]</a> page.
|
||||
</li>
|
||||
<li class="padding-top-double">
|
||||
<span class="circle">02</span>
|
||||
Select one of connected devices and view stats which are published by
|
||||
the device.
|
||||
</li>
|
||||
</ul>
|
||||
<br/>
|
||||
<p class="grey margin-top">Click on the image to zoom</p>
|
||||
<center>
|
||||
<a href="{{@unit.publicUri}}/images/devices_analytics.png" target="_blank">
|
||||
<img src="{{@unit.publicUri}}/images/devices_analytics.png" class="img-responsive">
|
||||
</a>
|
||||
</center>
|
||||
</div>
|
||||
|
||||
{{#zone "topCss"}}
|
||||
<style type="text/css">
|
||||
.circle {
|
||||
background: none repeat scroll 0 0 #191919;
|
||||
border-radius: 50px;
|
||||
height: 50px;
|
||||
padding: 10px;
|
||||
width: 50px;
|
||||
color: #fff;
|
||||
}
|
||||
.padding-top-double {
|
||||
padding-top: 20px;
|
||||
}
|
||||
.padding-double {
|
||||
padding: 20px;
|
||||
}
|
||||
.grey {
|
||||
color: #333;
|
||||
}
|
||||
hr {
|
||||
display: block;
|
||||
height: 1px;
|
||||
border: 0;
|
||||
border-top: 1px solid #7f7f7f;
|
||||
margin: 1em 0;
|
||||
padding: 0;
|
||||
opacity: 0.2;
|
||||
}
|
||||
.light-grey {
|
||||
color: #7c7c7c;
|
||||
}
|
||||
.uppercase {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.grey-bg {
|
||||
background-color: #f6f4f4;
|
||||
}
|
||||
.doc-link {
|
||||
background: #11375B;
|
||||
padding: 20px;
|
||||
color: white;
|
||||
margin-top: 0;
|
||||
}
|
||||
.doc-link a {
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
{{/zone}}
|
||||
|
||||
{{#zone "bottomJs"}}
|
||||
{{js "/js/download.js"}}
|
||||
{{js "/js/jquery.validate.js"}}
|
||||
{{/zone}}
|
||||
207
modules/samples/droneanalyzer/feature/feature/pom.xml
Normal file
@ -0,0 +1,207 @@
|
||||
<!--
|
||||
~ Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
~
|
||||
~ WSO2 Inc. licenses this file to you under the Apache License,
|
||||
~ Version 2.0 (the "License"); you may not use this file except
|
||||
~ in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing,
|
||||
~ software distributed under the License is distributed on an
|
||||
~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
~ KIND, either express or implied. See the License for the
|
||||
~ specific language governing permissions and limitations
|
||||
~ under the License.
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>org.homeautomation</groupId>
|
||||
<artifactId>droneanalyzer-feature</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<version>1.0.0</version>
|
||||
<artifactId>${project-base-package}.feature</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
<name>${project-base-package}.feature </name>
|
||||
<url>http://wso2.org</url>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.homeautomation</groupId>
|
||||
<artifactId>${project-base-package}.plugin</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.homeautomation</groupId>
|
||||
<artifactId>${project-base-package}.api</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<type>war</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database.wso2</groupId>
|
||||
<artifactId>h2-database-engine</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-resources</id>
|
||||
<phase>generate-resources</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>src/main/resources</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>resources</directory>
|
||||
<includes>
|
||||
<include>build.properties</include>
|
||||
<include>p2.inf</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>unpack</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>unpack</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>org.homeautomation</groupId>
|
||||
<artifactId>${project-base-package}.analytics</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<type>zip</type>
|
||||
<overWrite>true</overWrite>
|
||||
<outputDirectory>
|
||||
${project.build.directory}/maven-shared-archive-resources/carbonapps
|
||||
</outputDirectory>
|
||||
<includes>**/*</includes>
|
||||
</artifactItem>
|
||||
<artifactItem>
|
||||
<groupId>org.homeautomation</groupId>
|
||||
<artifactId>${project-base-package}.ui
|
||||
</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<type>zip</type>
|
||||
<overWrite>true</overWrite>
|
||||
<outputDirectory>
|
||||
${project.build.directory}/maven-shared-archive-resources/jaggeryapps/devicemgt
|
||||
</outputDirectory>
|
||||
<includes>**/*</includes>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>copy-jaxrs-war</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>copy</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>org.homeautomation</groupId>
|
||||
<artifactId>${project-base-package}.api</artifactId>
|
||||
<type>war</type>
|
||||
<overWrite>true</overWrite>
|
||||
<outputDirectory>
|
||||
${project.build.directory}/maven-shared-archive-resources/webapps/
|
||||
</outputDirectory>
|
||||
<destFileName>drone.war</destFileName>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
</configuration>
|
||||
</execution>
|
||||
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-antrun-plugin</artifactId>
|
||||
<version>${maven-antrun-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>create-drone-plugin-mgt-schema</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>run</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<tasks>
|
||||
<property name="db.dir"
|
||||
value="target/maven-shared-archive-resources/database"/>
|
||||
<property name="userid" value="wso2carbon"/>
|
||||
<property name="password" value="wso2carbon"/>
|
||||
<property name="dbURL"
|
||||
value="jdbc:h2:file:${basedir}/${db.dir}/droneDM_DB;DB_CLOSE_ON_EXIT=FALSE"/>
|
||||
<mkdir dir="${basedir}/${db.dir}"/>
|
||||
<sql driver="org.h2.Driver" url="${dbURL}" userid="${userid}"
|
||||
password="${password}"
|
||||
autocommit="true" onerror="continue">
|
||||
<classpath refid="maven.dependency.classpath"/>
|
||||
<classpath refid="maven.compile.classpath"/>
|
||||
<classpath refid="maven.runtime.classpath"/>
|
||||
<fileset file="${basedir}/src/main/resources/dbscripts/h2.sql"/>
|
||||
</sql>
|
||||
</tasks>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.wso2.maven</groupId>
|
||||
<artifactId>carbon-p2-plugin</artifactId>
|
||||
<version>${carbon-p2-plugin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>p2-feature-generation</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>p2-feature-gen</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<id>${project-base-package}</id>
|
||||
<propertiesFile>../../../features/etc/feature.properties</propertiesFile>
|
||||
<adviceFile>
|
||||
<properties>
|
||||
<propertyDef>org.wso2.carbon.p2.category.type:server</propertyDef>
|
||||
<propertyDef>org.eclipse.equinox.p2.type.group:true</propertyDef>
|
||||
</properties>
|
||||
</adviceFile>
|
||||
<bundles>
|
||||
<bundleDef>
|
||||
org.homeautomation:${project-base-package}.plugin:1.0.0
|
||||
</bundleDef>
|
||||
</bundles>
|
||||
<importFeatures>
|
||||
<importFeatureDef>org.wso2.carbon.core.server:${carbon.kernel.version}
|
||||
</importFeatureDef>
|
||||
<importFeatureDef>org.wso2.carbon.device.mgt.server:${carbon.device.mgt.version}
|
||||
</importFeatureDef>
|
||||
</importFeatures>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@ -0,0 +1,32 @@
|
||||
#
|
||||
# Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
#
|
||||
|
||||
[Device-Configurations]
|
||||
owner=${DEVICE_OWNER}
|
||||
deviceId=${DEVICE_ID}
|
||||
device-name=${DEVICE_NAME}
|
||||
server-name=${SERVER_NAME}
|
||||
controller-context=/drone
|
||||
device-type=drone
|
||||
mqtt-ep=${MQTT_EP}
|
||||
https-ep=${HTTPS_EP}
|
||||
auth-method=token
|
||||
auth-token=${DEVICE_TOKEN}
|
||||
refresh-token=${DEVICE_REFRESH_TOKEN}
|
||||
push-interval=15
|
||||
|
||||
|
||||
@ -0,0 +1,16 @@
|
||||
#/*
|
||||
# * Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
# * WSO2 Inc. licenses this file to you under the Apache License,
|
||||
# * Version 2.0 (the "License"); you may not use this file except
|
||||
# * in compliance with the License.
|
||||
# * You may obtain a copy of the License at
|
||||
# * http://www.apache.org/licenses/LICENSE-2.0
|
||||
# * Unless required by applicable law or agreed to in writing,
|
||||
# * software distributed under the License is distributed on an
|
||||
# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# * KIND, either express or implied. See the License for the
|
||||
# * specific language governing permissions and limitations
|
||||
# * under the License.
|
||||
# */
|
||||
templates=deviceConfig.properties
|
||||
zipfilename=drone.zip
|
||||
@ -0,0 +1,246 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
/**
|
||||
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
**/
|
||||
"""
|
||||
|
||||
import httplib
|
||||
import logging
|
||||
import logging.handlers
|
||||
import signal
|
||||
import ssl
|
||||
import sys
|
||||
import threading
|
||||
import time, calendar
|
||||
from functools import wraps
|
||||
import random
|
||||
import mqttHandler
|
||||
import iotUtils
|
||||
import argparse
|
||||
import math
|
||||
from dronekit import connect, VehicleMode
|
||||
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# Overriding the default SSL version used in some of the Python (2.7.x) versions
|
||||
# This is a known issue in earlier Python releases
|
||||
# But was fixed in later versions. Ex-2.7.11
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
def sslwrap(func):
|
||||
@wraps(func)
|
||||
def bar(*args, **kw):
|
||||
kw['ssl_version'] = ssl.PROTOCOL_TLSv1
|
||||
return func(*args, **kw)
|
||||
return bar
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
PUSH_INTERVAL = 2 # time interval between successive data pushes in seconds
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# Logger defaults
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
LOG_FILENAME = "agent.log"
|
||||
logging_enabled = False
|
||||
LOG_LEVEL = logging.INFO # Could be e.g. "DEBUG" or "WARNING"
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# Python version
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
if sys.version_info<(2,6,0):
|
||||
sys.stderr.write("You need python 2.6.0 or later to run this script\n")
|
||||
exit(1)
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# Define and parse command line arguments
|
||||
# If the log file is specified on the command line then override the default
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
CONNECTION_TARGET = "/dev/ttyACM0"
|
||||
PUSH_INTERVAL = 1
|
||||
BAUD = 57600
|
||||
|
||||
parser = argparse.ArgumentParser(description='Connects to drone on ')
|
||||
parser.add_argument("-c", "--connect", default='/dev/ttyACM0',help="vehicle connection target. Default '/dev/ttyACM0'")
|
||||
parser.add_argument("-b", '--baud', type=int ,default=57600,help="Serial communication speed. Default 57600")
|
||||
parser.add_argument("-i", "--push_interval", type=int, default=1,help="This is the interval which is used to push drone")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.connect:
|
||||
CONNECTION_TARGET = args.connect
|
||||
|
||||
if args.push_interval:
|
||||
PUSH_INTERVAL = args.push_interval
|
||||
|
||||
if args.baud:
|
||||
BAUD = args.baud
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# Endpoint specific settings to connect with the IoT Server
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
SERVER_ENDPOINT = iotUtils.HTTPS_EP.split(":")
|
||||
SERVER_IP = SERVER_ENDPOINT[1].replace('//', '')
|
||||
SERVER_PORT = int(SERVER_ENDPOINT[2])
|
||||
API_ENDPOINT_CONTEXT = iotUtils.CONTROLLER_CONTEXT
|
||||
REGISTER_ENDPOINT = str(API_ENDPOINT_CONTEXT) + '/device/register'
|
||||
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# A class we can use to capture stdout and sterr in the log
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
class IOTLogger(object):
|
||||
def __init__(self, logger, level):
|
||||
"""Needs a logger and a logger level."""
|
||||
self.logger = logger
|
||||
self.level = level
|
||||
|
||||
def write(self, message):
|
||||
if message.rstrip() != "": # Only log if there is a message (not just a new line)
|
||||
self.logger.log(self.level, message.rstrip())
|
||||
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# Configure logging to log to a file,
|
||||
# making a new file at midnight and keeping the last 3 day's data
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
def configureLogger(loggerName):
|
||||
logger = logging.getLogger(loggerName)
|
||||
logger.setLevel(LOG_LEVEL) # Set the log level to LOG_LEVEL
|
||||
handler = logging.handlers.TimedRotatingFileHandler(LOG_FILENAME, when="midnight",
|
||||
backupCount=3) # Handler that writes to a file,
|
||||
# ~~~make new file at midnight and keep 3 backups
|
||||
formatter = logging.Formatter('%(asctime)s %(levelname)-8s %(message)s') # Format each log message like this
|
||||
handler.setFormatter(formatter) # Attach the formatter to the handler
|
||||
logger.addHandler(handler) # Attach the handler to the logger
|
||||
|
||||
if (logging_enabled):
|
||||
sys.stdout = IOTLogger(logger, logging.INFO) # Replace stdout with logging to file at INFO level
|
||||
sys.stderr = IOTLogger(logger, logging.ERROR) # Replace stderr with logging to file at ERROR level
|
||||
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# This method is for register the sensor agent into the Device-Cloud
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
def registerAgent():
|
||||
ssl.wrap_socket = sslwrap(ssl.wrap_socket) # using the overridden sslwrap that uses TLSv1
|
||||
if sys.version_info<(2,7,9):
|
||||
dcConncection = httplib.HTTPSConnection(host=SERVER_IP, port=SERVER_PORT)
|
||||
else:
|
||||
dcConncection = httplib.HTTPSConnection(host=SERVER_IP, port=SERVER_PORT
|
||||
, context=ssl._create_unverified_context())
|
||||
dcConncection.set_debuglevel(1)
|
||||
dcConncection.connect()
|
||||
PUSH_DATA = iotUtils.DEVICE_INFO + iotUtils.DEVICE_DATA.format(sensorValue=0.0)
|
||||
PUSH_DATA += '}'
|
||||
print PUSH_DATA
|
||||
registerURL = str(REGISTER_ENDPOINT)
|
||||
dcConncection.putrequest('POST', registerURL)
|
||||
dcConncection.putheader('Authorization', 'Bearer ' + iotUtils.AUTH_TOKEN)
|
||||
dcConncection.putheader('Content-Type', 'application/json')
|
||||
dcConncection.putheader('Content-Length', len(PUSH_DATA))
|
||||
dcConncection.endheaders()
|
||||
dcConncection.send(PUSH_DATA)
|
||||
dcResponse = dcConncection.getresponse()
|
||||
if(dcResponse.status < 400):
|
||||
iotUtils.IS_REGISTERED = True
|
||||
print "Your device has been registered with IoT Server"
|
||||
else:
|
||||
iotUtils.IS_REGISTERED = False
|
||||
print "Your device hasn't been registered with IoT Server"
|
||||
print ('agentStats: ' + str(dcResponse.status))
|
||||
print ('agentStats: ' + str(dcResponse.reason))
|
||||
print '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'
|
||||
print ('agentStats: ' + str(registerURL))
|
||||
print ('agentStats: Response Message')
|
||||
print str(dcResponse.msg)
|
||||
dcConncection.close()
|
||||
print '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# This is a Thread object for listening for MQTT Messages
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
class ListenMQTTThread(object):
|
||||
def __init__(self):
|
||||
thread = threading.Thread(target=self.run, args=())
|
||||
thread.daemon = True # Daemonize thread
|
||||
thread.start() # Start the execution
|
||||
|
||||
def run(self):
|
||||
mqttHandler.main()
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
def getSensorValue():
|
||||
return iotUtils.generateRandomSensorValues()
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# When sysvinit sends the TERM signal, cleanup before exiting
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
def sigterm_handler(_signo, _stack_frame):
|
||||
print("[] received signal {}, exiting...".format(_signo))
|
||||
sys.exit(0)
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
def generateLatitudeLongitudeValue(lat, lon, num_rows):
|
||||
for _ in xrange(num_rows):
|
||||
hex1 = '%012x' % random.randrange(16**12) # 12 char random string
|
||||
flt = float(random.randint(0,100))
|
||||
dec_lat = random.random()/100
|
||||
dec_lon = random.random()/100
|
||||
return lon+dec_lon, lat+dec_lat
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
signal.signal(signal.SIGTERM, sigterm_handler)
|
||||
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# The Main method of the Agent
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
def main():
|
||||
configureLogger("agent")
|
||||
ListenMQTTThread()
|
||||
registerAgent() # Call the register endpoint and register Device I
|
||||
print "\nConnecting to vehicle on: %s" % CONNECTION_TARGET
|
||||
print "----------------------------------------------------------------------"
|
||||
vehicle = connect(CONNECTION_TARGET, wait_ready=True, baud=BAUD)
|
||||
while (True):
|
||||
if(iotUtils.IS_REGISTERED):
|
||||
currentTime = calendar.timegm(time.gmtime())
|
||||
PUSH_DATA = iotUtils.SENSOR_STATS + '"time":'+str(currentTime)+'},"payloadData":{"quatanium_val_q1":'+0\
|
||||
+',"quatanium_val_q2":'+0+',"quatanium_val_q3":'+0+',"quatanium_val_q4":'+0+',"velocity_x":'\
|
||||
+str(vehicle.velocity[0])+',"velocity_y":'+str(vehicle.velocity[1])+',"velocity_z":'\
|
||||
+str(vehicle.velocity[2])+',"global_location_lat":'+str(vehicle.location.global_relative_frame.lat)\
|
||||
+',"global_location_alt":'+str(vehicle.location.global_relative_frame.alt)\
|
||||
+',"global_location_lon":'+str(vehicle.location.global_relative_frame.lon)+',"battery_level":'\
|
||||
+str(vehicle.battery.level)+',"battery_voltage":'+str(vehicle.battery.voltage)+',"pitch":'\
|
||||
+str(vehicle.attitude.pitch)+',"roll":'+str(vehicle.attitude.roll)+',"yaw":'\
|
||||
+str(vehicle.attitude.yaw)+',"deviceType":"IRIS_DRONE"}}}';
|
||||
mqttHandler.sendSensorValue(PUSH_DATA)
|
||||
print '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Publishing Device-Data ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'
|
||||
print ('PUBLISHED DATA: ' + PUSH_DATA)
|
||||
time.sleep(PUSH_INTERVAL)
|
||||
else:
|
||||
registerAgent()
|
||||
time.sleep(PUSH_INTERVAL)
|
||||
print "-------------------------------------------------------------------------------------------------"
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,239 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
/**
|
||||
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
**/
|
||||
"""
|
||||
|
||||
import httplib
|
||||
import logging
|
||||
import logging.handlers
|
||||
import signal
|
||||
import ssl
|
||||
import sys
|
||||
import threading
|
||||
import time, calendar
|
||||
from functools import wraps
|
||||
import random
|
||||
import mqttHandler
|
||||
import iotUtils
|
||||
import argparse
|
||||
import math
|
||||
from dronekit import connect, VehicleMode
|
||||
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# Overriding the default SSL version used in some of the Python (2.7.x) versions
|
||||
# This is a known issue in earlier Python releases
|
||||
# But was fixed in later versions. Ex-2.7.11
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
def sslwrap(func):
|
||||
@wraps(func)
|
||||
def bar(*args, **kw):
|
||||
kw['ssl_version'] = ssl.PROTOCOL_TLSv1
|
||||
return func(*args, **kw)
|
||||
return bar
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
PUSH_INTERVAL = 2 # time interval between successive data pushes in seconds
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# Logger defaults
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
LOG_FILENAME = "agent.log"
|
||||
logging_enabled = False
|
||||
LOG_LEVEL = logging.INFO # Could be e.g. "DEBUG" or "WARNING"
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# Python version
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
if sys.version_info<(2,6,0):
|
||||
sys.stderr.write("You need python 2.6.0 or later to run this script\n")
|
||||
exit(1)
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# Define and parse command line arguments
|
||||
# If the log file is specified on the command line then override the default
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
PUSH_INTERVAL = 1
|
||||
|
||||
parser = argparse.ArgumentParser(description='Connects to drone on ')
|
||||
parser.add_argument("-i", "--push_interval", type=int, default=1,help="This is the interval which is used to push drone")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.push_interval:
|
||||
PUSH_INTERVAL = args.push_interval
|
||||
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# Endpoint specific settings to connect with the IoT Server
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
SERVER_ENDPOINT = iotUtils.HTTPS_EP.split(":")
|
||||
SERVER_IP = SERVER_ENDPOINT[1].replace('//', '')
|
||||
SERVER_PORT = int(SERVER_ENDPOINT[2])
|
||||
API_ENDPOINT_CONTEXT = iotUtils.CONTROLLER_CONTEXT
|
||||
REGISTER_ENDPOINT = str(API_ENDPOINT_CONTEXT) + '/device/register'
|
||||
INIT_LATITUDE = 19.99
|
||||
INIT_LONGITUDE = 73.78
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# A class we can use to capture stdout and sterr in the log
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
class IOTLogger(object):
|
||||
def __init__(self, logger, level):
|
||||
"""Needs a logger and a logger level."""
|
||||
self.logger = logger
|
||||
self.level = level
|
||||
|
||||
def write(self, message):
|
||||
if message.rstrip() != "": # Only log if there is a message (not just a new line)
|
||||
self.logger.log(self.level, message.rstrip())
|
||||
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# Configure logging to log to a file,
|
||||
# making a new file at midnight and keeping the last 3 day's data
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
def configureLogger(loggerName):
|
||||
logger = logging.getLogger(loggerName)
|
||||
logger.setLevel(LOG_LEVEL) # Set the log level to LOG_LEVEL
|
||||
handler = logging.handlers.TimedRotatingFileHandler(LOG_FILENAME, when="midnight",
|
||||
backupCount=3) # Handler that writes to a file,
|
||||
# ~~~make new file at midnight and keep 3 backups
|
||||
formatter = logging.Formatter('%(asctime)s %(levelname)-8s %(message)s') # Format each log message like this
|
||||
handler.setFormatter(formatter) # Attach the formatter to the handler
|
||||
logger.addHandler(handler) # Attach the handler to the logger
|
||||
|
||||
if (logging_enabled):
|
||||
sys.stdout = IOTLogger(logger, logging.INFO) # Replace stdout with logging to file at INFO level
|
||||
sys.stderr = IOTLogger(logger, logging.ERROR) # Replace stderr with logging to file at ERROR level
|
||||
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# This method is for register the sensor agent into the Device-Cloud
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
def registerAgent():
|
||||
ssl.wrap_socket = sslwrap(ssl.wrap_socket) # using the overridden sslwrap that uses TLSv1
|
||||
if sys.version_info<(2,7,9):
|
||||
dcConncection = httplib.HTTPSConnection(host=SERVER_IP, port=SERVER_PORT)
|
||||
else:
|
||||
dcConncection = httplib.HTTPSConnection(host=SERVER_IP, port=SERVER_PORT
|
||||
, context=ssl._create_unverified_context())
|
||||
dcConncection.set_debuglevel(1)
|
||||
dcConncection.connect()
|
||||
PUSH_DATA = iotUtils.DEVICE_INFO + iotUtils.DEVICE_DATA.format(sensorValue=0.0)
|
||||
PUSH_DATA += '}'
|
||||
print PUSH_DATA
|
||||
registerURL = str(REGISTER_ENDPOINT)
|
||||
dcConncection.putrequest('POST', registerURL)
|
||||
dcConncection.putheader('Authorization', 'Bearer ' + iotUtils.AUTH_TOKEN)
|
||||
dcConncection.putheader('Content-Type', 'application/json')
|
||||
dcConncection.putheader('Content-Length', len(PUSH_DATA))
|
||||
dcConncection.endheaders()
|
||||
dcConncection.send(PUSH_DATA)
|
||||
dcResponse = dcConncection.getresponse()
|
||||
if(dcResponse.status < 400):
|
||||
iotUtils.IS_REGISTERED = True
|
||||
print "Your device has been registered with IoT Server"
|
||||
else:
|
||||
iotUtils.IS_REGISTERED = False
|
||||
print "Your device hasn't been registered with IoT Server"
|
||||
print ('agentStats: ' + str(dcResponse.status))
|
||||
print ('agentStats: ' + str(dcResponse.reason))
|
||||
print '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'
|
||||
print ('agentStats: ' + str(registerURL))
|
||||
print ('agentStats: Response Message')
|
||||
print str(dcResponse.msg)
|
||||
dcConncection.close()
|
||||
print '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# This is a Thread object for listening for MQTT Messages
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
class ListenMQTTThread(object):
|
||||
def __init__(self):
|
||||
thread = threading.Thread(target=self.run, args=())
|
||||
thread.daemon = True # Daemonize thread
|
||||
thread.start() # Start the execution
|
||||
|
||||
def run(self):
|
||||
mqttHandler.main()
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
def getSensorValue():
|
||||
return iotUtils.generateRandomSensorValues()
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# When sysvinit sends the TERM signal, cleanup before exiting
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
def sigterm_handler(_signo, _stack_frame):
|
||||
print("[] received signal {}, exiting...".format(_signo))
|
||||
sys.exit(0)
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
def generateLatitudeLongitudeValue(lat, lon, num_rows):
|
||||
for _ in xrange(num_rows):
|
||||
hex1 = '%012x' % random.randrange(16**12) # 12 char random string
|
||||
flt = float(random.randint(0,100))
|
||||
dec_lat = random.random()/100
|
||||
dec_lon = random.random()/100
|
||||
return lon+dec_lon, lat+dec_lat
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
signal.signal(signal.SIGTERM, sigterm_handler)
|
||||
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# The Main method of the Agent
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
def main():
|
||||
configureLogger("agent")
|
||||
ListenMQTTThread()
|
||||
registerAgent() # Call the register endpoint and register Device I
|
||||
print "----------------------------------------------------------------------"
|
||||
while (True):
|
||||
if(iotUtils.IS_REGISTERED):
|
||||
print "-------------------------------------------------------------------------------------------------"
|
||||
currentTime = calendar.timegm(time.gmtime())
|
||||
randomLatitudeLongitudeValue = generateLatitudeLongitudeValue(INIT_LATITUDE, INIT_LONGITUDE, 5)
|
||||
print randomLatitudeLongitudeValue[1]
|
||||
PUSH_DATA = iotUtils.SENSOR_STATS + '"time":'+str(currentTime)+'},"payloadData":{"quatanium_val_q1":34,' \
|
||||
'"quatanium_val_q2":'+str(random.randint(15, 40))+',"quatanium_val_q3":'\
|
||||
+str(random.randint(15, 40))+',"quatanium_val_q4":'+str(random.randint(15, 40))+',"velocity_x":'\
|
||||
+str(random.randint(15, 40))+',"velocity_y":'+str(random.randint(15, 40))+',"velocity_z":678,' \
|
||||
'"global_location_lat":'+str(randomLatitudeLongitudeValue[0])+',"global_location_alt":45,' \
|
||||
'"global_location_lon":'+str(randomLatitudeLongitudeValue[1])+',"battery_level":'\
|
||||
+str(random.randint(15, 100))+',"battery_voltage":'+str(random.randint(15, 100))+',"pitch":'\
|
||||
+str(random.randint(15, 40))+',"roll":'+str(random.randint(15, 40))+',"yaw":'\
|
||||
+str(random.randint(15, 40))+'}}}';
|
||||
mqttHandler.sendSensorValue(PUSH_DATA)
|
||||
print '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Publishing Device-Data ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'
|
||||
print ('PUBLISHED DATA: ' + PUSH_DATA)
|
||||
time.sleep(PUSH_INTERVAL)
|
||||
else:
|
||||
registerAgent()
|
||||
time.sleep(PUSH_INTERVAL)
|
||||
print "-------------------------------------------------------------------------------------------------"
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
/**
|
||||
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
**/
|
||||
"""
|
||||
|
||||
import ConfigParser
|
||||
import os
|
||||
import random
|
||||
import time, calendar
|
||||
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# Device specific info when pushing data to server
|
||||
# Read from a file "deviceConfig.properties" in the same folder level
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
configParser = ConfigParser.RawConfigParser()
|
||||
configFilePath = os.path.join(os.path.dirname(__file__), './deviceConfig.properties')
|
||||
configParser.read(configFilePath)
|
||||
|
||||
DEVICE_OWNER = configParser.get('Device-Configurations', 'owner')
|
||||
DEVICE_ID = configParser.get('Device-Configurations', 'deviceId')
|
||||
DEVICE_NAME = configParser.get('Device-Configurations', 'device-name')
|
||||
DEVICE_TYPE = configParser.get('Device-Configurations', 'device-type')
|
||||
SERVER_NAME = configParser.get('Device-Configurations', 'server-name')
|
||||
MQTT_EP = configParser.get('Device-Configurations', 'mqtt-ep')
|
||||
AUTH_TOKEN = configParser.get('Device-Configurations', 'auth-token')
|
||||
CONTROLLER_CONTEXT = configParser.get('Device-Configurations', 'controller-context')
|
||||
DEVICE_INFO = '{"owner":"' + DEVICE_OWNER + '","deviceId":"' + DEVICE_ID + '",'
|
||||
HTTPS_EP = configParser.get('Device-Configurations', 'https-ep')
|
||||
DEVICE_DATA = '"sensorValue":"{sensorValue}"'
|
||||
|
||||
|
||||
|
||||
global IS_REGISTERED
|
||||
IS_REGISTERED = False
|
||||
|
||||
def isEmpty (string):
|
||||
if string and string.strip():
|
||||
#string is not None AND string is not empty or blank
|
||||
return False
|
||||
#string is None OR string is empty or blank
|
||||
return True
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# This method generate a random sensor value between 15 and 40
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
def generateRandomSensorValues():
|
||||
return random.randint(15, 40)
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
currentTime = calendar.timegm(time.gmtime())
|
||||
SENSOR_STATS = '{"event":{"metaData":{"owner":"' + DEVICE_OWNER + '","deviceType":"'+ DEVICE_TYPE +'","deviceId":"'+ DEVICE_ID + '",'
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
/**
|
||||
* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
*
|
||||
* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
* Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
**/
|
||||
"""
|
||||
|
||||
import time
|
||||
import paho.mqtt.client as mqtt
|
||||
import iotUtils
|
||||
|
||||
global mqttClient
|
||||
mqttClient = mqtt.Client()
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# The callback for when the client receives a CONNACK response from the server.
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
def on_connect(mqttClient, userdata, flags, rc):
|
||||
print("MQTT_LISTENER: Connected with result code " + str(rc))
|
||||
# Subscribing in on_connect() means that if we lose the connection and
|
||||
# reconnect then subscriptions will be renewed.
|
||||
print ("MQTT_LISTENER: Subscribing with topic " + TOPIC_TO_SUBSCRIBE)
|
||||
mqttClient.subscribe(TOPIC_TO_SUBSCRIBE)
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# The callback for when a PUBLISH message is received from the server.
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
def on_message(mqttClient, userdata, msg):
|
||||
print( "MQTT_LISTENER: " + msg.topic + " " + str(msg.payload))
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# The callback for when a PUBLISH message to the server when door is open or close
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
def on_publish(mqttClient, msg):
|
||||
mqttClient.publish(TOPIC_TO_PUBLISH, msg)
|
||||
|
||||
|
||||
def sendSensorValue(msg):
|
||||
global mqttClient
|
||||
on_publish(mqttClient, msg)
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
# The Main method of the server script
|
||||
# This method is invoked from Agent.py on a new thread
|
||||
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
def main():
|
||||
|
||||
MQTT_ENDPOINT = iotUtils.MQTT_EP.split(":")
|
||||
MQTT_IP = MQTT_ENDPOINT[1].replace('//','')
|
||||
MQTT_PORT = int(MQTT_ENDPOINT[2])
|
||||
|
||||
DEV_OWNER = iotUtils.DEVICE_OWNER
|
||||
DEV_ID = iotUtils.DEVICE_ID
|
||||
DEV_TYPE =iotUtils.DEVICE_TYPE
|
||||
TANENT_DOMAIN = iotUtils.SERVER_NAME
|
||||
global TOPIC_TO_SUBSCRIBE
|
||||
TOPIC_TO_SUBSCRIBE = TANENT_DOMAIN + "/" + DEV_TYPE + "/" + DEV_ID + "/command"
|
||||
global TOPIC_TO_PUBLISH
|
||||
TOPIC_TO_PUBLISH = TANENT_DOMAIN + "/" + DEV_TYPE + "/" + DEV_ID + "/publisher"
|
||||
|
||||
print ("MQTT_LISTENER: MQTT_ENDPOINT is " + str(MQTT_ENDPOINT))
|
||||
print ("MQTT_LISTENER: MQTT_TOPIC is " + TOPIC_TO_SUBSCRIBE)
|
||||
global mqttClient
|
||||
mqttClient.username_pw_set(iotUtils.AUTH_TOKEN, password = "")
|
||||
mqttClient.on_connect = on_connect
|
||||
mqttClient.on_message = on_message
|
||||
|
||||
while True:
|
||||
try:
|
||||
mqttClient.connect(MQTT_IP, MQTT_PORT, 60)
|
||||
print "MQTT_LISTENER: " + time.asctime(), "Connected to MQTT Broker - %s:%s" % (MQTT_IP, MQTT_PORT)
|
||||
mqttClient.loop_forever()
|
||||
|
||||
except (KeyboardInterrupt, Exception) as e:
|
||||
print "MQTT_LISTENER: Exception in MQTTServerThread (either KeyboardInterrupt or Other)"
|
||||
print ("MQTT_LISTENER: " + str(e))
|
||||
|
||||
mqttClient.disconnect()
|
||||
print "MQTT_LISTENER: " + time.asctime(), "Connection to Broker closed - %s:%s" % (MQTT_IP, MQTT_PORT)
|
||||
print '~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~'
|
||||
pass
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
@ -0,0 +1,104 @@
|
||||
#"""
|
||||
#/**
|
||||
#* Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
#*
|
||||
#* WSO2 Inc. licenses this file to you under the Apache License,
|
||||
#* Version 2.0 (the "License"); you may not use this file except
|
||||
#* in compliance with the License.
|
||||
#* You may obtain a copy of the License at
|
||||
#*
|
||||
#* http://www.apache.org/licenses/LICENSE-2.0
|
||||
#*
|
||||
#* Unless required by applicable law or agreed to in writing,
|
||||
#* software distributed under the License is distributed on an
|
||||
#* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
#* KIND, either express or implied. See the License for the
|
||||
#* specific language governing permissions and limitations
|
||||
#* under the License.
|
||||
#**/
|
||||
#"""
|
||||
|
||||
#!/bin/bash
|
||||
|
||||
echo "----------------------------------------------------------------"
|
||||
echo "| WSO2 IOT Sample "
|
||||
echo "| Drone Analyzer "
|
||||
echo "| ---------------- "
|
||||
echo "| ....initializing startup-script "
|
||||
echo "----------------------------------------------------------------"
|
||||
|
||||
currentDir=$PWD
|
||||
|
||||
|
||||
while true; do
|
||||
read -p "Do you wish to run 'apt-get update' and continue? [Yes/No] " yn
|
||||
case $yn in
|
||||
[Yy]* ) sudo apt-get update;
|
||||
break;;
|
||||
[Nn]* ) echo "Continuing without apt-get update...";
|
||||
break;;
|
||||
* ) echo "Please answer yes or no.";
|
||||
esac
|
||||
done
|
||||
|
||||
for f in ./deviceConfig.properties; do
|
||||
## Check if the glob gets expanded to existing files.
|
||||
## If not, f here will be exactly the pattern above
|
||||
## and the exists test will evaluate to false.
|
||||
if [ -e "$f" ]; then
|
||||
echo "Configuration file found......"
|
||||
else
|
||||
echo "'deviceConfig.properties' file does not exist in current path. \nExiting installation...";
|
||||
exit;
|
||||
fi
|
||||
## This is all we needed to know, so we can break after the first iteration
|
||||
break
|
||||
done
|
||||
|
||||
#installing dependencies
|
||||
sudo apt-get install python-pip python-dev
|
||||
git clone git://git.eclipse.org/gitroot/paho/org.eclipse.paho.mqtt.python.git
|
||||
cd org.eclipse.paho.mqtt.python
|
||||
sudo python setup.py install
|
||||
cd ../
|
||||
cp deviceConfig.properties ./src
|
||||
|
||||
while true; do
|
||||
read -p "Do you wish to run this as simulator if it is so enter yes and continue? [Yes/No] " yn
|
||||
case $yn in
|
||||
[Yy]* ) echo "Continuing with simulator...";
|
||||
while true; do
|
||||
read -p "Whats the time-interval (in seconds) between successive Data-Pushes to the DAS (ex: '60' indicates 1 minute) > " push_interval
|
||||
if [ $input -eq $input 2>/dev/null ]
|
||||
then
|
||||
echo "Setting data-push interval to $push_interval seconds."
|
||||
break
|
||||
else
|
||||
echo "Input needs to be an integer indicating the number seconds between successive data-pushes."
|
||||
fi
|
||||
done
|
||||
python ./src/droneSimulator.py -i $push_interval
|
||||
break;;
|
||||
[Nn]* ) echo "Continuing with real device...";
|
||||
sudo pip install dronekit
|
||||
while true; do
|
||||
read -p "Whats the time-interval (in seconds) between successive Data-Pushes to the DAS (ex: '60' indicates 1 minute) > " push_interval
|
||||
read -p "Vehicle connection target. Default '/dev/ttyACM0' :" connection_target
|
||||
read -p "Serial communication speed. Default 57600 :" baud
|
||||
|
||||
if [ $input -eq $input 2>/dev/null ]
|
||||
then
|
||||
echo "Setting data-push interval to $push_interval seconds."
|
||||
break
|
||||
else
|
||||
echo "Input needs to be an integer indicating the number seconds between successive data-pushes."
|
||||
fi
|
||||
done
|
||||
python ./src/drone.py -i $push_interval -b $baud -c $connection_target
|
||||
break;;
|
||||
* ) echo "Please answer yes or no.";
|
||||
esac
|
||||
done
|
||||
|
||||
echo "Could not start the service..."
|
||||
exit;
|
||||
@ -0,0 +1,18 @@
|
||||
#<!--
|
||||
# ~ Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
# ~
|
||||
# ~ WSO2 Inc. licenses this file to you under the Apache License,
|
||||
# ~ Version 2.0 (the "License"); you may not use this file except
|
||||
# ~ in compliance with the License.
|
||||
# ~ You may obtain a copy of the License at
|
||||
# ~
|
||||
# ~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
# ~
|
||||
# ~ Unless required by applicable law or agreed to in writing,
|
||||
# ~ software distributed under the License is distributed on an
|
||||
# ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# ~ KIND, either express or implied. See the License for the
|
||||
# ~ specific language governing permissions and limitations
|
||||
# ~ under the License.
|
||||
# -->
|
||||
custom = true
|
||||
@ -0,0 +1,20 @@
|
||||
{
|
||||
"deviceType": {
|
||||
"label": "drone",
|
||||
"category": "virtual",
|
||||
"downloadAgentUri": "drone/enrollment/devices/download"
|
||||
},
|
||||
"analyticStreams": [
|
||||
{
|
||||
"name": "Temperature",
|
||||
"table": "DEVICE_drone_droneStats_SUMMARY",
|
||||
"ui_unit": {
|
||||
"name": "cdmf.unit.analytics.line-chart",
|
||||
"data":[
|
||||
{"column": {"name":"TIME", "label":"time", "ui-mapping":"x-axis"}},
|
||||
{"column": {"name":"droneStats", "label":"droneStats", "ui-mapping":"y-axis"}}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
~
|
||||
~ WSO2 Inc. licenses this file to you under the Apache License,
|
||||
~ Version 2.0 (the "License"); you may not use this file except
|
||||
~ in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing,
|
||||
~ software distributed under the License is distributed on an
|
||||
~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
~ KIND, either express or implied. See the License for the
|
||||
~ specific language governing permissions and limitations
|
||||
~ under the License.
|
||||
-->
|
||||
|
||||
<DeviceTypeConfigurations>
|
||||
<DeviceTypeConfig type="drone">
|
||||
<DatasourceName>jdbc/droneDM_DB</DatasourceName>
|
||||
</DeviceTypeConfig>
|
||||
</DeviceTypeConfigurations>
|
||||
@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
~
|
||||
~ WSO2 Inc. licenses this file to you under the Apache License,
|
||||
~ Version 2.0 (the "License"); you may not use this file except
|
||||
~ in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing,
|
||||
~ software distributed under the License is distributed on an
|
||||
~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
~ KIND, either express or implied. See the License for the
|
||||
~ specific language governing permissions and limitations
|
||||
~ under the License.
|
||||
-->
|
||||
|
||||
<datasources-configuration xmlns:svns="http://org.wso2.securevault/configuration">
|
||||
<providers>
|
||||
<provider>org.wso2.carbon.ndatasource.rdbms.RDBMSDataSourceReader</provider>
|
||||
</providers>
|
||||
<datasources>
|
||||
<datasource>
|
||||
<name>droneDM_DB</name>
|
||||
<description>The datasource used for the this device type </description>
|
||||
<jndiConfig>
|
||||
<name>jdbc/droneDM_DB</name>
|
||||
</jndiConfig>
|
||||
<definition type="RDBMS">
|
||||
<configuration>
|
||||
<url>jdbc:h2:repository/database/droneDM_DB;DB_CLOSE_ON_EXIT=FALSE
|
||||
</url>
|
||||
<username>wso2carbon</username>
|
||||
<password>wso2carbon</password>
|
||||
<driverClassName>org.h2.Driver</driverClassName>
|
||||
<maxActive>50</maxActive>
|
||||
<maxWait>60000</maxWait>
|
||||
<testOnBorrow>true</testOnBorrow>
|
||||
<validationQuery>SELECT 1</validationQuery>
|
||||
<validationInterval>30000</validationInterval>
|
||||
</configuration>
|
||||
</definition>
|
||||
</datasource>
|
||||
</datasources>
|
||||
</datasources-configuration>
|
||||
@ -0,0 +1,11 @@
|
||||
|
||||
-- -----------------------------------------------------
|
||||
-- Agent Database
|
||||
-- -----------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS `drone_DEVICE` (
|
||||
`drone_DEVICE_ID` VARCHAR(45) NOT NULL ,
|
||||
`DEVICE_NAME` VARCHAR(100) NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`drone_DEVICE_ID`) );
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
|
||||
-- -----------------------------------------------------
|
||||
-- Table `drone_DEVICE`
|
||||
-- -----------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS drone_DEVICE (
|
||||
drone_DEVICE_ID VARCHAR(45) NOT NULL ,
|
||||
DEVICE_NAME VARCHAR(100) NULL DEFAULT NULL,
|
||||
PRIMARY KEY (drone_DEVICE_ID) );
|
||||
|
||||
@ -0,0 +1,10 @@
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `drone_DEVICE` (
|
||||
`drone_DEVICE_ID` VARCHAR(45) NOT NULL ,
|
||||
`DEVICE_NAME` VARCHAR(100) NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`drone_DEVICE_ID`) )
|
||||
ENGINE = InnoDB;
|
||||
|
||||
|
||||
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
|
||||
-- -----------------------------------------------------
|
||||
-- Table `drone_DEVICE`
|
||||
-- -----------------------------------------------------
|
||||
CREATE TABLE IF NOT EXISTS drone_DEVICE (
|
||||
drone_DEVICE_ID VARCHAR(45) NOT NULL ,
|
||||
DEVICE_NAME VARCHAR(100) NULL DEFAULT NULL,
|
||||
PRIMARY KEY (drone_DEVICE_ID) );
|
||||
|
||||
@ -0,0 +1,28 @@
|
||||
instructions.configure = \
|
||||
org.eclipse.equinox.p2.touchpoint.natives.mkdir(path:${installFolder}/../../conf/device-types/);\
|
||||
org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.homeautomation.droneanalyzer_${feature.version}/configs/,target:${installFolder}/../../conf/device-types/,overwrite:true);\
|
||||
org.eclipse.equinox.p2.touchpoint.natives.mkdir(path:${installFolder}/../../deployment/server/webapps/);\
|
||||
org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.homeautomation.droneanalyzer_${feature.version}/webapps/,target:${installFolder}/../../deployment/server/webapps/,overwrite:true);\
|
||||
org.eclipse.equinox.p2.touchpoint.natives.mkdir(path:${installFolder}/../../resources/sketches/);\
|
||||
org.eclipse.equinox.p2.touchpoint.natives.mkdir(path:${installFolder}/../../resources/sketches/droneanalyzer/);\
|
||||
org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.homeautomation.droneanalyzer_${feature.version}/agent/,target:${installFolder}/../../resources/sketches/drone/,overwrite:true);\
|
||||
org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.homeautomation.droneanalyzer_${feature.version}/dbscripts/,target:${installFolder}/../../../dbscripts/cdm/plugins/drone,overwrite:true);\
|
||||
org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.homeautomation.droneanalyzer_${feature.version}/datasources/,target:${installFolder}/../../conf/datasources/,overwrite:true);\
|
||||
org.eclipse.equinox.p2.touchpoint.natives.mkdir(path:${installFolder}/../../deployment/server/jaggeryapps/);\
|
||||
org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.homeautomation.droneanalyzer_${feature.version}/jaggeryapps/,target:${installFolder}/../../deployment/server/jaggeryapps/,overwrite:true);\
|
||||
org.eclipse.equinox.p2.touchpoint.natives.mkdir(path:${installFolder}/../../database/);\
|
||||
org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.homeautomation.droneanalyzer_${feature.version}/database/,target:${installFolder}/../../database/,overwrite:true);\
|
||||
org.eclipse.equinox.p2.touchpoint.natives.mkdir(path:${installFolder}/../../deployment/server/carbonapps/);\
|
||||
org.eclipse.equinox.p2.touchpoint.natives.copy(source:${installFolder}/../features/org.homeautomation.droneanalyzer_${feature.version}/carbonapps/,target:${installFolder}/../../deployment/server/carbonapps/,overwrite:true);\
|
||||
|
||||
instructions.unconfigure = \
|
||||
org.eclipse.equinox.p2.touchpoint.natives.remove(path:${installFolder}/../../conf/device-types/drone.xml);\
|
||||
org.eclipse.equinox.p2.touchpoint.natives.remove(path:${installFolder}/../../deployment/server/webapps/drone.war);\
|
||||
org.eclipse.equinox.p2.touchpoint.natives.remove(path:${installFolder}/../../deployment/server/webapps/drone);\
|
||||
org.eclipse.equinox.p2.touchpoint.natives.remove(path:${installFolder}/../../../dbscripts/cdm/plugins/drone);\
|
||||
org.eclipse.equinox.p2.touchpoint.natives.remove(path:${installFolder}/../../conf/datasources/drone-datasources.xml);\
|
||||
org.eclipse.equinox.p2.touchpoint.natives.remove(path:${installFolder}/../../database/droneDM_DB.h2.db);\
|
||||
org.eclipse.equinox.p2.touchpoint.natives.remove(path:${installFolder}/../../deployment/server/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.drone.device-view);\
|
||||
org.eclipse.equinox.p2.touchpoint.natives.remove(path:${installFolder}/../../deployment/server/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.drone.type-view);\
|
||||
org.eclipse.equinox.p2.touchpoint.natives.remove(path:${installFolder}/../../deployment/server/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.drone.realtime.analytics-view);\
|
||||
org.eclipse.equinox.p2.touchpoint.natives.remove(path:${installFolder}/../../deployment/server/jaggeryapps/devicemgt/app/units/cdmf.unit.device.type.drone.analytics-view);\
|
||||
32
modules/samples/droneanalyzer/feature/pom.xml
Normal file
@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ Copyright (c) 2016, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
|
||||
~
|
||||
~ WSO2 Inc. licenses this file to you under the Apache License,
|
||||
~ Version 2.0 (the "License"); you may not use this file except
|
||||
~ in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing,
|
||||
~ software distributed under the License is distributed on an
|
||||
~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
~ KIND, either express or implied. See the License for the
|
||||
~ specific language governing permissions and limitations
|
||||
~ under the License.
|
||||
--><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<groupId>org.homeautomation</groupId>
|
||||
<artifactId>droneanalyzer</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<artifactId>droneanalyzer-feature</artifactId>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<packaging>pom</packaging>
|
||||
<version>1.0.0</version>
|
||||
<modules>
|
||||
<module>feature</module>
|
||||
</modules>
|
||||
</project>
|
||||