Update of /cvsroot/jrobin/src/org/jrobin/data In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv10251/org/jrobin/data Modified Files: RpnCalculator.java Added Files: CDef.java DataProcessor.java Def.java PDef.java SDef.java Source.java Removed Files: DataStats.java Log Message: Added org.java.data.DataProcessor class. This class should be used to get datasource values for graphing purposes. AVERAGEs and TOTALs returned by this class will not change when the graph is resized. RPN calculator is greatly improved (it's very fast). --- NEW FILE: DataProcessor.java --- /* ============================================================ * JRobin : Pure java implementation of RRDTool's functionality * ============================================================ * * Project Info: http://www.jrobin.org * Project Lead: Sasa Markovic (sa...@jr...); * * (C) Copyright 2003, by Sasa Markovic. * * Developers: Sasa Markovic (sa...@jr...) * Arne Vandamme (cob...@jr...) * * This library is free software; you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Foundation; * either version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with this * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. */ package org.jrobin.data; import org.jrobin.graph.Plottable; import org.jrobin.core.*; import java.util.*; import java.io.IOException; public class DataProcessor implements ConsolFuns { public static final int DEFAUL_PIXEL_COUNT = 400; private int pixelCount = DEFAUL_PIXEL_COUNT; private int pixelsPerStep = 1; private boolean poolUsed = false; final private long tStart, tEnd; private double[] timestamps; private Map sources = new LinkedHashMap(); public DataProcessor(long tStart, long tEnd) { assert tStart < tEnd: "Invalid time span while constructing DataAnalyzer"; this.tStart = tStart; this.tEnd = tEnd; createTimestamps(); } ///////////////////////////////////////////////////////////////// // BASIC FUNCTIONS ///////////////////////////////////////////////////////////////// private void createTimestamps() { timestamps = new double[pixelCount]; final double span = tEnd - tStart; for(int i = 0; i < pixelCount; i++) { timestamps[i] = tStart + ((double) i / (double)(pixelCount - 1)) * span; } } public boolean isPoolUsed() { return poolUsed; } public void setPoolUsed(boolean poolUsed) { this.poolUsed = poolUsed; } public int getPixelCount() { return pixelCount; } public void setPixelCount(int pixelCount) { this.pixelCount = pixelCount; createTimestamps(); } public void setStep(long step) { double secondsPerPixel = getSecondsPerPixel(); pixelsPerStep = Math.max((int) Math.ceil(step / secondsPerPixel), 1); } private double getSecondsPerPixel() { return (double) (tEnd - tStart) / (double) (pixelCount - 1); } public double[] getTimestamps() { return timestamps; } public double[] getValues(String sourceName) throws RrdException { Source source = getSource(sourceName); double[] values = source.getValues(); if(values == null) { throw new RrdException("Values not available for source [" + sourceName + "]"); } return values; } public double getAggregate(String sourceName, String consolFun) throws RrdException { Source source = getSource(sourceName); return source.getAggregate(consolFun, getSecondsPerPixel()); } public String[] getSourceNames() { return (String[]) sources.keySet().toArray(new String[0]); } private Source getSource(String sourceName) throws RrdException { Object source = sources.get(sourceName); if(source != null) { return (Source) source; } throw new RrdException("Unknown source: " + sourceName); } ///////////////////////////////////////////////////////////////// // DATASOURCE DEFINITIONS ///////////////////////////////////////////////////////////////// public void addDatasource(String name, Plottable plottable) { PDef pDef = new PDef(name, plottable); sources.put(name, pDef); } public void addDatasource(String name, Plottable plottable, int index) { PDef pDef = new PDef(name, plottable, index); sources.put(name, pDef); } public void addDatasource(String name, Plottable plottable, String sourceName) { PDef pDef = new PDef(name, plottable, sourceName); sources.put(name, pDef); } public void addDatasource(String name, String rpnExpression) { CDef cDef = new CDef(name, rpnExpression); sources.put(name, cDef); } public void addDatasource(String name, String defName, String consolFun) { SDef sDef = new SDef(name, defName, consolFun); sources.put(name, sDef); } public void addDatasource(String name, String file, String dsName, String consolFunc) { Def def = new Def(name, file, dsName, consolFunc); sources.put(name, def); } public void addDatasource(String name, String file, String dsName, String consolFunc, String backend) { Def def = new Def(name, file, dsName, consolFunc, backend); sources.put(name, def); } ///////////////////////////////////////////////////////////////// // MAIN FUNCTION ///////////////////////////////////////////////////////////////// public void processData() throws IOException, RrdException { calculateDefs(); calculatePDefs(); calculateSdefsAndCdefs(); } ///////////////////////////////////////////////////////////////// // DEFS CALCULATION ///////////////////////////////////////////////////////////////// private void calculateDefs() throws IOException, RrdException { Def[] defs = getDefs(); for (int i = 0; i < defs.length; i++) { if (defs[i].getValues() == null) { // not fetched yet Set dsNames = new HashSet(); dsNames.add(defs[i].getDsName()); // look for all other datasources with the same path and the same consolidation function for (int j = i + 1; j < defs.length; j++) { if (defs[i].isCompatibleWith(defs[j])) { dsNames.add(defs[j].getDsName()); } } // now we have everything RrdDb rrd = null; try { rrd = getRrd(defs[i]); FetchRequest req = rrd.createFetchRequest(defs[i].getConsolFun(), tStart, tEnd); req.setFilter(dsNames); FetchData data = req.fetchData(); //System.out.println(data.getAggregate(defs[i].getDsName(), "AVERAGE")); double[] values = data.getValues(defs[i].getDsName()); normalizeDefValues(data.getTimestamps(), values, defs[i]); for (int j = i + 1; j < defs.length; j++) { if (defs[i].isCompatibleWith(defs[j])) { //System.out.println(data.getAggregate(defs[j].getDsName(), "AVERAGE")); values = data.getValues(defs[j].getDsName()); normalizeDefValues(data.getTimestamps(), values, defs[j]); } } } finally { if (rrd != null) { releaseRrd(rrd, defs[i]); } } } } } private void normalizeDefValues(long[] dsTimestamps, double[] dsValues, Def def) { double[] values = new double[pixelCount]; int dsSegment = 1, accumPixels = 0; values[0] = (tStart == dsTimestamps[0])? dsValues[0]: dsValues[1]; double totalValue = 0D, totalTime = 0D; for(int pixel = 1; pixel < pixelCount; pixel++) { double t0 = timestamps[pixel - 1], t1 = timestamps[pixel]; while(t0 < t1) { double tLimit = Math.min(dsTimestamps[dsSegment], t1); double dt = tLimit - t0; double val = dsValues[dsSegment]; if(!Double.isNaN(val)) { totalValue += val * dt; totalTime += dt; } t0 = tLimit; if(t0 == dsTimestamps[dsSegment]) { dsSegment++; } } if(++accumPixels == pixelsPerStep || pixel == pixelCount - 1) { double average = (totalTime > 0)? totalValue / totalTime: Double.NaN; for(int i = 0; i < accumPixels; i++) { values[pixel - i] = average; } totalValue = totalTime = 0; accumPixels = 0; } } def.setValues(values); } private Def[] getDefs() { List defs = new ArrayList(); Iterator it = sources.values().iterator(); while (it.hasNext()) { Object source = it.next(); if(source instanceof Def) { defs.add(source); } } return (Def[]) defs.toArray(new Def[defs.size()]); } private RrdDb getRrd(Def def) throws IOException, RrdException { String path = def.getPath(), backend = def.getBackend(); if (poolUsed && backend == null) { return RrdDbPool.getInstance().requestRrdDb(path); } else if (backend != null) { return new RrdDb(path, true, RrdBackendFactory.getFactory(backend)); } else { return new RrdDb(path, true); } } private void releaseRrd(RrdDb rrd, Def def) throws IOException, RrdException { String backend = def.getBackend(); if (poolUsed && backend == null) { RrdDbPool.getInstance().release(rrd); } else { rrd.close(); } } ///////////////////////////////////////////////////////////////// // PLOTTABLE CALCULATION ///////////////////////////////////////////////////////////////// private void calculatePDefs() { PDef[] pDefs = getPDefs(); for(int i = 0; i < pDefs.length; i++) { double[] values = new double[pixelCount]; for(int j = 0; j < pixelCount; j++) { double t = timestamps[j]; values[j] = pDefs[i].getValue(t); } pDefs[i].setValues(values); } } private PDef[] getPDefs() { List pDefs = new ArrayList(); Iterator it = sources.values().iterator(); while (it.hasNext()) { Object source = it.next(); if(source instanceof PDef) { pDefs.add(source); } } return (PDef[]) pDefs.toArray(new PDef[pDefs.size()]); } ///////////////////////////////////////////////////////////////// // SDEFS AND CDEFS ///////////////////////////////////////////////////////////////// private void calculateSdefsAndCdefs() throws RrdException { Iterator it = sources.values().iterator(); while (it.hasNext()) { Object source = it.next(); if(source instanceof SDef) { calculateSDef((SDef) source); } else if(source instanceof CDef) { calculateCDef((CDef) source); } } } private void calculateCDef(CDef cDef) throws RrdException { RpnCalculator calc = new RpnCalculator(cDef.getRpnExpression(), cDef.getName(), this); cDef.setValues(calc.calculateValues()); } private void calculateSDef(SDef sDef) throws RrdException { String defName = sDef.getDefName(); String consolFun = sDef.getConsolFun(); Source source = getSource(defName); double value = source.getAggregate(consolFun, getSecondsPerPixel()); sDef.setValue(value, pixelCount); } ///////////////////////////////////////////////////////////////// // TRIVIA ///////////////////////////////////////////////////////////////// /** * Dumps timestamps and values of all datasources in a tabelar form. Very useful for debugging. * @return Dumped object content */ public String dump() throws RrdException { String[] names = getSourceNames(); double[][] values = new double[names.length][]; for(int i = 0; i < names.length; i++) { values[i] = getValues(names[i]); } StringBuffer buffer = new StringBuffer(); buffer.append(fmt("timestamp", 12)); for(int i = 0; i < names.length; i++) { buffer.append(fmt(names[i], 20)); } buffer.append("\n"); for(int i = 0; i < timestamps.length; i++) { long t = (long) Math.round(timestamps[i]); buffer.append(fmt("" + t, 12)); for(int j = 0; j < names.length; j++) { buffer.append(fmt(Util.formatDouble(values[j][i]), 20)); } buffer.append("\n"); } return buffer.toString(); } private static String fmt(String s, int length) { StringBuffer b = new StringBuffer(s); for(int i = 0; i < length - s.length(); i++) { b.append(' '); } return b.toString(); } public static void main(String[] args) throws IOException, RrdException { final long t1 = Util.getTimestamp(2003, 4, 1); final long t2 = Util.getTimestamp(2003, 5, 1); DataProcessor da = new DataProcessor(t1, t2); da.addDatasource("x", "demo.rrd", "sun", "AVERAGE"); da.addDatasource("y", "demo.rrd", "shade", "AVERAGE"); da.addDatasource("zzzz", "x,y,+,2,/"); da.addDatasource("w", "PREV(x),PREV(y),+,2,/,1000,/"); da.setStep(86400); da.setPixelCount(100); da.processData(); //System.out.println("x=" + da.getAggregate("x", "AVERAGE")); //System.out.println("y=" + da.getAggregate("y", "AVERAGE")); //System.out.println(da.getAggregate("y", "MIN")); //System.out.println(da.getAggregate("y", "AVERAGE")); //System.out.println(da.getAggregate("y", "MAX")); System.out.println(da.dump()); } } Index: RpnCalculator.java =================================================================== RCS file: /cvsroot/jrobin/src/org/jrobin/data/RpnCalculator.java,v retrieving revision 1.1 retrieving revision 1.2 diff -C2 -d -r1.1 -r1.2 *** RpnCalculator.java 21 Nov 2004 13:25:39 -0000 1.1 --- RpnCalculator.java 25 Nov 2004 12:14:54 -0000 1.2 *************** *** 30,70 **** import java.util.StringTokenizer; ! import java.util.Map; ! import java.util.HashMap; ! ! /** ! * Class which implements simple RPN calculator (RRDTool-like). <p> ! * To calculate the value of expression: ! * <pre> ! * square_root[(x + y) * (x-y)] ! * </pre> ! * for <code>x=5, y=4</code>, than for <code>x=6, y=4</code>, use the following code: ! * <pre> ! * RpnCalculator c = new RpnCalculator("x,y,+,x,y,-,*,SQRT"); ! * c.setValue("x", 5); ! * c.setValue("y", 4); ! * System.out.println(c.calculate()); ! * // change the value of "x", and leave "y" as before ! * c.setValue("x", 6); ! * System.out.println(c.calculate()); ! * </pre> ! * Notes:<p> ! * <ul> ! * <li>If you call the {@link #setValue(double)} method with just one double argument, ! * it will set the value of variable named "value" by default. ! * <li>The method {@link #setTimestamp(long)} will set the value of variable "timestamp". ! * This special variable can be referenced in the RPN expression by using the token TIME. ! * <li>Once set, variable values remain preserved between consecutive {@link #calculate()} calls. You can overwrite ! * this values by calling the {@link #setValue(String, double)} method again. To get rid of all variable values, ! * use method {@link #clearValues()}. ! * </ul> ! * ! */ ! public class RpnCalculator { ! /** Default variable name for the {@link #setValue(double)} method ("value") */ ! public static final String VALUE_PLACEHOLDER = "value"; ! /** Default variable name for the {@link #setTimestamp(long)} method ("timestamp") */ ! public static final String TIMESTAMP_PLACEHOLDER = "timestamp"; private static final byte TKN_VAR = 0; private static final byte TKN_NUM = 1; --- 30,37 ---- import java.util.StringTokenizer; ! import java.util.Calendar; ! import java.util.GregorianCalendar; + class RpnCalculator { private static final byte TKN_VAR = 0; private static final byte TKN_NUM = 1; *************** *** 78,82 **** private static final byte TKN_LOG = 9; private static final byte TKN_EXP = 10; ! private static final byte TKN_FLOOR = 11; private static final byte TKN_CEIL = 12; private static final byte TKN_ROUND = 13; --- 45,49 ---- private static final byte TKN_LOG = 9; private static final byte TKN_EXP = 10; ! private static final byte TKN_FLOOR = 11; private static final byte TKN_CEIL = 12; private static final byte TKN_ROUND = 13; *************** *** 104,455 **** private static final byte TKN_E = 35; private static final byte TKN_AND = 36; ! private static final byte TKN_OR = 37; private static final byte TKN_XOR = 38; - private Map values = new HashMap(); private Token[] tokens; private RpnStack stack = new RpnStack(); ! private String rpnExpression; ! ! /** ! * Creates new RpnCalculator. RpnCalculator objects may be safely reused to calculate as many ! * expression values (for different variable values) as needed. ! * @param rpnExpression RPN expression to be used. RPN tokens should be comma (",") ! * or space (" ") delimited. ! */ ! public RpnCalculator(String rpnExpression) { this.rpnExpression = rpnExpression; ! createTokens(); ! } ! ! private void createTokens() { StringTokenizer st = new StringTokenizer(rpnExpression, ", "); tokens = new Token[st.countTokens()]; ! for(int i = 0; st.hasMoreTokens(); i++) { tokens[i] = createToken(st.nextToken()); } } ! private Token createToken(String str) { ! Token token = new Token(str); ! if(Util.isDouble(str)) { token.id = TKN_NUM; ! token.number = Util.parseDouble(str); } ! else if(str.equals("+")) { token.id = TKN_PLUS; } ! else if(str.equals("-")) { token.id = TKN_MINUS; } ! else if(str.equals("*")) { token.id = TKN_MULT; } ! else if(str.equals("/")) { token.id = TKN_DIV; } ! else if(str.equals("%")) { token.id = TKN_MOD; } ! else if(str.equals("SIN")) { token.id = TKN_SIN; } ! else if(str.equals("COS")) { token.id = TKN_COS; } ! else if(str.equals("LOG")) { token.id = TKN_LOG; } ! else if(str.equals("EXP")) { token.id = TKN_EXP; } ! else if(str.equals("FLOOR")) { token.id = TKN_FLOOR; } ! else if(str.equals("CEIL")) { token.id = TKN_CEIL; } ! else if(str.equals("ROUND")) { token.id = TKN_ROUND; } ! else if(str.equals("POW")) { token.id = TKN_POW; } ! else if(str.equals("ABS")) { token.id = TKN_ABS; } ! else if(str.equals("SQRT")) { token.id = TKN_SQRT; } ! else if(str.equals("RANDOM")) { token.id = TKN_RANDOM; } ! else if(str.equals("LT")) { token.id = TKN_LT; } ! else if(str.equals("LE")) { token.id = TKN_LE; } ! else if(str.equals("GT")) { token.id = TKN_GT; } ! else if(str.equals("GE")) { token.id = TKN_GE; } ! else if(str.equals("EQ")) { token.id = TKN_EQ; } ! else if(str.equals("IF")) { token.id = TKN_IF; } ! else if(str.equals("MIN")) { token.id = TKN_MIN; } ! else if(str.equals("MAX")) { token.id = TKN_MAX; } ! else if(str.equals("LIMIT")) { token.id = TKN_LIMIT; } ! else if(str.equals("DUP")) { token.id = TKN_DUP; } ! else if(str.equals("EXC")) { token.id = TKN_EXC; } ! else if(str.equals("POP")) { token.id = TKN_POP; } ! else if(str.equals("UN")) { token.id = TKN_UN; } ! else if(str.equals("UNKN")) { token.id = TKN_UNKN; } ! else if(str.equals("NOW")) { token.id = TKN_NOW; } ! else if(str.equals("TIME")) { token.id = TKN_TIME; } ! else if(str.equals("PI")) { token.id = TKN_PI; } ! else if(str.equals("E")) { token.id = TKN_E; } ! else if(str.equals("AND")) { token.id = TKN_AND; } ! else if(str.equals("OR")) { token.id = TKN_OR; } ! else if(str.equals("XOR")) { token.id = TKN_XOR; } else { token.id = TKN_VAR; } return token; } ! /** ! * Sets the value for the default variable if RPN expression ("value"). ! * @param value Value to be used in calculation ! */ ! public void setValue(double value) { ! setValue(VALUE_PLACEHOLDER, value); ! } ! ! /** ! * Sets the timestamp to be used in evaluation of the RPN expression. To use this ! * value in the RPN expression, use token TIME. ! * @param timestamp The value which will be used if token TIME is found in the RPN expression ! */ ! public void setTimestamp(long timestamp) { ! setValue(TIMESTAMP_PLACEHOLDER, timestamp); ! } ! ! /** ! * Sets new value for a variable in the RPN expression. ! * @param name Variable name ! * @param value Variable value ! */ ! public void setValue(String name, double value) { ! values.put(name, new Double(value)); ! } ! ! /** ! * Clears all values specified for variables in the RPN expression ! */ ! public void clearValues() { ! values.clear(); ! } ! ! /** ! * Evaluates RPN expression, by replacing variable placeholders with specified values. You are free ! * to call this method as many times as needed, with the same or modified variable values. ! * @return The value of the RPN expression ! * @throws org.jrobin.core.RrdException Thrown if some variable values are not specified before this method is called, or if the ! * RPN expression is not valid. ! */ ! public double calculate() throws RrdException { ! resetStack(); ! for(int i = 0; i < tokens.length; i++) { ! Token token = tokens[i]; ! double x1, x2, x3; ! switch(token.id) { ! case TKN_NUM: ! push(token.number); ! break; ! case TKN_VAR: ! push(getValue(token.str)); ! break; ! case TKN_PLUS: ! push(pop() + pop()); ! break; ! case TKN_MINUS: ! x2 = pop(); x1 = pop(); ! push(x1 - x2); ! break; ! case TKN_MULT: ! push(pop() * pop()); ! break; ! case TKN_DIV: ! x2 = pop(); x1 = pop(); ! push(x1 / x2); ! break; ! case TKN_MOD: ! x2 = pop(); x1 = pop(); ! push(x1 % x2); ! break; ! case TKN_SIN: ! push(Math.sin(pop())); ! break; ! case TKN_COS: ! push(Math.cos(pop())); ! break; ! case TKN_LOG: ! push(Math.log(pop())); ! break; ! case TKN_EXP: ! push(Math.exp(pop())); ! break; ! case TKN_FLOOR: ! push(Math.floor(pop())); ! break; ! case TKN_CEIL: ! push(Math.ceil(pop())); ! break; ! case TKN_ROUND: ! push(Math.round(pop())); ! break; ! case TKN_POW: ! x2 = pop(); x1 = pop(); ! push(Math.pow(x1, x2)); ! break; ! case TKN_ABS: ! push(Math.abs(pop())); ! break; ! case TKN_SQRT: ! push(Math.sqrt(pop())); ! break; ! case TKN_RANDOM: ! push(Math.random()); ! break; ! case TKN_LT: ! x2 = pop(); x1 = pop(); ! push(x1 < x2? 1: 0); ! break; ! case TKN_LE: ! x2 = pop(); x1 = pop(); ! push(x1 <= x2? 1: 0); ! break; ! case TKN_GT: ! x2 = pop(); x1 = pop(); ! push(x1 > x2? 1: 0); ! break; ! case TKN_GE: ! x2 = pop(); x1 = pop(); ! push(x1 >= x2? 1: 0); ! break; ! case TKN_EQ: ! x2 = pop(); x1 = pop(); ! push(x1 == x2? 1: 0); ! break; ! case TKN_IF: ! x3 = pop(); x2 = pop(); x1 = pop(); ! push(x1 != 0? x2: x3); ! break; ! case TKN_MIN: ! push(Math.min(pop(), pop())); ! break; ! case TKN_MAX: ! push(Math.max(pop(), pop())); ! break; ! case TKN_LIMIT: ! x3 = pop(); x2 = pop(); x1 = pop(); ! push(x1 < x2 || x1 > x3? Double.NaN: x1); ! break; ! case TKN_DUP: ! push(peek()); ! break; ! case TKN_EXC: ! x2 = pop(); x1 = pop(); ! push(x2); ! push(x1); ! break; ! case TKN_POP: ! pop(); ! break; ! case TKN_UN: ! push(Double.isNaN(pop())? 1: 0); ! break; ! case TKN_UNKN: ! push(Double.NaN); ! break; ! case TKN_NOW: ! push(Util.getTime()); ! break; ! case TKN_TIME: ! push(getValue(TIMESTAMP_PLACEHOLDER)); ! break; ! case TKN_PI: ! push(Math.PI); ! break; ! case TKN_E: ! push(Math.E); ! break; ! case TKN_AND: ! x2 = pop(); x1 = pop(); ! push((x1 != 0 && x2 != 0)? 1: 0); ! break; ! case TKN_OR: ! x2 = pop(); x1 = pop(); ! push((x1 != 0 || x2 != 0)? 1: 0); ! break; ! case TKN_XOR: ! x2 = pop(); x1 = pop(); ! push(((x1 != 0 && x2 == 0) || (x1 == 0 && x2 != 0))? 1: 0); ! break; ! default: ! throw new RrdException("Unexpected RPN token encountered [" + ! token.id + "," + token.str + "]"); } } ! double retVal = pop(); ! if(!isStackEmpty()) { ! throw new RrdException("Stack not empty at the end of calculation. " + ! "Probably bad RPN expression [" + rpnExpression + "]"); ! } ! return retVal; } ! private double getValue(String varName) throws RrdException { ! if(values.containsKey(varName)) { ! return ((Double) values.get(varName)).doubleValue(); ! } ! throw new RrdException("Value of variable [" + varName + "] not specified"); } --- 71,485 ---- private static final byte TKN_E = 35; private static final byte TKN_AND = 36; ! private static final byte TKN_OR = 37; private static final byte TKN_XOR = 38; + private static final byte TKN_PREV = 39; + private static final byte TKN_INF = 40; + private static final byte TKN_NEGINF = 41; + private static final byte TKN_STEP = 42; + private static final byte TKN_YEAR = 43; + private static final byte TKN_MONTH = 44; + private static final byte TKN_DATE = 45; + private static final byte TKN_HOUR = 46; + private static final byte TKN_MINUTE = 47; + private static final byte TKN_SECOND = 48; + private static final byte TKN_WEEK = 49; + + private String rpnExpression; + private String sourceName; + private DataProcessor dataProcessor; private Token[] tokens; private RpnStack stack = new RpnStack(); + private double[] calculatedValues; + private double[] timestamps; + private double timeStep; ! RpnCalculator(String rpnExpression, String sourceName, DataProcessor dataProcessor) throws RrdException { this.rpnExpression = rpnExpression; ! this.sourceName = sourceName; ! this.dataProcessor = dataProcessor; ! this.timestamps = dataProcessor.getTimestamps(); ! this.timeStep = this.timestamps[1] - this.timestamps[0]; ! this.calculatedValues = new double[this.timestamps.length]; StringTokenizer st = new StringTokenizer(rpnExpression, ", "); tokens = new Token[st.countTokens()]; ! for (int i = 0; st.hasMoreTokens(); i++) { tokens[i] = createToken(st.nextToken()); } } ! private Token createToken(String parsedText) throws RrdException { ! Token token = new Token(); ! if (Util.isDouble(parsedText)) { token.id = TKN_NUM; ! token.number = Util.parseDouble(parsedText); } ! else if (parsedText.equals("+")) { token.id = TKN_PLUS; } ! else if (parsedText.equals("-")) { token.id = TKN_MINUS; } ! else if (parsedText.equals("*")) { token.id = TKN_MULT; } ! else if (parsedText.equals("/")) { token.id = TKN_DIV; } ! else if (parsedText.equals("%")) { token.id = TKN_MOD; } ! else if (parsedText.equals("SIN")) { token.id = TKN_SIN; } ! else if (parsedText.equals("COS")) { token.id = TKN_COS; } ! else if (parsedText.equals("LOG")) { token.id = TKN_LOG; } ! else if (parsedText.equals("EXP")) { token.id = TKN_EXP; } ! else if (parsedText.equals("FLOOR")) { token.id = TKN_FLOOR; } ! else if (parsedText.equals("CEIL")) { token.id = TKN_CEIL; } ! else if (parsedText.equals("ROUND")) { token.id = TKN_ROUND; } ! else if (parsedText.equals("POW")) { token.id = TKN_POW; } ! else if (parsedText.equals("ABS")) { token.id = TKN_ABS; } ! else if (parsedText.equals("SQRT")) { token.id = TKN_SQRT; } ! else if (parsedText.equals("RANDOM")) { token.id = TKN_RANDOM; } ! else if (parsedText.equals("LT")) { token.id = TKN_LT; } ! else if (parsedText.equals("LE")) { token.id = TKN_LE; } ! else if (parsedText.equals("GT")) { token.id = TKN_GT; } ! else if (parsedText.equals("GE")) { token.id = TKN_GE; } ! else if (parsedText.equals("EQ")) { token.id = TKN_EQ; } ! else if (parsedText.equals("IF")) { token.id = TKN_IF; } ! else if (parsedText.equals("MIN")) { token.id = TKN_MIN; } ! else if (parsedText.equals("MAX")) { token.id = TKN_MAX; } ! else if (parsedText.equals("LIMIT")) { token.id = TKN_LIMIT; } ! else if (parsedText.equals("DUP")) { token.id = TKN_DUP; } ! else if (parsedText.equals("EXC")) { token.id = TKN_EXC; } ! else if (parsedText.equals("POP")) { token.id = TKN_POP; } ! else if (parsedText.equals("UN")) { token.id = TKN_UN; } ! else if (parsedText.equals("UNKN")) { token.id = TKN_UNKN; } ! else if (parsedText.equals("NOW")) { token.id = TKN_NOW; } ! else if (parsedText.equals("TIME")) { token.id = TKN_TIME; } ! else if (parsedText.equals("PI")) { token.id = TKN_PI; } ! else if (parsedText.equals("E")) { token.id = TKN_E; } ! else if (parsedText.equals("AND")) { token.id = TKN_AND; } ! else if (parsedText.equals("OR")) { token.id = TKN_OR; } ! else if (parsedText.equals("XOR")) { token.id = TKN_XOR; } + else if (parsedText.equals("PREV")) { + token.id = TKN_PREV; + token.variable = sourceName; + token.values = calculatedValues; + } + else if (parsedText.startsWith("PREV(") && parsedText.endsWith(")")) { + token.id = TKN_PREV; + token.variable = parsedText.substring(5, parsedText.length() - 1); + token.values = dataProcessor.getValues(token.variable); + } + else if (parsedText.equals("INF")) { + token.id = TKN_INF; + } + else if (parsedText.equals("NEGINF")) { + token.id = TKN_NEGINF; + } + else if (parsedText.equals("STEP")) { + token.id = TKN_STEP; + } + else if (parsedText.equals("YEAR")) { + token.id = TKN_YEAR; + } + else if (parsedText.equals("MONTH")) { + token.id = TKN_MONTH; + } + else if (parsedText.equals("DATE")) { + token.id = TKN_DATE; + } + else if (parsedText.equals("HOUR")) { + token.id = TKN_HOUR; + } + else if (parsedText.equals("MINUTE")) { + token.id = TKN_MINUTE; + } + else if (parsedText.equals("SECOND")) { + token.id = TKN_SECOND; + } + else if (parsedText.equals("WEEK")) { + token.id = TKN_WEEK; + } else { token.id = TKN_VAR; + token.variable = parsedText; + token.values = dataProcessor.getValues(token.variable); } return token; } ! double[] calculateValues() throws RrdException { ! for (int slot = 0; slot < timestamps.length; slot++) { ! resetStack(); ! for (int i = 0; i < tokens.length; i++) { ! Token token = tokens[i]; ! double x1, x2, x3; ! switch (token.id) { ! case TKN_NUM: ! push(token.number); ! break; ! case TKN_VAR: ! push(token.values[slot]); ! break; ! case TKN_PLUS: ! push(pop() + pop()); ! break; ! case TKN_MINUS: ! x2 = pop(); ! x1 = pop(); ! push(x1 - x2); ! break; ! case TKN_MULT: ! push(pop() * pop()); ! break; ! case TKN_DIV: ! x2 = pop(); ! x1 = pop(); ! push(x1 / x2); ! break; ! case TKN_MOD: ! x2 = pop(); ! x1 = pop(); ! push(x1 % x2); ! break; ! case TKN_SIN: ! push(Math.sin(pop())); ! break; ! case TKN_COS: ! push(Math.cos(pop())); ! break; ! case TKN_LOG: ! push(Math.log(pop())); ! break; ! case TKN_EXP: ! push(Math.exp(pop())); ! break; ! case TKN_FLOOR: ! push(Math.floor(pop())); ! break; ! case TKN_CEIL: ! push(Math.ceil(pop())); ! break; ! case TKN_ROUND: ! push(Math.round(pop())); ! break; ! case TKN_POW: ! x2 = pop(); ! x1 = pop(); ! push(Math.pow(x1, x2)); ! break; ! case TKN_ABS: ! push(Math.abs(pop())); ! break; ! case TKN_SQRT: ! push(Math.sqrt(pop())); ! break; ! case TKN_RANDOM: ! push(Math.random()); ! break; ! case TKN_LT: ! x2 = pop(); ! x1 = pop(); ! push(x1 < x2 ? 1 : 0); ! break; ! case TKN_LE: ! x2 = pop(); ! x1 = pop(); ! push(x1 <= x2 ? 1 : 0); ! break; ! case TKN_GT: ! x2 = pop(); ! x1 = pop(); ! push(x1 > x2 ? 1 : 0); ! break; ! case TKN_GE: ! x2 = pop(); ! x1 = pop(); ! push(x1 >= x2 ? 1 : 0); ! break; ! case TKN_EQ: ! x2 = pop(); ! x1 = pop(); ! push(x1 == x2 ? 1 : 0); ! break; ! case TKN_IF: ! x3 = pop(); ! x2 = pop(); ! x1 = pop(); ! push(x1 != 0 ? x2 : x3); ! break; ! case TKN_MIN: ! push(Math.min(pop(), pop())); ! break; ! case TKN_MAX: ! push(Math.max(pop(), pop())); ! break; ! case TKN_LIMIT: ! x3 = pop(); ! x2 = pop(); ! x1 = pop(); ! push(x1 < x2 || x1 > x3 ? Double.NaN : x1); ! break; ! case TKN_DUP: ! push(peek()); ! break; ! case TKN_EXC: ! x2 = pop(); ! x1 = pop(); ! push(x2); ! push(x1); ! break; ! case TKN_POP: ! pop(); ! break; ! case TKN_UN: ! push(Double.isNaN(pop()) ? 1 : 0); ! break; ! case TKN_UNKN: ! push(Double.NaN); ! break; ! case TKN_NOW: ! push(Util.getTime()); ! break; ! case TKN_TIME: ! push((long)Math.round(timestamps[slot])); ! break; ! case TKN_PI: ! push(Math.PI); ! break; ! case TKN_E: ! push(Math.E); ! break; ! case TKN_AND: ! x2 = pop(); ! x1 = pop(); ! push((x1 != 0 && x2 != 0) ? 1 : 0); ! break; ! case TKN_OR: ! x2 = pop(); ! x1 = pop(); ! push((x1 != 0 || x2 != 0) ? 1 : 0); ! break; ! case TKN_XOR: ! x2 = pop(); ! x1 = pop(); ! push(((x1 != 0 && x2 == 0) || (x1 == 0 && x2 != 0)) ? 1 : 0); ! break; ! case TKN_PREV: ! push((slot == 0)? Double.NaN: token.values[slot - 1]); ! break; ! case TKN_INF: ! push(Double.POSITIVE_INFINITY); ! break; ! case TKN_NEGINF: ! push(Double.NEGATIVE_INFINITY); ! break; ! case TKN_STEP: ! push(timeStep); ! break; ! case TKN_YEAR: ! push(getCalendarField(pop(), Calendar.YEAR)); ! break; ! case TKN_MONTH: ! push(getCalendarField(pop(), Calendar.MONTH)); ! break; ! case TKN_DATE: ! push(getCalendarField(pop(), Calendar.DAY_OF_MONTH)); ! break; ! case TKN_HOUR: ! push(getCalendarField(pop(), Calendar.HOUR_OF_DAY)); ! break; ! case TKN_MINUTE: ! push(getCalendarField(pop(), Calendar.MINUTE)); ! break; ! case TKN_SECOND: ! push(getCalendarField(pop(), Calendar.SECOND)); ! break; ! case TKN_WEEK: ! push(getCalendarField(pop(), Calendar.WEEK_OF_YEAR)); ! break; ! default: ! throw new RrdException("Unexpected RPN token encountered, token.id=" + token.id); ! } ! } ! calculatedValues[slot] = pop(); ! // check if stack is empty only on the first try ! if (slot == 0 && !isStackEmpty()) { ! throw new RrdException("Stack not empty at the end of calculation. " + ! "Probably bad RPN expression [" + rpnExpression + "]"); } } ! return calculatedValues; } ! private double getCalendarField(double timestamp, int field) { ! GregorianCalendar gc = new GregorianCalendar(); ! gc.setTimeInMillis((long)(timestamp * 1000)); ! return gc.get(field); } *************** *** 480,484 **** void push(double x) throws RrdException { ! if(pos >= MAX_STACK_SIZE) { throw new RrdException("PUSH failed, RPN stack full [" + MAX_STACK_SIZE + "]"); } --- 510,514 ---- void push(double x) throws RrdException { ! if (pos >= MAX_STACK_SIZE) { throw new RrdException("PUSH failed, RPN stack full [" + MAX_STACK_SIZE + "]"); } *************** *** 487,491 **** double pop() throws RrdException { ! if(pos <= 0) { throw new RrdException("POP failed, RPN stack is empty "); } --- 517,521 ---- double pop() throws RrdException { ! if (pos <= 0) { throw new RrdException("POP failed, RPN stack is empty "); } *************** *** 494,498 **** double peek() throws RrdException { ! if(pos <= 0) { throw new RrdException("PEEK failed, RPN stack is empty "); } --- 524,528 ---- double peek() throws RrdException { ! if (pos <= 0) { throw new RrdException("PEEK failed, RPN stack is empty "); } *************** *** 510,531 **** private class Token { ! byte id; ! String str; ! double number; ! ! Token(String str) { ! this.str = str; ! } ! } ! ! /* ! public static void main(String[] args) throws RrdException { ! RpnCalculator c = new RpnCalculator("x,y,+,x,y,-,*,SQRT"); ! c.setValue("x", 5); ! c.setValue("y", 4); ! System.out.println(c.calculate()); ! c.setValue("x", 6); ! System.out.println(c.calculate()); } - */ } --- 540,547 ---- private class Token { ! byte id = -1; ! double number = Double.NaN; ! String variable = null; ! double[] values = null; } } --- NEW FILE: PDef.java --- /* ============================================================ * JRobin : Pure java implementation of RRDTool's functionality * ============================================================ * * Project Info: http://www.jrobin.org * Project Lead: Sasa Markovic (sa...@jr...); * * (C) Copyright 2003, by Sasa Markovic. * * Developers: Sasa Markovic (sa...@jr...) * Arne Vandamme (cob...@jr...) * * This library is free software; you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Foundation; * either version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with this * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. */ package org.jrobin.data; import org.jrobin.graph.Plottable; class PDef extends Source { private final Plottable plottable; PDef(String name, Plottable plottable) { super(name); this.plottable = plottable; } PDef(String name, final Plottable plottable, final int index) { super(name); this.plottable = new Plottable() { public double getValue(long timestamp) { return plottable.getValue(timestamp, index); } }; } PDef(String name, final Plottable plottable, final String sourceName) { super(name); this.plottable = new Plottable() { public double getValue(long timestamp) { return plottable.getValue(timestamp, sourceName); } }; } double getValue(double timestamp) { long t = (long) Math.round(timestamp); return plottable.getValue(t); } } --- NEW FILE: SDef.java --- /* ============================================================ * JRobin : Pure java implementation of RRDTool's functionality * ============================================================ * * Project Info: http://www.jrobin.org * Project Lead: Sasa Markovic (sa...@jr...); * * (C) Copyright 2003, by Sasa Markovic. * * Developers: Sasa Markovic (sa...@jr...) * Arne Vandamme (cob...@jr...) * * This library is free software; you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Foundation; * either version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with this * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. */ package org.jrobin.data; class SDef extends Source { private String defName; private String consolFun; SDef(String name, String defName, String consolFun) { super(name); this.defName = defName; this.consolFun = consolFun; } String getDefName() { return defName; } String getConsolFun() { return consolFun; } void setValue(double value, int count) { double[] values = new double[count]; for(int i = 0; i < values.length; i++) { values[i] = value; } setValues(values); } } --- NEW FILE: Source.java --- /* ============================================================ * JRobin : Pure java implementation of RRDTool's functionality * ============================================================ * * Project Info: http://www.jrobin.org * Project Lead: Sasa Markovic (sa...@jr...); * * (C) Copyright 2003, by Sasa Markovic. * * Developers: Sasa Markovic (sa...@jr...) * Arne Vandamme (cob...@jr...) * * This library is free software; you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Foundation; * either version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with this * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. */ package org.jrobin.data; import org.jrobin.core.ConsolFuns; import org.jrobin.core.RrdException; import org.jrobin.core.Util; abstract class Source implements ConsolFuns { final private String name; private double[] values; Source(String name) { this.name = name; } final String getName() { return name; } final void setValues(double[] values) { this.values = values; } final double[] getValues() { return values; } final double getAggregate(String consolFun, double secondsPerPixel) throws RrdException { if(values == null) { throw new RrdException("Could not calculate " + consolFun + " for datasource [" + name + "], datasource values are still not available"); } if(consolFun.equals(ConsolFuns.CF_FIRST)) { return values[1]; } else if(consolFun.equals(ConsolFuns.CF_LAST)) { return values[values.length - 1]; } else if(consolFun.equals(ConsolFuns.CF_MIN)) { return getMin(); } else if(consolFun.equals(ConsolFuns.CF_MAX)) { return getMax(); } else if(consolFun.equals(ConsolFuns.CF_AVERAGE)) { return getAverage(); } else if(consolFun.equals(ConsolFuns.CF_TOTAL)) { return getTotal(secondsPerPixel); } else { throw new RrdException("Unsupported consolidation function: " + consolFun); } } private double getTotal(double secondsPerPixel) { double sum = 0; for(int i = 1; i < values.length; i++) { if(!Double.isNaN(values[i])) { sum += values[i]; } } return sum * secondsPerPixel; } private double getAverage() { double sum = 0; int count = 0; for(int i = 1; i < values.length; i++) { if(!Double.isNaN(values[i])) { sum += values[i]; count++; } } return sum / count; } private double getMax() { double max = Double.NaN; for(int i = 1; i < values.length; i++) { max = Util.max(max, values[i]); } return max; } private double getMin() { double min = Double.NaN; for(int i = 1; i < values.length; i++) { min = Util.min(min, values[i]); } return min; } } --- NEW FILE: Def.java --- /* ============================================================ * JRobin : Pure java implementation of RRDTool's functionality * ============================================================ * * Project Info: http://www.jrobin.org * Project Lead: Sasa Markovic (sa...@jr...); * * (C) Copyright 2003, by Sasa Markovic. * * Developers: Sasa Markovic (sa...@jr...) * Arne Vandamme (cob...@jr...) * * This library is free software; you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Foundation; * either version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with this * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. */ package org.jrobin.data; import org.jrobin.core.Util; import java.io.IOException; class Def extends Source { private String path, dsName, consolFun, backend; Def(String name, String path, String dsName, String consolFunc) { this(name, path, dsName, consolFunc, null); } Def(String name, String path, String dsName, String consolFunc, String backend) { super(name); this.path = path; this.dsName = dsName; this.consolFun = consolFunc; this.backend = backend; } String getPath() { return path; } String getCanonicalPath() throws IOException { return Util.getCanonicalPath(path); } String getDsName() { return dsName; } String getConsolFun() { return consolFun; } String getBackend() { return backend; } boolean isCompatibleWith(Def def) throws IOException { return getCanonicalPath().equals(def.getCanonicalPath()) && getConsolFun().equals(def.consolFun) && ((backend == null && def.backend == null) || (backend != null && def.backend != null && backend.equals(def.backend))); } } --- DataStats.java DELETED --- --- NEW FILE: CDef.java --- /* ============================================================ * JRobin : Pure java implementation of RRDTool's functionality * ============================================================ * * Project Info: http://www.jrobin.org * Project Lead: Sasa Markovic (sa...@jr...); * * (C) Copyright 2003, by Sasa Markovic. * * Developers: Sasa Markovic (sa...@jr...) * Arne Vandamme (cob...@jr...) * * This library is free software; you can redistribute it and/or modify it under the terms * of the GNU Lesser General Public License as published by the Free Software Foundation; * either version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along with this * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, * Boston, MA 02111-1307, USA. */ package org.jrobin.data; class CDef extends Source { private String rpnExpression; CDef(String name, String rpnExpression) { super(name); this.rpnExpression = rpnExpression; } String getRpnExpression() { return rpnExpression; } } |