com.sitewhere.server.device.DefaultDeviceModelInitializer.java Source code

Java tutorial

Introduction

Here is the source code for com.sitewhere.server.device.DefaultDeviceModelInitializer.java

Source

/*
 * DefaultDeviceModelInitializer.java 
 * --------------------------------------------------------------------------------------
 * Copyright (c) Reveal Technologies, LLC. All rights reserved. http://www.reveal-tech.com
 *
 * The software in this package is published under the terms of the CPAL v1.0
 * license, a copy of which has been included with this distribution in the
 * LICENSE.txt file.
 */
package com.sitewhere.server.device;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;

import org.apache.log4j.Logger;
import org.springframework.security.core.context.SecurityContextHolder;

import com.sitewhere.geo.GeoUtils;
import com.sitewhere.rest.model.common.Location;
import com.sitewhere.rest.model.device.DeviceEventBatch;
import com.sitewhere.rest.model.device.request.DeviceAlertCreateRequest;
import com.sitewhere.rest.model.device.request.DeviceAssignmentCreateRequest;
import com.sitewhere.rest.model.device.request.DeviceCreateRequest;
import com.sitewhere.rest.model.device.request.DeviceLocationCreateRequest;
import com.sitewhere.rest.model.device.request.DeviceMeasurementsCreateRequest;
import com.sitewhere.rest.model.device.request.SiteCreateRequest;
import com.sitewhere.rest.model.device.request.ZoneCreateRequest;
import com.sitewhere.server.SiteWhereServer;
import com.sitewhere.spi.SiteWhereException;
import com.sitewhere.spi.device.AlertLevel;
import com.sitewhere.spi.device.DeviceAssignmentType;
import com.sitewhere.spi.device.IDevice;
import com.sitewhere.spi.device.IDeviceAssignment;
import com.sitewhere.spi.device.IDeviceLocation;
import com.sitewhere.spi.device.IDeviceManagement;
import com.sitewhere.spi.device.IDeviceMeasurements;
import com.sitewhere.spi.device.ISite;
import com.sitewhere.spi.device.ISiteMapMetadata;
import com.sitewhere.spi.device.IZone;
import com.sitewhere.spi.server.device.IDeviceModelInitializer;
import com.vividsolutions.jts.algorithm.MinimumBoundingCircle;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;
import com.vividsolutions.jts.geom.util.AffineTransformation;

/**
 * Used to load a default site/devices/assignments/events so that there is demo data in
 * the system. The server only offers this functionality if no sites already exist.
 * 
 * @author Derek
 */
public class DefaultDeviceModelInitializer implements IDeviceModelInitializer {

    /** Static logger instance */
    private static Logger LOGGER = Logger.getLogger(DefaultDeviceModelInitializer.class);

    /** Prefix for create site log message */
    public static final String PREFIX_CREATE_SITE = "[Create Site]";

    /** Prefix for create device log message */
    public static final String PREFIX_CREATE_DEVICE = "[Create Device]";

    /** Prefix for create assignment log message */
    public static final String PREFIX_CREATE_ASSIGNMENT = "[Create Assignment]";

    /** Prefix for create zone log message */
    public static final String PREFIX_CREATE_ZONE = "[Create Zone]";

    /** Prefix for create event log message */
    public static final String PREFIX_CREATE_EVENTS = "[Create Events]";

    /** Number of devices to create */
    public static final int NUM_SITES = 1;

    /** Number of devices/assignments to create */
    public static final int ASSIGNMENTS_PER_SITE = 15;

    /** Number of events per assignment */
    public static final int EVENTS_PER_ASSIGNMENT = 75;

    /** Number of events per assignment */
    public static final int LOCATIONS_PER_ASSIGNMENT = 40;

    /** Minimum engine temp */
    public static final double MIN_TEMP = 80;

    /** Maximum engine temp */
    public static final double MAX_TEMP = 200;

    /** Temp at which a warning alert will be generated */
    public static final double WARN_TEMP = 160;

    /** Temp at which an error alert will be generated */
    public static final double ERROR_TEMP = 180;

    /** Temp at which a critical alert will be generated */
    public static final double CRITICAL_TEMP = 190;

    /** Image URL assocaited with sites */
    public static final String SITE_IMAGE_URL = "https://s3.amazonaws.com/sitewhere-demo/construction/construction.jpg";

    /** Available choices for devices/assignments that track location */
    protected static AssignmentChoice[] LOCATION_TRACKERS = {
            new AssignmentChoice("175", "Equipment Tracker", DeviceAssignmentType.Hardware, "300"),
            new AssignmentChoice("175", "Equipment Tracker", DeviceAssignmentType.Hardware, "301"),
            new AssignmentChoice("175", "Equipment Tracker", DeviceAssignmentType.Hardware, "302"),
            new AssignmentChoice("174", "Equipment Tracker", DeviceAssignmentType.Hardware, "303"),
            new AssignmentChoice("174", "Equipment Tracker", DeviceAssignmentType.Hardware, "304") };

