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:
parent
65eaec4035
commit
5796163068
11 changed files with 294 additions and 117 deletions
|
@ -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"
|
||||
ACTION=="add", KERNEL=="video[0-9]*", SUBSYSTEM=="video4linux", SUBSYSTEMS=="usb", ENV{ID_PATH}=="fillerText", 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-cam-left"
|
||||
|
|
|
@ -94,7 +94,7 @@ ls /dev/video-*
|
|||
|
||||
## 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`).
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<groupId>org.baxter.disco</groupId>
|
||||
<artifactId>ocr</artifactId>
|
||||
<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>
|
||||
<organization>
|
||||
<name>Baxter International</name>
|
||||
|
@ -26,11 +26,6 @@
|
|||
<plugin>
|
||||
<artifactId>maven-project-info-reports-plugin</artifactId>
|
||||
<version>3.0.0</version>
|
||||
<configuration>
|
||||
<suppressionFiles>
|
||||
<suppressionFile>./dependency-check-suppressions.xml</suppressionFile>
|
||||
</suppressionFiles>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
|
@ -100,6 +95,9 @@
|
|||
<plugin>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>3.4.1</version>
|
||||
<configuration>
|
||||
<show>private</show>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</reporting>
|
||||
|
|
10
pom.xml
10
pom.xml
|
@ -3,7 +3,7 @@
|
|||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.baxter.disco</groupId>
|
||||
<artifactId>ocr</artifactId>
|
||||
<version>4.3.7</version>
|
||||
<version>4.3.8</version>
|
||||
<packaging>jar</packaging>
|
||||
<name>Disco OCR Accuracy Over Life Testing</name>
|
||||
<description>Testing Discos for long-term accuracy, using automated optical character recognition.</description>
|
||||
|
@ -106,11 +106,11 @@
|
|||
<groupId>org.owasp</groupId>
|
||||
<artifactId>dependency-check-maven</artifactId>
|
||||
<version>8.1.2</version>
|
||||
<configuration>
|
||||
<!--<configuration>
|
||||
<suppressionFiles>
|
||||
<suppressionFile>dependency-check-suppressions.xml</suppressionFile>
|
||||
</suppressionFiles>
|
||||
</configuration>
|
||||
</configuration>-->
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
|
@ -192,9 +192,9 @@
|
|||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-javadoc-plugin</artifactId>
|
||||
<version>3.4.1</version>
|
||||
<!--<configuration>
|
||||
<configuration>
|
||||
<show>private</show>
|
||||
</configuration>-->
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</reporting>
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
#! /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
|
||||
|
|
|
@ -25,7 +25,7 @@ public class Cli
|
|||
/**
|
||||
* Complete build version number
|
||||
*/
|
||||
private static final String version = "4.3.7";
|
||||
private static final String version = "4.3.8";
|
||||
|
||||
/**
|
||||
* Currently saved iteration count.
|
||||
|
@ -50,6 +50,11 @@ public class Cli
|
|||
*/
|
||||
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.
|
||||
*/
|
||||
|
@ -85,7 +90,31 @@ public class Cli
|
|||
|
||||
ErrorLogging.logError("Calibrating motor movement. ");
|
||||
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
|
||||
{
|
||||
|
@ -188,6 +217,7 @@ public class Cli
|
|||
*/
|
||||
private static void printHelp()
|
||||
{
|
||||
ErrorLogging.logError("DEBUG: User asked for help at main menu.");
|
||||
println("\n\n");
|
||||
println("========================================");
|
||||
println("Explanations:");
|
||||
|
@ -348,6 +378,7 @@ public class Cli
|
|||
*/
|
||||
private static void printCameraConfigHelpMenu()
|
||||
{
|
||||
ErrorLogging.logError("DEBUG: User asked for help at camera config menu.");
|
||||
println("\n\n");
|
||||
println("============================================================");
|
||||
println("Camera Config Menu options:");
|
||||
|
@ -389,10 +420,11 @@ public class Cli
|
|||
*/
|
||||
private static void configureCameras()
|
||||
{
|
||||
ErrorLogging.logError("DEBUG: Configuing cameras...");
|
||||
List<String> cameraList = new ArrayList<>(OpenCVFacade.getCameraNames());
|
||||
|
||||
//Always wake the camera, to ensure that the image is useful
|
||||
MovementFacade.iterationMovement(true);
|
||||
if(safeGPIO) MovementFacade.iterationMovement(true);
|
||||
double tesseractValue = 0.0;
|
||||
|
||||
do
|
||||
|
@ -414,11 +446,14 @@ public class Cli
|
|||
|
||||
do
|
||||
{
|
||||
//Press button twice, to make sure the DUT is awake
|
||||
MovementFacade.pressButton();
|
||||
try{ Thread.sleep(2000); } catch(Exception e){ ErrorLogging.logError(e); }
|
||||
MovementFacade.pressButton();
|
||||
try{ Thread.sleep(2000); } catch(Exception e){ ErrorLogging.logError(e); }
|
||||
if(safeGPIO)
|
||||
{
|
||||
//Press button twice, to make sure the DUT is awake
|
||||
MovementFacade.pressButton();
|
||||
try{ Thread.sleep(2000); } catch(Exception e){ ErrorLogging.logError(e); }
|
||||
MovementFacade.pressButton();
|
||||
try{ Thread.sleep(2000); } catch(Exception e){ ErrorLogging.logError(e); }
|
||||
}
|
||||
|
||||
File image = OpenCVFacade.showImage(cameraName);
|
||||
tesseractValue = TesseractFacade.imageToDouble(image);
|
||||
|
@ -497,6 +532,7 @@ public class Cli
|
|||
*/
|
||||
private static void setDUTSerials()
|
||||
{
|
||||
ErrorLogging.logError("DEBUG: Setting DUT serials...");
|
||||
List<String> cameraList = new ArrayList<>(OpenCVFacade.getCameraNames());
|
||||
do
|
||||
{
|
||||
|
@ -534,6 +570,7 @@ public class Cli
|
|||
input = (int)inputFiltering(inputScanner.nextLine());
|
||||
} while(input == -1);
|
||||
iterationCount = input;
|
||||
ErrorLogging.logError("DEBUG: Iteration count modified! Iteration count is now: " + iterationCount);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -541,6 +578,7 @@ public class Cli
|
|||
*/
|
||||
private static void setActiveCameras()
|
||||
{
|
||||
ErrorLogging.logError("DEBUG: User modified active cameras.");
|
||||
List<String> cameraList = new ArrayList<>(OpenCVFacade.getCameraNames());
|
||||
|
||||
do
|
||||
|
@ -564,6 +602,13 @@ public class Cli
|
|||
ConfigFacade.setValue(cameraName,ConfigProperties.ACTIVE,newValue);
|
||||
|
||||
} 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
|
||||
ErrorLogging.logError("DEBUG: Waking devices...");
|
||||
MovementFacade.pressButton();
|
||||
try{ Thread.sleep(2000); } catch(Exception e){ ErrorLogging.logError(e); }
|
||||
if(safeGPIO)
|
||||
{
|
||||
MovementFacade.pressButton();
|
||||
try{ Thread.sleep(2000); } catch(Exception e){ ErrorLogging.logError(e); }
|
||||
}
|
||||
|
||||
Map<File,Double> resultMap = new HashMap<>();
|
||||
Map<String,File> cameraToFile = new HashMap<>();
|
||||
|
@ -623,12 +671,15 @@ public class Cli
|
|||
do
|
||||
{
|
||||
fail = false;
|
||||
while(!LOCK.tryLock()) {}
|
||||
MovementFacade.iterationMovement(prime);
|
||||
LOCK.unlock();
|
||||
if(safeGPIO)
|
||||
{
|
||||
while(!LOCK.tryLock()) {}
|
||||
MovementFacade.iterationMovement(prime);
|
||||
LOCK.unlock();
|
||||
|
||||
//Wait for the DUT to display an image
|
||||
try{ Thread.sleep(2000); } catch(Exception e){ ErrorLogging.logError(e); }
|
||||
//Wait for the DUT to display an image
|
||||
try{ Thread.sleep(2000); } catch(Exception e){ ErrorLogging.logError(e); }
|
||||
}
|
||||
|
||||
for(String cameraName : cameraList)
|
||||
{
|
||||
|
@ -692,7 +743,7 @@ public class Cli
|
|||
ErrorLogging.logError("DEBUG: PROGRAM CLOSING.");
|
||||
ErrorLogging.logError("DEBUG: =================");
|
||||
if(inputScanner != null) inputScanner.close();
|
||||
MovementFacade.closeGPIO();
|
||||
if(safeGPIO) MovementFacade.closeGPIO();
|
||||
ErrorLogging.logError("DEBUG: END OF PROGRAM.");
|
||||
ErrorLogging.closeLogs();
|
||||
println("The program has exited successfully. Please press Ctrl-c to return to the terminal prompt.");
|
||||
|
|
|
@ -148,6 +148,7 @@ public class ConfigFacade
|
|||
*/
|
||||
public static double getValue(String cameraName, ConfigProperties property)
|
||||
{
|
||||
ErrorLogging.logError("DEBUG: Getting " + property.getConfig() + " for " + cameraName + "camera...");
|
||||
double output = 0.0;
|
||||
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() {}
|
||||
|
||||
|
@ -182,12 +183,18 @@ public class ConfigFacade
|
|||
*/
|
||||
public static boolean setOutputSaveLocation(String path)
|
||||
{
|
||||
ErrorLogging.logError("DEBUG: Output save location modified!");
|
||||
boolean output = false;
|
||||
if(Files.exists(Paths.get(path)))
|
||||
{
|
||||
outputSaveLocation = path;
|
||||
ErrorLogging.logError("DEBUG: New output save location: " + outputSaveLocation);
|
||||
output = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ErrorLogging.logError("DEBUG: Output save location modification failed.");
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
|
@ -206,12 +213,18 @@ public class ConfigFacade
|
|||
*/
|
||||
public static boolean setImgSaveLocation(String path)
|
||||
{
|
||||
ErrorLogging.logError("DEBUG: Image save location modified!");
|
||||
boolean output = false;
|
||||
if(Files.exists(Paths.get(path)))
|
||||
{
|
||||
imageSaveLocation = path;
|
||||
ErrorLogging.logError("DEBUG: New image save location: " + outputSaveLocation);
|
||||
output = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ErrorLogging.logError("DEBUG: Output save location modification failed.");
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
|
@ -225,6 +238,7 @@ public class ConfigFacade
|
|||
*/
|
||||
public static boolean setValue(String cameraName, ConfigProperties property, double propertyValue)
|
||||
{
|
||||
ErrorLogging.logError("DEBUG: Setting " + property.getConfig() + " to " + propertyValue + "for " + cameraName);
|
||||
boolean output = false;
|
||||
|
||||
List<String> activeCameras = new ArrayList<>(OpenCVFacade.getCameraNames());
|
||||
|
@ -278,6 +292,7 @@ public class ConfigFacade
|
|||
*/
|
||||
public static boolean saveDefaultConfig(String filename)
|
||||
{
|
||||
ErrorLogging.logError("DEBUG: Saving default config to " + filename);
|
||||
boolean output = false;
|
||||
Set<String> cameraNames = OpenCVFacade.getCameraNames();
|
||||
|
||||
|
@ -335,6 +350,7 @@ public class ConfigFacade
|
|||
*/
|
||||
public static boolean saveCurrentConfig(String filename)
|
||||
{
|
||||
ErrorLogging.logError("DEBUG: Saving current config to " + filename);
|
||||
boolean output = false;
|
||||
|
||||
//Get a list of all cameras
|
||||
|
@ -379,6 +395,7 @@ public class ConfigFacade
|
|||
*/
|
||||
public static boolean loadConfig(String filename)
|
||||
{
|
||||
ErrorLogging.logError("DEBUG: Attempting to load config from " + filename);
|
||||
//Check if the current CONFIG_MAP is empty
|
||||
boolean emptyMap = CONFIG_MAP.keySet().size() == 0;
|
||||
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
|
||||
//the default values to a new file with that name
|
||||
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)
|
||||
{
|
||||
ErrorLogging.logError(e);
|
||||
|
@ -461,6 +485,7 @@ public class ConfigFacade
|
|||
*/
|
||||
private static boolean saveSingleDefault(String sectionName)
|
||||
{
|
||||
ErrorLogging.logError("DEBUG: Section unsuccessfully imported! Saving defaults to " + sectionName);
|
||||
boolean output = false;
|
||||
Map<ConfigProperties,Double> cameraConfig = new HashMap<>();
|
||||
for(ConfigProperties property : ConfigProperties.values())
|
||||
|
|
|
@ -91,6 +91,7 @@ public class DataSaving
|
|||
*/
|
||||
public static boolean initWorkbook(String filename, int camCount, double targetTemp, double failRange)
|
||||
{
|
||||
ErrorLogging.logError("DEBUG: Initialising workbook...");
|
||||
DataSaving.targetTemp = targetTemp;
|
||||
DataSaving.failRange = failRange;
|
||||
boolean output = false;
|
||||
|
@ -148,8 +149,15 @@ public class DataSaving
|
|||
passPercentCell.setCellValue("Pass %");
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -161,6 +169,7 @@ public class DataSaving
|
|||
*/
|
||||
private static void updateFormulas(int cameraCount)
|
||||
{
|
||||
ErrorLogging.logError("DEBUG: Updating formulas in Excel sheet...");
|
||||
int rowIndex = 0;
|
||||
FormulaEvaluator formulaEvaluator = outputWorkbook.getCreationHelper().createFormulaEvaluator();
|
||||
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)
|
||||
{
|
||||
ErrorLogging.logError("Writing values for " + cycle + " cycle to worksheet.");
|
||||
boolean output = false;
|
||||
int cellnum = 0;
|
||||
int startingRow = outputSheet.getLastRowNum();
|
||||
|
@ -283,8 +293,16 @@ public class DataSaving
|
|||
row.createCell(cellnum++);
|
||||
}
|
||||
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());
|
||||
return output;
|
||||
}
|
||||
|
|
|
@ -39,11 +39,24 @@ public class MovementFacade
|
|||
|
||||
/**
|
||||
* 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()}
|
||||
*/
|
||||
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
|
||||
//All addresses are in BCM format.
|
||||
|
||||
|
@ -77,11 +90,6 @@ public class MovementFacade
|
|||
*/
|
||||
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
|
||||
|
||||
/**
|
||||
|
@ -149,8 +157,32 @@ public class MovementFacade
|
|||
*/
|
||||
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...");
|
||||
runSwitchThread = new Thread(() ->
|
||||
{
|
||||
|
@ -175,17 +207,6 @@ public class MovementFacade
|
|||
}, "Run switch monitor.");
|
||||
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()
|
||||
{
|
||||
ErrorLogging.logError("DEBUG: --------------------------------------");
|
||||
ErrorLogging.logError("DEBUG: Resetting arm...");
|
||||
int counter;
|
||||
ErrorLogging.logError("DEBUG: Setting minimum frequency of PWM...");
|
||||
if(upperLimit.isHigh())
|
||||
{
|
||||
ErrorLogging.logError("DEBUG: Motor at highest point! Lowering to reset.");
|
||||
motorDirection.low();
|
||||
ErrorLogging.logError("DEBUG: Motor offset on.");
|
||||
motorEnable.on();
|
||||
motorDirectionDown();
|
||||
motorOn();
|
||||
try{ Thread.sleep(500); }
|
||||
catch (Exception e){ ErrorLogging.logError(e); }
|
||||
motorEnable.off();
|
||||
ErrorLogging.logError("DEBUG: Motor offset off.");
|
||||
motorOff();
|
||||
}
|
||||
ErrorLogging.logError("DEBUG: Moving motor to highest point.");
|
||||
motorDirection.high();
|
||||
motorDirectionUp();
|
||||
|
||||
ErrorLogging.logError("DEBUG: Motor return on.");
|
||||
motorEnable.on();
|
||||
motorOn();
|
||||
|
||||
ErrorLogging.logError("DEBUG: Is the upper limit switch reached? " + upperLimit.isHigh());
|
||||
for(counter = 0; counter < Integer.MAX_VALUE; counter++)
|
||||
for(counter = 0; counter < TIMEOUT; counter++)
|
||||
{
|
||||
try{ Thread.sleep(POLL_WAIT); } catch(Exception e){ ErrorLogging.logError(e); }
|
||||
if(upperLimit.isOn())
|
||||
|
@ -259,23 +276,33 @@ public class MovementFacade
|
|||
if(upperLimit.isOn()) break;
|
||||
}
|
||||
}
|
||||
motorEnable.off();
|
||||
ErrorLogging.logError("DEBUG: Motor returned after " + counter + " polls.");
|
||||
ErrorLogging.logError("DEBUG: --------------------------------------");
|
||||
return counter;
|
||||
motorOff();
|
||||
if(counter < TIMEOUT)
|
||||
{
|
||||
ErrorLogging.logError("DEBUG: Motor returned after " + counter + " polls.");
|
||||
ErrorLogging.logError("DEBUG: --------------------------------------");
|
||||
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.
|
||||
*/
|
||||
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 upTravelCounter = 0;
|
||||
//pwm.on(DUTY_CYCLE, MIN_FREQUENCY);
|
||||
motorDirection.low();
|
||||
motorEnable.on();
|
||||
motorDirectionDown();
|
||||
motorOn();
|
||||
for(downTravelCounter = 0; downTravelCounter < Integer.MAX_VALUE; downTravelCounter++)
|
||||
{
|
||||
try{ Thread.sleep(POLL_WAIT); } catch(Exception e){ ErrorLogging.logError(e); }
|
||||
|
@ -285,13 +312,13 @@ public class MovementFacade
|
|||
if(lowerLimit.isOn()) break;
|
||||
}
|
||||
}
|
||||
motorEnable.off();
|
||||
motorOff();
|
||||
if(lowerLimit.isOff()) ErrorLogging.logError("DEBUG: False positive on findDistance down!");
|
||||
|
||||
ErrorLogging.logError("DEBUG: Down travel count: " + downTravelCounter);
|
||||
|
||||
motorDirection.high();
|
||||
motorEnable.on();
|
||||
motorDirectionUp();
|
||||
motorOn();
|
||||
for(upTravelCounter = 0; upTravelCounter < Integer.MAX_VALUE; upTravelCounter++)
|
||||
{
|
||||
try{ Thread.sleep(POLL_WAIT); } catch(Exception e){ ErrorLogging.logError(e); }
|
||||
|
@ -301,7 +328,7 @@ public class MovementFacade
|
|||
if(upperLimit.isOn()) break;
|
||||
}
|
||||
}
|
||||
motorEnable.off();
|
||||
motorOff();
|
||||
if(upperLimit.isOff()) ErrorLogging.logError("DEBUG: False positive on findDistance up!");
|
||||
|
||||
ErrorLogging.logError("DEBUG: Up travel count: " + downTravelCounter);
|
||||
|
@ -327,15 +354,13 @@ public class MovementFacade
|
|||
DigitalInput limitSense;
|
||||
if(moveUp)
|
||||
{
|
||||
motorDirection.high();
|
||||
motorDirectionUp();
|
||||
limitSense = upperLimit;
|
||||
ErrorLogging.logError("DEBUG: Sending fixture up...");
|
||||
}
|
||||
else
|
||||
{
|
||||
motorDirection.low();
|
||||
motorDirectionDown();
|
||||
limitSense = lowerLimit;
|
||||
ErrorLogging.logError("DEBUG: Sending fixture down...");
|
||||
}
|
||||
|
||||
if(limitSense.isOn()) return FinalState.SAFE;
|
||||
|
@ -346,17 +371,17 @@ public class MovementFacade
|
|||
ErrorLogging.logError("DEBUG: Travel time: " + totalPollCount);
|
||||
ErrorLogging.logError("DEBUG: High speed poll count: " + highSpeedPolls);
|
||||
ErrorLogging.logError("DEBUG: =============================");
|
||||
motorEnable.on();
|
||||
motorOn();
|
||||
for(int i = 0; i < highSpeedPolls; i++)
|
||||
{
|
||||
try{ Thread.sleep(POLL_WAIT); } catch(Exception e){ ErrorLogging.logError(e); }
|
||||
if(limitSense.isOn())
|
||||
{
|
||||
motorEnable.off();
|
||||
motorOff();
|
||||
break;
|
||||
}
|
||||
}
|
||||
motorEnable.off();
|
||||
motorOff();
|
||||
|
||||
output = (limitSense.isOn() ? FinalState.UNSAFE : FinalState.SAFE);
|
||||
|
||||
|
@ -417,6 +442,33 @@ public class MovementFacade
|
|||
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
|
||||
{ UNSAFE, SAFE, FAILED; }
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ import java.util.Map;
|
|||
import java.util.Set;
|
||||
import java.io.File;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
@ -155,6 +156,7 @@ public class OpenCVFacade
|
|||
*/
|
||||
private static Mat takePicture(String cameraName)
|
||||
{
|
||||
ErrorLogging.logError("DEBUG: Taking picture...");
|
||||
Mat output = null;
|
||||
Frame temp = null;
|
||||
|
||||
|
@ -162,12 +164,16 @@ public class OpenCVFacade
|
|||
{
|
||||
try{ temp = cameraMap.get(cameraName).grab(); }
|
||||
catch(Exception e) { ErrorLogging.logError(e); }
|
||||
}
|
||||
|
||||
//Convert to grayscale
|
||||
Mat in = MAT_CONVERTER.convertToMat(temp);
|
||||
output = MAT_CONVERTER.convertToMat(temp);
|
||||
cvtColor(in,output,CV_BGR2GRAY);
|
||||
//Convert to grayscale
|
||||
Mat in = MAT_CONVERTER.convertToMat(temp);
|
||||
output = MAT_CONVERTER.convertToMat(temp);
|
||||
cvtColor(in,output,CV_BGR2GRAY);
|
||||
}
|
||||
else
|
||||
{
|
||||
ErrorLogging.logError("DEBUG: Invalid camera!");
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
@ -181,6 +187,7 @@ public class OpenCVFacade
|
|||
*/
|
||||
public static File showImage(String cameraName)
|
||||
{
|
||||
ErrorLogging.logError("DEBUG: Showing preview...");
|
||||
File imageLocation = completeProcess(cameraName,ConfigFacade.getImgSaveLocation() + "/config");
|
||||
if(imageLocation == null) return null;
|
||||
Frame outputImage = MAT_CONVERTER.convert(imread(imageLocation.getAbsolutePath()));
|
||||
|
@ -213,6 +220,7 @@ public class OpenCVFacade
|
|||
*/
|
||||
private static List<Mat> takeBurst(String cameraName, int frameCount)
|
||||
{
|
||||
ErrorLogging.logError("DEBUG: Taking burst of " + frameCount + " images...");
|
||||
List<Mat> output = null;
|
||||
if(getCameraNames().contains(cameraName))
|
||||
{
|
||||
|
@ -222,6 +230,10 @@ public class OpenCVFacade
|
|||
output.add(takePicture(cameraName));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ErrorLogging.logError("DEBUG: Invalid camera!");
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
|
@ -233,21 +245,25 @@ public class OpenCVFacade
|
|||
public static void setCrop(String cameraName)
|
||||
{
|
||||
Mat uncroppedImage = takePicture(cameraName);
|
||||
Rect roi = selectROI("Pick Crop Location", uncroppedImage);
|
||||
if(roi.x() == 0 && roi.y() == 0 && roi.width() == 0 && roi.height() == 0)
|
||||
if(uncroppedImage != null)
|
||||
{
|
||||
ErrorLogging.logError("Crop error! - Invalid crop selection.");
|
||||
ErrorLogging.logError("If the crop region did not have a box indicating is location, please restart the program.");
|
||||
ConfigFacade.setValue(cameraName,ConfigProperties.CROP_X,ConfigProperties.CROP_X.getDefaultValue());
|
||||
ConfigFacade.setValue(cameraName,ConfigProperties.CROP_Y,ConfigProperties.CROP_Y.getDefaultValue());
|
||||
ConfigFacade.setValue(cameraName,ConfigProperties.CROP_W,ConfigProperties.CROP_W.getDefaultValue());
|
||||
ConfigFacade.setValue(cameraName,ConfigProperties.CROP_H,ConfigProperties.CROP_H.getDefaultValue());
|
||||
return;
|
||||
ErrorLogging.logError("DEBUG: Allowing user to modify crop.");
|
||||
Rect roi = selectROI("Pick Crop Location", uncroppedImage);
|
||||
if(roi.x() == 0 && roi.y() == 0 && roi.width() == 0 && roi.height() == 0)
|
||||
{
|
||||
ErrorLogging.logError("Crop error! - Invalid crop selection.");
|
||||
ErrorLogging.logError("If the crop region did not have a box indicating is location, please restart the program.");
|
||||
ConfigFacade.setValue(cameraName,ConfigProperties.CROP_X,ConfigProperties.CROP_X.getDefaultValue());
|
||||
ConfigFacade.setValue(cameraName,ConfigProperties.CROP_Y,ConfigProperties.CROP_Y.getDefaultValue());
|
||||
ConfigFacade.setValue(cameraName,ConfigProperties.CROP_W,ConfigProperties.CROP_W.getDefaultValue());
|
||||
ConfigFacade.setValue(cameraName,ConfigProperties.CROP_H,ConfigProperties.CROP_H.getDefaultValue());
|
||||
return;
|
||||
}
|
||||
ConfigFacade.setValue(cameraName,ConfigProperties.CROP_X, roi.x());
|
||||
ConfigFacade.setValue(cameraName,ConfigProperties.CROP_Y, roi.y());
|
||||
ConfigFacade.setValue(cameraName,ConfigProperties.CROP_W, roi.width());
|
||||
ConfigFacade.setValue(cameraName,ConfigProperties.CROP_H, roi.height());
|
||||
}
|
||||
ConfigFacade.setValue(cameraName,ConfigProperties.CROP_X, roi.x());
|
||||
ConfigFacade.setValue(cameraName,ConfigProperties.CROP_Y, roi.y());
|
||||
ConfigFacade.setValue(cameraName,ConfigProperties.CROP_W, roi.width());
|
||||
ConfigFacade.setValue(cameraName,ConfigProperties.CROP_H, roi.height());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -322,6 +338,7 @@ public class OpenCVFacade
|
|||
*/
|
||||
private static File saveImage(Mat image, String fileLocation, String cameraName)
|
||||
{
|
||||
ErrorLogging.logError("DEBUG: Saving image from " + cameraName + " to " + fileLocation);
|
||||
File output = null;
|
||||
IplImage temp = MAT_CONVERTER.convertToIplImage(MAT_CONVERTER.convert(image));
|
||||
fileLocation = fileLocation + "/" + ErrorLogging.fileDatetime.format(LocalDateTime.now()) + "-" + cameraName + ".png";
|
||||
|
@ -337,30 +354,21 @@ public class OpenCVFacade
|
|||
* from {@link ConfigFacade}.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
private static Mat compose(List<Mat> images, boolean threshold,
|
||||
boolean crop, String cameraName)
|
||||
private static Mat compose(List<Mat> images)
|
||||
{
|
||||
ErrorLogging.logError("DEBUG: Attempting to compose " + images.size() + " images...");
|
||||
Mat output = null;
|
||||
int iterationCount = 1;
|
||||
for(Mat image : images)
|
||||
{
|
||||
Mat processedImage = image.clone();
|
||||
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!");
|
||||
|
@ -391,8 +399,32 @@ public class OpenCVFacade
|
|||
return output;
|
||||
}
|
||||
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);
|
||||
return output;
|
||||
}
|
||||
|
|
|
@ -101,6 +101,7 @@ public class TesseractFacade
|
|||
else ErrorLogging.logError("OCR ERROR!!! - OCR output is not a Double.");
|
||||
}
|
||||
}
|
||||
else { ErrorLogging.logError("OCR Failed to retrieve information!"); }
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue