package com.esri.geoevent.solutions.processor.stwa; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Observable; import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.esri.ges.core.ConfigurationException; import com.esri.ges.core.component.ComponentException; import com.esri.ges.core.geoevent.DefaultFieldDefinition; import com.esri.ges.core.geoevent.DefaultGeoEventDefinition; import com.esri.ges.core.geoevent.FieldDefinition; import com.esri.ges.core.geoevent.FieldException; import com.esri.ges.core.geoevent.FieldType; import com.esri.ges.core.geoevent.GeoEvent; import com.esri.ges.core.geoevent.GeoEventDefinition; import com.esri.ges.core.geoevent.GeoEventPropertyName; import com.esri.ges.core.validation.ValidationException; import com.esri.ges.manager.geoeventdefinition.GeoEventDefinitionManager; import com.esri.ges.manager.geoeventdefinition.GeoEventDefinitionManagerException; import com.esri.ges.messaging.EventDestination; import com.esri.ges.messaging.EventUpdatable; import com.esri.ges.messaging.GeoEventCreator; import com.esri.ges.messaging.GeoEventProducer; import com.esri.ges.messaging.Messaging; import com.esri.ges.messaging.MessagingException; import com.esri.ges.processor.GeoEventProcessorBase; import com.esri.ges.processor.GeoEventProcessorDefinition; public class STWAProcessor extends GeoEventProcessorBase implements Runnable, GeoEventProducer, EventUpdatable { private static final Log LOG = LogFactory .getLog(STWAProcessor.class); private boolean monitoring = false; private boolean running = false; private Integer interval; private String aggregateFld; private GeoEventDefinition gedout = null; private HashMap<Long, Double> cache = new HashMap<Long, Double>(); private GeoEventCreator geoEventCreator; private GeoEventDefinitionManager manager; private Messaging messaging; private GeoEventProducer geoEventProducer; //private Double sum = Double.NaN; //private Double mean = Double.NaN;; //private Double variance = Double.NaN; //private Double sd = Double.NaN; private long timestamp; private Thread t; private ArrayList<FieldType>typelist = new ArrayList<FieldType>(); //private List<FieldDefinition> appendFields; public STWAProcessor(GeoEventProcessorDefinition definition) throws ComponentException { super(definition); typelist.add(FieldType.Integer); typelist.add(FieldType.Short); typelist.add(FieldType.Long); typelist.add(FieldType.Float); typelist.add(FieldType.Double); } @Override public void send(GeoEvent geoEvent) throws MessagingException { if (geoEventProducer != null && geoEvent != null) geoEventProducer.send(geoEvent); } @Override public void setId(String id) { super.setId(id); geoEventProducer = messaging .createGeoEventProducer(new EventDestination(id + ":event")); } @Override public void afterPropertiesSet() { try { aggregateFld = properties.get("aggregate").getValueAsString(); interval = (Integer) properties.get("interval").getValue(); ArrayList<FieldDefinition>fields = new ArrayList<FieldDefinition>(); this.running = true; String countFld = aggregateFld + "_count"; FieldDefinition fdCount = new DefaultFieldDefinition(countFld, FieldType.Integer, "AGGREGATE_COUNT"); String sumFld = aggregateFld + "_sum"; FieldDefinition fdSum = new DefaultFieldDefinition(sumFld, FieldType.Double, "AGGREGATE_SUM"); String meanFld = aggregateFld + "_mean"; FieldDefinition fdMean = new DefaultFieldDefinition(meanFld, FieldType.Double, "AGGREGATE_MEAN"); String sdFld = aggregateFld + "_stddev"; FieldDefinition fdsd = new DefaultFieldDefinition(sdFld, FieldType.Double, "AGGREGATE_STANDARD_DEVIATION"); String sdVar = aggregateFld + "_var"; FieldDefinition fdvar = new DefaultFieldDefinition(sdVar, FieldType.Double, "AGGREGATE_VARIANCE"); String sdts = "timestamp"; FieldDefinition fdts = new DefaultFieldDefinition(sdts, FieldType.Date, "TIMESTAMP"); fields.add(fdCount); fields.add(fdSum); fields.add(fdMean); fields.add(fdvar); fields.add(fdsd); fields.add(fdts); String outgedname = "aggregate_" + aggregateFld; Collection<GeoEventDefinition> gedColl = manager.searchGeoEventDefinitionByName(outgedname); if(gedColl.isEmpty()) { gedout = new DefaultGeoEventDefinition(); gedout.setFieldDefinitions(fields); gedout.setOwner(getId()); gedout.setName(outgedname); manager.addGeoEventDefinition(gedout); } else { gedout = gedColl.iterator().next(); } } catch (ConfigurationException e) { LOG.error(e.getMessage()); } catch (GeoEventDefinitionManagerException e) { LOG.error(e.getMessage()); } } public GeoEvent process(GeoEvent evt) throws Exception { GeoEventDefinition ged = evt.getGeoEventDefinition(); FieldDefinition fd = ged.getFieldDefinition(aggregateFld); if (fd == null) return null; FieldType type = fd.getType(); if (!typelist.contains(type)) { return null; } Double val = null; if (type == FieldType.Double) { val = (Double) evt.getField(aggregateFld); } else if (type == FieldType.Integer) { val = ((Integer) evt.getField(aggregateFld)) * 1.0; } else if (type == FieldType.Long) { val = ((Long) evt.getField(aggregateFld)) * 1.0; } else if (type == FieldType.Short) { val = ((Short) evt.getField(aggregateFld)) * 1.0; } else if (type == FieldType.Float) { val = ((Float) evt.getField(aggregateFld)) * 1.0; } if(val == null) { return null; } timestamp = System.currentTimeMillis(); cache.put(timestamp, val); return null; } private GeoEvent createAggregateGeoEvent(Integer count, Double sum, Double mean, Double variance, Double sd) throws MessagingException, FieldException { try { if(gedout == null) return null; GeoEvent outevt = geoEventCreator.create(gedout.getGuid()); outevt.setField("AGGREGATE_COUNT", count); outevt.setField("AGGREGATE_MEAN", mean); outevt.setField("AGGREGATE_STANDARD_DEVIATION", sd); outevt.setField("AGGREGATE_SUM", sum); outevt.setField("AGGREGATE_VARIANCE", variance); long now = System.currentTimeMillis(); Date time = new Date(now); outevt.setField("TIMESTAMP", time); outevt.setProperty(GeoEventPropertyName.TYPE, "event"); outevt.setProperty(GeoEventPropertyName.OWNER_ID, getId()); outevt.setProperty(GeoEventPropertyName.OWNER_URI, definition.getUri()); return outevt; } catch (MessagingException e) { throw (e); } catch (FieldException e) { throw (e); } } private synchronized void updateCache() { try { HashMap<Long, Double> workingCache = cache; Integer count = workingCache.size(); if(count==0) return; boolean isEmpty = workingCache.isEmpty(); if (isEmpty) { workingCache.clear(); cache.clear(); return; } Set<Long> keys = workingCache.keySet(); Iterator<Long> it = keys.iterator(); long now = System.currentTimeMillis(); Double currentsum = 0.0; Double currentMean = 0.0; Double varsum = 0.0; //int count = 0; while (it.hasNext()) { long ts = it.next(); if (now - ts > interval) { cache.remove(ts); workingCache.remove(ts); } else { currentsum += workingCache.get(ts); //count += 1; } } Double sum = currentsum; currentMean = sum / count; it = keys.iterator(); while (it.hasNext()) { long ts = it.next(); Double x = workingCache.get(ts); varsum += Math.pow((x - currentMean), 2); } Double variance = varsum / count; Double sd = Math.sqrt(variance); Double mean = currentMean; GeoEvent msg = createAggregateGeoEvent(count, sum, mean, variance, sd); send(msg); } catch (MessagingException | FieldException e) { LOG.error(e.getMessage()); } } @Override public synchronized void validate() throws ValidationException { // Validation Phase ... super.validate(); } @Override public void onServiceStart() { startMonitoring(); } @Override public void onServiceStop() { stopMonitoring(); } @Override public void shutdown() { cache.clear(); super.shutdown(); } @Override public boolean isGeoEventMutator() { return true; } @Override public EventDestination getEventDestination() { return (geoEventProducer != null) ? geoEventProducer .getEventDestination() : null; } @Override public List<EventDestination> getEventDestinations() { return (geoEventProducer != null) ? Arrays.asList(geoEventProducer .getEventDestination()) : new ArrayList<EventDestination>(); } @Override public void disconnect() { if (geoEventProducer != null) geoEventProducer.disconnect(); } @Override public boolean isConnected() { return (geoEventProducer != null) ? geoEventProducer.isConnected() : false; } @Override public String getStatusDetails() { return (geoEventProducer != null) ? geoEventProducer.getStatusDetails() : ""; } @Override public void setup() throws MessagingException { ; } @Override public void init() throws MessagingException { ; } @Override public void update(Observable o, Object arg) { ; } public void setManager(GeoEventDefinitionManager manager) { this.manager = manager; } public void setMessaging(Messaging messaging) { this.messaging = messaging; this.geoEventCreator = messaging.createGeoEventCreator(); } private void startMonitoring() { try { this.monitoring=true; t = new Thread(this); t.start(); } catch (Exception e) { e.printStackTrace(); } } private void stopMonitoring() { this.monitoring=false; cache.clear(); t.interrupt(); } @Override public void run() { while (this.monitoring) { try { //Thread.sleep(5); if (running) { updateCache(); } } catch (Exception e) { } } } }