diff --git a/README.md b/README.md
index 2a2df83..35cbf88 100644
--- a/README.md
+++ b/README.md
@@ -5,22 +5,28 @@ This is a personal/professional project, which makes use of JavaCV, OpenCV, Tess
## Currently Working Features
- CLI Interface (All instances have user input checking)
- - [ x ] Main Menu
+ - [x] Main Menu
- Camera config menu
- - [ x ] Takes in inputs
- - [ x ] Sets config values
- - [ x ] Saves config values
- - [ x ] Shows camera preview*
+ - [x] Takes in inputs
+ - [x] Sets config values
+ - [x] Saves config values
+ - [x] Shows camera preview*
- currently broken, for unknown reasons. In process of parsing...
- - [ x ] GPIO test interactions
+ - [x] GPIO test interactions
- Test suite
- - [ x ] OpenCV image capture
- - [ x ] OpenCV image processing
- - [ x ] Tesseract OCR processing
- - [ ] Data storage in defined XLSX file (implemented, untested)
- - [ x ] modify number of iterations for test suite
-- [ ] JavaFX GUI (Designed, yet to be implemented)
+ - [x] OpenCV image capture
+ - [x] OpenCV image processing
+ - [x] Tesseract OCR processing
+ - [x] Data storage in defined XLSX file*
+ - [x] modify number of iterations for test suite
+- [x] JavaFX GUI (Designed, implemented)
+## Known Bugs
+
+- Closing the program throws a fatal error. This is due to the current implementation of monitoring of the Run switch, and as of now, to my knowledge, cannot be resolved.
+ - As of CLI build 1.2.0, the external run switch bricks the system.
+- Currently, all images are parsed on a per-iteration level. This means that we cannot guarantee the output columns are correct. As a temporary solution, image location, serial number, and parsed reading are placed in groups. Intention is to ultimately have them be column-wise written.
+- As of CLI build 1.3.0, the first reading row must be discarded, due to one side of the fixture producing a consistent failed reading. This has yet to be debugged, or fully understood.
## Dependencies
To install this project, and use it fully, you must have the following:
- a Raspberry Pi 4 or 400 (other Pis may work properly, but has not been tested)
diff --git a/antrun/build.xml b/antrun/build.xml
deleted file mode 100644
index 970415b..0000000
--- a/antrun/build.xml
+++ /dev/null
@@ -1,215 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
- Remote platform name must be set.
-
-
- 'target.platform.host' property is missing in ${target.platform.filename}.
- 'target.platform.port' property is missing in ${target.platform.filename}.
- 'target.platform.username' property is missing in ${target.platform.filename}.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Either 'target.platform.password' or 'target.platform.privatekey' + 'target.platform.passphrase' properties must be set in ${target.platform.filename}.
- 'target.remote.jre' property is missing in ${target.platform.filename}.
- 'target.remote.home' property is missing in ${target.platform.filename}.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/dependency-reduced-pom.xml b/dependency-reduced-pom.xml
index 5152179..c3c9cc5 100644
--- a/dependency-reduced-pom.xml
+++ b/dependency-reduced-pom.xml
@@ -4,7 +4,7 @@
org.baxter.disco
ocr
Disco OCR Accuracy Over Life Testing
- 4.0.0-rc1
+ 4.0.0-rc2
Testing Discos for long-term accuracy, using automated optical character recognition.
Baxter International
diff --git a/pom.xml b/pom.xml
index b2f6297..614b1e1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
4.0.0
org.baxter.disco
ocr
- 4.0.0-rc1
+ 4.0.0-rc2
jar
Disco OCR Accuracy Over Life Testing
Testing Discos for long-term accuracy, using automated optical character recognition.
@@ -86,7 +86,7 @@
${pi4j.version}
-
+
org.openjfx
javafx-fxml
@@ -178,7 +178,7 @@
-
+ -->
diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java
index ef88b29..80e8d13 100644
--- a/src/main/java/module-info.java
+++ b/src/main/java/module-info.java
@@ -4,6 +4,7 @@ module org.baxter.disco.ocr {
requires com.pi4j.plugin.pigpio;
requires com.pi4j.library.pigpio;
requires javafx.fxml;
+ requires javafx.controls;
requires org.apache.poi.poi;
requires org.apache.commons.configuration2;
requires org.apache.xmlbeans;
diff --git a/src/main/java/org/baxter/disco/ocr/Cli.java b/src/main/java/org/baxter/disco/ocr/Cli.java
index 610909b..5e9baf5 100644
--- a/src/main/java/org/baxter/disco/ocr/Cli.java
+++ b/src/main/java/org/baxter/disco/ocr/Cli.java
@@ -1,11 +1,14 @@
package org.baxter.disco.ocr;
import java.io.File;
+import java.io.PrintStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
import org.bytedeco.javacv.CanvasFrame;
@@ -13,17 +16,18 @@ import org.bytedeco.javacv.CanvasFrame;
* CLI for the Fixture.
*
* Creates a terminal-based user interface for the other
- * classes in this package (with the exception of {@link Gui} [for now]).
+ * classes in this package (with the exception of Gui-related
+ * classes).
*
* @author Blizzard Finnegan
- * @version 1.0.0, 27 Jan. 2023
+ * @version 1.3.0, 03 Feb. 2023
*/
public class Cli
{
/**
* Currently saved iteration count.
*/
- private static int iterationCount = 3;
+ private static int iterationCount = 10;
/**
* Scanner used for monitoring user input.
@@ -33,10 +37,20 @@ public class Cli
*/
private static Scanner inputScanner;
+ /**
+ * Whether the user has set the serial numbers yet.
+ */
+ private static boolean serialsSet = false;
+
+ /**
+ * Whether the user has successfully configured the cameras.
+ */
+ private static boolean camerasConfigured = false;
+
/**
* Number of options currently available in the main menu.
*/
- private static final int mainMenuOptionCount = 6;
+ private static final int mainMenuOptionCount = 7;
/**
* Number of options currently available in the movement sub-menu.
@@ -48,11 +62,33 @@ public class Cli
*/
private static final int cameraMenuOptionCount = 9;
+ /**
+ * Lock object, used for temporary interruption of {@link #runTests()}
+ */
+ private static Lock LOCK = new ReentrantLock();
+
+ /**
+ * Instance of {@link MovementFacade} for controlling the fixture.
+ */
+ private static MovementFacade fixture;
+
+ static
+ {
+ ErrorLogging.logError("DEBUG: START OF PROGRAM");
+ }
+
public static void main(String[] args)
{
try{
inputScanner = new Scanner(System.in);
- ErrorLogging.logError("DEBUG: START OF PROGRAM");
+
+ //ErrorLogging.logError("DEBUG: Setting up multithreading...");
+ fixture = new MovementFacade(LOCK);
+ //ErrorLogging.logError("DEBUG: Multithreading complete!");
+
+ //ErrorLogging.logError("DEBUG: Importing config...");
+ ConfigFacade.init();
+ //ErrorLogging.logError("DEBUG: Config imported!");
int userInput = 0;
@@ -69,24 +105,62 @@ public class Cli
println("Setting up cameras...");
println("This may take a moment...");
configureCameras();
+ camerasConfigured = true;
break;
case 3:
- setIterationCount();
+ setDUTSerials();
+ serialsSet = true;
break;
case 4:
- runTests();
- println("Test complete!");
+ setIterationCount();
break;
case 5:
- printHelp();
+ if(!camerasConfigured)
+ {
+ prompt("You have not configured the cameras yet! Are you sure you would like to continue? (y/N): ");
+ String input = inputScanner.nextLine().toLowerCase();
+ if( input.isBlank())
+ {
+ break;
+ }
+ else if (input.charAt(0) != 'y' )
+ {
+ break;
+ }
+ else
+ {
+ ErrorLogging.logError("WARNING! - Potential for error: Un-initialised cameras.");
+ }
+ }
+ if(!serialsSet)
+ {
+ prompt("You have not set the serial numbers for your DUTs yet! Are you sure you would like to continue? (y/N): ");
+ String input = inputScanner.nextLine().toLowerCase();
+ if( input.isBlank())
+ {
+ break;
+ }
+ else if (input.charAt(0) != 'y' )
+ {
+ break;
+ }
+ else
+ {
+ ErrorLogging.logError("WARNING! - Potential for error: Un-initialised DUT Serial numbers.");
+ }
+ }
+ runTests();
break;
case 6:
+ printHelp();
+ break;
+ case 7:
break;
default:
//Input handling already done by inputFiltering()
}
- } while (userInput != 6);
+ } while (userInput != mainMenuOptionCount);
}
catch(Exception e)
@@ -96,11 +170,7 @@ public class Cli
}
finally
{
- ErrorLogging.logError("DEBUG: PROGRAM CLOSING.");
- inputScanner.close();
- MovementFacade.closeGPIO();
- ErrorLogging.logError("DEBUG: END OF PROGRAM.");
- ErrorLogging.closeLogs();
+ close();
}
}
@@ -150,18 +220,22 @@ public class Cli
"\n\tAvailable variables to change:"+
"\n\t\tCrop dimensions");
println("----------------------------------------");
- println("3. Change test iteration count:"+
+ println("3. Set serial numbers: Set the serial " +
+ "\n\tnumber for the device under test." +
+ "\n\tThis is used in final data saving.");
+ println("----------------------------------------");
+ println("4. Change test iteration count:"+
"\n\tChange the number of times to"+
"\n\trun the tests of the device(s)"+
"\n\tunder test.");
println("----------------------------------------");
- println("4. Run tests: Run tests, with defined"+
+ println("5. Run tests: Run tests, with defined"+
"\n\tnumber of iterations. Uses"+
"\n\tvalues defined in config file.");
println("----------------------------------------");
- println("5. Help: Show this help page.");
+ println("6. Help: Show this help page.");
println("----------------------------------------");
- println("6. Exit: Close the program.");
+ println("7. Exit: Close the program.");
println("========================================");
println("Press Enter to continue...");
inputScanner.nextLine();
@@ -180,22 +254,26 @@ public class Cli
println("--------------------------------------");
println("1. Test and configure fixture movement");
println("2. Configure camera");
- println("3. Change test iteration count");
- println("4. Run tests");
- println("5. Help");
- println("6. Exit");
+ println("3. Set serial numbers");
+ println("4. Change test iteration count");
+ println("5. Run tests");
+ println("6. Help");
+ println("7. Exit");
println("======================================");
}
+ /**
+ * Predefined print statements for the movement submenu.
+ */
private static void printMovementMenu()
{
println("\n\n");
println("====================================");
println("Movement Menu:");
println("------------------------------------");
- println("Current Duty Cycle: " + MovementFacade.getDutyCycle());
- println("Current Frequency: " + MovementFacade.getFrequency());
- println("Current Motor Time-out: " + MovementFacade.getTimeout());
+ println("Current Duty Cycle: " + fixture.getDutyCycle());
+ println("Current Frequency: " + fixture.getFrequency());
+ println("Current Motor Time-out: " + fixture.getTimeout());
println("------------------------------------");
println("1. Change Duty Cycle");
println("2. Change Frequency");
@@ -204,6 +282,9 @@ public class Cli
println("====================================");
}
+ /**
+ * Pre-defined method for printing all available cameras in a menu
+ */
private static void printCameraMenu(List cameraList)
{
println("Available cameras to configure:");
@@ -218,6 +299,30 @@ public class Cli
println("------------------------------------");
}
+ /**
+ * Pre-defined method for printing all available cameras and the associated serials in a menu
+ */
+ private static void printSerialMenu(List cameraList)
+ {
+ println("Available serial numbers to set:");
+ println("------------------------------------");
+ for(int index = 0; index < cameraList.size(); index++)
+ {
+ int humanIndex = index+1;
+ String cameraName = (String)cameraList.get(index);
+ print(humanIndex + " - " + cameraName + " : ");
+ if(ConfigFacade.getSerial(cameraName) != null)
+ println(ConfigFacade.getSerial(cameraName));
+ else
+ println("");
+ }
+ println( (cameraList.size() + 1) + " - Exit to Main Menu");
+ println("------------------------------------");
+ }
+
+ /**
+ * Pre-defined menu for printing camera configuration options
+ */
private static void printCameraConfigMenu(String cameraName)
{
println("\n\n");
@@ -247,20 +352,24 @@ public class Cli
println("3. Change Crop Width");
println("4. Change Crop Height");
println("5. Change Composite Frame Count");
- println("6. Toggle crop");
- println("7. Toggle threshold");
- println("8. Exit");
+ println("6. Change Threshold Value");
+ println("7. Toggle crop");
+ println("8. Toggle threshold");
+ println("9. Exit");
println("====================================");
}
+ /**
+ * Function for testing movement, and modifying hardware values
+ */
private static void testMovement()
{
int userInput = -1;
do
{
println("Testing movement...");
- MovementFacade.testMotions();
+ fixture.testMotions();
printMovementMenu();
userInput = inputFiltering(inputScanner.nextLine());
switch (userInput)
@@ -277,7 +386,7 @@ public class Cli
int newDutyCycle = inputFiltering(inputScanner.nextLine());
if (newDutyCycle != -1)
{
- MovementFacade.setDutyCycle(newDutyCycle);
+ fixture.setDutyCycle(newDutyCycle);
break;
}
case 2:
@@ -285,7 +394,7 @@ public class Cli
int newFrequency = inputFiltering(inputScanner.nextLine());
if (newFrequency != -1)
{
- MovementFacade.setFrequency(newFrequency);
+ fixture.setFrequency(newFrequency);
break;
}
case 3:
@@ -293,7 +402,7 @@ public class Cli
int newTimeout = inputFiltering(inputScanner.nextLine());
if (newTimeout != -1)
{
- MovementFacade.setTimeout(newTimeout);
+ fixture.setTimeout(newTimeout);
break;
}
case 4:
@@ -313,28 +422,29 @@ public class Cli
List cameraList = new ArrayList<>(OpenCVFacade.getCameraNames());
//println(cameraList.toString());
- //Open a single new thread, so the canvas
- //used further down to display the temporary
- //image doesn't accidentally kill the program.
- //Created at beginning of function call to reduce
- //thread spawn count.
- //See also: https://docs.oracle.com/javase/8/docs/api/java/awt/doc-files/AWTThreadIssues.html#Autoshutdown
- Runnable r = new Runnable() {
- public void run() {
- Object o = new Object();
- try {
- synchronized (o) {
- o.wait();
- }
- } catch (InterruptedException ie) {
- }
- }
- };
- Thread t = new Thread(r);
- t.setDaemon(false);
- t.start();
+ // The below code should be unnecessary now. Leaving in for now to ensure things work properly.
+ ////Open a single new thread, so the canvas
+ ////used further down to display the temporary
+ ////image doesn't accidentally kill the program.
+ ////Created at beginning of function call to reduce
+ ////thread spawn count.
+ ////See also: https://docs.oracle.com/javase/8/docs/api/java/awt/doc-files/AWTThreadIssues.html#Autoshutdown
+ //Runnable r = new Runnable() {
+ // public void run() {
+ // Object o = new Object();
+ // try {
+ // synchronized (o) {
+ // o.wait();
+ // }
+ // } catch (InterruptedException ie) {
+ // }
+ // }
+ //};
+ //Thread t = new Thread(r);
+ //t.setDaemon(false);
+ //t.start();
- MovementFacade.iterationMovement(true);
+ fixture.iterationMovement(true);
do
{
@@ -354,11 +464,13 @@ public class Cli
//Leave do-while loop if the user asks to
if(userInput == (cameraList.size())) break;
+ else if(userInput < 0)
+ {}
else cameraName = cameraList.get((userInput));
do
{
- MovementFacade.pressButton();
+ fixture.pressButton();
try{ Thread.sleep(2000); } catch(Exception e){ ErrorLogging.logError(e); }
//Show image
CanvasFrame canvas = OpenCVFacade.showImage(cameraName);
@@ -390,12 +502,15 @@ public class Cli
modifiedProperty = ConfigProperties.COMPOSITE_FRAMES;
break;
case 6:
- modifiedProperty = ConfigProperties.CROP;
+ modifiedProperty = ConfigProperties.THRESHOLD_VALUE;
break;
case 7:
- modifiedProperty = ConfigProperties.THRESHOLD;
+ modifiedProperty = ConfigProperties.CROP;
break;
case 8:
+ modifiedProperty = ConfigProperties.THRESHOLD;
+ break;
+ case 9:
modifiedProperty = ConfigProperties.PRIME;
break;
default:
@@ -420,12 +535,45 @@ public class Cli
} while(true);
+ ConfigFacade.saveCurrentConfig();
println("Configuration complete!");
}
+ /**
+ * Sub-function used for defining the serial numbers of the devices under test
+ */
+ private static void setDUTSerials()
+ {
+ List cameraList = new ArrayList<>(OpenCVFacade.getCameraNames());
+ do
+ {
+ //Main menu
+ printSerialMenu(cameraList);
+
+ //Pick a camera to configure
+ int userInput;
+
+ String cameraName = "";
+ do
+ {
+ prompt("Enter the camera you wish to set the serial of: ");
+ userInput = inputFiltering(inputScanner.nextLine());
+ userInput--;
+ } while (cameraList.size() < userInput || userInput < 0);
+
+ //Leave do-while loop if the user asks to
+ if(userInput == (cameraList.size())) break;
+ else cameraName = cameraList.get((userInput));
+
+ prompt("Enter the serial number you wish to use for this camera: ");
+ ConfigFacade.setSerial(cameraName,inputScanner.nextLine());
+
+ } while(true);
+ }
+
/**
- * Setter for {@link #iterationCount}
+ * CLI-level setter for {@link #iterationCount}
*/
private static void setIterationCount()
{
@@ -443,31 +591,72 @@ public class Cli
*/
private static void runTests()
{
- DataSaving.initWorkbook(ConfigFacade.getOutputSaveLocation());
- boolean prime = false;
- for(String cameraName : OpenCVFacade.getCameraNames())
- {
- if(ConfigFacade.getValue(cameraName,ConfigProperties.PRIME) != 0)
+ final int localIterations = iterationCount;
+ //testingThread = new Thread(() ->
+ //{
+ DataSaving.initWorkbook(ConfigFacade.getOutputSaveLocation(),OpenCVFacade.getCameraNames().size());
+ boolean prime = false;
+ for(String cameraName : OpenCVFacade.getCameraNames())
{
- prime = true;
+ if(cameraName != null) { /*println(cameraName);*/ }
+ else ErrorLogging.logError("Null camera!");
+ if(ConfigFacade.getValue(cameraName,ConfigProperties.PRIME) != 0)
+ {
+ prime = true;
+ }
}
- }
- for(int i = 0; i < iterationCount; i++)
- {
- Map resultMap = new HashMap<>();
- MovementFacade.iterationMovement(prime);
- List iteration = OpenCVFacade.singleIteration();
- for(File file : iteration)
+ ErrorLogging.logError("DEBUG: Waking devices.");
+ fixture.iterationMovement(prime);
+ fixture.pressButton();
+ fixture.iterationMovement(prime);
+ ErrorLogging.logError("DEBUG: Starting tests...");
+ for(int i = 0; i < localIterations; i++)
{
- Double result = TesseractFacade.imageToDouble(file);
- String fileLocation = file.getAbsolutePath();
- resultMap.put(fileLocation,result);
- ErrorLogging.logError("DEBUG: Tesseract final output: " + result);
+ Map resultMap = new HashMap<>();
+ while(!LOCK.tryLock()) {}
+ fixture.iterationMovement(prime);
+ LOCK.unlock();
+ while(!LOCK.tryLock()) {}
+ List iteration = OpenCVFacade.singleIteration();
+ LOCK.unlock();
+ for(File file : iteration)
+ {
+ while(!LOCK.tryLock()) {}
+ //ErrorLogging.logError("DEBUG: File passed to Tesseract: " + file.getAbsolutePath());
+ Double result = TesseractFacade.imageToDouble(file);
+ LOCK.unlock();
+ while(!LOCK.tryLock()) {}
+ resultMap.put(file.getPath(),result);
+ ErrorLogging.logError("DEBUG: Tesseract final output: " + result);
+ LOCK.unlock();
+ }
+ while(!LOCK.tryLock()) {}
+ DataSaving.writeValues(i,resultMap);
+ LOCK.unlock();
}
- DataSaving.writeValues(i,resultMap);
- }
- println("=======================================");
- println("Tests complete!");
+ println("=======================================");
+ println("Testing complete!");
+ //});
+ //testingThread.start();
+ }
+
+
+ /**
+ * Function used if a config file was successfully imported.
+ */
+ public static void configImported()
+ { camerasConfigured = true; }
+
+ /**
+ * Function that closes GPIO and logging.
+ */
+ private static void close()
+ {
+ ErrorLogging.logError("DEBUG: PROGRAM CLOSING.");
+ if(inputScanner != null) inputScanner.close();
+ fixture.closeGPIO();
+ ErrorLogging.logError("DEBUG: END OF PROGRAM.");
+ ErrorLogging.closeLogs();
}
/**
@@ -482,7 +671,7 @@ public class Cli
* Parse the user's input, and check it for errors.
*
* @param input The unparsed user input, directly from the {@link Scanner}
- * @param mainMenu Whether or not the parsed input is a main menu value
+ * @param menu Which menu is being parsed
*
* @return The parsed value from the user. Returns -1 upon any error.
*/
@@ -578,7 +767,7 @@ public class Cli
*/
private static void invalidInput()
{
- invalidInput("");
+ invalidInput("Please input a valid number.");
}
/**
@@ -596,5 +785,8 @@ public class Cli
println("");
}
+ /**
+ * Enum of possible menus available
+ */
private enum Menus { MAIN,MOVEMENT,CAMERA,OTHER; }
}
diff --git a/src/main/java/org/baxter/disco/ocr/ConfigFacade.java b/src/main/java/org/baxter/disco/ocr/ConfigFacade.java
index f0dde06..c037d3d 100644
--- a/src/main/java/org/baxter/disco/ocr/ConfigFacade.java
+++ b/src/main/java/org/baxter/disco/ocr/ConfigFacade.java
@@ -13,6 +13,8 @@ import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
/**
@@ -22,7 +24,7 @@ import java.util.ArrayList;
* Can write to file when requested, reads from file on initial start.
*
* @author Blizzard Finnegan
- * @version 1.0.0, 27 Jan. 2023
+ * @version 1.2.0, 03 Feb. 2023
*/
public class ConfigFacade
{
@@ -41,19 +43,20 @@ public class ConfigFacade
/**
* Location to save output XLSX file to.
*/
- private static String outputSaveLocation = "outputData.xlsx";
+ public static String outputSaveLocation = "outputData-" +
+ (LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE)) + ".xlsx";
- //For values that are ultimately ints, truncate.
- //For values that are ultimately booleans, anything that isn't 0 should be considered True.
/**
* Map of all config values relating to the camera.
+ * For values that are ultimately ints, truncate.
+ * For values that are ultimately booleans, anything that isn't 0 should be considered True.
*/
private static final Map> configMap = new HashMap<>();
/**
- * Base class used for interacting with Configuration files.
+ * Temporary storage for the DUT's serial number.
*/
- private static final Configurations CONFIGURATIONS = new Configurations();
+ private static final Map DUT_SERIALS = new HashMap<>();
/**
* Builder for the main Configuration object.
@@ -71,26 +74,34 @@ public class ConfigFacade
static
{
+ ErrorLogging.logError("Starting configuration setup...");
CONFIG_STORE = null;
File configFile = new File(configFileLocation);
boolean newConfig = true;
try{ newConfig = configFile.createNewFile(); }
catch(Exception e){ ErrorLogging.logError(e); }
+
+ ErrorLogging.logError("Creating config file interface...");
CONFIG_BUILDER = new FileBasedConfigurationBuilder<>(INIConfiguration.class)
.configure(new Parameters().fileBased().setFile(configFile));
+
+ ErrorLogging.logError("Attempting to import config...");
try
{
CONFIG_STORE = CONFIG_BUILDER.getConfiguration();
}
catch(Exception e) { ErrorLogging.logError(e); }
finally { if(CONFIG_STORE == null) ErrorLogging.logError("CONFIG INIT ERROR!!! - Unsuccessful config initialisation. Camera commands will fail!"); }
- CONFIGURATIONS.iniBuilder(configFileLocation);
+
+ ErrorLogging.logError("Creating image storage directories...");
File imageLocation = new File(imageSaveLocation);
imageLocation.mkdir();
File debugImageLocation = new File(imageSaveLocation + "/debug");
debugImageLocation.mkdir();
File configImageLocation = new File(imageSaveLocation + "/config");
configImageLocation.mkdir();
+
+ ErrorLogging.logError("Creating output file....");
File outputFile = new File(outputSaveLocation);
try{ outputFile.createNewFile(); }
catch(Exception e){ ErrorLogging.logError(e); }
@@ -98,8 +109,13 @@ public class ConfigFacade
{
boolean saveSuccessful = saveDefaultConfig();
if(!saveSuccessful) ErrorLogging.logError("Save config failed!!!");
+ else ErrorLogging.logError("Configuration settings set up!");
+ }
+ else
+ {
+ loadConfig();
+ ErrorLogging.logError("Configuration settings loaded!");
}
- else { loadConfig(); }
CONFIG_BUILDER.setAutoSave(true);
}
/**
@@ -108,7 +124,7 @@ public class ConfigFacade
* Ints should be truncated.
* Any boolean that should be false should be stored as 0.
*
- * @param cameraName Name of the camera (as defined in {@link #activeCameras})
+ * @param cameraName Name of the camera (defined in {@link OpenCVFacade})
* @param property name of the property ({@link ConfigProperties})
*
* @return double of config value. Returns 0 if invalid input.
@@ -119,8 +135,13 @@ public class ConfigFacade
public static double getValue(String cameraName, ConfigProperties property)
{
double output = 0.0;
- List activeCameras = new ArrayList<>(OpenCVFacade.getCameraNames());
- if(!activeCameras.contains(cameraName)) return output;
+ if(!configMap.keySet().contains(cameraName))
+ {
+ ErrorLogging.logError("CONFIG ERROR!!! - Invalid camera name: " + cameraName);
+ ErrorLogging.logError("\tProperty: " + property.getConfig());
+ ErrorLogging.logError("\tconfigMap keys: " + configMap.keySet().toString());
+ return output;
+ }
Map cameraConfig = configMap.get(cameraName);
output = cameraConfig.get(property);
//Debug logger.
@@ -129,15 +150,21 @@ public class ConfigFacade
return output;
}
+ /**
+ * Initialises local list of available cameras
+ */
+ public static void init()
+ {
+ //ErrorLogging.logError("Starting import...");
+ }
+
/**
* Getter for the location of the output XLSX file.
*
* @return Absolute path of the image save location.
*/
public static String getOutputSaveLocation()
- {
- return outputSaveLocation;
- }
+ { return outputSaveLocation; }
/**
* Setter for the location of the output XLSX file.
@@ -161,9 +188,7 @@ public class ConfigFacade
* @return Absolute path of the image save location.
*/
public static String getImgSaveLocation()
- {
- return imageSaveLocation;
- }
+ { return imageSaveLocation; }
/**
* Setter for the saved image location.
@@ -203,6 +228,31 @@ public class ConfigFacade
return output;
}
+ /**
+ * Setter for a Device Under Test's serial number.
+ *
+ * @param cameraName The camera observing the given serial number
+ * @param serial The serial of the DUT
+ */
+ public static void setSerial(String cameraName, String serial)
+ { DUT_SERIALS.put(cameraName,serial); }
+
+ /**
+ * Getter for a Device Under Test's serial number.
+ *
+ * @param cameraName The camera observing the given serial number
+ *
+ * @return The DUT's serial
+ */
+ public static String getSerial(String cameraName)
+ {
+ if(!DUT_SERIALS.keySet().contains(cameraName)) return null;
+ return DUT_SERIALS.get(cameraName);
+ }
+
+ public static Map getSerials()
+ { return DUT_SERIALS; }
+
//**********************************************
//SAVE AND LOAD SETTINGS
//**********************************************
@@ -228,7 +278,7 @@ public class ConfigFacade
Map cameraConfig = new HashMap<>();
for(ConfigProperties property : ConfigProperties.values())
{
- String propertyName = camera + "/" + property.getConfig();
+ String propertyName = camera + "." + property.getConfig();
double propertyValue = property.getDefaultValue();
cameraConfig.put(property,propertyValue);
//ErrorLogging.logError("DEBUG: Attempting to save to config: ");
@@ -271,7 +321,7 @@ public class ConfigFacade
{
for(ConfigProperties property : ConfigProperties.values())
{
- String propertyName = camera + "/" + property.toString();
+ String propertyName = camera + "." + property.toString();
String propertyValue =configMap.get(camera).get(property).toString();
CONFIG_STORE.addProperty(propertyName,propertyValue);
}
@@ -304,17 +354,18 @@ public class ConfigFacade
*/
public static boolean loadConfig(String filename)
{
+ boolean emptyMap = configMap.keySet().size() == 0;
boolean output = false;
File file = new File(filename);
try
{
if(file.createNewFile())
- saveDefaultConfig();
+ return saveDefaultConfig();
}
catch(Exception e)
{
ErrorLogging.logError(e);
- saveDefaultConfig();
+ return saveDefaultConfig();
}
List cameraNames = new ArrayList<>(OpenCVFacade.getCameraNames());
if(Files.isRegularFile(Path.of(file.toURI())))
@@ -322,18 +373,17 @@ public class ConfigFacade
try
{
CONFIG_BUILDER = new FileBasedConfigurationBuilder<>(INIConfiguration.class).configure(new Parameters().fileBased().setFile(file));
- CONFIG_STORE = CONFIGURATIONS.ini(filename);
+ CONFIG_STORE = CONFIG_BUILDER.getConfiguration();
}
catch(Exception e){ ErrorLogging.logError(e); }
Set configSections = CONFIG_STORE.getSections();
- ErrorLogging.logError("DEBUG: imported sections - " + configSections.toString());
- ErrorLogging.logError("DEBUG: imported section size - " + configSections.size());
+ //ErrorLogging.logError("DEBUG: imported sections - " + configSections.toString());
+ //ErrorLogging.logError("DEBUG: empty map? : " + (configMap.keySet().size() == 0));
+ //ErrorLogging.logError("DEBUG: imported section size - " + configSections.size());
for(String sectionName : configSections)
{
Map savedSection = new HashMap<>();
- SubnodeConfiguration subSection = CONFIG_STORE.getSection(sectionName);
-
String subSectionPrefix = "";
for(String cameraName : cameraNames)
{
@@ -362,27 +412,26 @@ public class ConfigFacade
for(ConfigProperties configState : ConfigProperties.values())
{
- String subSectionValueName = subSectionPrefix + "/" + configState.getConfig();
- if(!subSection.containsKey(subSectionValueName))
- {
- ErrorLogging.logError("CONFIG LOAD ERROR!!! - Invalid config file.");
- return saveDefaultConfig(file.getAbsolutePath());
- }
- else
- {
- Double configValue = subSection.getDouble(configState.toString());
- savedSection.put(configState,configValue);
- }
+ Double configValue = CONFIG_STORE.getDouble(sectionName + "." + configState.getConfig());
+ //ErrorLogging.logError("DEBUG: Imported config value: " + Double.toString(configValue));
+ savedSection.put(configState,configValue);
}
- if(configMap.containsKey(sectionName))
+ if(emptyMap) configMap.put(sectionName,savedSection);
+ else
{
- configMap.put(sectionName,savedSection);
+ for(String key : configMap.keySet())
+ {
+ //ErrorLogging.logError("DEBUG: configMap Key: " + key + " ?= " + sectionName);
+ if( key.equals(sectionName))
+ { configMap.put(key,savedSection); }
+ }
}
}
output = true;
}
if(!output) ErrorLogging.logError("CONFIG LOAD ERROR!!! - Invalid path.");
+ else Cli.configImported();
return output;
}
diff --git a/src/main/java/org/baxter/disco/ocr/ConfigProperties.java b/src/main/java/org/baxter/disco/ocr/ConfigProperties.java
index 282938b..baf4f52 100644
--- a/src/main/java/org/baxter/disco/ocr/ConfigProperties.java
+++ b/src/main/java/org/baxter/disco/ocr/ConfigProperties.java
@@ -39,7 +39,11 @@ public enum ConfigProperties
/**
* Whether or not to press the button on the device twice, when under test.
*/
- PRIME("Prime device?","prime",0.0);
+ PRIME("Prime device?","prime",0.0),
+ /**
+ * Where the threshold point should land.
+ */
+ THRESHOLD_VALUE("Threshold value","thresholdValue",50.0);
/**
* Internal storage of human-readable name/meaning
diff --git a/src/main/java/org/baxter/disco/ocr/DataSaving.java b/src/main/java/org/baxter/disco/ocr/DataSaving.java
index b4059fd..ddfb8ff 100644
--- a/src/main/java/org/baxter/disco/ocr/DataSaving.java
+++ b/src/main/java/org/baxter/disco/ocr/DataSaving.java
@@ -2,10 +2,10 @@ package org.baxter.disco.ocr;
import java.io.File;
import java.io.FileOutputStream;
+import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
-import java.util.Set;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
@@ -16,7 +16,7 @@ import org.apache.poi.xssf.usermodel.XSSFWorkbook;
* Facade for saving data out to a file.
*
* @author Blizzard Finnegan
- * @version 1.0.0, 27 Jan. 2023
+ * @version 2.0.0, 02 Feb. 2023
*/
public class DataSaving
{
@@ -34,10 +34,12 @@ public class DataSaving
*/
private static File outputFile;
+ private static Map serials;
+
/**
* Prepares writer to write to XLSX file.
*/
- public static boolean initWorkbook(String filename)
+ public static boolean initWorkbook(String filename, int camCount)
{
boolean output = false;
outputFile = new File(filename);
@@ -45,6 +47,27 @@ public class DataSaving
{
outputWorkbook = new XSSFWorkbook();
outputSheet = outputWorkbook.createSheet();
+ int startingRow = outputSheet.getLastRowNum();
+ Row row = outputSheet.createRow(++startingRow);
+ int cellnum = 0;
+ Cell cell = row.createCell(cellnum++);
+ cell.setCellValue("Iteration");
+ for(int i = 0; i < camCount; i++)
+ {
+ cell = row.createCell(cellnum++);
+ cell.setCellValue("Serial");
+ cell = row.createCell(cellnum++);
+ cell.setCellValue("Image Location");
+ cell = row.createCell(cellnum++);
+ cell.setCellValue("Read Value");
+ cell = row.createCell(cellnum++);
+ cell.setCellValue("");
+ }
+ FileOutputStream outputStream = new FileOutputStream(outputFile);
+ outputWorkbook.write(outputStream);
+ output = true;
+ outputStream.close();
+ serials = ConfigFacade.getSerials();
}
catch(Exception e) { ErrorLogging.logError(e); }
return output;
@@ -62,13 +85,39 @@ public class DataSaving
boolean output = false;
int startingRow = outputSheet.getLastRowNum();
Row row = outputSheet.createRow(++startingRow);
- Set imageLocations = inputMap.keySet();
+ List imageLocations = new ArrayList<>(inputMap.keySet());
+ //ErrorLogging.logError("DEBUG: image locations: " + imageLocations.toString());
List