Skip to content

Commit

Permalink
analyzers use logger instead of print statements (close #150) (#152)
Browse files Browse the repository at this point in the history
* analyzers use logger instead of print statements (close #150)

* RequestScoped analyzers, ApplicationScoped factory

* linted:

* analyzerparams, analyzer injectible

* Hai

* fixed scope of analyzerparams

* fixed scope of analyzerparams

* One of em work

* Got em all

* Added constructors for them all

* Linting

* Updated scope

* Linting

* Cleaning up PR

* Renaming variables

* Updated Qualifier and Factory

* Better anotation used

---------

Co-authored-by: Kai Arseneau <[email protected]>
Co-authored-by: Kai <[email protected]>
  • Loading branch information
3 people authored Dec 25, 2024
1 parent 1816718 commit ee6454f
Show file tree
Hide file tree
Showing 17 changed files with 284 additions and 361 deletions.
15 changes: 7 additions & 8 deletions backend/src/main/java/com/mcmasterbaja/FileAnalyzeResource.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public class FileAnalyzeResource {
@Inject Logger logger;
@Inject StorageService storageService;
@Inject FileMetadataService fileMetadataService;
@Inject AnalyzerFactory analyzerFactory;

// TODO: Convert to using POST body rather than path variables
@POST
Expand All @@ -45,14 +46,12 @@ public RestResponse<File> runAnalyzer(@BeanParam AnalyzerParams params) {

// TODO: Can't pass in null to createAnalyzer, this if statement feels redundant
if (params.getType() != null) {
Analyzer analyzer = AnalyzerFactory.createAnalyzer(params);
if (analyzer != null) {
try {
analyzer.analyze();
} catch (Exception e) {
logger.error("Error running analyzer", e);
throw new RuntimeException("Error running analyzer");
}
Analyzer analyzer = analyzerFactory.getAnalyzer(params.getType());
try {
analyzer.analyze(params); // No need to pass params; it's injected
} catch (Exception e) {
logger.error("Error running analyzer", e);
throw new RuntimeException("Error running analyzer");
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,53 +1,62 @@
package com.mcmasterbaja.analyzer;

import com.mcmasterbaja.model.AnalyzerParams;
import com.mcmasterbaja.model.AnalyzerType;
import com.opencsv.exceptions.CsvException;
import jakarta.enterprise.context.Dependent;
import jakarta.inject.Inject;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.jboss.logging.Logger;

@Dependent
@AnalyzerQualifier(AnalyzerType.ACCEL_CURVE)
public class AccelCurveAnalyzer extends Analyzer {

@Inject Logger logger;
@Inject AnalyzerParams params;

// inputFiles are first primary RPM, then secondary RPM
// outputFiles are first primary RPM rolling average, then secondary RPM rolling average, then
// interpolated, then accel curve (runs)
public AccelCurveAnalyzer(String[] inputFiles, String[] inputColumns, String[] outputFiles) {
super(inputFiles, inputColumns, outputFiles);
}

@Override
public void analyze() throws IOException, CsvException {
System.out.println("Combining \"" + inputFiles[0] + "\" and \"" + inputFiles[1] + "\"");
System.out.println("sGolay Averaging...");

SGolayFilter s =
new SGolayFilter(
Arrays.copyOfRange(inputFiles, 0, 1),
new String[] {"Timestamp (ms)", inputColumns[0]},
Arrays.copyOfRange(outputFiles, 0, 1),
300,
3);
s.analyze();
SGolayFilter s2 =
new SGolayFilter(
Arrays.copyOfRange(inputFiles, 1, 2),
new String[] {"Timestamp (ms)", inputColumns[1]},
Arrays.copyOfRange(outputFiles, 1, 2),
300,
3);
s2.analyze();

System.out.println("Interpolating...");

InterpolaterProAnalyzer linearInterpolate =
new InterpolaterProAnalyzer(
Arrays.copyOfRange(outputFiles, 0, 2),
new String[] {"Timestamp (ms)", inputColumns[0], inputColumns[1]},
Arrays.copyOfRange(outputFiles, 2, 3));
linearInterpolate.analyze();
public void analyze(AnalyzerParams params) throws IOException, CsvException {
extractParams(params);
logger.info("Combining \"" + inputFiles[0] + "\" and \"" + inputFiles[1] + "\"");
logger.info("sGolay Averaging...");

SGolayFilter golayer = new SGolayFilter();
AnalyzerParams golayParams = new AnalyzerParams();
golayParams.setInputFiles(Arrays.copyOfRange(inputFiles, 0, 1));
golayParams.setInputColumns(new String[] {"Timestamp (ms)", inputColumns[0]});
golayParams.setOutputFiles(Arrays.copyOfRange(outputFiles, 0, 1));
golayParams.setOptions(new String[] {"300", "3"});
golayer.analyze(golayParams);

SGolayFilter golayer2 = new SGolayFilter();
AnalyzerParams golayParams2 = new AnalyzerParams();
golayParams2.setInputFiles(Arrays.copyOfRange(inputFiles, 1, 2));
golayParams2.setInputColumns(new String[] {"Timestamp (ms)", inputColumns[1]});
golayParams2.setOutputFiles(Arrays.copyOfRange(outputFiles, 1, 2));
golayParams2.setOptions(new String[] {"300", "3"});
golayer2.analyze(golayParams2);

logger.info("Interpolating...");

InterpolaterProAnalyzer interpolater = new InterpolaterProAnalyzer();
AnalyzerParams interpolateParams = new AnalyzerParams();
interpolateParams.setInputFiles(Arrays.copyOfRange(outputFiles, 0, 2));
interpolateParams.setInputColumns(
new String[] {"Timestamp (ms)", inputColumns[0], inputColumns[1]});
interpolateParams.setOutputFiles(Arrays.copyOfRange(outputFiles, 2, 3));
interpolater.analyze(interpolateParams);

logger.info("Done");
}

// Gets start and end timestamps of accel runs based on GPS speed
Expand Down
40 changes: 8 additions & 32 deletions backend/src/main/java/com/mcmasterbaja/analyzer/Analyzer.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.mcmasterbaja.analyzer;

import com.mcmasterbaja.model.AnalyzerParams;
import com.opencsv.CSVReader;
import com.opencsv.CSVReaderBuilder;
import com.opencsv.CSVWriter;
Expand All @@ -12,34 +13,22 @@
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.List;

public abstract class Analyzer {

// Input and output files are arrays because some analyzers may need multiple input files
protected String[] inputFiles;
protected String[] inputColumns;
protected String[] outputFiles;

public Analyzer(String[] inputFiles, String[] inputColumns, String[] outputFiles) {
this.inputFiles = inputFiles;
// inputColumns is the names of the columns we are analyzing. index 0 is the independent
// variable (usually timestamp), 1+ are dependent variable(s)
this.inputColumns = inputColumns;
this.outputFiles = outputFiles;
}
// Abstract method to be implemented by subclasses
public abstract void analyze(AnalyzerParams params)
throws IOException, CsvValidationException, CsvException;

// Some analyzers work on entire rows and don't need to select columns (e.g. compression), they
// should use this constructor
public Analyzer(String[] inputFiles, String[] outputFiles) {
this.inputFiles = inputFiles;
this.inputColumns = new String[1];
this.outputFiles = outputFiles;
public void extractParams(AnalyzerParams params) {
this.inputFiles = params.getInputFiles();
this.inputColumns = params.getInputColumns();
this.outputFiles = params.getOutputFiles();
}

// Abstract method to be implemented by subclasses
public abstract void analyze() throws IOException, CsvValidationException, CsvException;

// I/O methods
// Streams as they avoid loading the entire file into memory at once
public CSVReader getReader(String filePath) throws IOException {
Expand All @@ -61,19 +50,6 @@ public ICSVWriter getWriter(String filePath) throws IOException {
.build();
}

// From this list of headers, which one are we actually doing analysis on
// fileIndex is basically the axis, 0=X, 1=Y, I made it a int to future-proof adding new columns
public int getAnalysisColumnIndex(int fileIndex, List<String> fileHeaders)
throws RuntimeException {
for (int i = 0; i < fileHeaders.size(); i++) {
if (fileHeaders.get(i).trim().equals(this.inputColumns[fileIndex])) {
return i;
}
}
// The inputColum is wrong somehow, should never happen with working frontend
throw new RuntimeException("No column in file exists with analysis column name");
}

public int getColumnIndex(String columnName, String[] fileHeaders) throws RuntimeException {
for (int i = 0; i < fileHeaders.length; i++) {
if (fileHeaders[i].trim().equals(columnName)) {
Expand Down
108 changes: 30 additions & 78 deletions backend/src/main/java/com/mcmasterbaja/analyzer/AnalyzerFactory.java
Original file line number Diff line number Diff line change
@@ -1,88 +1,40 @@
package com.mcmasterbaja.analyzer;

import com.mcmasterbaja.model.AnalyzerParams;

import com.mcmasterbaja.model.AnalyzerType;
import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.inject.Any;
import jakarta.enterprise.inject.Instance;
import jakarta.inject.Inject;
import java.util.EnumMap;
import java.util.Map;
import org.jboss.logging.Logger;

@ApplicationScoped
public class AnalyzerFactory {
@Inject Logger logger;

public static Analyzer createAnalyzer(AnalyzerParams params) {

String[] inputFiles = params.getInputFiles();
String[] inputColumns = params.getInputColumns();
String[] outputFiles = params.getOutputFiles();
Object[] options = params.getOptions();

switch (params.getType()) {
case ACCEL_CURVE:
return new AccelCurveAnalyzer(inputFiles, inputColumns, outputFiles);

case ROLL_AVG:
if (options.length == 0) {
return new RollingAvgAnalyzer(inputFiles, inputColumns, outputFiles);
}
int windowSize = Integer.parseInt((String) options[0]);
return new RollingAvgAnalyzer(inputFiles, inputColumns, outputFiles, windowSize);

case SGOLAY:
// Check if passed a window size
if (options.length == 0) {
return new SGolayFilter(inputFiles, inputColumns, outputFiles);
}
windowSize = Integer.parseInt((String) options[0]);
int polynomialDegree = Integer.parseInt((String) options[1]);
return new SGolayFilter(
inputFiles, inputColumns, outputFiles, windowSize, polynomialDegree);

case RDP_COMPRESSION:
if (options.length == 0) {
return new RDPCompressionAnalyzer(inputFiles, inputColumns, outputFiles, 15);
}
double epsilon = Double.parseDouble((String) options[0]);
return new RDPCompressionAnalyzer(inputFiles, inputColumns, outputFiles, epsilon);
@Inject @Any Instance<Analyzer> analyzers;

case SPLIT:
System.out.println("SplitAnalyzer");
if (options[1] == "" || options[0] == "") {
return null;
}
int start = Integer.parseInt((String) options[0]);
int end = Integer.parseInt((String) options[1]);
return new SplitAnalyzer(inputFiles, inputColumns, outputFiles, start, end);
private Map<AnalyzerType, Analyzer> analyzerMap;

case LINEAR_MULTIPLY:
if (options[1] == "" || options[0] == "") {
return null;
}
double m = Double.parseDouble((String) options[0]);
double b = Double.parseDouble((String) options[1]);
return new LinearMultiplyAnalyzer(inputFiles, inputColumns, outputFiles, m, b);

case CONSTANT_ADDER:
if (options[3] == "" || options[2] == "" || options[1] == "" || options[0] == "") {
return null;
}
double a1 = Double.parseDouble((String) options[0]);
double b2 = Double.parseDouble((String) options[1]);
double c1 = Double.parseDouble((String) options[2]);
double d1 = Double.parseDouble((String) options[3]);
return new ConstantAdderAnalyzer(inputFiles, inputColumns, outputFiles, a1, b2, c1, d1);

case AVERAGE:
int[] range = new int[2];
range[0] = Integer.parseInt((String) options[0]);
range[1] = Integer.parseInt((String) options[1]);
return new AverageAnalyzer(inputFiles, outputFiles, range);

case INTERPOLATER_PRO:
return new InterpolaterProAnalyzer(inputFiles, inputColumns, outputFiles);
@PostConstruct
public void init() {
analyzerMap = new EnumMap<>(AnalyzerType.class);
for (Analyzer analyzer : analyzers) {
AnalyzerQualifier qualifier = analyzer.getClass().getAnnotation(AnalyzerQualifier.class);
if (qualifier != null) {
analyzerMap.put(qualifier.value(), analyzer);
}
}
}

case CUBIC:
double a = Double.parseDouble((String) options[0]);
double b1 = Double.parseDouble((String) options[1]);
double c = Double.parseDouble((String) options[2]);
double d = Double.parseDouble((String) options[3]);
return new CubicAnalyzer(inputFiles, inputColumns, outputFiles, a, b1, c, d);
default:
return null;
public Analyzer getAnalyzer(AnalyzerType type) {
Analyzer analyzer = analyzerMap.get(type);
if (analyzer == null) {
logger.errorf("No Analyzer found for type: %s", type);
throw new IllegalArgumentException("No Analyzer found for type: " + type);
}
return analyzer;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.mcmasterbaja.analyzer;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import com.mcmasterbaja.model.AnalyzerType;
import jakarta.inject.Qualifier;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

@Qualifier
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER})
public @interface AnalyzerQualifier {
AnalyzerType value();
}
Original file line number Diff line number Diff line change
@@ -1,40 +1,49 @@
package com.mcmasterbaja.analyzer;

import com.mcmasterbaja.model.AnalyzerParams;
import com.mcmasterbaja.model.AnalyzerType;
import com.opencsv.CSVReader;
import com.opencsv.ICSVWriter;
import com.opencsv.exceptions.CsvException;
import jakarta.enterprise.context.Dependent;
import jakarta.inject.Inject;
import java.io.IOException;
import java.util.List;
import org.jboss.logging.Logger;

@Dependent
@AnalyzerQualifier(AnalyzerType.AVERAGE)
public class AverageAnalyzer extends Analyzer {
// This class takes the average of a range of a column and returns it as a double
private final int[] range;
@Inject Logger logger;

public AverageAnalyzer(String[] inputFiles, String[] outputFiles, int[] range) {
super(inputFiles, outputFiles);
// This range is the value, not the index. BinarySearch will be used to find the index
this.range = range;
}
public void analyze(AnalyzerParams params) throws IOException, CsvException {
int[] range = new int[2];
range[0] = Integer.parseInt((String) params.getOptions()[0]);
range[1] = Integer.parseInt((String) params.getOptions()[1]);

public void analyze() throws IOException, CsvException {
System.out.println(
logger.info(
"Taking the average of "
+ super.inputFiles[0]
+ params.getInputFiles()[0]
+ " to "
+ super.outputFiles[0]
+ params.getOutputFiles()[0]
+ " with a range of "
+ range[0]
+ " to "
+ range[1]);

CSVReader reader = getReader(super.inputFiles[0]);
ICSVWriter writer = getWriter(super.outputFiles[0]);
CSVReader reader = getReader(params.getInputFiles()[0]);
ICSVWriter writer = getWriter(params.getOutputFiles()[0]);

String[] headers = {"TempColumn", "Average"};
writer.writeNext(headers);

reader.readNext(); // Skip headers
String[] dataPoint = {"0", Double.toString(average(reader.readAll(), range[0], range[1]))};
writer.writeNext(dataPoint);

logger.info("Average: " + dataPoint[1]);
writer.close();
}

// Takes average at found indices of second column
Expand Down
Loading

0 comments on commit ee6454f

Please sign in to comment.