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:
Blizzard Finnegan 2023-03-01 15:55:34 -05:00
parent 1feb66674d
commit 19977655c4
No known key found for this signature in database
GPG key ID: DE547EDF547DDA49
7 changed files with 300 additions and 213 deletions

View file

@ -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>

View file

@ -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>

View file

@ -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

View file

@ -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.");

View file

@ -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; }
}

View file

@ -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; }
}

View file

@ -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);
}
/**