    /** Locations that determine zone edges */
    protected List<Location> zoneLocations;

    /** Device management implementation */
    protected IDeviceManagement deviceManagement;

    /** Indiates whether model should be initialized if no console is available for input */
    private boolean initializeIfNoConsole = false;

    /*
     * (non-Javadoc)
     * 
     * @see
     * com.sitewhere.spi.server.device.IDeviceModelInitializer#initialize(com.sitewhere
     * .spi.device.IDeviceManagement)
     */
    @Override
    public void initialize(IDeviceManagement deviceManagement) throws SiteWhereException {
        this.deviceManagement = deviceManagement;

        // Use the system account for logging "created by" on created elements.
        SecurityContextHolder.getContext().setAuthentication(SiteWhereServer.getSystemAuthentication());

        // Coordinates for edges of zone.
        zoneLocations = new ArrayList<Location>();
        zoneLocations.add(new Location(34.10260138703638, -84.24412965774536));
        zoneLocations.add(new Location(34.101837372446774, -84.24243450164795));
        zoneLocations.add(new Location(34.101517550337825, -84.24091100692749));
        zoneLocations.add(new Location(34.10154953265732, -84.23856675624847));
        zoneLocations.add(new Location(34.10153176473365, -84.23575580120087));
        zoneLocations.add(new Location(34.10409030732968, -84.23689305782318));
        zoneLocations.add(new Location(34.104996439280704, -84.23700034618376));
        zoneLocations.add(new Location(34.10606246444614, -84.23700034618376));
        zoneLocations.add(new Location(34.107691680235604, -84.23690915107727));

        List<ISite> sites = createSites();
        for (ISite site : sites) {
            List<IDeviceAssignment> assignments = createAssignments(site);
            for (IDeviceAssignment assignment : assignments) {
                assignment.getActiveDate();
            }
        }

        SecurityContextHolder.getContext().setAuthentication(null);
    }

    /*
     * (non-Javadoc)
     * 
     * @see com.sitewhere.spi.server.IModelInitializer#isInitializeIfNoConsole()
     */
    public boolean isInitializeIfNoConsole() {
        return initializeIfNoConsole;
    }

    public void setInitializeIfNoConsole(boolean initializeIfNoConsole) {
        this.initializeIfNoConsole = initializeIfNoConsole;
    }

    /**
     * Create example sites.
     * 
     * @return
     * @throws SiteWhereException
     */
    public List<ISite> createSites() throws SiteWhereException {
        List<ISite> results = new ArrayList<ISite>();
        for (int x = 0; x < NUM_SITES; x++) {
            SiteCreateRequest request = new SiteCreateRequest();
            request.setName("Construction Site " + (x + 1));
            request.setDescription("A construction site with many high-value assets that should "
                    + "not be taken offsite. The system provides location tracking for the assets and notifies "
                    + "administrators if any of the assets move outside of the general site area or "
                    + "into areas where they are not allowed.");
            request.setImageUrl(SITE_IMAGE_URL);
            request.getMap().setType("mapquest");
            request.getMap().addOrReplaceMetadata(ISiteMapMetadata.MAP_CENTER_LATITUDE, "34.10469794977326");
            request.getMap().addOrReplaceMetadata(ISiteMapMetadata.MAP_CENTER_LONGITUDE, "-84.23966646194458");
            request.getMap().addOrReplaceMetadata(ISiteMapMetadata.MAP_ZOOM_LEVEL, "15");
            ISite site = getDeviceManagement().createSite(request);
            results.add(site);
            LOGGER.info(PREFIX_CREATE_SITE + " " + request.getName());

            // Create a zone for the site.
            createZone(site);
        }
        return results;
    }

    /**
     * Create the construction zone.
     * 
     * @param site
     * @return
     * @throws SiteWhereException
     */
    public IZone createZone(ISite site) throws SiteWhereException {
        ZoneCreateRequest request = new ZoneCreateRequest();
        request.setName("Construction Site");
        request.setBorderColor("#017112");
        request.setFillColor("#1db32e");
        request.setOpacity(0.4);
        request.setCoordinates(zoneLocations);
        IZone zone = getDeviceManagement().createZone(site, request);
        LOGGER.info(PREFIX_CREATE_ZONE + " " + zone.getToken());
        return zone;
    }

