/** * * @author greg (at) myrobotlab.org * * This file is part of MyRobotLab (http://myrobotlab.org). * * MyRobotLab is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version (subject to the "Classpath" exception * as provided in the LICENSE.txt file that accompanied this code). * * MyRobotLab is distributed in the hope that it will be useful or fun, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * All libraries in thirdParty bundle are subject to their own license * requirements - please refer to http://myrobotlab.org/libraries for * details. * * Enjoy ! * * TODO - generalize all tracing to a static image package (share with Arduino) * * * */ package org.myrobotlab.service; import java.util.HashMap; import org.myrobotlab.framework.Message; import org.myrobotlab.framework.Service; import org.myrobotlab.framework.ServiceType; import org.myrobotlab.logging.Level; import org.myrobotlab.logging.LoggerFactory; import org.myrobotlab.logging.Logging; import org.myrobotlab.logging.LoggingFactory; import org.myrobotlab.service.data.Pin; import org.myrobotlab.service.data.Trigger; import org.slf4j.Logger; /** * * SensorMonitor - he SensorMonitor takes a variety of input data and displays * it to a user interface. It also has the capability of adding alerts. An alert * would be triggered if a sensor goes above or below some threshold. * */ public class SensorMonitor extends Service { private static final long serialVersionUID = 1L; public final static Logger log = LoggerFactory.getLogger(SensorMonitor.class.getCanonicalName()); public HashMap<String, Trigger> triggers = new HashMap<String, Trigger>(); public HashMap<String, Trigger> triggers_nameIndex = new HashMap<String, Trigger>(); public HashMap<String, Pin> lastValue = new HashMap<String, Pin>(); public static void main(String[] args) throws InterruptedException { LoggingFactory.init(Level.DEBUG); try { SensorMonitor sm = new SensorMonitor("sensors"); sm.startService(); Runtime.createAndStart("arduino", "Arduino"); Runtime.createAndStart("gui", "GUIService"); /* * * Random rand = new Random(); for (int i = 0; i < 10000; ++i) { Message * msg = new Message(); msg.name="sensors"; msg.sender="SEAR"; * msg.method="input"; Float[] gps = new Float[]{rand.nextFloat()*200, * rand.nextFloat()*200, rand.nextFloat()*200}; msg.data = new * Object[]{gps}; //msg.data = new Object[]{rand.nextFloat()*200}; * sm.in(msg); } */ } catch (Exception e) { Logging.logError(e); } } final static public String makeKey(Pin pinData) { return makeKey(pinData.source, pinData.pin); } final static public String makeKey(String source, Integer pin) { return String.format("%s_%d", source, pin); } public SensorMonitor(String n) { super(n); } /* * publishing point to add trace data to listeners (like the gui) */ public Pin addTraceData(Pin pinData) { return pinData; } public final void addTrigger(String source, String name, int min, int max, int type, int delay, int targetPin) { Trigger pa = new Trigger(name, min, max, type, delay, targetPin); triggers.put(makeKey(source, targetPin), pa); triggers_nameIndex.put(name, pa); } // sensorInput - an input point for sensor info public final void addTrigger(Trigger trigger) { if (trigger.pinData.source == null) { log.error("addTrigger adding trigger with no source controller - will be based on pin only ! " + trigger.pinData.pin); } triggers.put(makeKey(trigger.pinData), trigger); triggers_nameIndex.put(trigger.name, trigger); } public int getLastValue(String source, Integer pin) { String key = makeKey(source, pin); if (lastValue.containsKey(key)) { return lastValue.get(key).value; } log.error("getLastValue for pin " + key + " does not exist"); return -1; } @Override public boolean preProcessHook(Message m) // FIXME - WTF??? { if (m.method.equals("input")) { if (m.data.length != 1) { log.error("where's my data"); return false; } Object data = m.data[0]; if (data instanceof Float) { Pin pinData = new Pin(0, 0, ((Float) data).intValue(), m.sender); sensorInput(pinData); } return false; } return true; } public Trigger publishPinTrigger(Trigger trigger) { return trigger; } public String publishPinTriggerText(Trigger trigger) { return trigger.name; } // output public Pin publishSensorData(Pin pinData) { // TODO - wrap with more info if possible return pinData; } public void removeTrigger(String name) { if (triggers_nameIndex.containsKey(name)) { triggers.remove(name); triggers_nameIndex.remove(name); } else { log.error("removeTrigger " + name + " not found"); } } /** * sensorInput is the destination of sensor data all types will funnel into a * pinData type - this is used to standardize and simplify the display. * Additionally, the source can be attached so that trace lines can be * identified * * @param pinData */ public void sensorInput(Pin pinData) { String key = makeKey(pinData); if (triggers.containsKey(key)) { Trigger trigger = triggers.get(key); if (trigger.threshold < pinData.value) { trigger.pinData = pinData; invoke("publishPinTrigger", trigger); invoke("publishPinTriggerText", trigger);// FIXME - deprecate - // silly triggers.remove(key); } } if (!lastValue.containsKey(key)) { lastValue.put(key, pinData); } lastValue.get(key).value = pinData.value; invoke("publishSensorData", pinData); } /** * This static method returns all the details of the class without it having * to be constructed. It has description, categories, dependencies, and peer * definitions. * * @return ServiceType - returns all the data * */ static public ServiceType getMetaData() { ServiceType meta = new ServiceType(SensorMonitor.class.getCanonicalName()); meta.addDescription("sensor monitor - capable of displaying sensor information in a crude oscilliscope fasion"); meta.addCategory("sensor", "display"); return meta; } }