Version bump

- Update all classes to more verbosely log what's going on.
- Update readme and udev rule for automatic camera checking
- Add dependency-checking plugin to pom.xml
- Add GPIO check on initialisation, and override to not drive GPIO if
  not plugged in
- Continue updating functions to be closer and more accurate to
  documentation
This commit is contained in:
Blizzard Finnegan 2023-03-15 11:10:09 -04:00
parent 65eaec4035
commit 5796163068
No known key found for this signature in database
GPG key ID: 86DC1B09AC2E1807
11 changed files with 294 additions and 117 deletions

View file

@ -1,2 +1,2 @@
##EXAMPLE: ACTION=="add", KERNEL=="video[0-9]*", SUBSYSTEM=="video4linux", SUBSYSTEMS=="usb", ENV{ID_PATH}=="platform-fd500000.pcie-pci-0000:01:00.0-usb-0:1.4:1.0", SYMLINK+="video-cam1" ##EXAMPLE: ACTION=="add", KERNEL=="video[0-9]*", SUBSYSTEM=="video4linux", SUBSYSTEMS=="usb", ENV{ID_PATH}=="platform-fd500000.pcie-pci-0000:01:00.0-usb-0:1.4:1.0", SYMLINK+="video-cam-left"
ACTION=="add", KERNEL=="video[0-9]*", SUBSYSTEM=="video4linux", SUBSYSTEMS=="usb", ENV{ID_PATH}=="fillerText", SYMLINK+="video-cam1" ACTION=="add", KERNEL=="video[0-9]*", SUBSYSTEM=="video4linux", SUBSYSTEMS=="usb", ENV{ID_PATH}=="fillerText", SYMLINK+="video-cam-left"

View file

@ -94,7 +94,7 @@ ls /dev/video-*
## Installation ## Installation
The project is then built from source, and the output final binary (located in `target/discoTesting.jar`) is copied to the Raspberry Pi for use. The project is then built from source, and the output final binary (located in `target/discoTesting-[version].jar`) is copied to the Raspberry Pi for use.
This project requires use of `udev` rules to ensure that cameras are in the proper location, by default. This can be modified in this project (camera initialisation is done in the initialisation of `OpenCVFacade`). This project requires use of `udev` rules to ensure that cameras are in the proper location, by default. This can be modified in this project (camera initialisation is done in the initialisation of `OpenCVFacade`).

View file

@ -4,7 +4,7 @@
<groupId>org.baxter.disco</groupId> <groupId>org.baxter.disco</groupId>
<artifactId>ocr</artifactId> <artifactId>ocr</artifactId>
<name>Disco OCR Accuracy Over Life Testing</name> <name>Disco OCR Accuracy Over Life Testing</name>
<version>4.3.7</version> <version>4.3.8</version>
<description>Testing Discos for long-term accuracy, using automated optical character recognition.</description> <description>Testing Discos for long-term accuracy, using automated optical character recognition.</description>
<organization> <organization>
<name>Baxter International</name> <name>Baxter International</name>
@ -26,11 +26,6 @@
<plugin> <plugin>
<artifactId>maven-project-info-reports-plugin</artifactId> <artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.0.0</version> <version>3.0.0</version>
<configuration>
<suppressionFiles>
<suppressionFile>./dependency-check-suppressions.xml</suppressionFile>
</suppressionFiles>
</configuration>
</plugin> </plugin>
<plugin> <plugin>
<artifactId>maven-javadoc-plugin</artifactId> <artifactId>maven-javadoc-plugin</artifactId>
@ -100,6 +95,9 @@
<plugin> <plugin>
<artifactId>maven-javadoc-plugin</artifactId> <artifactId>maven-javadoc-plugin</artifactId>
<version>3.4.1</version> <version>3.4.1</version>
<configuration>
<show>private</show>
</configuration>
</plugin> </plugin>
</plugins> </plugins>
</reporting> </reporting>

10
pom.xml
View file

@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>org.baxter.disco</groupId> <groupId>org.baxter.disco</groupId>
<artifactId>ocr</artifactId> <artifactId>ocr</artifactId>
<version>4.3.7</version> <version>4.3.8</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>Disco OCR Accuracy Over Life Testing</name> <name>Disco OCR Accuracy Over Life Testing</name>
<description>Testing Discos for long-term accuracy, using automated optical character recognition.</description> <description>Testing Discos for long-term accuracy, using automated optical character recognition.</description>
@ -106,11 +106,11 @@
<groupId>org.owasp</groupId> <groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId> <artifactId>dependency-check-maven</artifactId>
<version>8.1.2</version> <version>8.1.2</version>
<configuration> <!--<configuration>
<suppressionFiles> <suppressionFiles>
<suppressionFile>dependency-check-suppressions.xml</suppressionFile> <suppressionFile>dependency-check-suppressions.xml</suppressionFile>
</suppressionFiles> </suppressionFiles>
</configuration> </configuration>-->
</plugin> </plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
@ -192,9 +192,9 @@
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId> <artifactId>maven-javadoc-plugin</artifactId>
<version>3.4.1</version> <version>3.4.1</version>
<!--<configuration> <configuration>
<show>private</show> <show>private</show>
</configuration>--> </configuration>
</plugin> </plugin>
</plugins> </plugins>
</reporting> </reporting>