    /**
     * Create devices for a site and assign them.
     * 
     * @param site
     * @return
     * @throws SiteWhereException
     */
    public List<IDeviceAssignment> createAssignments(ISite site) throws SiteWhereException {
        Date now = new Date();
        List<IDeviceAssignment> results = new ArrayList<IDeviceAssignment>();
        for (int x = 0; x < ASSIGNMENTS_PER_SITE; x++) {
            AssignmentChoice assnChoice = getRandomAssignmentChoice();

            // Create device.
            DeviceCreateRequest request = new DeviceCreateRequest();
            request.setHardwareId(UUID.randomUUID().toString());
            request.setComments(assnChoice.getDeviceDescriptionBase() + " " + (x + 1) + ".");
            request.setAssetId(assnChoice.getDeviceAssetId());
            IDevice device = getDeviceManagement().createDevice(request);
            LOGGER.info(PREFIX_CREATE_DEVICE + " " + device.getHardwareId());

            // Create assignment.
            DeviceAssignmentCreateRequest assnRequest = new DeviceAssignmentCreateRequest();
            assnRequest.setAssignmentType(assnChoice.getAssignmentType());
            assnRequest.setAssetId(assnChoice.getAssignmentAssetId());
            assnRequest.setDeviceHardwareId(device.getHardwareId());
            assnRequest.setSiteToken(site.getToken());
            assnRequest.addOrReplaceMetadata("S/N", UUID.randomUUID().toString());
            IDeviceAssignment assignment = getDeviceManagement().createDeviceAssignment(assnRequest);
            LOGGER.info(PREFIX_CREATE_ASSIGNMENT + " " + assignment.getToken());

            // Create events for assignment.
            createDeviceMeasurements(assignment, now);
            createDeviceLocations(assignment, now);

            results.add(assignment);
        }
        return results;
    }

    /**
     * Create device measurements associated with an assignment.
     * 
     * @param assignment
     * @return
     * @throws SiteWhereException
     */
    protected List<IDeviceMeasurements> createDeviceMeasurements(IDeviceAssignment assignment, Date start)
            throws SiteWhereException {
        long current = start.getTime();
        double temp = MIN_TEMP;
        double fuel = 100;
        double delta = 4;
        double mult = 6;
        int measurementCount = 0;
        int alertCount = 0;
        List<IDeviceMeasurements> results = new ArrayList<IDeviceMeasurements>();
        DeviceMeasurementsCreateRequest lastMx = null;
        DeviceAlertCreateRequest lastAlert = null;
        for (int x = 0; x < EVENTS_PER_ASSIGNMENT; x++) {
            // Simulate temperature changes.
            temp = temp + (delta + ((Math.random() * mult * 2) - mult));
            temp = Math.round(temp * 100.0) / 100.0;
            if ((temp > MAX_TEMP) || (temp < MIN_TEMP)) {
                delta = -delta;
            }

            // Simulate fuel changes.
            fuel -= (Math.random() * 2);
            fuel = Math.round(fuel * 100.0) / 100.0;
            if (fuel < 0) {
                fuel = 0;
            }

            // Store current temperature measurement.
            DeviceMeasurementsCreateRequest mreq = new DeviceMeasurementsCreateRequest();
            mreq.addOrReplaceMeasurement("engine.temperature", temp);
            mreq.addOrReplaceMeasurement("fuel.level", fuel);
            mreq.setEventDate(new Date(current));
            results.add(getDeviceManagement().addDeviceMeasurements(assignment, mreq));
            lastMx = mreq;
            measurementCount++;

            // Create alerts based on current temperature.
            if (temp > WARN_TEMP) {
                DeviceAlertCreateRequest areq = new DeviceAlertCreateRequest();
                areq.setType("engine.overheat");
                areq.setEventDate(new Date(current));
                areq.setMessage("Engine temperature is at top of operating range.");
                areq.setLevel(AlertLevel.Warning);
                if (temp > ERROR_TEMP) {
                    areq.setMessage("Engine temperature is at a dangerous level.");
                    areq.setLevel(AlertLevel.Error);
                } else if (temp > CRITICAL_TEMP) {
                    areq.setMessage("Engine temperature critical. Shutting down.");
                    areq.setLevel(AlertLevel.Critical);
                    break;
                }
                getDeviceManagement().addDeviceAlert(assignment, areq);
                lastAlert = areq;
                alertCount++;
            }

            current += 10000;
        }
        LOGGER.info(PREFIX_CREATE_EVENTS + " " + measurementCount + " measurements. " + alertCount + " alerts.");

        // Update assignment state.
        DeviceEventBatch batch = new DeviceEventBatch();
        if (lastMx != null) {
            batch.getMeasurements().add(lastMx);
        }
        if (lastAlert != null) {
            batch.getAlerts().add(lastAlert);
        }
        getDeviceManagement().updateDeviceAssignmentState(assignment.getToken(), batch);
        return results;
    }

