End of Mar. 1 commit
- Start adding motor protections - add motor config variables to config file - remove magic numbers in TesseractFacade
This commit is contained in:
parent
1feb66674d
commit
19977655c4
7 changed files with 300 additions and 213 deletions
|
@ -4,7 +4,7 @@
|
|||
<groupId>org.baxter.disco</groupId>
|
||||
<artifactId>ocr</artifactId>
|
||||
<name>Disco OCR Accuracy Over Life Testing</name>
|
||||
<version>4.3.4</version>
|
||||
<version>4.3.5</version>
|
||||
<description>Testing Discos for long-term accuracy, using automated optical character recognition.</description>
|
||||
<organization>
|
||||
<name>Baxter International</name>
|
||||
|
|
2
pom.xml
2
pom.xml
|
@ -3,7 +3,7 @@
|
|||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.baxter.disco</groupId>
|
||||
<artifactId>ocr</artifactId>
|
||||
<version>4.3.4</version>
|
||||
<version>4.3.5</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>
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
#! /usr/bin/env sh
|
||||
sudo java -jar discoTesting-4.3.4.jar 2>/dev/null
|
||||
sudo java -jar discoTesting-4.3.5.jar 2>/dev/null
|
||||
|
|
|
@ -25,7 +25,7 @@ public class Cli
|
|||
/**
|
||||
* Complete build version number
|
||||
*/
|
||||
private static final String version = "4.3.4";
|
||||
private static final String version = "4.3.5";
|
||||
|
||||
/**
|
||||
* Currently saved iteration count.
|
||||
|
@ -58,22 +58,17 @@ public class Cli
|
|||
/**
|
||||
* Number of options currently available in the movement sub-menu.
|
||||
*/
|
||||
private static final int movementMenuOptionCount = 4;
|
||||
private static final int movementMenuOptionCount = 3;
|
||||
|
||||
/**
|
||||
* Number of options currently available in the camera configuration sub-menu.
|
||||
*/
|
||||
private static final int cameraMenuOptionCount = 10;
|
||||
private static final int cameraMenuOptionCount = 6;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
public static final Lock LOCK = new ReentrantLock();
|
||||
|
||||
//private static Thread safeThread;
|
||||
|
||||
|
@ -91,112 +86,80 @@ public class Cli
|
|||
ErrorLogging.logError("Version: " + version);
|
||||
ErrorLogging.logError("========================");
|
||||
try{
|
||||
//Create scanner for user input from the console
|
||||
inputScanner = new Scanner(System.in);
|
||||
|
||||
//Initialise the fixture, start monitor thread
|
||||
fixture = new MovementFacade(LOCK);
|
||||
|
||||
//Initialise the config
|
||||
ConfigFacade.init();
|
||||
|
||||
//Create the user input value
|
||||
int userInput = 0;
|
||||
|
||||
//Main menu loop
|
||||
boolean fixtureTested = false;
|
||||
|
||||
do
|
||||
{
|
||||
//Show the main menu, wait for user input
|
||||
printMainMenu();
|
||||
userInput = (int)inputFiltering(inputScanner.nextLine());
|
||||
|
||||
//Perform action based on user input
|
||||
switch (userInput)
|
||||
{
|
||||
case 1:
|
||||
//Test fixture movement, modify fixture values as necessary
|
||||
testMovement();
|
||||
fixtureTested = true;
|
||||
break;
|
||||
case 2:
|
||||
//Warn user that starting cameras will take a moment.
|
||||
println("Setting up cameras...");
|
||||
println("This may take a moment...");
|
||||
configureCameras();
|
||||
//Set that cameras are successfully configured, to mute runTests warning
|
||||
camerasConfigured = true;
|
||||
break;
|
||||
case 3:
|
||||
//Set serials of the DUTs
|
||||
setDUTSerials();
|
||||
break;
|
||||
case 4:
|
||||
//Change the number of iterations to run the tests
|
||||
setIterationCount();
|
||||
break;
|
||||
case 5:
|
||||
//Set cameras to use in testing
|
||||
setActiveCameras();
|
||||
break;
|
||||
case 6:
|
||||
//Warn user that cameras haven't been set up, if they haven't been set up
|
||||
//Won't warn user if config was imported successfully
|
||||
if(!fixtureTested)
|
||||
{
|
||||
prompt("You have not tested the fixture's movement configuration! Are you sure you would like to continue? (y/N): ");
|
||||
String input = inputScanner.nextLine().toLowerCase().trim();
|
||||
if(input.isBlank() || input.charAt(0) != 'y') break;
|
||||
else
|
||||
ErrorLogging.logError("DEBUG: Potential for error: Untested fixture movement config.");
|
||||
}
|
||||
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;
|
||||
}
|
||||
//Save in the logs that cameras may not have been configured.
|
||||
String input = inputScanner.nextLine().toLowerCase().trim();
|
||||
if( input.isBlank() || input.charAt(0) != 'y' ) break;
|
||||
else
|
||||
{
|
||||
ErrorLogging.logError("WARNING! - Potential for error: Un-initialised cameras.");
|
||||
}
|
||||
ErrorLogging.logError("DEBUG: Potential for error: Un-initialised cameras.");
|
||||
}
|
||||
|
||||
//If there's an unset camera serial, prompt the user to go back and set that up
|
||||
serialsSet = true;
|
||||
for(String cameraName : OpenCVFacade.getCameraNames())
|
||||
{
|
||||
if(ConfigFacade.getValue(cameraName,ConfigProperties.ACTIVE) != 0 &&
|
||||
ConfigFacade.getSerial(cameraName) == null )
|
||||
{
|
||||
serialsSet = false;
|
||||
break;
|
||||
}
|
||||
else serialsSet = true;
|
||||
}
|
||||
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;
|
||||
}
|
||||
String input = inputScanner.nextLine().toLowerCase().trim();
|
||||
if( input.isBlank() || input.charAt(0) != 'y' ) break;
|
||||
else
|
||||
{
|
||||
ErrorLogging.logError("WARNING! - Potential for error: Un-initialised DUT Serial numbers.");
|
||||
}
|
||||
ErrorLogging.logError("DEBUG: Potential for error: Un-initialised DUT Serial numbers.");
|
||||
}
|
||||
|
||||
//Run tests for the given number of iterations
|
||||
runTests();
|
||||
break;
|
||||
case 7:
|
||||
//Show help menu
|
||||
printHelp();
|
||||
break;
|
||||
case 8:
|
||||
//Leave the menu
|
||||
break;
|
||||
default:
|
||||
//Input handling already done by inputFiltering()
|
||||
|
@ -253,7 +216,6 @@ public class Cli
|
|||
"\n\tdirections, to check range"+
|
||||
"\n\tof motion." +
|
||||
"\n\tAvailable variables to change:"+
|
||||
"\n\t\tPWM Duty Cycle"+
|
||||
"\n\t\tPWM Frequency"+
|
||||
"\n\t\tMotor Time-out");
|
||||
println("----------------------------------------");
|
||||
|
@ -321,14 +283,12 @@ public class Cli
|
|||
println("====================================");
|
||||
println("Movement Menu:");
|
||||
println("------------------------------------");
|
||||
println("Current Duty Cycle: " + fixture.getDutyCycle());
|
||||
println("Current Frequency: " + fixture.getFrequency());
|
||||
println("Current Motor Time-out: " + fixture.getTimeout());
|
||||
println("Current Frequency: " + MovementFacade.getUserFrequency() + "KHz");
|
||||
println("Current Motor Time-out: " + MovementFacade.getTimeout());
|
||||
println("------------------------------------");
|
||||
println("1. Change Duty Cycle");
|
||||
println("2. Change Frequency");
|
||||
println("3. Change Motor Time-out");
|
||||
println("4. Exit");
|
||||
println("1. Change Frequency");
|
||||
println("2. Change Motor Time-out");
|
||||
println("3. Exit");
|
||||
println("====================================");
|
||||
}
|
||||
|
||||
|
@ -408,7 +368,7 @@ public class Cli
|
|||
println("Will the image be thresholded? " + thresholdImage);
|
||||
println("Tesseract parsed value for camera " + cameraName + ": " + tesseractValue);
|
||||
println("------------------------------------");
|
||||
println("1. Change Crop Point");
|
||||
println("1. Change Crop Region");
|
||||
println("2. Change Composite Frame Count");
|
||||
println("3. Change Threshold Value");
|
||||
println("4. Toggle crop");
|
||||
|
@ -428,49 +388,64 @@ public class Cli
|
|||
do
|
||||
{
|
||||
println("Testing movement...");
|
||||
fixture.testMotions();
|
||||
MovementFacade.reset();
|
||||
MovementFacade.FinalState downwardMove = MovementFacade.goDown();
|
||||
switch(downwardMove)
|
||||
{
|
||||
case UNSAFE:
|
||||
ErrorLogging.logError("Movement warning!!! - Time-out too long, motor does not slow down before hitting limit switch. Consider setting this value to be lower.");
|
||||
break;
|
||||
case FAILED:
|
||||
ErrorLogging.logError("Movement warning!!! - Fixture did not hit lower limit switch. Consider changing frequency.");
|
||||
ErrorLogging.logError("Fixture movement configuration: Downward movement missed.");
|
||||
break;
|
||||
case SAFE:
|
||||
MovementFacade.FinalState returnMove = MovementFacade.goUp();
|
||||
switch(returnMove)
|
||||
{
|
||||
case UNSAFE:
|
||||
ErrorLogging.logError("Movement warning!!! - Time-out too long, motor does not slow down before hitting limit switch. Consider setting this value to be lower.");
|
||||
break;
|
||||
case FAILED:
|
||||
ErrorLogging.logError("Movement warning!!! - Fixture did not hit upper limit switch. Consider changing frequency.");
|
||||
ErrorLogging.logError("Fixture movement configuration: Return movement missed.");
|
||||
break;
|
||||
case SAFE:
|
||||
}
|
||||
}
|
||||
printMovementMenu();
|
||||
userInput = (int)inputFiltering(inputScanner.nextLine());
|
||||
switch (userInput)
|
||||
{
|
||||
/*
|
||||
* Menu options:
|
||||
* 1. Change Duty Cycle
|
||||
* 2. Change Frequency
|
||||
* 3. Change Motor Time-out
|
||||
* 4. Exit
|
||||
* 1. Change Frequency
|
||||
* 2. Change Motor Time-out
|
||||
* 3. Exit
|
||||
*/
|
||||
case 1:
|
||||
prompt("Input the desired duty cycle value: ");
|
||||
int newDutyCycle = (int)inputFiltering(inputScanner.nextLine());
|
||||
if (newDutyCycle != -1)
|
||||
{
|
||||
fixture.setDutyCycle(newDutyCycle);
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
prompt("Input the desired frequency value: ");
|
||||
prompt("Input the desired frequency value (in KHz): ");
|
||||
int newFrequency = (int)inputFiltering(inputScanner.nextLine());
|
||||
if (newFrequency != -1)
|
||||
{
|
||||
fixture.setFrequency(newFrequency);
|
||||
MovementFacade.setFrequency(newFrequency);
|
||||
break;
|
||||
}
|
||||
case 3:
|
||||
case 2:
|
||||
prompt("Input the desired time-out (in seconds): ");
|
||||
double newTimeout = inputFiltering(inputScanner.nextLine());
|
||||
if (newTimeout != -1)
|
||||
{
|
||||
fixture.setTimeout(newTimeout);
|
||||
MovementFacade.setTimeout(newTimeout);
|
||||
break;
|
||||
}
|
||||
case 4:
|
||||
case 3:
|
||||
break;
|
||||
default:
|
||||
ErrorLogging.logError("User Input Error!!! - Invalid input.");
|
||||
}
|
||||
}
|
||||
while(userInput != 4);
|
||||
while(userInput != 3);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -481,7 +456,7 @@ public class Cli
|
|||
List<String> cameraList = new ArrayList<>(OpenCVFacade.getCameraNames());
|
||||
|
||||
//Always wake the camera, to ensure that the image is useful
|
||||
fixture.iterationMovement(true);
|
||||
MovementFacade.iterationMovement(true);
|
||||
double tesseractValue = 0.0;
|
||||
|
||||
//Main camera config loop
|
||||
|
@ -509,9 +484,9 @@ public class Cli
|
|||
do
|
||||
{
|
||||
//Press button twice, to make sure the DUT is awake
|
||||
fixture.pressButton();
|
||||
MovementFacade.pressButton();
|
||||
try{ Thread.sleep(2000); } catch(Exception e){ ErrorLogging.logError(e); }
|
||||
fixture.pressButton();
|
||||
MovementFacade.pressButton();
|
||||
try{ Thread.sleep(2000); } catch(Exception e){ ErrorLogging.logError(e); }
|
||||
|
||||
//Show image
|
||||
|
@ -707,7 +682,7 @@ public class Cli
|
|||
|
||||
//Wake the device, then wait to ensure they're awake before continuing
|
||||
ErrorLogging.logError("DEBUG: Waking devices...");
|
||||
fixture.pressButton();
|
||||
MovementFacade.pressButton();
|
||||
try{ Thread.sleep(2000); } catch(Exception e){ ErrorLogging.logError(e); }
|
||||
|
||||
//Create final maps for result images, result values, and camera names
|
||||
|
@ -739,7 +714,7 @@ public class Cli
|
|||
fail = false;
|
||||
//Move the fixture for one iteration, with whether or not the DUTs need to be primed
|
||||
while(!LOCK.tryLock()) {}
|
||||
fixture.iterationMovement(prime);
|
||||
MovementFacade.iterationMovement(prime);
|
||||
LOCK.unlock();
|
||||
|
||||
//Wait for the DUT to display an image
|
||||
|
@ -778,10 +753,10 @@ public class Cli
|
|||
result >= 100 ||
|
||||
result == Double.NEGATIVE_INFINITY)
|
||||
{
|
||||
fixture.goUp();
|
||||
MovementFacade.goUp();
|
||||
try{ Thread.sleep(20000); }
|
||||
catch(Exception e){ ErrorLogging.logError(e); }
|
||||
fixture.pressButton();
|
||||
MovementFacade.pressButton();
|
||||
fail = true;
|
||||
break;
|
||||
}
|
||||
|
@ -819,7 +794,7 @@ public class Cli
|
|||
{
|
||||
ErrorLogging.logError("DEBUG: PROGRAM CLOSING.");
|
||||
if(inputScanner != null) inputScanner.close();
|
||||
fixture.closeGPIO();
|
||||
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.");
|
||||
|
|
|
@ -14,6 +14,7 @@ import java.util.ArrayList;
|
|||
|
||||
//Apache Commons Configuration imports
|
||||
import org.apache.commons.configuration2.INIConfiguration;
|
||||
import org.apache.commons.configuration2.SubnodeConfiguration;
|
||||
import org.apache.commons.configuration2.builder.FileBasedConfigurationBuilder;
|
||||
import org.apache.commons.configuration2.builder.fluent.Parameters;
|
||||
|
||||
|
@ -59,6 +60,15 @@ public class ConfigFacade
|
|||
*/
|
||||
private static final Map<String, String> DUT_SERIALS = new HashMap<>();
|
||||
|
||||
private static final String MOVEMENT_SECTION = "Fixture";
|
||||
private static int MOVEMENT_FREQ;
|
||||
|
||||
private static final int MOVEMENT_DEFAULT_FREQ = 70000;
|
||||
|
||||
private static double MOVEMENT_TIME_OUT;
|
||||
|
||||
private static final double MOVEMENT_DEFAULT_TIME_OUT = 2.5;
|
||||
|
||||
/**
|
||||
* Builder for the main Configuration object.
|
||||
*
|
||||
|
@ -76,6 +86,7 @@ public class ConfigFacade
|
|||
//This block will ALWAYS run first.
|
||||
static
|
||||
{
|
||||
//Get config values
|
||||
ErrorLogging.logError("Starting configuration setup...");
|
||||
//Give CONFIG_STORE an intentionally bad value
|
||||
CONFIG_STORE = null;
|
||||
|
@ -140,6 +151,31 @@ public class ConfigFacade
|
|||
//Autosave the config
|
||||
CONFIG_BUILDER.setAutoSave(true);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public static double getFixtureValue(FixtureValues value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case FREQUENCY: return (double)MOVEMENT_FREQ;
|
||||
case TIMEOUT: return MOVEMENT_TIME_OUT;
|
||||
default: return -1.0;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean setFixtureValue(FixtureValues type, double value)
|
||||
{
|
||||
boolean output = true;
|
||||
switch(type)
|
||||
{
|
||||
case FREQUENCY: MOVEMENT_FREQ = (int)value;
|
||||
case TIMEOUT: MOVEMENT_TIME_OUT = value;
|
||||
default:
|
||||
}
|
||||
return output;
|
||||
}
|
||||
/**
|
||||
* Get a given config value.
|
||||
* All values are stored as doubles.
|
||||
|
@ -169,7 +205,6 @@ public class ConfigFacade
|
|||
Map<ConfigProperties,Double> cameraConfig = configMap.get(cameraName);
|
||||
output = cameraConfig.get(property);
|
||||
//Debug logger.
|
||||
//NOTE THAT THIS BREAKS TUI MENUS, AS OF ErrorLogging 1.1.0
|
||||
//ErrorLogging.logError("DEBUG: getValue - return value: " + cameraName
|
||||
// + "/" + property.getConfig() + " = " + output);
|
||||
return output;
|
||||
|
@ -329,6 +364,9 @@ public class ConfigFacade
|
|||
configMap.put(camera,cameraConfig);
|
||||
}
|
||||
|
||||
CONFIG_STORE.setProperty(MOVEMENT_SECTION + "." + "frequency", MOVEMENT_DEFAULT_FREQ);
|
||||
CONFIG_STORE.setProperty(MOVEMENT_SECTION + "." + "timeout", MOVEMENT_DEFAULT_TIME_OUT);
|
||||
|
||||
//Save out to the file
|
||||
try
|
||||
{
|
||||
|
@ -373,6 +411,9 @@ public class ConfigFacade
|
|||
}
|
||||
}
|
||||
|
||||
CONFIG_STORE.setProperty(MOVEMENT_SECTION + "." + "frequency", MOVEMENT_FREQ);
|
||||
CONFIG_STORE.setProperty(MOVEMENT_SECTION + "." + "timeout", MOVEMENT_TIME_OUT);
|
||||
|
||||
//Save to the file
|
||||
try
|
||||
{
|
||||
|
@ -430,6 +471,17 @@ public class ConfigFacade
|
|||
|
||||
//Iterate over the imported object, saving the file's config values to the map
|
||||
Set<String> configSections = CONFIG_STORE.getSections();
|
||||
if(configSections.remove(MOVEMENT_SECTION))
|
||||
{
|
||||
SubnodeConfiguration fixtureConfiguration = CONFIG_STORE.getSection(MOVEMENT_SECTION);
|
||||
MOVEMENT_FREQ = fixtureConfiguration.getInt("frequency");
|
||||
MOVEMENT_TIME_OUT = fixtureConfiguration.getDouble("timeout");
|
||||
}
|
||||
else
|
||||
{
|
||||
MOVEMENT_FREQ = MOVEMENT_DEFAULT_FREQ;
|
||||
MOVEMENT_TIME_OUT = MOVEMENT_DEFAULT_TIME_OUT;
|
||||
}
|
||||
for(String sectionName : configSections)
|
||||
{
|
||||
Map<ConfigProperties,Double> savedSection = new HashMap<>();
|
||||
|
@ -482,4 +534,7 @@ public class ConfigFacade
|
|||
* @return true if loaded successfully, otherwise false
|
||||
*/
|
||||
public static boolean loadConfig() { return loadConfig(configFileLocation); }
|
||||
|
||||
public enum FixtureValues
|
||||
{ FREQUENCY, TIMEOUT; }
|
||||
}
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
package org.baxter.disco.ocr;
|
||||
|
||||
//Standard imports
|
||||
import java.util.concurrent.locks.Lock;
|
||||
|
||||
//Pi4J imports
|
||||
import com.pi4j.Pi4J;
|
||||
import com.pi4j.context.Context;
|
||||
|
@ -23,7 +20,7 @@ import com.pi4j.io.pwm.PwmType;
|
|||
* Currently missing Run switch compatibility.
|
||||
*
|
||||
* @author Blizzard Finnegan
|
||||
* @version 2.3.0, 27 Feb. 2023
|
||||
* @version 2.4.0, 01 Mar. 2023
|
||||
*/
|
||||
public class MovementFacade
|
||||
{
|
||||
|
@ -34,51 +31,71 @@ public class MovementFacade
|
|||
* @param LOCK A Lock object, used for interactions with
|
||||
* the physical lock switch on the fixture.
|
||||
*/
|
||||
public MovementFacade(Lock LOCK)
|
||||
{
|
||||
//ErrorLogging.logError("DEBUG: Starting lock thread...");
|
||||
runSwitchThread = new Thread(() ->
|
||||
{
|
||||
boolean unlock = false;
|
||||
while(!exit)
|
||||
{
|
||||
if(runSwitch.isOn())
|
||||
{
|
||||
ErrorLogging.logError("Run switch turned off!");
|
||||
while(!LOCK.tryLock())
|
||||
{}
|
||||
unlock = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
//ErrorLogging.logError("Run switch on!");
|
||||
if(unlock)
|
||||
{ LOCK.unlock(); unlock = false; }
|
||||
}
|
||||
//try{ Thread.sleep(100); } catch(Exception e) { ErrorLogging.logError(e); }
|
||||
}
|
||||
}, "Run switch monitor.");
|
||||
runSwitchThread.start();
|
||||
//ErrorLogging.logError("DEBUG: Lock thread started!");
|
||||
}
|
||||
//public MovementFacade(Lock LOCK)
|
||||
//{
|
||||
// //ErrorLogging.logError("DEBUG: Starting lock thread...");
|
||||
// runSwitchThread = new Thread(() ->
|
||||
// {
|
||||
// boolean unlock = false;
|
||||
// while(!exit)
|
||||
// {
|
||||
// if(runSwitch.isOn())
|
||||
// {
|
||||
// ErrorLogging.logError("Run switch turned off!");
|
||||
// while(!LOCK.tryLock())
|
||||
// {}
|
||||
// unlock = true;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// //ErrorLogging.logError("Run switch on!");
|
||||
// if(unlock)
|
||||
// { LOCK.unlock(); unlock = false; }
|
||||
// }
|
||||
// //try{ Thread.sleep(100); } catch(Exception e) { ErrorLogging.logError(e); }
|
||||
// }
|
||||
// }, "Run switch monitor.");
|
||||
// runSwitchThread.start();
|
||||
// //ErrorLogging.logError("DEBUG: Lock thread started!");
|
||||
//}
|
||||
|
||||
private static Thread runSwitchThread;
|
||||
|
||||
/**
|
||||
* Internal PWM Frequency
|
||||
*/
|
||||
private static int FREQUENCY;
|
||||
|
||||
/**
|
||||
* Conversion factor for freq to FREQUENCY
|
||||
*/
|
||||
private static final int FREQUENCY_UNITS = 1000;
|
||||
|
||||
/**
|
||||
* Max allowable frequency by current fixture design.
|
||||
*/
|
||||
private static final int MAX_FREQUENCY = 175000;
|
||||
|
||||
/**
|
||||
* Minimum allowed frequency; also used for reset travels.
|
||||
*/
|
||||
private static final int MIN_FREQUENCY = 25000;
|
||||
|
||||
//Externally Available Variables
|
||||
/**
|
||||
* PWM Frequency
|
||||
* Human-readable frequency
|
||||
*/
|
||||
private static int FREQUENCY = 75000;
|
||||
private static double freq;
|
||||
|
||||
/**
|
||||
* PWM Duty Cycle
|
||||
*/
|
||||
private static int DUTY_CYCLE = 50;
|
||||
private static final int DUTY_CYCLE = 50;
|
||||
|
||||
/**
|
||||
* Number of seconds to wait before timing out a fixture movement.
|
||||
*/
|
||||
private static double TIME_OUT = 2.5;
|
||||
private static double TIME_OUT;
|
||||
|
||||
//PWM Addresses
|
||||
//All addresses are in BCM format.
|
||||
|
@ -121,7 +138,7 @@ public class MovementFacade
|
|||
/**
|
||||
* How many milliseconds to wait before polling the GPIO
|
||||
*/
|
||||
private static final int POLL_WAIT = 20;
|
||||
private static final int POLL_WAIT = 10;
|
||||
|
||||
/**
|
||||
* How many times to poll the GPIO during a movement call.
|
||||
|
@ -192,6 +209,35 @@ public class MovementFacade
|
|||
|
||||
static
|
||||
{
|
||||
FREQUENCY = (int)ConfigFacade.getFixtureValue(ConfigFacade.FixtureValues.FREQUENCY);
|
||||
freq = FREQUENCY / FREQUENCY_UNITS;
|
||||
TIME_OUT = (int)ConfigFacade.getFixtureValue(ConfigFacade.FixtureValues.TIMEOUT);
|
||||
|
||||
//ErrorLogging.logError("DEBUG: Starting lock thread...");
|
||||
runSwitchThread = new Thread(() ->
|
||||
{
|
||||
boolean unlock = false;
|
||||
while(!exit)
|
||||
{
|
||||
if(runSwitch.isOn())
|
||||
{
|
||||
ErrorLogging.logError("Run switch turned off!");
|
||||
while(!Cli.LOCK.tryLock())
|
||||
{}
|
||||
unlock = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
//ErrorLogging.logError("Run switch on!");
|
||||
if(unlock)
|
||||
{ Cli.LOCK.unlock(); unlock = false; }
|
||||
}
|
||||
//try{ Thread.sleep(100); } catch(Exception e) { ErrorLogging.logError(e); }
|
||||
}
|
||||
}, "Run switch monitor.");
|
||||
runSwitchThread.start();
|
||||
//ErrorLogging.logError("DEBUG: Lock thread started!");
|
||||
|
||||
//Initialise Pi4J
|
||||
pi4j = Pi4J.newAutoContext();
|
||||
|
||||
|
@ -306,35 +352,12 @@ public class MovementFacade
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for the fixture's PWM duty cycle.
|
||||
*
|
||||
* @param newDutyCycle The new duty cycle to be set by the user.
|
||||
*
|
||||
* @return True if the value was set successfully; otherwise false.
|
||||
*/
|
||||
public boolean setDutyCycle(int newDutyCycle)
|
||||
{
|
||||
boolean output = false;
|
||||
if(newDutyCycle < 0)
|
||||
{
|
||||
ErrorLogging.logError("Movement error!!! - Invalid DutyCycle input.");
|
||||
}
|
||||
else
|
||||
{
|
||||
DUTY_CYCLE = newDutyCycle;
|
||||
pwm.on(DUTY_CYCLE, FREQUENCY);
|
||||
output = true;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter for the fixture's PWM duty cycle.
|
||||
*
|
||||
* @return The current DutyCycle.
|
||||
*/
|
||||
public int getDutyCycle() { return DUTY_CYCLE; }
|
||||
public static int getDutyCycle() { return DUTY_CYCLE; }
|
||||
|
||||
/**
|
||||
* Setter for the fixture's time to give up on a movement.
|
||||
|
@ -343,7 +366,7 @@ public class MovementFacade
|
|||
*
|
||||
* @return True if the value was set successfully; otherwise false.
|
||||
*/
|
||||
public boolean setTimeout(double newTimeout)
|
||||
public static boolean setTimeout(double newTimeout)
|
||||
{
|
||||
boolean output = false;
|
||||
if(newTimeout < 0)
|
||||
|
@ -353,6 +376,7 @@ public class MovementFacade
|
|||
else
|
||||
{
|
||||
TIME_OUT = newTimeout;
|
||||
ConfigFacade.setFixtureValue(ConfigFacade.FixtureValues.TIMEOUT, newTimeout);
|
||||
POLL_COUNT = TIME_OUT * ( 1000 / POLL_WAIT );
|
||||
output = true;
|
||||
}
|
||||
|
@ -364,7 +388,7 @@ public class MovementFacade
|
|||
*
|
||||
* @return The current timeout.
|
||||
*/
|
||||
public double getTimeout() { return TIME_OUT; }
|
||||
public static double getTimeout() { return TIME_OUT; }
|
||||
|
||||
/**
|
||||
* Setter for the fixture's PWM frequency.
|
||||
|
@ -373,16 +397,22 @@ public class MovementFacade
|
|||
*
|
||||
* @return True if the value was set successfully; otherwise false.
|
||||
*/
|
||||
public boolean setFrequency(int newFrequency)
|
||||
public static boolean setFrequency(double newFrequency)
|
||||
{
|
||||
boolean output = false;
|
||||
if(newFrequency < 0)
|
||||
{
|
||||
ErrorLogging.logError("Movement error!!! - Invalid frequency input.");
|
||||
ErrorLogging.logError("Movement error! - Invalid frequency input.");
|
||||
}
|
||||
else if(newFrequency > MAX_FREQUENCY)
|
||||
{
|
||||
ErrorLogging.logError("Movement warning!!! - Value above maximum allowed.");
|
||||
}
|
||||
else
|
||||
{
|
||||
FREQUENCY = newFrequency;
|
||||
freq = newFrequency;
|
||||
ConfigFacade.setFixtureValue(ConfigFacade.FixtureValues.FREQUENCY, newFrequency);
|
||||
FREQUENCY = (int)(freq * FREQUENCY_UNITS);
|
||||
pwm.on(DUTY_CYCLE, FREQUENCY);
|
||||
output = true;
|
||||
}
|
||||
|
@ -390,24 +420,33 @@ public class MovementFacade
|
|||
}
|
||||
|
||||
/**
|
||||
* Getter for the fixture's PWM frequency.
|
||||
* Getter for the fixture's PWM frequency, in hertz.
|
||||
*
|
||||
* @return The current PWM frequency.
|
||||
*/
|
||||
public int getFrequency() { return FREQUENCY; }
|
||||
public static int getFrequency() { return FREQUENCY; }
|
||||
|
||||
/**
|
||||
* Getter for the fixture's PWM frequency, in KHz.
|
||||
*
|
||||
* @return The current PWM frequency.
|
||||
*/
|
||||
public static double getUserFrequency() { return freq; }
|
||||
|
||||
/**
|
||||
* Internal function to send the fixture to a given limit switch.
|
||||
*
|
||||
* Detects if the limit switch is active before activating motor.
|
||||
*
|
||||
* Motor slows down after timeout is halfway through, to protect hardware.
|
||||
*
|
||||
* @param moveUp Whether to send the fixture up or down. (True = up, False = down)
|
||||
* @param timeout How long (in seconds) to wait before timing out.
|
||||
* @return true if movement was successful; otherwise false
|
||||
*/
|
||||
private boolean gotoLimit(boolean moveUp, double timeout)
|
||||
private static FinalState gotoLimit(boolean moveUp, double timeout)
|
||||
{
|
||||
boolean output = false;
|
||||
FinalState output = FinalState.FAILED;
|
||||
DigitalInput limitSense;
|
||||
if(moveUp)
|
||||
{
|
||||
|
@ -422,32 +461,50 @@ public class MovementFacade
|
|||
ErrorLogging.logError("DEBUG: Sending fixture down...");
|
||||
}
|
||||
|
||||
double mostlyThere = (POLL_COUNT * 2) / 3;
|
||||
if(limitSense.isHigh()) return FinalState.SAFE;
|
||||
|
||||
double mostlyThere = (POLL_COUNT * 1) / 2;
|
||||
int slowerSpeed = FREQUENCY / 4;
|
||||
|
||||
motorEnable.on();
|
||||
for(int i = 0; i < (POLL_COUNT);i++)
|
||||
{
|
||||
try{ Thread.sleep(POLL_WAIT); } catch(Exception e) {ErrorLogging.logError(e);};
|
||||
output = limitSense.isHigh();
|
||||
if(output) break;
|
||||
try{ Thread.sleep(POLL_WAIT); }
|
||||
catch(Exception e) {ErrorLogging.logError(e);};
|
||||
|
||||
if(limitSense.isHigh())
|
||||
{
|
||||
output = ( (i >= mostlyThere) ? FinalState.SAFE : FinalState.UNSAFE);
|
||||
break;
|
||||
}
|
||||
else if(i >= mostlyThere)
|
||||
{ pwm.on(DUTY_CYCLE, slowerSpeed); continue; }
|
||||
{
|
||||
pwm.on(DUTY_CYCLE, slowerSpeed);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if(output == false)
|
||||
|
||||
if(output == FinalState.FAILED)
|
||||
ErrorLogging.logError("FIXTURE MOVEMENT ERROR! - Motor movement timed out!");
|
||||
motorEnable.off();
|
||||
pwm.on(DUTY_CYCLE, FREQUENCY);
|
||||
return output;
|
||||
}
|
||||
|
||||
public static void reset()
|
||||
{
|
||||
pwm.on(DUTY_CYCLE, MIN_FREQUENCY);
|
||||
goUp(Double.POSITIVE_INFINITY);
|
||||
pwm.on(DUTY_CYCLE, FREQUENCY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the fixture to the lower limit switch.
|
||||
*
|
||||
* @param timeout How long (in seconds) to wait before timing out.
|
||||
* @return true if movement was successful; otherwise false
|
||||
*/
|
||||
public boolean goDown(double timeout) { return gotoLimit(false, timeout); }
|
||||
public static FinalState goDown(double timeout) { return gotoLimit(false, timeout); }
|
||||
|
||||
/**
|
||||
* Send the fixture to the upper limit switch.
|
||||
|
@ -455,7 +512,7 @@ public class MovementFacade
|
|||
* @param timeout How long (in seconds) to wait before timing out.
|
||||
* @return true if movement was successful; otherwise false
|
||||
*/
|
||||
public boolean goUp(double timeout) { return gotoLimit(true, timeout); }
|
||||
public static FinalState goUp(double timeout) { return gotoLimit(true, timeout); }
|
||||
|
||||
/**
|
||||
* Send the fixture to the lower limit switch.
|
||||
|
@ -463,7 +520,7 @@ public class MovementFacade
|
|||
*
|
||||
* @return true if movement was successful; otherwise false
|
||||
*/
|
||||
public boolean goDown() { return goDown(TIME_OUT); }
|
||||
public static FinalState goDown() { return goDown(TIME_OUT); }
|
||||
|
||||
/**
|
||||
* Send the fixture to the upper limit switch.
|
||||
|
@ -471,12 +528,12 @@ public class MovementFacade
|
|||
*
|
||||
* @return true if movement was successful; otherwise false
|
||||
*/
|
||||
public boolean goUp() { return goUp(TIME_OUT); }
|
||||
public static FinalState goUp() { return goUp(TIME_OUT); }
|
||||
|
||||
/**
|
||||
* Extends the piston for 1 second, pushing the button on the DUT.
|
||||
*/
|
||||
public void pressButton()
|
||||
public static void pressButton()
|
||||
{
|
||||
ErrorLogging.logError("DEBUG: Pressing button...");
|
||||
pistonActivate.on();
|
||||
|
@ -488,9 +545,9 @@ public class MovementFacade
|
|||
/**
|
||||
* Closes connections to all GPIO pins.
|
||||
*/
|
||||
public void closeGPIO()
|
||||
public static void closeGPIO()
|
||||
{
|
||||
goUp();
|
||||
reset();
|
||||
if(runSwitchThread.isAlive())
|
||||
{
|
||||
exit = true;
|
||||
|
@ -499,34 +556,20 @@ public class MovementFacade
|
|||
pi4j.shutdown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests all available motions of the fixture.
|
||||
*
|
||||
* @return True if all movements worked properly; otherwise False
|
||||
*/
|
||||
public boolean testMotions()
|
||||
{
|
||||
boolean output = goUp();
|
||||
if(!output) return output;
|
||||
pressButton();
|
||||
output = goDown();
|
||||
if(!output) return output;
|
||||
pressButton();
|
||||
output = goUp();
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to move the fixture once for an iteration.
|
||||
*
|
||||
* @param prime Whether or not to wake up the DUT
|
||||
*/
|
||||
public void iterationMovement(boolean prime)
|
||||
public static void iterationMovement(boolean prime)
|
||||
{
|
||||
goUp();
|
||||
//if(prime) pressButton();
|
||||
if(prime) pressButton();
|
||||
goDown();
|
||||
try{ Thread.sleep(100); } catch(Exception e){ ErrorLogging.logError(e); }
|
||||
pressButton();
|
||||
}
|
||||
|
||||
public enum FinalState
|
||||
{ UNSAFE, SAFE, FAILED; }
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ import org.bytedeco.tesseract.TessBaseAPI;
|
|||
* information for this specific testing aparatus.
|
||||
*
|
||||
* @author Blizzard Finnegan
|
||||
* @version 2.2.0, 27 Feb. 2023
|
||||
* @version 2.2.1, 27 Feb. 2023
|
||||
*/
|
||||
public class TesseractFacade
|
||||
{
|
||||
|
@ -30,21 +30,35 @@ public class TesseractFacade
|
|||
*/
|
||||
private static TessBaseAPI api;
|
||||
|
||||
/**
|
||||
* OCR engine mode.
|
||||
*
|
||||
* From https://ai-facets.org/tesseract-ocr-best-practices/:
|
||||
* 0: Legacy engine only
|
||||
* 1: Neural nets Long Short-Term Memory (LSTM) engine only. This form of neural network has feedback, as well as feedforward within the design, allowing the neural network to learn from itself.
|
||||
* 2: Legacy + LSTM engines
|
||||
* 3: Default, based on what is available
|
||||
*
|
||||
* As I didn't write the training data, and don't actually know what kind of network the training set requires, this value is set to default.
|
||||
*/
|
||||
private static final int OCR_ENGINE_MODE = 3;
|
||||
|
||||
/**
|
||||
* OCR language name, or training data filename.
|
||||
*/
|
||||
private static final String OCR_LANGUAGE = "Pro6_temp_test";
|
||||
|
||||
/**
|
||||
* Location on the file system that the OCR languages are stored.
|
||||
*
|
||||
* This value requires that the folder "tessdata" be in the same location as your current working directory.
|
||||
*/
|
||||
private static final String OCR_LANGUAGE_LOCATION = "tessdata";
|
||||
|
||||
static
|
||||
{
|
||||
//Initialise the Tesseract API
|
||||
api = new TessBaseAPI();
|
||||
|
||||
//Magic number below.
|
||||
//The seemingly random 3 in the following line
|
||||
//is used to define the OCR Engine mode.
|
||||
//This mode autoselects the OCR Engine, based on
|
||||
//available hardware.
|
||||
//This line also sets the location of the language
|
||||
//files, and declares the language as "Pro6_temp_test".
|
||||
//Considering changing this to be more understandable,
|
||||
//but potential consequences are unclear.
|
||||
api.Init("tessdata", "Pro6_temp_test", 3);
|
||||
api.Init(OCR_LANGUAGE_LOCATION, OCR_LANGUAGE, OCR_ENGINE_MODE);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue