package ch.epfl.gsn.monitoring; import org.slf4j.LoggerFactory; import ch.epfl.gsn.beans.DataField; import org.slf4j.Logger; /* Each virtual sensor could have multiple anomalies * * Each anomaly object consists of: * function to be applied * field on which it is applied * groupBy clause (Optional) * time interval window over which this anomaly has to be detected * (anomalies would be calculated over entire historical data if an erroneous value is provided) * threshold value (Optional) */ public class Anomaly { private static final transient Logger logger = LoggerFactory.getLogger (Anomaly.class); // Field which this anomaly represents private DataField field; // Threshold value private String value; // Function to be applied (for e.g. positive_outlier, negative_outlier, iqr, unique etc.) private String function; // Time interval window private long time; private String timeStr; // Storing original timeStr to provide feedback in case of error private boolean groupBy; // If anomaly specs have a groupBy field specified private DataField groupByField; // groupByField public Anomaly() {} public Anomaly (String function, DataField field, String timeStr, DataField groupByField) { this.function = function; this.field = field; this.timeStr = timeStr; this.time = this.parseTime(timeStr); this.groupByField = groupByField; if (groupByField != null) this.groupBy = true; } public Anomaly ( String function, DataField field, String timeStr, DataField groupByField ,String value) { this (function, field, timeStr, groupByField); this.value= value; this.time = this.parseTime(timeStr); } /* Parses time. Accepted formal : value.timespecifer * timespecifier belongs to { m, h, d} */ private long parseTime (String timeStr) { timeStr = timeStr.trim(); if (timeStr.equals("-1")) //timeStr == -1 means anomaly has to be detected over all the historical data return -1; long toReturn = 0; // timeSpecifer could be m (minutes) , h (hours) or d (days) Character timeSpecifier = timeStr.toLowerCase().charAt(timeStr.length()-1); double rawTime = 0; switch (timeSpecifier) { case 'm': //Converting minutes to milliseconds rawTime = Double.parseDouble(timeStr.substring(0,timeStr.length()-1)); toReturn = (long)(rawTime * 60 * 1000); break; case 'h': // Converting hours ti milliseconds rawTime = Double.parseDouble(timeStr.substring(0,timeStr.length()-1)); toReturn = (long)(rawTime * 60* 60 * 1000); break; case 'd': // Converting days to milliseconds rawTime = Double.parseDouble(timeStr.substring(0,timeStr.length()-1)); toReturn = (long)(rawTime * 24 * 60 * 60 * 1000); break; default: toReturn = -1; logger.info ("Invalid time specifer in anomaly: " + this.toString() + ". Anomaly would be detected over entire data\nValid time specifiers include m, d and h preceeded by a number"); } return toReturn; } // Getters public DataField getField () { return field; } public String getValue () { return value; } public String getFunction () { return function; } public long getTime () { return time;} public String getTimeStr () { return timeStr;} public boolean isGroupBy () { return groupBy; } public DataField getGroupByField () { return this.groupByField; } public String toString () { StringBuilder sb = new StringBuilder(); sb.append("anomaly." + this.function + "." + this.field.getName() + "="); if (this.timeStr != null) sb.append(this.timeStr); if (this.value!= null) sb.append("," + this.value); if (this.isGroupBy()) sb.append(",["+ this.groupByField.getName() + "]"); return sb.toString(); } }