    /**
     * Create device locations in a path near the main zone.
     * 
     * @param assignment
     * @param start
     * @return
     * @throws SiteWhereException
     */
    protected List<IDeviceLocation> createDeviceLocations(IDeviceAssignment assignment, Date date)
            throws SiteWhereException {
        long current = date.getTime();
        Polygon zone = GeoUtils.createPolygonForLocations(zoneLocations);
        Point centroid = zone.getCentroid();

        // Calculate length of steps between locations based on bounding circle.
        MinimumBoundingCircle circle = new MinimumBoundingCircle(zone);
        double step = circle.getRadius() / 10;

        double cx = centroid.getX();
        double cy = centroid.getY();
        double deltaX = (Math.sqrt(Math.random()) * step * 2) - step;
        double deltaY = (Math.sqrt(Math.random()) * step * 2) - step;

        // Used to rotate deltas to turn path and stay inside polygon.
        AffineTransformation xform = new AffineTransformation();
        xform.rotate(Math.toRadians(22.5));

        List<IDeviceLocation> results = new ArrayList<IDeviceLocation>();
        GeometryFactory factory = new GeometryFactory();
        DeviceLocationCreateRequest lastLoc = null;
        for (int x = 0; x < LOCATIONS_PER_ASSIGNMENT; x++) {
            boolean foundNext = false;

            // Add a little randomness to path.
            double waver = ((Math.random() * 20) - 10.0);
            AffineTransformation waverXform = new AffineTransformation();
            waverXform.rotate(Math.toRadians(waver));
            Coordinate waverDelta = new Coordinate(deltaX, deltaY);
            waverXform.transform(waverDelta, waverDelta);
            deltaX = waverDelta.x;
            deltaY = waverDelta.y;

            while (!foundNext) {
                Coordinate start = new Coordinate(cx, cy);
                Coordinate end = new Coordinate(cx + deltaX, cy + deltaY);
                Coordinate[] lineCoords = { start, end };
                LineString line = factory.createLineString(lineCoords);
                if (zone.contains(line)) {
                    DeviceLocationCreateRequest request = new DeviceLocationCreateRequest();
                    request.setLatitude(end.y);
                    request.setLongitude(end.x);
                    request.setElevation(0.0);
                    request.setEventDate(new Date(current));
                    IDeviceLocation created = getDeviceManagement().addDeviceLocation(assignment, request);
                    lastLoc = request;
                    results.add(created);

                    cx = cx + deltaX;
                    cy = cy + deltaY;
                    foundNext = true;
                } else {
                    // Rotate deltas and try again.
                    Coordinate delta = new Coordinate(deltaX, deltaY);
                    xform.transform(delta, delta);
                    deltaX = delta.x;
                    deltaY = delta.y;
                }
            }
            current += 30000;
        }
        LOGGER.info(PREFIX_CREATE_EVENTS + " " + results.size() + " locations. ");

        // Update assignment state.
        if (lastLoc != null) {
            DeviceEventBatch batch = new DeviceEventBatch();
            batch.getLocations().add(lastLoc);
            getDeviceManagement().updateDeviceAssignmentState(assignment.getToken(), batch);
        }
        return results;
    }

    /**
     * Gets a random location tracker assignment choice entry.
     * 
     * @return
     */
    protected AssignmentChoice getRandomAssignmentChoice() {
        int slot = (int) Math.floor(LOCATION_TRACKERS.length * Math.random());
        return LOCATION_TRACKERS[slot];
    }

    /**
     * Internal class for choosing device/asset assignments that make sense.
     * 
     * @author Derek
     */
    private static class AssignmentChoice {

        private String devAssetId;
        private String devDescBase;
        private DeviceAssignmentType assnType;
        private String assnAssetId;

        public AssignmentChoice(String devAssetId, String devDescBase, DeviceAssignmentType assnType,
                String assnAssetId) {
            this.devAssetId = devAssetId;
            this.devDescBase = devDescBase;
            this.assnType = assnType;
            this.assnAssetId = assnAssetId;
        }

        protected String getDeviceAssetId() {
            return devAssetId;
        }

        protected String getDeviceDescriptionBase() {
            return devDescBase;
        }

        protected DeviceAssignmentType getAssignmentType() {
            return assnType;
        }

        protected String getAssignmentAssetId() {
            return assnAssetId;
        }
    }

    protected IDeviceManagement getDeviceManagement() {
        return deviceManagement;
    }

    protected void setDeviceManagement(IDeviceManagement deviceManagement) {
        this.deviceManagement = deviceManagement;
    }
}