View file

@ -1,2 +1,2 @@
#! /usr/bin/env sh #! /usr/bin/env sh
sudo java -jar discoTesting-4.3.7.jar 2>/dev/null sudo java -jar discoTesting-4.3.8.jar 2>/dev/null

View file

@ -25,7 +25,7 @@ public class Cli
/** /**
* Complete build version number * Complete build version number
*/ */
private static final String version = "4.3.7"; private static final String version = "4.3.8";
/** /**
* Currently saved iteration count. * Currently saved iteration count.
@ -50,6 +50,11 @@ public class Cli
*/ */
private static boolean camerasConfigured = false; private static boolean camerasConfigured = false;
/**
* Whether GPIO is safe to use
*/
private static boolean safeGPIO = false;
/** /**
* Number of options currently available in the main menu. * Number of options currently available in the main menu.
*/ */
@ -85,7 +90,31 @@ public class Cli
ErrorLogging.logError("Calibrating motor movement. "); ErrorLogging.logError("Calibrating motor movement. ");
ErrorLogging.logError("The piston will fire momentarily when the motor calibration is complete."); ErrorLogging.logError("The piston will fire momentarily when the motor calibration is complete.");
MovementFacade.pressButton();
boolean testedGPIO = false;
do
{
try
{
MovementFacade.init();
ErrorLogging.logError("Motor movement successfully calibrated!");
testedGPIO = true;
safeGPIO = true;
}
catch(Exception e)
{
ErrorLogging.logError(e);
ErrorLogging.logError("GPIO initialisation error! Please check GPIO connections.");
println("");
prompt("Press enter to try again.");
String userInputString = inputScanner.nextLine();
if(userInputString.length() > 0)
{
testedGPIO = userInputString.contains("override");
ErrorLogging.logError("DEBUG: WARNING! - User override of GPIO check.");
}
}
}while(!testedGPIO);
do do
{ {
@ -188,6 +217,7 @@ public class Cli
*/ */
private static void printHelp() private static void printHelp()
{ {
ErrorLogging.logError("DEBUG: User asked for help at main menu.");
println("\n\n"); println("\n\n");
println("========================================"); println("========================================");
println("Explanations:"); println("Explanations:");
@ -348,6 +378,7 @@ public class Cli
*/ */
private static void printCameraConfigHelpMenu() private static void printCameraConfigHelpMenu()
{ {
ErrorLogging.logError("DEBUG: User asked for help at camera config menu.");
println("\n\n"); println("\n\n");
println("============================================================"); println("============================================================");
println("Camera Config Menu options:"); println("Camera Config Menu options:");
@ -389,10 +420,11 @@ public class Cli
*/ */
private static void configureCameras() private static void configureCameras()
{ {
ErrorLogging.logError("DEBUG: Configuing cameras...");
List<String> cameraList = new ArrayList<>(OpenCVFacade.getCameraNames()); List<String> cameraList = new ArrayList<>(OpenCVFacade.getCameraNames());
//Always wake the camera, to ensure that the image is useful //Always wake the camera, to ensure that the image is useful
MovementFacade.iterationMovement(true); if(safeGPIO) MovementFacade.iterationMovement(true);
double tesseractValue = 0.0; double tesseractValue = 0.0;
do do
@ -413,12 +445,15 @@ public class Cli
else cameraName = cameraList.get((userInput)); else cameraName = cameraList.get((userInput));
do do
{
if(safeGPIO)
{ {
//Press button twice, to make sure the DUT is awake //Press button twice, to make sure the DUT is awake
MovementFacade.pressButton(); MovementFacade.pressButton();
try{ Thread.sleep(2000); } catch(Exception e){ ErrorLogging.logError(e); } try{ Thread.sleep(2000); } catch(Exception e){ ErrorLogging.logError(e); }
MovementFacade.pressButton(); MovementFacade.pressButton();
try{ Thread.sleep(2000); } catch(Exception e){ ErrorLogging.logError(e); } try{ Thread.sleep(2000); } catch(Exception e){ ErrorLogging.logError(e); }
}
File image = OpenCVFacade.showImage(cameraName); File image = OpenCVFacade.showImage(cameraName);
tesseractValue = TesseractFacade.imageToDouble(image); tesseractValue = TesseractFacade.imageToDouble(image);
@ -497,6 +532,7 @@ public class Cli
*/ */
private static void setDUTSerials() private static void setDUTSerials()
{ {
ErrorLogging.logError("DEBUG: Setting DUT serials...");
List<String> cameraList = new ArrayList<>(OpenCVFacade.getCameraNames()); List<String> cameraList = new ArrayList<>(OpenCVFacade.getCameraNames());
do do
{ {
@ -534,6 +570,7 @@ public class Cli
input = (int)inputFiltering(inputScanner.nextLine()); input = (int)inputFiltering(inputScanner.nextLine());
} while(input == -1); } while(input == -1);
iterationCount = input; iterationCount = input;
ErrorLogging.logError("DEBUG: Iteration count modified! Iteration count is now: " + iterationCount);
} }
/** /**
@ -541,6 +578,7 @@ public class Cli
*/ */
private static void setActiveCameras() private static void setActiveCameras()
{ {
ErrorLogging.logError("DEBUG: User modified active cameras.");
List<String> cameraList = new ArrayList<>(OpenCVFacade.getCameraNames()); List<String> cameraList = new ArrayList<>(OpenCVFacade.getCameraNames());
do do
@ -564,6 +602,13 @@ public class Cli
ConfigFacade.setValue(cameraName,ConfigProperties.ACTIVE,newValue); ConfigFacade.setValue(cameraName,ConfigProperties.ACTIVE,newValue);
} while(true); } while(true);
ErrorLogging.logError("DEBUG: Currently active cameras:");
for(String camera : OpenCVFacade.getCameraNames())
{
if(ConfigFacade.getValue(camera,ConfigProperties.ACTIVE) != 0)
ErrorLogging.logError("DEBUG: - " + camera);
}
ErrorLogging.logError("DEBUG:");
} }
/** /**
@ -594,8 +639,11 @@ public class Cli
//Wake the device, then wait to ensure they're awake before continuing //Wake the device, then wait to ensure they're awake before continuing
ErrorLogging.logError("DEBUG: Waking devices..."); ErrorLogging.logError("DEBUG: Waking devices...");
if(safeGPIO)
{
MovementFacade.pressButton(); MovementFacade.pressButton();
try{ Thread.sleep(2000); } catch(Exception e){ ErrorLogging.logError(e); } try{ Thread.sleep(2000); } catch(Exception e){ ErrorLogging.logError(e); }
}
Map<File,Double> resultMap = new HashMap<>(); Map<File,Double> resultMap = new HashMap<>();
Map<String,File> cameraToFile = new HashMap<>(); Map<String,File> cameraToFile = new HashMap<>();
@ -623,12 +671,15 @@ public class Cli
do do
{ {
fail = false; fail = false;
if(safeGPIO)
{
while(!LOCK.tryLock()) {} while(!LOCK.tryLock()) {}
MovementFacade.iterationMovement(prime); MovementFacade.iterationMovement(prime);
LOCK.unlock(); LOCK.unlock();
//Wait for the DUT to display an image //Wait for the DUT to display an image
try{ Thread.sleep(2000); } catch(Exception e){ ErrorLogging.logError(e); } try{ Thread.sleep(2000); } catch(Exception e){ ErrorLogging.logError(e); }
}
for(String cameraName : cameraList) for(String cameraName : cameraList)
{ {
@ -692,7 +743,7 @@ public class Cli
ErrorLogging.logError("DEBUG: PROGRAM CLOSING."); ErrorLogging.logError("DEBUG: PROGRAM CLOSING.");
ErrorLogging.logError("DEBUG: ================="); ErrorLogging.logError("DEBUG: =================");
if(inputScanner != null) inputScanner.close(); if(inputScanner != null) inputScanner.close();
MovementFacade.closeGPIO(); if(safeGPIO) MovementFacade.closeGPIO();
ErrorLogging.logError("DEBUG: END OF PROGRAM."); ErrorLogging.logError("DEBUG: END OF PROGRAM.");
ErrorLogging.closeLogs(); ErrorLogging.closeLogs();
println("The program has exited successfully. Please press Ctrl-c to return to the terminal prompt."); println("The program has exited successfully. Please press Ctrl-c to return to the terminal prompt.");

View file

@ -148,6 +148,7 @@ public class ConfigFacade
*/ */
public static double getValue(String cameraName, ConfigProperties property) public static double getValue(String cameraName, ConfigProperties property)
{ {
ErrorLogging.logError("DEBUG: Getting " + property.getConfig() + " for " + cameraName + "camera...");
double output = 0.0; double output = 0.0;
if(!CONFIG_MAP.keySet().contains(cameraName)) if(!CONFIG_MAP.keySet().contains(cameraName))
{ {
@ -163,7 +164,7 @@ public class ConfigFacade
} }
/** /**
* Called to force early calling of the static block * Initialises ConfigFacade and OpenCVFacade values
*/ */
public static void init() {} public static void init() {}
@ -182,12 +183,18 @@ public class ConfigFacade
*/ */
public static boolean setOutputSaveLocation(String path) public static boolean setOutputSaveLocation(String path)
{ {
ErrorLogging.logError("DEBUG: Output save location modified!");
boolean output = false; boolean output = false;
if(Files.exists(Paths.get(path))) if(Files.exists(Paths.get(path)))
{ {
outputSaveLocation = path; outputSaveLocation = path;
ErrorLogging.logError("DEBUG: New output save location: " + outputSaveLocation);
output = true; output = true;
} }
else
{
ErrorLogging.logError("DEBUG: Output save location modification failed.");
}
return output; return output;
} }
@ -206,12 +213,18 @@ public class ConfigFacade
*/ */
public static boolean setImgSaveLocation(String path) public static boolean setImgSaveLocation(String path)
{ {
ErrorLogging.logError("DEBUG: Image save location modified!");
boolean output = false; boolean output = false;
if(Files.exists(Paths.get(path))) if(Files.exists(Paths.get(path)))
{ {
imageSaveLocation = path; imageSaveLocation = path;
ErrorLogging.logError("DEBUG: New image save location: " + outputSaveLocation);
output = true; output = true;
} }
else
{
ErrorLogging.logError("DEBUG: Output save location modification failed.");
}
return output; return output;
} }
@ -225,6 +238,7 @@ public class ConfigFacade
*/ */
public static boolean setValue(String cameraName, ConfigProperties property, double propertyValue) public static boolean setValue(String cameraName, ConfigProperties property, double propertyValue)
{ {
ErrorLogging.logError("DEBUG: Setting " + property.getConfig() + " to " + propertyValue + "for " + cameraName);
boolean output = false; boolean output = false;
List<String> activeCameras = new ArrayList<>(OpenCVFacade.getCameraNames()); List<String> activeCameras = new ArrayList<>(OpenCVFacade.getCameraNames());
@ -278,6 +292,7 @@ public class ConfigFacade
*/ */
public static boolean saveDefaultConfig(String filename) public static boolean saveDefaultConfig(String filename)
{ {
ErrorLogging.logError("DEBUG: Saving default config to " + filename);
boolean output = false; boolean output = false;
Set<String> cameraNames = OpenCVFacade.getCameraNames(); Set<String> cameraNames = OpenCVFacade.getCameraNames();
@ -335,6 +350,7 @@ public class ConfigFacade
*/ */
public static boolean saveCurrentConfig(String filename) public static boolean saveCurrentConfig(String filename)
{ {
ErrorLogging.logError("DEBUG: Saving current config to " + filename);
boolean output = false; boolean output = false;
//Get a list of all cameras //Get a list of all cameras
@ -379,6 +395,7 @@ public class ConfigFacade
*/ */
public static boolean loadConfig(String filename) public static boolean loadConfig(String filename)
{ {
ErrorLogging.logError("DEBUG: Attempting to load config from " + filename);
//Check if the current CONFIG_MAP is empty //Check if the current CONFIG_MAP is empty
boolean emptyMap = CONFIG_MAP.keySet().size() == 0; boolean emptyMap = CONFIG_MAP.keySet().size() == 0;
boolean output = false; boolean output = false;
@ -386,7 +403,14 @@ public class ConfigFacade
//If the config file we're trying to load from doesn't exist, failover to saving //If the config file we're trying to load from doesn't exist, failover to saving
//the default values to a new file with that name //the default values to a new file with that name
File file = new File(filename); File file = new File(filename);
try{ if(file.createNewFile()) return saveDefaultConfig(); } try
{
if(file.createNewFile())
{
ErrorLogging.logError("DEBUG: Listed file does not exist!");
return saveDefaultConfig();
}
}
catch(Exception e) catch(Exception e)
{ {
ErrorLogging.logError(e); ErrorLogging.logError(e);
@ -461,6 +485,7 @@ public class ConfigFacade
*/ */
private static boolean saveSingleDefault(String sectionName) private static boolean saveSingleDefault(String sectionName)
{ {
ErrorLogging.logError("DEBUG: Section unsuccessfully imported! Saving defaults to " + sectionName);
boolean output = false; boolean output = false;
Map<ConfigProperties,Double> cameraConfig = new HashMap<>(); Map<ConfigProperties,Double> cameraConfig = new HashMap<>();
for(ConfigProperties property : ConfigProperties.values()) for(ConfigProperties property : ConfigProperties.values())

View file

@ -91,6 +91,7 @@ public class DataSaving
*/ */
public static boolean initWorkbook(String filename, int camCount, double targetTemp, double failRange) public static boolean initWorkbook(String filename, int camCount, double targetTemp, double failRange)
{ {
ErrorLogging.logError("DEBUG: Initialising workbook...");
DataSaving.targetTemp = targetTemp; DataSaving.targetTemp = targetTemp;
DataSaving.failRange = failRange; DataSaving.failRange = failRange;
boolean output = false; boolean output = false;
@ -148,8 +149,15 @@ public class DataSaving
passPercentCell.setCellValue("Pass %"); passPercentCell.setCellValue("Pass %");
try (FileOutputStream outputStream = new FileOutputStream(outputFile)) try (FileOutputStream outputStream = new FileOutputStream(outputFile))
{ outputWorkbook.write(outputStream); } {
catch(Exception e) {ErrorLogging.logError(e);} outputWorkbook.write(outputStream);
ErrorLogging.logError("DEBUG: Initialising workbook completed successfully.");
}
catch(Exception e)
{
ErrorLogging.logError("DEBUG: Workbook save failed!");
ErrorLogging.logError(e);
}
return output; return output;
} }
@ -161,6 +169,7 @@ public class DataSaving
*/ */
private static void updateFormulas(int cameraCount) private static void updateFormulas(int cameraCount)
{ {
ErrorLogging.logError("DEBUG: Updating formulas in Excel sheet...");
int rowIndex = 0; int rowIndex = 0;
FormulaEvaluator formulaEvaluator = outputWorkbook.getCreationHelper().createFormulaEvaluator(); FormulaEvaluator formulaEvaluator = outputWorkbook.getCreationHelper().createFormulaEvaluator();
int lastColumnOfData = outputSheet.getRow(rowIndex).getLastCellNum(); int lastColumnOfData = outputSheet.getRow(rowIndex).getLastCellNum();
@ -224,6 +233,7 @@ public class DataSaving
*/ */
public static boolean writeValues(int cycle, Map<File,Double> inputMap, Map<String,File> cameraToFile) public static boolean writeValues(int cycle, Map<File,Double> inputMap, Map<String,File> cameraToFile)
{ {
ErrorLogging.logError("Writing values for " + cycle + " cycle to worksheet.");
boolean output = false; boolean output = false;
int cellnum = 0; int cellnum = 0;
int startingRow = outputSheet.getLastRowNum(); int startingRow = outputSheet.getLastRowNum();
@ -283,8 +293,16 @@ public class DataSaving
row.createCell(cellnum++); row.createCell(cellnum++);
} }
try (FileOutputStream outputStream = new FileOutputStream(outputFile)) try (FileOutputStream outputStream = new FileOutputStream(outputFile))
{ outputWorkbook.write(outputStream); output = true; } {
catch(Exception e) {ErrorLogging.logError(e);} outputWorkbook.write(outputStream);
output = true;
ErrorLogging.logError("DEBUG: Writing values to Excel sheet was successful.");
}
catch(Exception e)
{
ErrorLogging.logError("DEBUG: Writing values to Excel sheet failed!");
ErrorLogging.logError(e);
}
updateFormulas(cameraNames.size()); updateFormulas(cameraNames.size());
return output; return output;
} }

View file

@ -39,11 +39,24 @@ public class MovementFacade
/** /**
* Amount of distance to travel. * Amount of distance to travel.
* Measured in... seemingly arbitrary units? Not sure on the math here. * Measured in polls (~10ms intervals currently)
* Set in {@link #findDistance()} * Set in {@link #findDistance()}
*/ */
private static double TRAVEL_DIST; private static double TRAVEL_DIST;
/**
* How many milliseconds to wait before polling the GPIO
*/
private static final int POLL_WAIT = 10;
/**
* How many polls before assuming bad GPIO connection.
* Fixture movement takes approximately 2.5s, or 250 polls.
* Double this to account for errors, or slower fixtures.
*/
private static final int TIMEOUT = 500;
//PWM Addresses //PWM Addresses
//All addresses are in BCM format. //All addresses are in BCM format.
@ -77,11 +90,6 @@ public class MovementFacade
*/ */
private static final int LOWER_LIMIT_ADDR = 24; private static final int LOWER_LIMIT_ADDR = 24;
/**
* How many milliseconds to wait before polling the GPIO
*/
private static final int POLL_WAIT = 10;
//Pi GPIO pin objects //Pi GPIO pin objects
/** /**
@ -149,8 +157,32 @@ public class MovementFacade
*/ */
private static Context pi4j; private static Context pi4j;
static public static void init() throws Exception
{ {
pi4j = Pi4J.newAutoContext();
ErrorLogging.logError("DEBUG: Opening input GPIO pins...");
upperLimit = inputBuilder(UPPER_LIMIT_ADDR);
lowerLimit = inputBuilder(LOWER_LIMIT_ADDR);
runSwitch = inputBuilder(RUN_SWITCH_ADDR);
ErrorLogging.logError("DEBUG: Opening output GPIO pins...");
motorEnable = outputBuilder(MOTOR_ENABLE_ADDR);
motorDirection = outputBuilder(MOTOR_DIRECTION_ADDR);
pistonActivate = outputBuilder(PISTON_ADDR);
try
{
findDistance();
pressButton();
}
catch(Exception e)
{
ErrorLogging.logError(e);
pi4j.shutdown();
throw new Exception("GPIO init error!!! Check GPIO connections.");
}
ErrorLogging.logError("DEBUG: Starting lock thread..."); ErrorLogging.logError("DEBUG: Starting lock thread...");
runSwitchThread = new Thread(() -> runSwitchThread = new Thread(() ->
{ {
@ -175,17 +207,6 @@ public class MovementFacade
}, "Run switch monitor."); }, "Run switch monitor.");
runSwitchThread.start(); runSwitchThread.start();
pi4j = Pi4J.newAutoContext();
upperLimit = inputBuilder(UPPER_LIMIT_ADDR);
lowerLimit = inputBuilder(LOWER_LIMIT_ADDR);
runSwitch = inputBuilder(RUN_SWITCH_ADDR);
motorEnable = outputBuilder(MOTOR_ENABLE_ADDR);
motorDirection = outputBuilder(MOTOR_DIRECTION_ADDR);
pistonActivate = outputBuilder(PISTON_ADDR);
findDistance();
} }
@ -230,27 +251,23 @@ public class MovementFacade
public static int resetArm() public static int resetArm()
{ {
ErrorLogging.logError("DEBUG: --------------------------------------"); ErrorLogging.logError("DEBUG: --------------------------------------");
ErrorLogging.logError("DEBUG: Resetting arm...");
int counter; int counter;
ErrorLogging.logError("DEBUG: Setting minimum frequency of PWM...");
if(upperLimit.isHigh()) if(upperLimit.isHigh())
{ {
ErrorLogging.logError("DEBUG: Motor at highest point! Lowering to reset."); ErrorLogging.logError("DEBUG: Motor at highest point! Lowering to reset.");
motorDirection.low(); motorDirectionDown();
ErrorLogging.logError("DEBUG: Motor offset on."); motorOn();
motorEnable.on();
try{ Thread.sleep(500); } try{ Thread.sleep(500); }
catch (Exception e){ ErrorLogging.logError(e); } catch (Exception e){ ErrorLogging.logError(e); }
motorEnable.off(); motorOff();
ErrorLogging.logError("DEBUG: Motor offset off.");
} }
ErrorLogging.logError("DEBUG: Moving motor to highest point."); ErrorLogging.logError("DEBUG: Moving motor to highest point.");
motorDirection.high(); motorDirectionUp();
ErrorLogging.logError("DEBUG: Motor return on."); motorOn();
motorEnable.on();
ErrorLogging.logError("DEBUG: Is the upper limit switch reached? " + upperLimit.isHigh()); for(counter = 0; counter < TIMEOUT; counter++)
for(counter = 0; counter < Integer.MAX_VALUE; counter++)
{ {
try{ Thread.sleep(POLL_WAIT); } catch(Exception e){ ErrorLogging.logError(e); } try{ Thread.sleep(POLL_WAIT); } catch(Exception e){ ErrorLogging.logError(e); }
if(upperLimit.isOn()) if(upperLimit.isOn())
@ -259,23 +276,33 @@ public class MovementFacade
if(upperLimit.isOn()) break; if(upperLimit.isOn()) break;
} }
} }
motorEnable.off(); motorOff();
if(counter < TIMEOUT)
{
ErrorLogging.logError("DEBUG: Motor returned after " + counter + " polls."); ErrorLogging.logError("DEBUG: Motor returned after " + counter + " polls.");
ErrorLogging.logError("DEBUG: --------------------------------------"); ErrorLogging.logError("DEBUG: --------------------------------------");
return counter; return counter;
} }
else
{
ErrorLogging.logError("DEBUG: No motor return after 30 seconds.");
ErrorLogging.logError("DEBUG: --------------------------------------");
return -1;
}
}
/** /**
* Used to programmatically find the distance between the upper and lower limit switches. * Used to programmatically find the distance between the upper and lower limit switches.
*/ */
private static void findDistance() private static void findDistance() throws Exception
{ {
resetArm(); ErrorLogging.logError("DEBUG: Measuring travel time to buttons...");
int safeGPIO = resetArm();
if(safeGPIO < 0) throw new Exception("Failed GPIO initialisation!");
int downTravelCounter = 0; int downTravelCounter = 0;
int upTravelCounter = 0; int upTravelCounter = 0;
//pwm.on(DUTY_CYCLE, MIN_FREQUENCY); motorDirectionDown();
motorDirection.low(); motorOn();
motorEnable.on();
for(downTravelCounter = 0; downTravelCounter < Integer.MAX_VALUE; downTravelCounter++) for(downTravelCounter = 0; downTravelCounter < Integer.MAX_VALUE; downTravelCounter++)
{ {
try{ Thread.sleep(POLL_WAIT); } catch(Exception e){ ErrorLogging.logError(e); } try{ Thread.sleep(POLL_WAIT); } catch(Exception e){ ErrorLogging.logError(e); }
@ -285,13 +312,13 @@ public class MovementFacade
if(lowerLimit.isOn()) break; if(lowerLimit.isOn()) break;
} }
} }
motorEnable.off(); motorOff();
if(lowerLimit.isOff()) ErrorLogging.logError("DEBUG: False positive on findDistance down!"); if(lowerLimit.isOff()) ErrorLogging.logError("DEBUG: False positive on findDistance down!");
ErrorLogging.logError("DEBUG: Down travel count: " + downTravelCounter); ErrorLogging.logError("DEBUG: Down travel count: " + downTravelCounter);
motorDirection.high(); motorDirectionUp();
motorEnable.on(); motorOn();
for(upTravelCounter = 0; upTravelCounter < Integer.MAX_VALUE; upTravelCounter++) for(upTravelCounter = 0; upTravelCounter < Integer.MAX_VALUE; upTravelCounter++)
{ {
try{ Thread.sleep(POLL_WAIT); } catch(Exception e){ ErrorLogging.logError(e); } try{ Thread.sleep(POLL_WAIT); } catch(Exception e){ ErrorLogging.logError(e); }
@ -301,7 +328,7 @@ public class MovementFacade
if(upperLimit.isOn()) break; if(upperLimit.isOn()) break;
} }
} }
motorEnable.off(); motorOff();
if(upperLimit.isOff()) ErrorLogging.logError("DEBUG: False positive on findDistance up!"); if(upperLimit.isOff()) ErrorLogging.logError("DEBUG: False positive on findDistance up!");
ErrorLogging.logError("DEBUG: Up travel count: " + downTravelCounter); ErrorLogging.logError("DEBUG: Up travel count: " + downTravelCounter);
@ -327,15 +354,13 @@ public class MovementFacade
DigitalInput limitSense; DigitalInput limitSense;
if(moveUp) if(moveUp)
{ {
motorDirection.high(); motorDirectionUp();
limitSense = upperLimit; limitSense = upperLimit;
ErrorLogging.logError("DEBUG: Sending fixture up...");
} }
else else
{ {
motorDirection.low(); motorDirectionDown();
limitSense = lowerLimit; limitSense = lowerLimit;
ErrorLogging.logError("DEBUG: Sending fixture down...");
} }
if(limitSense.isOn()) return FinalState.SAFE; if(limitSense.isOn()) return FinalState.SAFE;
@ -346,17 +371,17 @@ public class MovementFacade
ErrorLogging.logError("DEBUG: Travel time: " + totalPollCount); ErrorLogging.logError("DEBUG: Travel time: " + totalPollCount);
ErrorLogging.logError("DEBUG: High speed poll count: " + highSpeedPolls); ErrorLogging.logError("DEBUG: High speed poll count: " + highSpeedPolls);
ErrorLogging.logError("DEBUG: ============================="); ErrorLogging.logError("DEBUG: =============================");
motorEnable.on(); motorOn();
for(int i = 0; i < highSpeedPolls; i++) for(int i = 0; i < highSpeedPolls; i++)
{ {
try{ Thread.sleep(POLL_WAIT); } catch(Exception e){ ErrorLogging.logError(e); } try{ Thread.sleep(POLL_WAIT); } catch(Exception e){ ErrorLogging.logError(e); }
if(limitSense.isOn()) if(limitSense.isOn())
{ {
motorEnable.off(); motorOff();
break; break;
} }
} }
motorEnable.off(); motorOff();
output = (limitSense.isOn() ? FinalState.UNSAFE : FinalState.SAFE); output = (limitSense.isOn() ? FinalState.UNSAFE : FinalState.SAFE);
@ -417,6 +442,33 @@ public class MovementFacade
pressButton(); pressButton();
} }
private static void motorOn()
{
ErrorLogging.logError("DEBUG: Motor on.");
motorEnable.on();
}
private static void motorOff()
{
ErrorLogging.logError("DEBUG: Motor off.");
motorEnable.off();
}
private static void motorDirectionUp()
{
ErrorLogging.logError("DEBUG: Motor travelling up.");
motorDirection.high();
}
private static void motorDirectionDown()
{
ErrorLogging.logError("DEBUG: Motor travelling down.");
motorDirection.low();
}
/**
* Possible states for motor control movement.
*/
public enum FinalState public enum FinalState
{ UNSAFE, SAFE, FAILED; } { UNSAFE, SAFE, FAILED; }
} }

View file

@ -27,6 +27,7 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.io.File; import java.io.File;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -155,6 +156,7 @@ public class OpenCVFacade
*/ */
private static Mat takePicture(String cameraName) private static Mat takePicture(String cameraName)
{ {
ErrorLogging.logError("DEBUG: Taking picture...");
Mat output = null; Mat output = null;
Frame temp = null; Frame temp = null;
@ -162,12 +164,16 @@ public class OpenCVFacade
{ {
try{ temp = cameraMap.get(cameraName).grab(); } try{ temp = cameraMap.get(cameraName).grab(); }
catch(Exception e) { ErrorLogging.logError(e); } catch(Exception e) { ErrorLogging.logError(e); }
}
//Convert to grayscale //Convert to grayscale
Mat in = MAT_CONVERTER.convertToMat(temp); Mat in = MAT_CONVERTER.convertToMat(temp);
output = MAT_CONVERTER.convertToMat(temp); output = MAT_CONVERTER.convertToMat(temp);
cvtColor(in,output,CV_BGR2GRAY); cvtColor(in,output,CV_BGR2GRAY);
}
else
{
ErrorLogging.logError("DEBUG: Invalid camera!");
}
return output; return output;
} }
@ -181,6 +187,7 @@ public class OpenCVFacade
*/ */
public static File showImage(String cameraName) public static File showImage(String cameraName)
{ {
ErrorLogging.logError("DEBUG: Showing preview...");
File imageLocation = completeProcess(cameraName,ConfigFacade.getImgSaveLocation() + "/config"); File imageLocation = completeProcess(cameraName,ConfigFacade.getImgSaveLocation() + "/config");
if(imageLocation == null) return null; if(imageLocation == null) return null;
Frame outputImage = MAT_CONVERTER.convert(imread(imageLocation.getAbsolutePath())); Frame outputImage = MAT_CONVERTER.convert(imread(imageLocation.getAbsolutePath()));
@ -213,6 +220,7 @@ public class OpenCVFacade
*/ */
private static List<Mat> takeBurst(String cameraName, int frameCount) private static List<Mat> takeBurst(String cameraName, int frameCount)
{ {
ErrorLogging.logError("DEBUG: Taking burst of " + frameCount + " images...");
List<Mat> output = null; List<Mat> output = null;
if(getCameraNames().contains(cameraName)) if(getCameraNames().contains(cameraName))
{ {
@ -222,6 +230,10 @@ public class OpenCVFacade
output.add(takePicture(cameraName)); output.add(takePicture(cameraName));
} }
} }
else
{
ErrorLogging.logError("DEBUG: Invalid camera!");
}
return output; return output;
} }
@ -233,6 +245,9 @@ public class OpenCVFacade
public static void setCrop(String cameraName) public static void setCrop(String cameraName)
{ {
Mat uncroppedImage = takePicture(cameraName); Mat uncroppedImage = takePicture(cameraName);
if(uncroppedImage != null)
{
ErrorLogging.logError("DEBUG: Allowing user to modify crop.");
Rect roi = selectROI("Pick Crop Location", uncroppedImage); Rect roi = selectROI("Pick Crop Location", uncroppedImage);
if(roi.x() == 0 && roi.y() == 0 && roi.width() == 0 && roi.height() == 0) if(roi.x() == 0 && roi.y() == 0 && roi.width() == 0 && roi.height() == 0)
{ {
@ -249,6 +264,7 @@ public class OpenCVFacade
ConfigFacade.setValue(cameraName,ConfigProperties.CROP_W, roi.width()); ConfigFacade.setValue(cameraName,ConfigProperties.CROP_W, roi.width());
ConfigFacade.setValue(cameraName,ConfigProperties.CROP_H, roi.height()); ConfigFacade.setValue(cameraName,ConfigProperties.CROP_H, roi.height());
} }
}
/** /**
* Crop a given image, based on dimensions in the configuration. * Crop a given image, based on dimensions in the configuration.
@ -322,6 +338,7 @@ public class OpenCVFacade
*/ */
private static File saveImage(Mat image, String fileLocation, String cameraName) private static File saveImage(Mat image, String fileLocation, String cameraName)
{ {
ErrorLogging.logError("DEBUG: Saving image from " + cameraName + " to " + fileLocation);
File output = null; File output = null;
IplImage temp = MAT_CONVERTER.convertToIplImage(MAT_CONVERTER.convert(image)); IplImage temp = MAT_CONVERTER.convertToIplImage(MAT_CONVERTER.convert(image));
fileLocation = fileLocation + "/" + ErrorLogging.fileDatetime.format(LocalDateTime.now()) + "-" + cameraName + ".png"; fileLocation = fileLocation + "/" + ErrorLogging.fileDatetime.format(LocalDateTime.now()) + "-" + cameraName + ".png";
@ -337,30 +354,21 @@ public class OpenCVFacade
* from {@link ConfigFacade}. * from {@link ConfigFacade}.
* *
* @param images List of images to be composed * @param images List of images to be composed
* @param threshold Whether to put the image through a binary threshold
* @param crop Whether to crop the image
* @param cameraName Name of the camera the images came from (used to determine crop sizing and threshold value)
*
* @return A single image, found by boolean AND-ing together all parsed images. * @return A single image, found by boolean AND-ing together all parsed images.
*/ */
private static Mat compose(List<Mat> images, boolean threshold, private static Mat compose(List<Mat> images)
boolean crop, String cameraName)
{ {
ErrorLogging.logError("DEBUG: Attempting to compose " + images.size() + " images..."); ErrorLogging.logError("DEBUG: Attempting to compose " + images.size() + " images...");
Mat output = null; Mat output = null;
int iterationCount = 1;
for(Mat image : images) for(Mat image : images)
{ {
Mat processedImage = image.clone(); Mat processedImage = image.clone();
image.copyTo(processedImage); image.copyTo(processedImage);
if(crop) processedImage = crop(processedImage,cameraName);
if(threshold) processedImage = thresholdImage(processedImage,cameraName);
if(iterationCount == 1) output = processedImage.clone(); if(output == null) output = processedImage.clone();
bitwise_and((iterationCount == 1 ? processedImage : output),processedImage, output); bitwise_and(output,processedImage, output);
iterationCount++;
} }
if(output != null) ErrorLogging.logError("DEBUG: Compositing successful!"); if(output != null) ErrorLogging.logError("DEBUG: Compositing successful!");
@ -391,8 +399,32 @@ public class OpenCVFacade
return output; return output;
} }
List<Mat> imageList = takeBurst(cameraName, compositeFrames); List<Mat> imageList = takeBurst(cameraName, compositeFrames);
List<Mat> processedImageList = new ArrayList<>();
Mat finalImage = compose(imageList, threshold, crop, cameraName); Mat finalImage = null;
if(crop) ErrorLogging.logError("Cropping is enabled.");
if(threshold) ErrorLogging.logError("Thresholding is enabled.");
for(Mat image : imageList)
{
Mat processedImage = image.clone();
image.copyTo(processedImage);
if(crop)
processedImage = crop(processedImage,cameraName);
if(threshold)
processedImage = thresholdImage(processedImage,cameraName);
if(finalImage == null)
finalImage = processedImage.clone();
processedImageList.add(processedImage);
}
if(compositeFrames > 1)
finalImage = compose(processedImageList);
//Mat finalImage = compose(imageList, threshold, crop, cameraName);
output = saveImage(finalImage, saveLocation,cameraName); output = saveImage(finalImage, saveLocation,cameraName);
return output; return output;
} }

View file

@ -101,6 +101,7 @@ public class TesseractFacade
else ErrorLogging.logError("OCR ERROR!!! - OCR output is not a Double."); else ErrorLogging.logError("OCR ERROR!!! - OCR output is not a Double.");
} }
} }
else { ErrorLogging.logError("OCR Failed to retrieve information!"); }
return output; return output;
} }
} }