1 package edu.uci.iotproject.evaluation;
3 import edu.uci.iotproject.analysis.TriggerTrafficExtractor;
4 import edu.uci.iotproject.analysis.UserAction;
5 import edu.uci.iotproject.io.PrintWriterUtils;
6 import edu.uci.iotproject.io.TriggerTimesFileReader;
9 import java.time.Instant;
10 import java.util.ArrayList;
11 import java.util.List;
12 import java.util.Optional;
15 * Utility for comparing detected events to logged (actual) events.
17 * @author Janus Varmarken {@literal <jvarmark@uci.edu>}
18 * @author Rahmadi Trimananda {@literal <rtrimana@uci.edu>}
20 public class DetectionResultsAnalyzer {
22 private static boolean DUPLICATE_OUTPUT_TO_STD_OUT = true;
24 public static void main(String[] args) throws IOException {
25 if (args.length < 3) {
26 String errMsg = String.format("Usage: %s triggerTimesFile detectionOutputFile [stdOut]" +
27 "\n - triggerTimesFile: the file that contains the timestamps for the user actions" +
28 "\n - detectionOutputFile: the file that contains the detected events" +
29 "\n - analysisResultsFile: where to write the results of the detection analysis" +
30 "\n - stdOut: optional true/false literal indicating if output should also be printed to std out; default is true",
31 DetectionResultsAnalyzer.class.getSimpleName());
34 String triggerTimesFile = args[0];
35 File detectionOutputFile = new File(args[1]);
36 String analysisResultsFile = args[2];
37 if (args.length > 3) {
38 DUPLICATE_OUTPUT_TO_STD_OUT = Boolean.parseBoolean(args[3]);
41 // -------------------------------------- Parse the input files --------------------------------------
43 // Read the trigger times.
44 // The trigger times file does not contain event types as we initially assumed that we would just be alternating
45 // between ON and OFF.
46 List<Instant> triggerTimestamps = new TriggerTimesFileReader().readTriggerTimes(triggerTimesFile, false);
47 // Now generate user actions based on this alternating ON/OFF pattern.
48 List<UserAction> triggers = new ArrayList<>();
49 for (int i = 0; i < triggerTimestamps.size(); i++) {
50 // NOTE: assumes triggers alternate between ON and OFF
51 UserAction.Type actionType = i % 2 == 0 ? UserAction.Type.TOGGLE_ON : UserAction.Type.TOGGLE_OFF;
52 triggers.add(new UserAction(actionType, triggerTimestamps.get(i)));
54 // Read the detection output file, assuming a format as specified in UserAction.toString()
55 List<UserAction> detectedEvents = new ArrayList<>();
56 try (BufferedReader br = new BufferedReader(new FileReader(detectionOutputFile))) {
58 while ((s = br.readLine()) != null) {
59 if (s.startsWith("#")) {
63 detectedEvents.add(UserAction.fromString(s));
67 // ----------------- Now ready to compare the detected events with the logged events -----------------
69 // To contain all detected events that could be mapped to a trigger
70 List<UserAction> truePositives = new ArrayList<>();
71 for (UserAction detectedEvent : detectedEvents) {
72 Optional<UserAction> matchingTrigger = triggers.stream()
73 .filter(t -> t.getType() == detectedEvent.getType() &&
74 t.getTimestamp().isBefore(detectedEvent.getTimestamp()) &&
75 t.getTimestamp().plusMillis(TriggerTrafficExtractor.INCLUSION_WINDOW_MILLIS).
76 isAfter(detectedEvent.getTimestamp())
78 matchingTrigger.ifPresent(mt -> {
79 // We've consumed the trigger (matched it with a detected event), so remove it so we don't match with
80 // another detected event.
82 // The current detected event was a true positive as we could match it with a trigger.
83 truePositives.add(detectedEvent);
86 // Now the false positives are those elements in detectedEvents that are not in truePositives
87 List<UserAction> falsePositives = new ArrayList<>();
88 falsePositives.addAll(detectedEvents);
89 falsePositives.removeAll(truePositives);
91 // Output the results...
92 PrintWriter outputter = new PrintWriter(new FileWriter(analysisResultsFile));
93 PrintWriterUtils.println("---------- False negatives (events that where not detected) ----------", outputter, DUPLICATE_OUTPUT_TO_STD_OUT);
94 for (UserAction missing : triggers) {
95 PrintWriterUtils.println(missing, outputter, DUPLICATE_OUTPUT_TO_STD_OUT);
97 PrintWriterUtils.println("Total of " + Integer.toString(triggers.size()), outputter, DUPLICATE_OUTPUT_TO_STD_OUT);
98 PrintWriterUtils.printEmptyLine(outputter, DUPLICATE_OUTPUT_TO_STD_OUT);
99 PrintWriterUtils.println("---------- False positives (detected, but no matching trigger) ----------", outputter, DUPLICATE_OUTPUT_TO_STD_OUT);
100 for (UserAction fp : falsePositives) {
101 PrintWriterUtils.println(fp, outputter, DUPLICATE_OUTPUT_TO_STD_OUT);
103 PrintWriterUtils.println("Total of " + Integer.toString(falsePositives.size()), outputter, DUPLICATE_OUTPUT_TO_STD_OUT);