Compare commits
4 Commits
b07e8c7dab
...
v1.1.0-rc1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9af6015aa3 | ||
|
|
9c2ac97158 | ||
|
|
e6934cdb18 | ||
|
|
636e1252dc |
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "weevil"
|
name = "weevil"
|
||||||
version = "1.1.0-beta.2"
|
version = "1.1.0-rc1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["Eric Ratliff <eric@nxlearn.net>"]
|
authors = ["Eric Ratliff <eric@nxlearn.net>"]
|
||||||
description = "FTC robotics project generator - bores into complexity, emerges with clean code"
|
description = "FTC robotics project generator - bores into complexity, emerges with clean code"
|
||||||
|
|||||||
@@ -513,7 +513,7 @@ class BasicTest {
|
|||||||
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
|
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
|
||||||
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||||
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
|
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
|
||||||
<option name="INTERPRETER_PATH" value="cmd.exe" />
|
<option name="INTERPRETER_PATH" value="C:\Windows\System32\cmd.exe" />
|
||||||
<option name="INTERPRETER_OPTIONS" value="/c" />
|
<option name="INTERPRETER_OPTIONS" value="/c" />
|
||||||
<option name="EXECUTE_IN_TERMINAL" value="true" />
|
<option name="EXECUTE_IN_TERMINAL" value="true" />
|
||||||
<option name="EXECUTE_SCRIPT_FILE" value="true" />
|
<option name="EXECUTE_SCRIPT_FILE" value="true" />
|
||||||
@@ -561,7 +561,7 @@ class BasicTest {
|
|||||||
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
|
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
|
||||||
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||||
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
|
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
|
||||||
<option name="INTERPRETER_PATH" value="cmd.exe" />
|
<option name="INTERPRETER_PATH" value="C:\Windows\System32\cmd.exe" />
|
||||||
<option name="INTERPRETER_OPTIONS" value="/c" />
|
<option name="INTERPRETER_OPTIONS" value="/c" />
|
||||||
<option name="EXECUTE_IN_TERMINAL" value="true" />
|
<option name="EXECUTE_IN_TERMINAL" value="true" />
|
||||||
<option name="EXECUTE_SCRIPT_FILE" value="true" />
|
<option name="EXECUTE_SCRIPT_FILE" value="true" />
|
||||||
@@ -609,7 +609,7 @@ class BasicTest {
|
|||||||
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
|
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
|
||||||
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||||
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
|
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
|
||||||
<option name="INTERPRETER_PATH" value="cmd.exe" />
|
<option name="INTERPRETER_PATH" value="C:\Windows\System32\cmd.exe" />
|
||||||
<option name="INTERPRETER_OPTIONS" value="/c" />
|
<option name="INTERPRETER_OPTIONS" value="/c" />
|
||||||
<option name="EXECUTE_IN_TERMINAL" value="true" />
|
<option name="EXECUTE_IN_TERMINAL" value="true" />
|
||||||
<option name="EXECUTE_SCRIPT_FILE" value="true" />
|
<option name="EXECUTE_SCRIPT_FILE" value="true" />
|
||||||
@@ -657,7 +657,7 @@ class BasicTest {
|
|||||||
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
|
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
|
||||||
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||||
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
|
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
|
||||||
<option name="INTERPRETER_PATH" value="cmd.exe" />
|
<option name="INTERPRETER_PATH" value="C:\Windows\System32\cmd.exe" />
|
||||||
<option name="INTERPRETER_OPTIONS" value="/c" />
|
<option name="INTERPRETER_OPTIONS" value="/c" />
|
||||||
<option name="EXECUTE_IN_TERMINAL" value="true" />
|
<option name="EXECUTE_IN_TERMINAL" value="true" />
|
||||||
<option name="EXECUTE_SCRIPT_FILE" value="true" />
|
<option name="EXECUTE_SCRIPT_FILE" value="true" />
|
||||||
@@ -705,7 +705,7 @@ class BasicTest {
|
|||||||
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
|
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
|
||||||
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
|
||||||
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
|
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
|
||||||
<option name="INTERPRETER_PATH" value="cmd.exe" />
|
<option name="INTERPRETER_PATH" value="C:\Windows\System32\cmd.exe" />
|
||||||
<option name="INTERPRETER_OPTIONS" value="/c" />
|
<option name="INTERPRETER_OPTIONS" value="/c" />
|
||||||
<option name="EXECUTE_IN_TERMINAL" value="true" />
|
<option name="EXECUTE_IN_TERMINAL" value="true" />
|
||||||
<option name="EXECUTE_SCRIPT_FILE" value="true" />
|
<option name="EXECUTE_SCRIPT_FILE" value="true" />
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use colored::*;
|
|||||||
// Embed template directories at compile time
|
// Embed template directories at compile time
|
||||||
static BASIC_TEMPLATE: Dir = include_dir!("$CARGO_MANIFEST_DIR/templates/basic");
|
static BASIC_TEMPLATE: Dir = include_dir!("$CARGO_MANIFEST_DIR/templates/basic");
|
||||||
static TESTING_TEMPLATE: Dir = include_dir!("$CARGO_MANIFEST_DIR/templates/testing");
|
static TESTING_TEMPLATE: Dir = include_dir!("$CARGO_MANIFEST_DIR/templates/testing");
|
||||||
|
static LOCALIZATION_TEMPLATE: Dir = include_dir!("$CARGO_MANIFEST_DIR/templates/localization");
|
||||||
|
|
||||||
pub struct TemplateManager {
|
pub struct TemplateManager {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@@ -39,13 +40,14 @@ impl TemplateManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn template_exists(&self, name: &str) -> bool {
|
pub fn template_exists(&self, name: &str) -> bool {
|
||||||
matches!(name, "basic" | "testing")
|
matches!(name, "basic" | "testing" | "localization")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn list_templates(&self) -> Vec<String> {
|
pub fn list_templates(&self) -> Vec<String> {
|
||||||
vec![
|
vec![
|
||||||
" basic - Minimal FTC project (default)".to_string(),
|
" basic - Minimal FTC project (default)".to_string(),
|
||||||
" testing - Testing showcase with examples".to_string(),
|
" testing - Testing showcase with examples".to_string(),
|
||||||
|
" localization - Grid-based positioning with sensor fusion".to_string(),
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,6 +70,14 @@ impl TemplateManager {
|
|||||||
test_count: 45,
|
test_count: 45,
|
||||||
is_default: false,
|
is_default: false,
|
||||||
},
|
},
|
||||||
|
TemplateInfo {
|
||||||
|
name: "localization".to_string(),
|
||||||
|
description: "Grid-based robot localization with sensor fusion".to_string(),
|
||||||
|
file_count: 21,
|
||||||
|
line_count: 1500,
|
||||||
|
test_count: 3,
|
||||||
|
is_default: false,
|
||||||
|
},
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,6 +100,14 @@ impl TemplateManager {
|
|||||||
test_count: 45,
|
test_count: 45,
|
||||||
is_default: false,
|
is_default: false,
|
||||||
},
|
},
|
||||||
|
"localization" => TemplateInfo {
|
||||||
|
name: "localization".to_string(),
|
||||||
|
description: "Grid-based robot localization system with multi-sensor fusion and fault tolerance.".to_string(),
|
||||||
|
file_count: 21,
|
||||||
|
line_count: 1500,
|
||||||
|
test_count: 3,
|
||||||
|
is_default: false,
|
||||||
|
},
|
||||||
_ => bail!("Unknown template: {}", name),
|
_ => bail!("Unknown template: {}", name),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -109,6 +127,16 @@ impl TemplateManager {
|
|||||||
println!();
|
println!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if info.name == "localization" {
|
||||||
|
println!("{}", "Features:".bright_white().bold());
|
||||||
|
println!(" • 12x12 field grid system (12-inch cells)");
|
||||||
|
println!(" • Multi-sensor fusion (encoders + IMU + vision)");
|
||||||
|
println!(" • Fault-tolerant positioning (graceful degradation)");
|
||||||
|
println!(" • Kalman-filter-style sensor fusion");
|
||||||
|
println!(" • Professional robotics patterns");
|
||||||
|
println!();
|
||||||
|
}
|
||||||
|
|
||||||
println!("{}", "Files included:".bright_white().bold());
|
println!("{}", "Files included:".bright_white().bold());
|
||||||
println!(" {} files", info.file_count);
|
println!(" {} files", info.file_count);
|
||||||
println!(" ~{} lines of code", info.line_count);
|
println!(" ~{} lines of code", info.line_count);
|
||||||
@@ -132,6 +160,7 @@ impl TemplateManager {
|
|||||||
let template_dir = match template_name {
|
let template_dir = match template_name {
|
||||||
"basic" => &BASIC_TEMPLATE,
|
"basic" => &BASIC_TEMPLATE,
|
||||||
"testing" => &TESTING_TEMPLATE,
|
"testing" => &TESTING_TEMPLATE,
|
||||||
|
"localization" => &LOCALIZATION_TEMPLATE,
|
||||||
_ => bail!("Unknown template: {}", template_name),
|
_ => bail!("Unknown template: {}", template_name),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -233,6 +262,7 @@ mod tests {
|
|||||||
let mgr = TemplateManager::new().unwrap();
|
let mgr = TemplateManager::new().unwrap();
|
||||||
assert!(mgr.template_exists("basic"));
|
assert!(mgr.template_exists("basic"));
|
||||||
assert!(mgr.template_exists("testing"));
|
assert!(mgr.template_exists("testing"));
|
||||||
|
assert!(mgr.template_exists("localization"));
|
||||||
assert!(!mgr.template_exists("nonexistent"));
|
assert!(!mgr.template_exists("nonexistent"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,6 +270,19 @@ mod tests {
|
|||||||
fn test_list_templates() {
|
fn test_list_templates() {
|
||||||
let mgr = TemplateManager::new().unwrap();
|
let mgr = TemplateManager::new().unwrap();
|
||||||
let templates = mgr.list_templates();
|
let templates = mgr.list_templates();
|
||||||
assert_eq!(templates.len(), 2);
|
assert_eq!(templates.len(), 3);
|
||||||
|
assert!(templates[0].contains("basic"));
|
||||||
|
assert!(templates[1].contains("testing"));
|
||||||
|
assert!(templates[2].contains("localization"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_template_info_all() {
|
||||||
|
let mgr = TemplateManager::new().unwrap();
|
||||||
|
let infos = mgr.get_template_info_all().unwrap();
|
||||||
|
assert_eq!(infos.len(), 3);
|
||||||
|
assert_eq!(infos[0].name, "basic");
|
||||||
|
assert_eq!(infos[1].name, "testing");
|
||||||
|
assert_eq!(infos[2].name, "localization");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -11,7 +11,7 @@ Grid-based robot localization with sensor fusion and fault tolerance.
|
|||||||
- **Grid System** - 12x12 field grid (12" cells)
|
- **Grid System** - 12x12 field grid (12" cells)
|
||||||
- **Sensor Fusion** - Combine encoders, IMU, vision
|
- **Sensor Fusion** - Combine encoders, IMU, vision
|
||||||
- **Fault Tolerance** - Graceful sensor failure handling
|
- **Fault Tolerance** - Graceful sensor failure handling
|
||||||
- **20+ Tests** - All passing
|
- **3 Tests** - All passing
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
9 . . . . . . . . . . . .
|
9 . . . . . . . . . . . .
|
||||||
8 . . . . . . . . . . . .
|
8 . . . . . . . . . . . .
|
||||||
7 . . . . . . . . . . . .
|
7 . . . . . . . . . . . .
|
||||||
6 . . . . . . X . . . . .
|
6 . . . . . X . . . . . .
|
||||||
5 . . . . . . . . . . . .
|
5 . . . . . . . . . . . .
|
||||||
4 . . . . . . . . . . . .
|
4 . . . . . . . . . . . .
|
||||||
3 . . . . . . . . . . . .
|
3 . . . . . . . . . . . .
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
rootProject.name = '{{PROJECT_NAME}}'
|
|
||||||
17
templates/localization/settings.gradle.kts.template
Normal file
17
templates/localization/settings.gradle.kts.template
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
pluginManagement {
|
||||||
|
repositories {
|
||||||
|
google()
|
||||||
|
mavenCentral()
|
||||||
|
gradlePluginPortal()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencyResolutionManagement {
|
||||||
|
repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
google()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rootProject.name = "{{PROJECT_NAME}}"
|
||||||
@@ -4,4 +4,5 @@ public interface GyroSensor {
|
|||||||
double getHeading();
|
double getHeading();
|
||||||
boolean isConnected();
|
boolean isConnected();
|
||||||
void calibrate();
|
void calibrate();
|
||||||
|
boolean isCalibrated();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,14 +13,17 @@ public class ImuLocalizer {
|
|||||||
|
|
||||||
public void calibrate(double initialHeading) {
|
public void calibrate(double initialHeading) {
|
||||||
if (gyro.isConnected()) {
|
if (gyro.isConnected()) {
|
||||||
|
gyro.calibrate();
|
||||||
this.headingOffset = initialHeading - gyro.getHeading();
|
this.headingOffset = initialHeading - gyro.getHeading();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Double getHeading() {
|
public Double getHeading() {
|
||||||
if (!gyro.isConnected()) return null;
|
if (!gyro.isConnected() || !gyro.isCalibrated()) return null;
|
||||||
return Pose2D.normalizeAngle(gyro.getHeading() + headingOffset);
|
return Pose2D.normalizeAngle(gyro.getHeading() + headingOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isWorking() { return gyro.isConnected(); }
|
public boolean isWorking() {
|
||||||
|
return gyro.isConnected() && gyro.isCalibrated();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,14 @@ package robot.hardware;
|
|||||||
public class MockGyroSensor implements GyroSensor {
|
public class MockGyroSensor implements GyroSensor {
|
||||||
private double heading = 0;
|
private double heading = 0;
|
||||||
private boolean connected = true;
|
private boolean connected = true;
|
||||||
|
private boolean calibrated = true;
|
||||||
|
|
||||||
public double getHeading() { return heading; }
|
public double getHeading() { return heading; }
|
||||||
public boolean isConnected() { return connected; }
|
public boolean isConnected() { return connected; }
|
||||||
public void calibrate() { }
|
public void calibrate() { calibrated = true; }
|
||||||
|
public boolean isCalibrated() { return calibrated; }
|
||||||
|
|
||||||
public void setHeading(double h) { heading = h; }
|
public void setHeading(double h) { heading = h; }
|
||||||
public void setConnected(boolean c) { connected = c; }
|
public void setConnected(boolean c) { connected = c; }
|
||||||
|
public void setCalibrated(boolean c) { calibrated = c; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,11 +34,15 @@ class SensorFusionTest {
|
|||||||
MockGyroSensor gyro = new MockGyroSensor();
|
MockGyroSensor gyro = new MockGyroSensor();
|
||||||
MockVisionCamera camera = new MockVisionCamera();
|
MockVisionCamera camera = new MockVisionCamera();
|
||||||
|
|
||||||
|
// Set a pose so vision is actually "working" (not just connected)
|
||||||
|
camera.setPose(new Pose2D(12, 12, 0));
|
||||||
|
|
||||||
OdometryTracker odometry = new OdometryTracker(left, right);
|
OdometryTracker odometry = new OdometryTracker(left, right);
|
||||||
ImuLocalizer imu = new ImuLocalizer(gyro);
|
ImuLocalizer imu = new ImuLocalizer(gyro);
|
||||||
VisionLocalizer vision = new VisionLocalizer(camera);
|
VisionLocalizer vision = new VisionLocalizer(camera);
|
||||||
|
|
||||||
RobotLocalizer localizer = new RobotLocalizer(odometry, imu, vision);
|
RobotLocalizer localizer = new RobotLocalizer(odometry, imu, vision);
|
||||||
|
localizer.update(); // Need to update to actually use vision
|
||||||
|
|
||||||
assertEquals(1.0, localizer.getConfidence(), 0.01);
|
assertEquals(1.0, localizer.getConfidence(), 0.01);
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
// Intentionally hardcoded. When you bump the version in Cargo.toml,
|
// Intentionally hardcoded. When you bump the version in Cargo.toml,
|
||||||
// tests will fail here until you update this to match.
|
// tests will fail here until you update this to match.
|
||||||
pub const EXPECTED_VERSION: &str = "1.1.0-beta.2";
|
pub const EXPECTED_VERSION: &str = "1.1.0-rc1";
|
||||||
@@ -37,7 +37,7 @@ fn test_list_templates() {
|
|||||||
let mgr = TemplateManager::new().unwrap();
|
let mgr = TemplateManager::new().unwrap();
|
||||||
let templates = mgr.list_templates();
|
let templates = mgr.list_templates();
|
||||||
|
|
||||||
assert_eq!(templates.len(), 2, "Should have exactly 2 templates");
|
assert_eq!(templates.len(), 3, "Should have exactly 3 templates");
|
||||||
assert!(templates.iter().any(|t| t.contains("basic")), "Should list basic template");
|
assert!(templates.iter().any(|t| t.contains("basic")), "Should list basic template");
|
||||||
assert!(templates.iter().any(|t| t.contains("testing")), "Should list testing template");
|
assert!(templates.iter().any(|t| t.contains("testing")), "Should list testing template");
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user